From f30827312461fabea5a37c088a9c0662bc708b05 Mon Sep 17 00:00:00 2001
From: Drew Bednar <drew@runcible.io>
Date: Wed, 21 Aug 2024 11:19:06 -0400
Subject: [PATCH] Finished up concurrency

---
 .../concurrency/CheckWebsites.go              | 23 ++++++++++++++++++-
 .../concurrency/CheckWebsites_test.go         | 18 +++++++++++++++
 learn_go_with_tests/concurrency/READMD.md     |  9 ++++++++
 3 files changed, 49 insertions(+), 1 deletion(-)
 create mode 100644 learn_go_with_tests/concurrency/READMD.md

diff --git a/learn_go_with_tests/concurrency/CheckWebsites.go b/learn_go_with_tests/concurrency/CheckWebsites.go
index 9438de0..1fbbfda 100644
--- a/learn_go_with_tests/concurrency/CheckWebsites.go
+++ b/learn_go_with_tests/concurrency/CheckWebsites.go
@@ -1,12 +1,33 @@
 package concurrency
 
 type WebsiteChecker func(string) bool
+type result struct {
+	string
+	bool
+}
 
 func CheckWebsites(wc WebsiteChecker, urls []string) map[string]bool {
 	results := make(map[string]bool)
+	resultChannel := make(chan result)
 
 	for _, url := range urls {
-		results[url] = wc(url)
+		// anonymous function call required because go routines must call
+		// a function to start. An anonymous function maintains access to
+		// the lexical scope in which they are defined - all the variables
+		// that are available at the point when you declare the anonymous
+		//function are also available in the body of the function.
+		// url := url // create a new variable to avoid capturing the loop variable.
+		// go func() {
+		// 	results[url] = wc(url)
+		// }()
+		go func(u string) {
+			resultChannel <- result{u, wc(u)} // send statement
+		}(url) // or just pass by value to give the func it's own url
+	}
+
+	for i := 0; i < len(urls); i++ {
+		r := <-resultChannel // receive statement
+		results[r.string] = r.bool
 	}
 
 	return results
diff --git a/learn_go_with_tests/concurrency/CheckWebsites_test.go b/learn_go_with_tests/concurrency/CheckWebsites_test.go
index 9a4ca69..be58d15 100644
--- a/learn_go_with_tests/concurrency/CheckWebsites_test.go
+++ b/learn_go_with_tests/concurrency/CheckWebsites_test.go
@@ -3,12 +3,30 @@ package concurrency
 import (
 	"reflect"
 	"testing"
+	"time"
 )
 
 func mockWebsiteChecker(url string) bool {
 	return url != "waat://furhurterwe.geds"
 }
 
+func slowStubWebsiteChecker(_ string) bool {
+	time.Sleep(20 * time.Millisecond)
+	return true
+}
+
+// What does testing.B do?
+func BenchmarkCheckWebsites(b *testing.B) {
+	urls := make([]string, 100)
+	for i := 0; i < len(urls); i++ {
+		urls[i] = "a url"
+	}
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		CheckWebsites(slowStubWebsiteChecker, urls)
+	}
+}
+
 func TestWebsites(t *testing.T) {
 	websites := []string{
 		"http://google.com",
diff --git a/learn_go_with_tests/concurrency/READMD.md b/learn_go_with_tests/concurrency/READMD.md
new file mode 100644
index 0000000..5efa9ec
--- /dev/null
+++ b/learn_go_with_tests/concurrency/READMD.md
@@ -0,0 +1,9 @@
+# Concurrency 
+
+https://quii.gitbook.io/learn-go-with-tests/go-fundamentals/concurrency
+
+This was an interesting chapter. The high-level takeaways:
+
+- An anonymous function maintains access to the lexical scope in which they are defined 
+- Go can help identify race conditions with [race detector](https://blog.golang.org/race-detector) `go test -race`
+- Coordinating go routines can be accomplished with channels. Channels are a data structure that can both receive and send values. This allows cross go routine communication. Channels have a type, and you will commonly see structs passed around.
\ No newline at end of file