Skip to content

Add Time Exercise: 41_time_delay (#83) - #159

Merged
kawpii merged 3 commits into
zhravan:mainfrom
sidharth-chauhan:feat/time_exercises
Oct 25, 2025
Merged

Add Time Exercise: 41_time_delay (#83)#159
kawpii merged 3 commits into
zhravan:mainfrom
sidharth-chauhan:feat/time_exercises

Conversation

@sidharth-chauhan

@sidharth-chauhan sidharth-chauhan commented Oct 19, 2025

Copy link
Copy Markdown
Contributor

Summary

Added a new concept exercise for the time package.

Changes

  • Introduced 41_time_delay under templates/ and solutions/.
  • Updated catalog.yaml with metadata and hints.

Concept

Covers key time concepts:

  • Using time.Sleep() for delays.
  • Using channels to send signals after timeouts.
  • Working with time.Duration for millisecond-based waits.

Closes #83

Summary by CodeRabbit

  • New Features
    • Added a new time-delay exercise with starter templates and a sample implementation demonstrating relative/absolute waits, notifications, timeout-aware waits, elapsed-time checks, and scheduled callbacks.
  • Tests
    • Added comprehensive tests covering wait timing, notify semantics, wait-until/notify-at behavior, timeout handling, elapsed-time validation, and scheduled callback completion.
  • Chores
    • Updated metadata for an existing exercise and applied a minor hint formatting fix.

@coderabbitai

coderabbitai Bot commented Oct 19, 2025

Copy link
Copy Markdown

Caution

Review failed

The pull request is closed.

Walkthrough

Adds a new exercise "41_time_delay": catalog entry, solution implementation with multiple time-based utilities, template stubs, and tests covering waits, notifications, timeouts, elapsed calculation, and scheduling.

Changes

Cohort / File(s) Summary
Catalog Entry
internal/exercises/catalog.yaml
Adds 41_time_delay metadata (title, difficulty, topics, test_regex, hints) and updates 42_wait_group with difficulty and topics.
Solution Implementation
internal/exercises/solutions/41_time_delay/time_delay.go
Adds package timedelay with concrete implementations: WaitFor, NotifyAfter, WaitUntil, NotifyAt, ElapsedMillis, WaitForOrTimeout, and ScheduleAfter (uses real time, goroutines, polling loops, channels, and timeout error handling).
Template & Tests
internal/exercises/templates/41_time_delay/time_delay.go, internal/exercises/templates/41_time_delay/time_delay_test.go
Adds template stubs for timedelay functions (TODOs) and comprehensive tests validating durations, notify channels, elapsed calculation, timeout behavior, and scheduled callback completion.

Sequence Diagram(s)

sequenceDiagram
    participant Caller
    participant Timedelay as timedelay
    participant BG as background goroutine
    participant Time as time pkg
    participant Chan as channel

    Note over Caller,Timedelay: Async notifier flow (NotifyAfter / NotifyAt)
    Caller->>Timedelay: NotifyAfter(ms) / NotifyAt(target)
    Timedelay->>Chan: create buffered channel
    Timedelay->>Caller: return Chan
    Timedelay->>BG: spawn goroutine
    alt target in future
        BG->>Time: poll / Sleep(short)
        Time-->>BG: tick
        BG->>Chan: send true or close
    else target in past
        BG-->>Chan: send immediately
    end

    Note over Caller,Timedelay: Synchronous wait flow (WaitFor / WaitUntil)
    Caller->>Timedelay: WaitFor(ms) / WaitUntil(target)
    Timedelay->>Time: check target / Sleep until reached
    Time-->>Timedelay: elapsed
    Timedelay-->>Caller: return (or error for timeout)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Pay attention to timing tolerances and test flakiness in internal/exercises/templates/41_time_delay/time_delay_test.go.
  • Review error handling and race conditions around channels and goroutines in internal/exercises/solutions/41_time_delay/time_delay.go.

Suggested labels

beginners

Suggested reviewers

  • zhravan

Poem

🐰 I waited, I watched, then nudged the springy bell,
A channel chimed softly when moments chose to tell.
Goroutines hummed, the schedule blinked on cue,
Time tucked its secrets — now exercises bloom anew.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 70.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title "Add Time Exercise: 41_time_delay (#83)" clearly and concisely describes the primary change in the changeset. It directly references the exercise being added with its specific slug identifier and links to the related issue, making it immediately clear what the changeset accomplishes. The title is specific enough for a developer reviewing history to understand the main purpose without ambiguity, avoiding vague or generic phrasing.
Linked Issues Check ✅ Passed The pull request meets all acceptance criteria from linked issue #83. The changes include template functions in internal/exercises/templates/41_time_delay/time_delay.go with comprehensive tests in time_delay_test.go, an updated internal/exercises/catalog.yaml with the new exercise entry including hints, and a complete solution file. All four acceptance criteria are satisfied: templates are added under the correct directory structure, tests are provided, the catalog is updated, and hints are included in the metadata.
Out of Scope Changes Check ✅ Passed All changes in the pull request are directly related to adding the new 41_time_delay exercise as specified in issue #83. The catalog update, template functions, and test coverage all serve the primary objective of introducing this new timing-related exercise. The inclusion of a solution file in internal/exercises/solutions/41_time_delay/time_delay.go goes beyond the explicit acceptance criteria but represents a complementary addition to the exercise structure and appears intentional based on the PR description, so it does not constitute out-of-scope unrelated work.

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 58d51e7 and bfcb42f.

📒 Files selected for processing (1)
  • internal/exercises/catalog.yaml (2 hunks)

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@sidharth-chauhan

Copy link
Copy Markdown
Contributor Author

@kaushalyap, please take a look at this PR and add the Hacktoberfest label.
Let me know if there are any improvements or feedback needed.

@kawpii kawpii added hacktoberfest Hacktoberfest participation hacktoberfest-accepted hacktoberfest2025 patch Bug fixes and small improvements labels Oct 20, 2025

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (4)
internal/exercises/solutions/41_time_delay/time_delay.go (4)

10-31: Recommend using stdlib timing facilities instead of busy-wait loops.

WaitFor and NotifyAfter use inefficient busy-wait loops with 1ms sleeps. For an exercise teaching the time package, consider demonstrating the idiomatic stdlib approaches:

  • WaitFor can simply call time.Sleep(time.Duration(ms) * time.Millisecond) directly
  • NotifyAfter can use time.After(time.Duration(ms) * time.Millisecond) or time.NewTimer()

The current implementations consume unnecessary CPU cycles and miss the opportunity to teach proper Go timing patterns.

Option 1: Direct stdlib usage

 func WaitFor(ms int) {
-	target := time.Now().Add(time.Duration(ms) * time.Millisecond)
-	for time.Now().Before(target) {
-		time.Sleep(1 * time.Millisecond)
-	}
+	time.Sleep(time.Duration(ms) * time.Millisecond)
 }

 func NotifyAfter(ms int) chan bool {
-	ch := make(chan bool, 1)
-	target := time.Now().Add(time.Duration(ms) * time.Millisecond)
-
-	go func() {
-		for time.Now().Before(target) {
-			time.Sleep(1 * time.Millisecond)
-		}
-		ch <- true
-	}()
-
-	return ch
+	ch := make(chan bool, 1)
+	go func() {
+		time.Sleep(time.Duration(ms) * time.Millisecond)
+		ch <- true
+	}()
+	return ch
 }

Option 2: Using time.After (even more idiomatic for NotifyAfter)

If the exercise goal is to teach channel-based timing, NotifyAfter could directly return time.After(), though note it returns <-chan time.Time instead of chan bool.


33-52: Use time.Until() to eliminate busy-wait loops.

WaitUntil and NotifyAt use the same inefficient busy-wait pattern. For absolute time targets, use time.Until(target) to calculate the duration and sleep once:

 func WaitUntil(target time.Time) {
-	for time.Now().Before(target) {
-		time.Sleep(1 * time.Millisecond)
+	if duration := time.Until(target); duration > 0 {
+		time.Sleep(duration)
 	}
 }

 func NotifyAt(target time.Time) chan bool {
 	ch := make(chan bool, 1)
-
 	go func() {
-		for time.Now().Before(target) {
-			time.Sleep(1 * time.Millisecond)
+		if duration := time.Until(target); duration > 0 {
+			time.Sleep(duration)
 		}
 		ch <- true
 	}()
-
 	return ch
 }

59-74: Consider using select with time.After for idiomatic timeout handling.

The busy-wait loop works but doesn't demonstrate Go's preferred timeout patterns. Consider using select with channels or context.WithTimeout:

 func WaitForOrTimeout(ms int, timeoutMs int) error {
-	target := time.Now().Add(time.Duration(ms) * time.Millisecond)
-	deadline := time.Now().Add(time.Duration(timeoutMs) * time.Millisecond)
-
-	for {
-		now := time.Now()
-		if !now.Before(target) {
-			return nil
-		}
-		if !now.Before(deadline) {
-			return errors.New("timeout exceeded before target reached")
-		}
-		time.Sleep(1 * time.Millisecond)
+	if ms > timeoutMs {
+		return errors.New("timeout exceeded before target reached")
+	}
+	
+	select {
+	case <-time.After(time.Duration(ms) * time.Millisecond):
+		return nil
+	case <-time.After(time.Duration(timeoutMs) * time.Millisecond):
+		return errors.New("timeout exceeded before target reached")
 	}
 }

Note: The logic above is simplified. If timeoutMs > ms, both timers fire but select picks arbitrarily when both are ready simultaneously. A more robust implementation would use a timeout timer and a wait timer, or check ms > timeoutMs upfront.


76-90: Use time.AfterFunc or direct time.Sleep for scheduling.

The busy-wait loop in ScheduleAfter can be replaced with Go's built-in scheduling facilities:

Option 1: Using time.AfterFunc

 func ScheduleAfter(ms int, fn func()) chan struct{} {
 	done := make(chan struct{})
-	target := time.Now().Add(time.Duration(ms) * time.Millisecond)
-
-	go func() {
-		for time.Now().Before(target) {
-			time.Sleep(1 * time.Millisecond)
-		}
-		fn()
-		close(done)
-	}()
-
+	time.AfterFunc(time.Duration(ms)*time.Millisecond, func() {
+		fn()
+		close(done)
+	})
 	return done
 }

Option 2: Direct sleep (simpler, keeps explicit goroutine)

 func ScheduleAfter(ms int, fn func()) chan struct{} {
 	done := make(chan struct{})
-	target := time.Now().Add(time.Duration(ms) * time.Millisecond)
-
 	go func() {
-		for time.Now().Before(target) {
-			time.Sleep(1 * time.Millisecond)
-		}
+		time.Sleep(time.Duration(ms) * time.Millisecond)
 		fn()
 		close(done)
 	}()
-
 	return done
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7fb505f and 58d51e7.

📒 Files selected for processing (3)
  • internal/exercises/solutions/41_time_delay/time_delay.go (1 hunks)
  • internal/exercises/templates/41_time_delay/time_delay.go (1 hunks)
  • internal/exercises/templates/41_time_delay/time_delay_test.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/exercises/templates/41_time_delay/time_delay.go
🧰 Additional context used
🧬 Code graph analysis (1)
internal/exercises/templates/41_time_delay/time_delay_test.go (1)
internal/exercises/solutions/41_time_delay/time_delay.go (7)
  • WaitFor (11-16)
  • NotifyAfter (19-31)
  • WaitUntil (34-38)
  • NotifyAt (41-52)
  • ElapsedMillis (55-57)
  • WaitForOrTimeout (60-74)
  • ScheduleAfter (77-90)
🪛 GitHub Actions: CI
internal/exercises/templates/41_time_delay/time_delay_test.go

[error] 12-12: vet: undefined: WaitFor

🔇 Additional comments (1)
internal/exercises/solutions/41_time_delay/time_delay.go (1)

54-57: LGTM!

This is a clean wrapper around time.Since() that appropriately demonstrates duration conversion to milliseconds.

Comment thread internal/exercises/templates/41_time_delay/time_delay_test.go
@sidharth-chauhan

Copy link
Copy Markdown
Contributor Author

@kaushalyap, Let me know if there are any improvements or feedback needed.

@kawpii

kawpii commented Oct 21, 2025

Copy link
Copy Markdown
Collaborator

Please update catalog.yaml too.

@sidharth-chauhan

sidharth-chauhan commented Oct 21, 2025

Copy link
Copy Markdown
Contributor Author

@kaushalyap done updating the catalog

@sidharth-chauhan

Copy link
Copy Markdown
Contributor Author

@zhravan can you please review it

Signed-off-by: Sidharth Chauhan <chauhansiddharth71@gmail.com>
@kawpii
kawpii merged commit c3eb861 into zhravan:main Oct 25, 2025
1 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

hacktoberfest Hacktoberfest participation hacktoberfest2025 hacktoberfest-accepted patch Bug fixes and small improvements

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Concept] Time - add exercise templates

2 participants