Skip to content

05 - Feat/sorting exercise - #96

Closed
kawpii wants to merge 6 commits into
zhravan:mainfrom
kawpii:feat/sorting-exercise
Closed

05 - Feat/sorting exercise#96
kawpii wants to merge 6 commits into
zhravan:mainfrom
kawpii:feat/sorting-exercise

Conversation

@kawpii

@kawpii kawpii commented Oct 1, 2025

Copy link
Copy Markdown
Collaborator

Summary

Adds sorting exercises for sorting slices. Contain two cases for both number and string slices, to show sorting strings (multiple characters) also possible, not just numbers.

Checklist

  • Tests pass: make verify or golearn verify <slug> (CLI does not work as said in an issue, but tested locally using go test.)
  • Docs updated (README/CONTRIBUTING) if needed
  • No large new dependencies

Screenshots / Output (if CLI UX)

Paste before/after where helpful.

Related issues

Fixes #72

Summary by CodeRabbit

  • New Features
    • Added five new concept exercises to the catalog: Defer, Go Routines, Channels, Mutexes, and Sorting.
    • Each exercise includes starter templates and reference solutions to practice deferred cleanup, concurrency, synchronization, channel messaging, and basic sorting.
  • Tests
    • Introduced unit tests for the new exercises to verify expected behavior and provide guidance during learning.

@coderabbitai

coderabbitai Bot commented Oct 1, 2025

Copy link
Copy Markdown

Warning

Rate limit exceeded

@kaushalyap has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 27 minutes and 55 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 3d54262 and 439f692.

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

Walkthrough

Adds five new concept entries (defer, go routines, channels, mutexes, sorting) to the catalog and introduces corresponding solution and template implementations with tests for each. New code covers file handling with defer, goroutines, channels messaging, mutex-protected counting, and slice sorting.

Changes

Cohort / File(s) Summary
Catalog updates
internal/exercises/catalog.yaml
Added five concept entries: 28_defer, 29_go_routines, 30_channels, 31_mutexes, 32_sorting with titles, test_regex, and hints.
Solutions: Defer, Go Routines, Channels, Mutexes, Sorting
internal/exercises/solutions/28_defer/defer.go, internal/exercises/solutions/29_go_routines/go_routines.go, internal/exercises/solutions/30_channels/channels.go, internal/exercises/solutions/31_mutexes/mutexes.go, internal/exercises/solutions/32_sorting/sorting.go
Added working reference solutions: deferred file close and write; launching goroutines; channel send/receive returning "Hi!"; mutex-protected 10k counter; sorting int and string slices.
Templates: Defer
internal/exercises/templates/28_defer/defer.go, internal/exercises/templates/28_defer/defer_test.go
Stubbed CreateFile singleton and WriteToFile with TODO; test expects file to be closed before subsequent write.
Templates: Go Routines
internal/exercises/templates/29_go_routines/go_routines.go, internal/exercises/templates/29_go_routines/go_routines_test.go
Stub calls sleeps sequentially with TODO to run concurrently; test/benchmark expect fast concurrent behavior.
Templates: Channels
internal/exercises/templates/30_channels/channels.go, internal/exercises/templates/30_channels/channels_test.go
Stub spawns goroutine printing "Hi!" and returns ""; test expects ReadMessage to return "Hi!" via channel.
Templates: Mutexes
internal/exercises/templates/31_mutexes/mutexes.go, internal/exercises/templates/31_mutexes/mutexes_test.go
Stub increments shared counter across 10k goroutines without sync; test expects result to equal numWorkers.
Templates: Sorting
internal/exercises/templates/32_sorting/sorting.go, internal/exercises/templates/32_sorting/sorting_test.go
Public slices Years and Pets with stub sort functions; tests verify slices are sorted using slices.IsSorted.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor T as Test
  participant C as channels.ReadMessage
  participant G as Goroutine(Sender)
  Note over C: Create channel
  T->>C: ReadMessage()
  activate C
  C->>G: start goroutine
  activate G
  G-->>C: send "Hi!" on chan
  deactivate G
  C-->>T: return "Hi!"
  deactivate C
Loading
sequenceDiagram
  autonumber
  actor T as Test
  participant M as mutexes.Counting
  participant W as Workers (x10k)
  T->>M: Counting()
  activate M
  Note over M: Initialize counter, mutex, wg
  M->>W: start workers
  rect rgba(200,230,255,0.3)
    loop for each worker
      W->>M: lock, counter++, unlock
      W-->>M: done
    end
  end
  M-->>T: return final count
  deactivate M
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

patch

Poem

I nibble code like clover leaves,
Goroutines hop and channels weave,
A mutex guards my carrot stash,
With sorted greens in tidy cache,
I write, defer, then softly close—
“Hi!” squeaks through, as logic grows. 🥕🐇

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings, 1 inconclusive)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning This pull request includes out-of-scope additions unrelated to issue #72, such as new catalog entries, solutions, and templates for defer, go_routines, channels, and mutexes, which extend beyond the sorting exercise requirements. Please remove or isolate the changes unrelated to the sorting exercise and submit them in separate pull requests, keeping this PR focused solely on the sorting exercise templates, tests, and catalog update.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title Check ❓ Inconclusive The title “05 - Feat/sorting exercise” uses a numeric prefix and unconventional formatting that does not clearly convey the main changes made in this pull request and includes a generic “Feat” label and slash which add noise without summarizing the added sorting exercise templates, tests, and catalog entry in a concise manner. Please rename the title to directly describe the change, for example “Add sorting exercise templates, tests, and catalog entry,” which clearly summarizes the main enhancements without numbering or ambiguous labels.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues Check ✅ Passed All acceptance criteria from issue #72 are satisfied: the sorting templates and tests under internal/exercises/templates/32_sorting are present, the catalog entry for sorting was added with hints, and no additional dependencies were introduced.

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.

@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: 7

🧹 Nitpick comments (8)
internal/exercises/templates/28_defer/utils.go (1)

8-23: Singleton pattern correctly implemented, but consider exposing cleanup.

The sync.Once usage ensures thread-safe singleton initialization. However, the package exposes no cleanup mechanism to close fileInstance. While this is an exercise template where students will practice defer, consider adding a CloseFile() helper or documenting that callers must manage cleanup.

Optional: Add a cleanup helper for completeness:

+// CloseFile closes the singleton file if it exists
+func CloseFile() error {
+	if fileInstance != nil {
+		return fileInstance.Close()
+	}
+	return nil
+}
internal/exercises/templates/32_sorting/sorting_test.go (1)

8-22: Consider test isolation to avoid shared state issues.

Both subtests mutate the global Years and Pets slices. If tests run in different orders or are expanded, this shared state could cause flaky results.

Consider resetting state between tests:

 func TestSorting(t *testing.T) {
 	t.Run("Sorting numbers", func(t *testing.T) {
+		Years = []int{2017, 2003, 2026}
 		SortYears()
 		if !slices.IsSorted(Years) {
-			t.Fatal("Strings are not sorted!")
+			t.Fatal("Numbers are not sorted!")
 		}
 	})
 
 	t.Run("Sorting strings", func(t *testing.T) {
+		Pets = []string{"Dog", "Cat", "Parrot"}
 		SortPets()
 		if !slices.IsSorted(Pets) {
 			t.Fatal("Strings are not sorted!")
 		}
 	})
 }
internal/exercises/solutions/28_defer/defer.go (1)

10-11: Consider handling errors from Close() and Fprintln().

Both f.Close() and fmt.Fprintln() can return errors that are currently ignored. For teaching purposes, demonstrating proper error handling would be valuable:

 func WriteToFile(*os.File) {
     f := CreateFile()
-    defer f.Close()
-    fmt.Fprintln(f, "data")
+    defer func() {
+        if err := f.Close(); err != nil {
+            // Handle or log error
+        }
+    }()
+    if _, err := fmt.Fprintln(f, "data"); err != nil {
+        // Handle error
+    }
 }

Alternatively, if this is intentionally simplified for the exercise, the current approach is acceptable.

internal/exercises/templates/29_go_routines/utils.go (1)

5-11: Fix naming inconsistency: singular vs plural "Millisecond".

The first function uses singular "Millisecond" while the second uses plural "Milliseconds". For consistency, both should use the same form.

-func sleepForOneHundredMillisecond() {
+func sleepForOneHundredMilliseconds() {
     time.Sleep(100 * time.Millisecond)
 }
internal/exercises/solutions/29_go_routines/go_routines.go (1)

7-13: Naming inconsistency: "Millisecond" vs "Milliseconds".

Same issue as in the templates/utils.go file - the first function uses singular "Millisecond" while the second uses plural "Milliseconds".

-func sleepForOneHundredMillisecond() {
+func sleepForOneHundredMilliseconds() {
     time.Sleep(100 * time.Millisecond)
 }
internal/exercises/solutions/30_channels/channels.go (1)

3-13: LGTM! Consider simplifying the return statement.

The channel implementation is correct. The unbuffered channel properly synchronizes the goroutine and main routine, and there are no goroutine leaks.

For slightly more idiomatic Go, you could simplify lines 10-12:

 func ReadMessage() string {
 	chat := make(chan string)
 	go func() {
 		senderMessage := "Hi!"
 		chat <- senderMessage
 	}()
 
-	msg := <-chat
-
-	return msg
+	return <-chat
 }
internal/exercises/solutions/31_mutexes/mutexes.go (1)

7-31: Consider simplifying synchronization for pedagogical clarity.

The code correctly uses a mutex to protect the shared counter, which is the primary learning objective. However, it demonstrates two redundant synchronization mechanisms:

  1. WaitGroup (lines 11, 15, 17, 29) - tracks goroutine completion
  2. Buffered done channel (lines 10, 21, 25-27) - also tracks completion

Either mechanism alone is sufficient. For a mutex-focused exercise, the extra synchronization complexity may distract learners from the core concept.

Simplify by removing the redundant channel:

 func Counting() int {
 
 	count := 0
-	done := make(chan bool, numWorkers)
 	var wg sync.WaitGroup
 	var mu sync.Mutex
 
 	for i := 1; i <= numWorkers; i++ {
 		wg.Add(1)
 		go func() {
 			defer wg.Done()
 			mu.Lock()
 			count += 1
 			mu.Unlock()
-			done <- true
 		}()
 	}
 
-	for i := 1; i <= numWorkers; i++ {
-		<-done
-	}
-
 	wg.Wait()
 	return count
 }

Alternatively, if you want to teach channels for synchronization, remove the WaitGroup and keep the channel. However, WaitGroup is more conventional for this pattern.

internal/exercises/templates/31_mutexes/mutexes.go (1)

10-26: Consider simplifying the synchronization pattern.

The function uses both a sync.WaitGroup and a buffered channel for coordination, which is redundant. Idiomatic Go typically uses one or the other:

  • Option 1 (simpler): Use only WaitGroup.Wait() and remove the channel entirely.
  • Option 2: Use only the channel and remove the WaitGroup.

Option 1: Use only WaitGroup (simpler and more common):

 func Counting() int {
 
 	count := 0
-	done := make(chan bool, numWorkers)
 	var wg sync.WaitGroup
 
 	for i := 1; i <= numWorkers; i++ {
 		wg.Add(1)
 		go func() {
 			defer wg.Done()
 			count += 1
-			done <- true
 		}()
 	}
 
-	for i := 1; i <= numWorkers; i++ {
-		<-done
-	}
-
 	wg.Wait()
 	return count
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b99da22 and a0eab8e.

📒 Files selected for processing (18)
  • internal/exercises/catalog.yaml (1 hunks)
  • internal/exercises/solutions/28_defer/defer.go (1 hunks)
  • internal/exercises/solutions/29_go_routines/go_routines.go (1 hunks)
  • internal/exercises/solutions/30_channels/channels.go (1 hunks)
  • internal/exercises/solutions/31_mutexes/mutexes.go (1 hunks)
  • internal/exercises/solutions/32_sorting/sorting.go (1 hunks)
  • internal/exercises/templates/28_defer/defer.go (1 hunks)
  • internal/exercises/templates/28_defer/defer_test.go (1 hunks)
  • internal/exercises/templates/28_defer/utils.go (1 hunks)
  • internal/exercises/templates/29_go_routines/go_routines.go (1 hunks)
  • internal/exercises/templates/29_go_routines/go_routines_test.go (1 hunks)
  • internal/exercises/templates/29_go_routines/utils.go (1 hunks)
  • internal/exercises/templates/30_channels/channels.go (1 hunks)
  • internal/exercises/templates/30_channels/channels_test.go (1 hunks)
  • internal/exercises/templates/31_mutexes/mutexes.go (1 hunks)
  • internal/exercises/templates/31_mutexes/mutexes_test.go (1 hunks)
  • internal/exercises/templates/32_sorting/sorting.go (1 hunks)
  • internal/exercises/templates/32_sorting/sorting_test.go (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (15)
internal/exercises/templates/29_go_routines/go_routines_test.go (2)
internal/exercises/solutions/29_go_routines/go_routines.go (1)
  • RunConcurrently (15-26)
internal/exercises/templates/29_go_routines/go_routines.go (1)
  • RunConcurrently (3-6)
internal/exercises/templates/29_go_routines/go_routines.go (1)
internal/exercises/solutions/29_go_routines/go_routines.go (1)
  • RunConcurrently (15-26)
internal/exercises/templates/32_sorting/sorting.go (1)
internal/exercises/solutions/32_sorting/sorting.go (4)
  • SortYears (10-13)
  • Years (7-7)
  • SortPets (15-18)
  • Pets (8-8)
internal/exercises/templates/28_defer/defer_test.go (3)
internal/exercises/templates/28_defer/utils.go (1)
  • CreateFile (14-23)
internal/exercises/solutions/28_defer/defer.go (1)
  • WriteToFile (8-12)
internal/exercises/templates/28_defer/defer.go (1)
  • WriteToFile (8-11)
internal/exercises/templates/31_mutexes/mutexes.go (1)
internal/exercises/solutions/31_mutexes/mutexes.go (1)
  • Counting (7-31)
internal/exercises/templates/32_sorting/sorting_test.go (1)
internal/exercises/templates/32_sorting/sorting.go (4)
  • SortYears (6-8)
  • Years (3-3)
  • SortPets (10-12)
  • Pets (4-4)
internal/exercises/solutions/31_mutexes/mutexes.go (1)
internal/exercises/templates/31_mutexes/mutexes.go (1)
  • Counting (7-28)
internal/exercises/templates/28_defer/defer.go (2)
internal/exercises/solutions/28_defer/defer.go (1)
  • WriteToFile (8-12)
internal/exercises/templates/28_defer/utils.go (1)
  • CreateFile (14-23)
internal/exercises/templates/31_mutexes/mutexes_test.go (2)
internal/exercises/solutions/31_mutexes/mutexes.go (1)
  • Counting (7-31)
internal/exercises/templates/31_mutexes/mutexes.go (1)
  • Counting (7-28)
internal/exercises/solutions/28_defer/defer.go (2)
internal/exercises/templates/28_defer/defer.go (1)
  • WriteToFile (8-11)
internal/exercises/templates/28_defer/utils.go (1)
  • CreateFile (14-23)
internal/exercises/templates/30_channels/channels_test.go (2)
internal/exercises/solutions/30_channels/channels.go (1)
  • ReadMessage (3-13)
internal/exercises/templates/30_channels/channels.go (1)
  • ReadMessage (5-15)
internal/exercises/solutions/30_channels/channels.go (1)
internal/exercises/templates/30_channels/channels.go (1)
  • ReadMessage (5-15)
internal/exercises/solutions/29_go_routines/go_routines.go (1)
internal/exercises/templates/29_go_routines/go_routines.go (1)
  • RunConcurrently (3-6)
internal/exercises/templates/30_channels/channels.go (1)
internal/exercises/solutions/30_channels/channels.go (1)
  • ReadMessage (3-13)
internal/exercises/solutions/32_sorting/sorting.go (1)
internal/exercises/templates/32_sorting/sorting.go (4)
  • SortYears (6-8)
  • Years (3-3)
  • SortPets (10-12)
  • Pets (4-4)
🔇 Additional comments (13)
internal/exercises/templates/32_sorting/sorting.go (1)

1-12: Exercise template correctly structured.

The template provides the correct scaffold for students to implement sorting. Functions currently return unsorted slices, which is the expected starting point. Students should add slices.Sort() calls to complete the exercise, as shown in the solution file.

internal/exercises/solutions/32_sorting/sorting.go (1)

7-18: Solution implementation is correct.

The solution properly uses slices.Sort for in-place sorting of both integer and string slices. The in-place modification of global variables is consistent with the exercise template design.

internal/exercises/templates/30_channels/channels_test.go (1)

5-12: Test correctly validates channel communication.

The test straightforwardly verifies that ReadMessage() returns the expected message sent via channel. The assertion is clear and the error message is descriptive.

internal/exercises/templates/28_defer/defer_test.go (1)

9-10: Potential test isolation issue with singleton CreateFile().

CreateFile() uses sync.Once to return a singleton file instance. If tests run in parallel or if multiple tests call CreateFile(), they'll all share the same file handle, which could lead to:

  • Race conditions on file operations
  • Tests interfering with each other
  • Flaky test behavior

Verify that:

  1. Tests in this package don't run in parallel, or
  2. The singleton pattern is intentional for the exercise's teaching purpose
internal/exercises/templates/31_mutexes/mutexes_test.go (1)

5-12: Test may be flaky on template version due to race condition.

The test expects Counting() to return exactly numWorkers. However, the template implementation (shown in snippets) increments count without mutex protection, creating a race condition.

With 10,000 workers, the race will likely manifest as count < numWorkers, but the test could sporadically pass if lucky timing aligns. This non-determinism may confuse learners.

Consider adding a comment in the test or exercise description explaining:

  1. The template version demonstrates a race condition
  2. The test is expected to fail (or produce inconsistent results) until learners add mutex protection
  3. Running with -race flag will definitively catch the issue

Alternatively, verify this is the intended teaching approach.

internal/exercises/templates/29_go_routines/go_routines.go (1)

3-6: LGTM! Template is correctly structured for learning.

This template provides appropriate starter code for learners to practice adding the go keyword. The sequential implementation is intentional.

Note: Ensure the test file (go_routines_test.go) correctly validates concurrent execution—there are critical issues with the test logic flagged separately.

internal/exercises/templates/30_channels/channels.go (1)

5-15: LGTM! Template appropriately scaffolds the learning exercise.

The starter code provides clear structure with a helpful TODO comment. Learners will naturally eliminate the fmt.Println side effect when they implement the channel communication.

Note: There's a potential race condition where the program might return before "Hi!" is printed, but this will be resolved when learners complete the exercise by using a channel for synchronization.

internal/exercises/templates/28_defer/defer.go (2)

8-11: The missing defer statement is the intended learning exercise.

As an exercise template, the absence of defer f.Close() is intentional—learners should add it based on the catalog hint. The template structure is appropriate for teaching deferred execution.


8-11: Remove the unused parameter from the function signature.

The *os.File parameter is declared but never used. The function creates its own file via CreateFile() and ignores the parameter entirely. This creates confusion about the function's intent.

Apply this diff to remove the unused parameter:

-func WriteToFile(*os.File) {
+func WriteToFile() {
 	f := CreateFile()
 	fmt.Fprintln(f, "ABC")
 }

Note: If this signature is required by a test interface, consider naming the parameter with _ to explicitly mark it as unused: func WriteToFile(_ *os.File).

Likely an incorrect or invalid review comment.

internal/exercises/templates/31_mutexes/mutexes.go (1)

17-17: The missing mutex protection is the intended learning exercise.

The unsynchronized increment count += 1 creates a race condition when multiple goroutines execute concurrently. This is intentional—learners should add mutex protection as indicated by the catalog hint. The template structure correctly demonstrates the problem that mutexes solve.

internal/exercises/catalog.yaml (3)

137-161: Verify the scope alignment with PR objectives.

The PR description states "Adds sorting exercises for sorting slices" (issue #72), but this catalog update introduces five new concepts (defer, go_routines, channels, mutexes, sorting). Confirm whether:

  1. All five concepts are intentionally part of this PR, or
  2. Only the sorting exercise should be included and the others were added inadvertently.

If all five are intentional, consider updating the PR description to reflect the broader scope.


137-161: The new concept entries and hints are well-structured.

The five new concept entries (28_defer through 32_sorting) follow the established catalog pattern consistently. Each includes appropriate titles, test patterns, and helpful hints that guide learners effectively.


164-213: Project renumbering to 100s range improves organization.

Moving projects from the 28-36 range to 101-109 creates clear separation between concepts and projects, making the catalog structure more maintainable and allowing room for additional concepts.

Comment thread internal/exercises/catalog.yaml Outdated
Comment on lines +8 to +12
func WriteToFile(*os.File) {
f := CreateFile()
defer f.Close()
fmt.Fprintln(f, "data")
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Remove unused parameter or use it instead of calling CreateFile().

The function signature accepts *os.File but ignores it, calling CreateFile() instead. This is confusing and creates an inconsistent API.

Consider one of these approaches:

  1. Remove the parameter if CreateFile() should always be used:

    func WriteToFile() {
        f := CreateFile()
        defer f.Close()
        fmt.Fprintln(f, "data")
    }
  2. Use the parameter if the file should be provided by the caller:

    func WriteToFile(f *os.File) {
        defer f.Close()
        fmt.Fprintln(f, "data")
    }

The second approach is more flexible and matches the function signature, but verify that the test expectations align with the chosen design.

🤖 Prompt for AI Agents
internal/exercises/solutions/28_defer/defer.go lines 8-12: the function
signature declares a *os.File parameter but ignores it and always calls
CreateFile(); update the implementation to use the provided parameter instead of
calling CreateFile() — remove the CreateFile() call, defer closing the passed-in
file (defer f.Close()), and write to that file with fmt.Fprintln(f, "data");
ensure tests expecting the caller to supply the file still pass.

Comment on lines +15 to +26
func RunConcurrently() {
go sleepForOneHundredMillisecond()
go sleepForTwoHundredMilliseconds()

// Alternatively : Anonymous Goroutines
// go func() {
// sleepForOneHundredMillisecond()
// }()
// go func() {
// sleepForTwoHundredMilliseconds()
// }()
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical: Goroutines launched without synchronization.

Lines 16-17 launch goroutines with go keyword but RunConcurrently() returns immediately without waiting for them to complete. This means:

  1. The goroutines may not execute before the program exits
  2. Tests calling this function may have flaky results
  3. The behavior is non-deterministic

For a solution/reference implementation, proper synchronization is essential:

+import (
+    "sync"
+    "time"
+)
+
 func RunConcurrently() {
-    go sleepForOneHundredMillisecond()
-    go sleepForTwoHundredMilliseconds()
+    var wg sync.WaitGroup
+    wg.Add(2)
+    
+    go func() {
+        defer wg.Done()
+        sleepForOneHundredMillisecond()
+    }()
+    go func() {
+        defer wg.Done()
+        sleepForTwoHundredMilliseconds()
+    }()
+    
+    wg.Wait()
-
-    // Alternatively : Anonymous Goroutines
-    // go func() {
-    //     sleepForOneHundredMillisecond()
-    // }()
-    // go func() {
-    //     sleepForTwoHundredMilliseconds()
-    // }()
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func RunConcurrently() {
go sleepForOneHundredMillisecond()
go sleepForTwoHundredMilliseconds()
// Alternatively : Anonymous Goroutines
// go func() {
// sleepForOneHundredMillisecond()
// }()
// go func() {
// sleepForTwoHundredMilliseconds()
// }()
}
import (
"sync"
"time"
)
func RunConcurrently() {
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
sleepForOneHundredMillisecond()
}()
go func() {
defer wg.Done()
sleepForTwoHundredMilliseconds()
}()
wg.Wait()
}
🤖 Prompt for AI Agents
In internal/exercises/solutions/29_go_routines/go_routines.go around lines 15 to
26, the function launches two goroutines and returns immediately, causing
non-deterministic behavior and flaky tests; fix by synchronizing: create a
sync.WaitGroup, Add(2) before starting goroutines, have each goroutine call
Done() when finished, and call wg.Wait() at the end of RunConcurrently to block
until both goroutines complete (alternatively use a two-value channel and block
reading two results) so the function only returns after both sleep routines
finish.

Comment on lines +8 to +17
func TestClosingFileAfterWriting(t *testing.T) {
f := CreateFile()
WriteToFile(f)

// Trying to write to the file after closing
_, err := fmt.Fprintln(f, "123")
if err == nil {
t.Fatal("File wasn't closed!")
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

Test relies on confusing API where WriteToFile ignores its parameter.

The test passes a file handle to WriteToFile(f) on line 10, but as noted in the solution file review, WriteToFile ignores this parameter and calls CreateFile() internally. This makes the test's intent unclear.

Additionally, since CreateFile() uses a singleton pattern (sync.Once), this test depends on:

  1. Being the first/only test to run, or
  2. WriteToFile closing the singleton file instance

Consider clarifying the API:

  • If WriteToFile should accept and use the file parameter, update the implementation
  • If WriteToFile should always use CreateFile(), remove the parameter from both the function signature and this test call

🏁 Script executed:

#!/bin/bash
# Locate WriteToFile and CreateFile implementations
rg -n --type=go 'func WriteToFile' -g '*.go'
rg -n --type=go 'func CreateFile' -g '*.go'

Length of output: 234


🏁 Script executed:

#!/bin/bash
# Extract WriteToFile implementation
sed -n '1,50p' internal/exercises/templates/28_defer/defer.go | sed -n '1,50p'
# Extract CreateFile and sync.Once usage
sed -n '1,200p' internal/exercises/templates/28_defer/utils.go | sed -n '1,200p'

Length of output: 424


Clarify WriteToFile API to use or remove its *os.File parameter

  • WriteToFile (internal/exercises/templates/28_defer/defer.go:8) ignores its *os.File argument and unconditionally calls CreateFile(), so the test’s call on line 10 is misleading.
  • CreateFile() uses a singleton (sync.Once), making the test’s behavior dependent on execution order or implicit closing.
    Choose one:
    • Have WriteToFile write to the passed-in file, or
    • Drop the file parameter (and its use in the test) to reflect its singleton design.
🤖 Prompt for AI Agents
In internal/exercises/templates/28_defer/defer_test.go around lines 8 to 17 the
test calls WriteToFile(f) but WriteToFile currently ignores its *os.File
parameter and instead calls CreateFile() (which itself is a singleton via
sync.Once), making the test misleading and order-dependent; fix by choosing one
of two options and applying matching changes: either (A) make WriteToFile
actually write to the provided *os.File (remove its internal CreateFile call)
and keep the test as-is so it creates a file, passes it to WriteToFile, then
closes it and asserts writes fail; or (B) remove the *os.File parameter from
WriteToFile and update the test to call WriteToFile() (no arg) to reflect the
singleton CreateFile usage; in either case update the function signature,
implementation, and the test call sites consistently and ensure file closing
behavior in the test remains correct.

Comment on lines +8 to +18
func TestGoRoutines(t *testing.T) {
start := time.Now()
RunConcurrently()
elapsed := time.Since(start)

// according to benchmark it is about ~1.5 microseconds
// x2 for more breathing room, so 3
if elapsed.Microseconds() >= 3 {
t.Fatal("Go routines was not used!")
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Test logic is critically flawed and will produce false positives.

The test has multiple issues:

  1. Incorrect timing threshold: The test expects concurrent execution to complete in <3 microseconds, but goroutines that sleep for 100ms and 200ms will take at minimum ~200ms (the duration of the longer sleep) when properly concurrent. The test will pass for the wrong reason—if RunConcurrently() spawns goroutines without waiting for them (as the template version does), the function returns immediately (~microseconds), and the test passes even though the work isn't complete.

  2. Missing synchronization: The template's RunConcurrently() (lines 3-6 of go_routines.go) runs sequentially. The solution spawns goroutines but doesn't wait for them to complete. A correct concurrent implementation needs a time.Sleep() or sync.WaitGroup to ensure goroutines finish before returning.

  3. Inverted expectation: Sequential execution should take ~300ms (100ms + 200ms). Concurrent should take ~200ms (max of the two). The test should verify elapsed < sequentialTime, not check microsecond timing.

Apply this diff to fix the test logic:

 func TestGoRoutines(t *testing.T) {
 	start := time.Now()
 	RunConcurrently()
 	elapsed := time.Since(start)
 
-	// according to benchmark it is about ~1.5 microseconds
-	// x2 for more breathing room, so 3
-	if elapsed.Microseconds() >= 3 {
-		t.Fatal("Go routines was not used!")
+	// Sequential execution would take ~300ms (100ms + 200ms)
+	// Concurrent execution should take ~200ms (max of the two)
+	// Allow 250ms threshold to account for scheduling overhead
+	if elapsed.Milliseconds() >= 250 {
+		t.Fatalf("Go routines was not used! Elapsed: %v", elapsed)
 	}
 }

Note: The solution's RunConcurrently() also needs synchronization (e.g., time.Sleep(250*time.Millisecond) at the end) to wait for goroutines to complete.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func TestGoRoutines(t *testing.T) {
start := time.Now()
RunConcurrently()
elapsed := time.Since(start)
// according to benchmark it is about ~1.5 microseconds
// x2 for more breathing room, so 3
if elapsed.Microseconds() >= 3 {
t.Fatal("Go routines was not used!")
}
}
func TestGoRoutines(t *testing.T) {
start := time.Now()
RunConcurrently()
elapsed := time.Since(start)
// Sequential execution would take ~300ms (100ms + 200ms)
// Concurrent execution should take ~200ms (max of the two)
// Allow 250ms threshold to account for scheduling overhead
if elapsed.Milliseconds() >= 250 {
t.Fatalf("Go routines was not used! Elapsed: %v", elapsed)
}
}
🤖 Prompt for AI Agents
internal/exercises/templates/29_go_routines/go_routines_test.go lines 8-18: the
test’s timing and synchronization are wrong — it currently asserts elapsed < 3µs
which yields false positives if RunConcurrently returns before goroutines
complete. Fix by measuring the sequential baseline (e.g., sequential :=
100*time.Millisecond + 200*time.Millisecond), ensure RunConcurrently waits for
work to finish (prefer sync.WaitGroup or a final sleep in the implementation),
then assert the concurrent elapsed is less than the sequential baseline (use a
tolerant threshold like elapsed < 250*time.Millisecond) and fail otherwise.

Comment thread internal/exercises/templates/29_go_routines/go_routines_test.go Outdated
Comment on lines +9 to +14
t.Run("Sorting numbers", func(t *testing.T) {
SortYears()
if !slices.IsSorted(Years) {
t.Fatal("Strings are not sorted!")
}
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix incorrect error message.

Line 12 reports "Strings are not sorted!" when testing numbers. The message should accurately reflect that integers are being validated.

Apply this diff:

 	t.Run("Sorting numbers", func(t *testing.T) {
 		SortYears()
 		if !slices.IsSorted(Years) {
-			t.Fatal("Strings are not sorted!")
+			t.Fatal("Numbers are not sorted!")
 		}
 	})
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
t.Run("Sorting numbers", func(t *testing.T) {
SortYears()
if !slices.IsSorted(Years) {
t.Fatal("Strings are not sorted!")
}
})
t.Run("Sorting numbers", func(t *testing.T) {
SortYears()
if !slices.IsSorted(Years) {
t.Fatal("Numbers are not sorted!")
}
})
🤖 Prompt for AI Agents
In internal/exercises/templates/32_sorting/sorting_test.go around lines 9 to 14,
the test's failure message incorrectly says "Strings are not sorted!" while
validating integer sorting; update the t.Fatal call to use an accurate message
such as "Numbers are not sorted!" or "Years are not sorted!" so the error
reflects that integers (Years) are being tested.

@kawpii kawpii changed the title Feat/sorting exercise 05 - Feat/sorting exercise Oct 1, 2025
@kawpii kawpii closed this Oct 2, 2025
@kawpii
kawpii force-pushed the feat/sorting-exercise branch from a0eab8e to 94fd4b3 Compare October 2, 2025 08:10
@kawpii kawpii reopened this Oct 2, 2025
@kawpii

kawpii commented Oct 2, 2025

Copy link
Copy Markdown
Collaborator Author

Rebased and updated!

@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: 2

♻️ Duplicate comments (5)
internal/exercises/solutions/29_go_routines/go_routines.go (1)

15-25: Critical: Goroutines launched without synchronization.

RunConcurrently() launches goroutines at lines 16-17 but returns immediately without waiting for them to complete. This causes:

  1. Goroutines may not execute before program exit
  2. Flaky test results
  3. Non-deterministic behavior

For a solution/reference implementation, proper synchronization is essential.

Apply this diff to fix:

+import (
+	"sync"
+	"time"
+)
+
 func RunConcurrently() {
+	var wg sync.WaitGroup
+	wg.Add(2)
+	
+	go func() {
+		defer wg.Done()
+		sleepForOneHundredMillisecond()
+	}()
+	go func() {
+		defer wg.Done()
+		sleepForTwoHundredMilliseconds()
+	}()
+	
+	wg.Wait()
-	go sleepForOneHundredMillisecond()
-	go sleepForTwoHundredMilliseconds()
-
-	// go func() {
-	// 	sleepForOneHundredMillisecond()
-	// }()
-	// go func() {
-	// 	sleepForTwoHundredMilliseconds()
-	// }()
 }
internal/exercises/templates/29_go_routines/go_routines_test.go (1)

8-18: Test logic is critically flawed and will produce false positives.

The test has multiple issues:

  1. Incorrect timing threshold: Line 15 expects <4 microseconds, but goroutines sleeping for 100ms and 200ms will take at minimum ~200ms when properly concurrent. The test passes for the wrong reason—if RunConcurrently() returns immediately without waiting (as both template and solution currently do), the test passes even though work isn't complete.

  2. Missing synchronization: The solution's RunConcurrently() spawns goroutines but doesn't wait. A correct concurrent implementation needs sync.WaitGroup to ensure goroutines finish before returning.

  3. Inverted expectation: Sequential execution takes ~300ms (100ms + 200ms). Concurrent should take ~200ms (max of the two). Test should verify elapsed < sequentialTime, not check microsecond timing.

Apply this diff to fix:

 func TestGoRoutines(t *testing.T) {
 	start := time.Now()
 	RunConcurrently()
 	elapsed := time.Since(start)
 
-	// according to benchmark it is about ~2 microseconds
-	// x2 for more breathing room, so 4
-	if elapsed.Microseconds() >= 4 {
-		t.Fatal("Go routines was not used!")
+	// Sequential execution would take ~300ms (100ms + 200ms)
+	// Concurrent execution should take ~200ms (max of the two)
+	// Allow 250ms threshold to account for scheduling overhead
+	if elapsed.Milliseconds() >= 250 {
+		t.Fatalf("Go routines was not used! Elapsed: %v", elapsed)
 	}
 }

Note: The solution's RunConcurrently() also needs synchronization (see review comment on solutions/29_go_routines/go_routines.go).

internal/exercises/templates/28_defer/defer_test.go (1)

8-17: Test parameter usage remains pedagogically unclear despite intentional design.

The test passes f to WriteToFile(f) on line 10, but WriteToFile ignores this parameter and calls CreateFile() internally. While the retrieved learning indicates this is intentional pedagogy for teaching defer concepts, the API remains confusing for learners who expect the passed parameter to be used.

For a defer-focused exercise, consider whether the learning objective is better served by either:

  • Having WriteToFile() accept no parameters (emphasizing singleton + defer pattern), or
  • Having WriteToFile(f) actually use the provided file (emphasizing defer with caller-provided resources)

Based on learnings.

internal/exercises/solutions/28_defer/defer.go (1)

25-29: Solution demonstrates unused parameter pattern despite pedagogical intent.

The function signature declares *os.File but ignores it, always calling CreateFile() instead. Past reviews flagged this as confusing API design. While the retrieved learning indicates this is intentionally part of the defer pedagogy, teaching this pattern in a solution file may reinforce practices that would be problematic in production code.

Based on learnings.

internal/exercises/templates/28_defer/defer.go (1)

25-29: Template preserves the unused parameter pattern from solution.

The function accepts *os.File but uses CreateFile() instead on line 26. The TODO on line 27 guides students to add defer cleanup, but the ignored parameter may confuse learners about Go function parameter conventions. While the retrieved learning indicates this design is intentional for defer pedagogy, the pattern remains potentially misleading for teaching idiomatic Go.

Based on learnings.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a0eab8e and 3d54262.

📒 Files selected for processing (16)
  • internal/exercises/catalog.yaml (1 hunks)
  • internal/exercises/solutions/28_defer/defer.go (1 hunks)
  • internal/exercises/solutions/29_go_routines/go_routines.go (1 hunks)
  • internal/exercises/solutions/30_channels/channels.go (1 hunks)
  • internal/exercises/solutions/31_mutexes/mutexes.go (1 hunks)
  • internal/exercises/solutions/32_sorting/sorting.go (1 hunks)
  • internal/exercises/templates/28_defer/defer.go (1 hunks)
  • internal/exercises/templates/28_defer/defer_test.go (1 hunks)
  • internal/exercises/templates/29_go_routines/go_routines.go (1 hunks)
  • internal/exercises/templates/29_go_routines/go_routines_test.go (1 hunks)
  • internal/exercises/templates/30_channels/channels.go (1 hunks)
  • internal/exercises/templates/30_channels/channels_test.go (1 hunks)
  • internal/exercises/templates/31_mutexes/mutexes.go (1 hunks)
  • internal/exercises/templates/31_mutexes/mutexes_test.go (1 hunks)
  • internal/exercises/templates/32_sorting/sorting.go (1 hunks)
  • internal/exercises/templates/32_sorting/sorting_test.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (6)
  • internal/exercises/catalog.yaml
  • internal/exercises/solutions/30_channels/channels.go
  • internal/exercises/templates/31_mutexes/mutexes_test.go
  • internal/exercises/solutions/31_mutexes/mutexes.go
  • internal/exercises/templates/32_sorting/sorting_test.go
  • internal/exercises/templates/32_sorting/sorting.go
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-10-02T06:30:47.143Z
Learnt from: kaushalyap
PR: zhravan/golearn#110
File: internal/exercises/templates/28_defer/defer.go:14-19
Timestamp: 2025-10-02T06:30:47.143Z
Learning: In the defer exercise template (internal/exercises/templates/28_defer/defer.go), the unused *os.File parameter in WriteToFile is intentional and part of the pedagogical design for teaching defer concepts.

Applied to files:

  • internal/exercises/solutions/28_defer/defer.go
  • internal/exercises/templates/28_defer/defer.go
  • internal/exercises/templates/28_defer/defer_test.go
🧬 Code graph analysis (8)
internal/exercises/templates/30_channels/channels_test.go (2)
internal/exercises/solutions/30_channels/channels.go (1)
  • ReadMessage (3-13)
internal/exercises/templates/30_channels/channels.go (1)
  • ReadMessage (5-15)
internal/exercises/templates/30_channels/channels.go (1)
internal/exercises/solutions/30_channels/channels.go (1)
  • ReadMessage (3-13)
internal/exercises/solutions/29_go_routines/go_routines.go (1)
internal/exercises/templates/29_go_routines/go_routines.go (1)
  • RunConcurrently (15-19)
internal/exercises/templates/29_go_routines/go_routines.go (1)
internal/exercises/solutions/29_go_routines/go_routines.go (1)
  • RunConcurrently (15-25)
internal/exercises/templates/28_defer/defer.go (1)
internal/exercises/solutions/28_defer/defer.go (2)
  • CreateFile (14-23)
  • WriteToFile (25-29)
internal/exercises/templates/28_defer/defer_test.go (2)
internal/exercises/solutions/28_defer/defer.go (2)
  • CreateFile (14-23)
  • WriteToFile (25-29)
internal/exercises/templates/28_defer/defer.go (2)
  • CreateFile (14-23)
  • WriteToFile (25-29)
internal/exercises/templates/29_go_routines/go_routines_test.go (2)
internal/exercises/solutions/29_go_routines/go_routines.go (1)
  • RunConcurrently (15-25)
internal/exercises/templates/29_go_routines/go_routines.go (1)
  • RunConcurrently (15-19)
internal/exercises/templates/31_mutexes/mutexes.go (1)
internal/exercises/solutions/31_mutexes/mutexes.go (1)
  • Counting (7-31)
🔇 Additional comments (9)
internal/exercises/templates/30_channels/channels_test.go (1)

5-12: LGTM! Clear test for the channels exercise.

The test correctly validates that ReadMessage() returns "Hi!" from the channel-based goroutine communication. The test will intentionally fail against the template (which returns "") until learners complete the exercise, providing clear feedback through the error message.

internal/exercises/templates/30_channels/channels.go (1)

5-15: LGTM! Template appropriately demonstrates the problem learners must solve.

The incomplete implementation correctly sets up the learning scenario:

  • The goroutine prints directly to stdout instead of using a channel
  • The function returns "" immediately, creating a race condition
  • The TODO clearly guides learners to introduce channel-based communication

This design effectively illustrates why channels are needed for goroutine communication. Once learners implement the solution (as shown in the reference solution), the goroutine will send "Hi!" through a channel, and the main routine will receive and return it.

internal/exercises/solutions/29_go_routines/go_routines.go (1)

7-13: LGTM!

The helper functions correctly implement the required sleep durations using time.Sleep.

internal/exercises/templates/29_go_routines/go_routines_test.go (1)

20-24: LGTM!

The benchmark function is now correctly named BenchmarkRunConcurrently and properly exercises the function under test.

internal/exercises/templates/29_go_routines/go_routines.go (2)

7-13: LGTM!

The helper functions correctly implement the required sleep durations and match the solution implementation.


15-19: LGTM!

The template correctly provides a sequential starting point with a clear TODO comment. Learners will update this to use goroutines as part of the exercise.

internal/exercises/solutions/32_sorting/sorting.go (3)

1-5: LGTM!

Package declaration and import are appropriate. Using the slices package is the modern, idiomatic approach for sorting in Go 1.21+.


10-13: Sorting logic is correct.

The use of slices.Sort is appropriate and will correctly sort integers in ascending order. However, the mutation of global state remains a concern (flagged separately).


15-18: Sorting logic is correct.

The use of slices.Sort is appropriate and will correctly sort strings in lexicographic order. The sample data demonstrates sorting of multi-character strings as specified in the PR objectives.

Comment on lines +7 to +8
var Years = []int{2017, 2003, 2026}
var Pets = []string{"Dog", "Cat", "Parrot"}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Refactor to avoid mutable global state.

Exported package-level variables that are mutated by the sorting functions create several issues:

  1. Non-idempotent behavior: After the first call, the data is already sorted, making subsequent calls redundant.
  2. Test reliability: If tests run multiple times or in parallel, shared mutable state can cause unpredictable results.
  3. Poor practice demonstration: For an educational exercise, this pattern teaches anti-patterns rather than best practices.

Consider refactoring to accept input parameters and return new sorted slices:

-var Years = []int{2017, 2003, 2026}
-var Pets = []string{"Dog", "Cat", "Parrot"}
-
-func SortNumbers() []int {
-	slices.Sort(Years)
-	return Years
+func SortNumbers(numbers []int) []int {
+	sorted := make([]int, len(numbers))
+	copy(sorted, numbers)
+	slices.Sort(sorted)
+	return sorted
 }
 
-func SortAnimals() []string {
-	slices.Sort(Pets)
-	return Pets
+func SortAnimals(animals []string) []string {
+	sorted := make([]string, len(animals))
+	copy(sorted, animals)
+	slices.Sort(sorted)
+	return sorted
 }

Alternatively, if in-place sorting is the intended teaching point, document this behavior clearly and accept the slice as a parameter.

🤖 Prompt for AI Agents
internal/exercises/solutions/32_sorting/sorting.go lines 7-8: the file currently
defines exported package-level mutable variables Years and Pets which causes
non-idempotent behavior and test flakiness; refactor by removing these exported
globals and instead implement functions that accept a slice parameter and return
a sorted copy (or, if demonstrating in-place sorting, accept a slice parameter
and clearly document/make the function name reflect that it mutates the input),
ensure you do not mutate package state, and update callers/tests to pass slices
and receive sorted results (or pass slices explicitly when in-place).

Comment on lines +15 to +21
for i := 1; i <= numWorkers; i++ {
wg.Add(1)
go func() {
defer wg.Done()
count += 1
done <- true
}()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Synchronize the shared counter to avoid races.

count += 1 runs concurrently without a mutex, so increments are lost and Counting() can return less than numWorkers, making the template fail its own test. Please guard the increment with a sync.Mutex.

 	count := 0
 	done := make(chan bool, numWorkers)
 	var wg sync.WaitGroup
+	var mu sync.Mutex
 
 	for i := 1; i <= numWorkers; i++ {
 		wg.Add(1)
 		go func() {
 			defer wg.Done()
-			count += 1
+			mu.Lock()
+			count++
+			mu.Unlock()
 			done <- true
 		}()
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for i := 1; i <= numWorkers; i++ {
wg.Add(1)
go func() {
defer wg.Done()
count += 1
done <- true
}()
count := 0
done := make(chan bool, numWorkers)
var wg sync.WaitGroup
var mu sync.Mutex
for i := 1; i <= numWorkers; i++ {
wg.Add(1)
go func() {
defer wg.Done()
mu.Lock()
count++
mu.Unlock()
done <- true
}()
}
🤖 Prompt for AI Agents
In internal/exercises/templates/31_mutexes/mutexes.go around lines 15 to 21, the
concurrent increment "count += 1" is not synchronized which causes data races
and lost increments; declare a sync.Mutex (e.g., var mu sync.Mutex) in the
surrounding scope and inside the goroutine lock the mutex before incrementing
and unlock it afterward (mu.Lock(); count += 1; mu.Unlock()), leaving wg.Done
and done <- true as-is so each worker still signals completion.

@kawpii

kawpii commented Oct 2, 2025

Copy link
Copy Markdown
Collaborator Author

Rebased and updated!

@kawpii kawpii closed this Oct 5, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Concept] Sorting - add exercise templates

1 participant