├── .gitignore ├── 01-class-codewalk ├── 01-sync-waitgroup │ └── main.go ├── 02-closure │ └── main.go ├── 03-channel │ └── main.go ├── 04-deep-dive-channels │ └── main.go ├── 05-select-timeout │ └── main.go ├── 06-select-deadline │ └── main.go ├── 07-sync-cond │ └── main.go ├── 08-sync-cond │ └── main.go └── 09-sync-cond │ └── main.go ├── 01-exercise-solution ├── 01-goroutines │ ├── 01-hello │ │ └── main.go │ ├── 02-client-server │ │ ├── client │ │ │ └── main.go │ │ └── server │ │ │ └── main.go │ ├── 03-join │ │ └── main.go │ ├── 04-add │ │ ├── counting │ │ │ ├── count.go │ │ │ └── count_test.go │ │ └── main.go │ ├── 05-closure │ │ └── main.go │ └── 06-closure │ │ └── main.go ├── 02-channel │ ├── 01-channel │ │ └── main.go │ ├── 02-channel │ │ └── main.go │ ├── 03-channel │ │ └── main.go │ ├── 04-channel │ │ └── main.go │ └── 05-channel │ │ └── main.go ├── 03-select │ ├── 01-select │ │ └── main.go │ ├── 02-select │ │ └── main.go │ └── 03-select │ │ └── main.go ├── 04-sync │ ├── 01-mutex │ │ └── main.go │ ├── 02-mutex │ │ └── main.go │ ├── 11-atomic │ │ └── main.go │ ├── 21-cond │ │ └── main.go │ ├── 22-cond │ │ └── main.go │ ├── 31-once │ │ └── main.go │ └── 41-pool │ │ └── main.go ├── 05-race │ └── main.go ├── 07-exercise-web-crawler-solution │ └── main.go └── 07-exercise-web-crawler │ └── main.go ├── 01-exercise ├── 01-goroutines │ ├── 01-hello │ │ └── main.go │ ├── 02-client-server │ │ ├── client │ │ │ └── main.go │ │ └── server │ │ │ └── main.go │ ├── 03-join │ │ └── main.go │ ├── 04-add │ │ ├── counting │ │ │ ├── count.go │ │ │ └── count_test.go │ │ └── main.go │ ├── 05-closure │ │ └── main.go │ └── 06-closure │ │ └── main.go ├── 02-channel │ ├── 01-channel │ │ └── main.go │ ├── 02-channel │ │ └── main.go │ ├── 03-channel │ │ └── main.go │ ├── 04-channel │ │ └── main.go │ └── 05-channel │ │ └── main.go ├── 03-select │ ├── 01-select │ │ └── main.go │ ├── 02-select │ │ └── main.go │ └── 03-select │ │ └── main.go ├── 04-sync │ ├── 01-mutex │ │ └── main.go │ ├── 02-mutex │ │ └── main.go │ ├── 11-atomic │ │ └── main.go │ ├── 21-cond │ │ └── main.go │ ├── 22-cond │ │ └── main.go │ ├── 31-once │ │ └── main.go │ └── 41-pool │ │ └── main.go ├── 05-race │ └── main.go ├── 07-exercise-web-crawler-solution │ └── main.go └── 07-exercise-web-crawler │ └── main.go ├── 02-class-codewalk ├── 01-pipeline │ └── main.go └── 02-context │ ├── 01-withcancel │ └── main.go │ ├── 02-withdeadline │ └── main.go │ └── 03-withvalue │ └── main.go ├── 02-exercise-solution ├── 02-pipeline │ ├── 01-pipeline │ │ └── main.go │ ├── 02-pipeline │ │ └── main.go │ ├── 03-pipeline │ │ └── main.go │ └── 04-image-processing-pipeline │ │ ├── imgs │ │ ├── 1.jpg │ │ ├── 10.jpg │ │ ├── 11.jpg │ │ ├── 12.jpg │ │ ├── 13.jpg │ │ ├── 14.jpg │ │ ├── 15.jpg │ │ ├── 16.jpg │ │ ├── 17.jpg │ │ ├── 18.jpg │ │ ├── 19.jpg │ │ ├── 2.jpg │ │ ├── 20.jpg │ │ ├── 21.jpg │ │ ├── 22.jpg │ │ ├── 23.jpg │ │ ├── 24.jpg │ │ ├── 25.jpg │ │ ├── 26.jpg │ │ ├── 27.jpg │ │ ├── 28.jpg │ │ ├── 29.jpg │ │ ├── 3.jpg │ │ ├── 30.jpg │ │ ├── 31.jpg │ │ ├── 32.jpg │ │ ├── 33.jpg │ │ ├── 34.jpg │ │ ├── 35.jpg │ │ ├── 36.jpg │ │ ├── 37.jpg │ │ ├── 38.jpg │ │ ├── 39.jpg │ │ ├── 4.jpg │ │ ├── 40.jpg │ │ ├── 5.jpg │ │ ├── 6.jpg │ │ ├── 7.jpg │ │ ├── 8.jpg │ │ └── 9.jpg │ │ ├── main.go │ │ ├── main_premature_termination.go │ │ └── thumbnail │ │ └── testfile └── 03-context │ ├── 01-withcancel │ └── main.go │ ├── 02-withdeadline │ └── main.go │ ├── 03-timeout │ └── main.go │ ├── 04-value │ └── main.go │ └── 05-server-timeout │ └── main.go ├── 02-exercise ├── 02-pipeline │ ├── 01-pipeline │ │ └── main.go │ ├── 02-pipeline │ │ └── main.go │ ├── 03-pipeline │ │ └── main.go │ └── 04-image-processing-sequential │ │ ├── imgs │ │ ├── 1.jpg │ │ ├── 10.jpg │ │ ├── 11.jpg │ │ ├── 12.jpg │ │ ├── 13.jpg │ │ ├── 14.jpg │ │ ├── 15.jpg │ │ ├── 16.jpg │ │ ├── 17.jpg │ │ ├── 18.jpg │ │ ├── 19.jpg │ │ ├── 2.jpg │ │ ├── 20.jpg │ │ ├── 21.jpg │ │ ├── 22.jpg │ │ ├── 23.jpg │ │ ├── 24.jpg │ │ ├── 25.jpg │ │ ├── 26.jpg │ │ ├── 27.jpg │ │ ├── 28.jpg │ │ ├── 29.jpg │ │ ├── 3.jpg │ │ ├── 30.jpg │ │ ├── 31.jpg │ │ ├── 32.jpg │ │ ├── 33.jpg │ │ ├── 34.jpg │ │ ├── 35.jpg │ │ ├── 36.jpg │ │ ├── 37.jpg │ │ ├── 38.jpg │ │ ├── 39.jpg │ │ ├── 4.jpg │ │ ├── 40.jpg │ │ ├── 5.jpg │ │ ├── 6.jpg │ │ ├── 7.jpg │ │ ├── 8.jpg │ │ └── 9.jpg │ │ ├── main.go │ │ └── thumbnail │ │ └── testfile └── 03-context │ ├── 01-withcancel │ └── main.go │ ├── 02-withdeadline │ └── main.go │ ├── 03-timeout │ └── main.go │ ├── 04-value │ └── main.go │ └── 05-server-timeout │ └── main.go ├── 03-class-codewalk └── 01-interface │ ├── 01-Fprintf │ └── main.go │ ├── 02-assign │ └── main.go │ ├── 04-printer │ └── main.go │ └── 05-empty-interface │ └── main.go ├── 03-exercise-solution └── 01-Interfaces │ ├── 01-density │ └── main.go │ ├── 02-Fprintf │ └── main.go │ ├── 03-byte-counter │ └── main.go │ ├── 04-String │ └── main.go │ ├── 05-assign │ └── main.go │ ├── 05-printer │ └── main.go │ ├── 06-nil │ └── main.go │ ├── 09-triangle │ └── main.go │ └── 10-empty-interface │ └── main.go ├── 03-exercise └── 01-Interfaces │ ├── 01-density │ └── main.go │ ├── 02-Fprintf │ └── main.go │ ├── 03-byte-counter │ └── main.go │ ├── 04-String │ └── main.go │ ├── 05-assign │ └── main.go │ ├── 05-printer │ └── main.go │ ├── 05-printer01 │ └── main.go │ ├── 06-nil │ └── main.go │ ├── 09-triangle │ └── main.go │ └── 10-empty-interface │ └── main.go ├── README.md └── concurrency-in-go-slides.pdf /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /01-class-codewalk/01-sync-waitgroup/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func main() { 8 | var data int 9 | 10 | go func() { 11 | data++ 12 | }() 13 | 14 | if data == 0 { 15 | fmt.Printf("the value of data is %v\n", data) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /01-class-codewalk/02-closure/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | func incr(wg *sync.WaitGroup) { 9 | var i int 10 | wg.Add(1) 11 | go func() { 12 | defer wg.Done() 13 | i++ 14 | fmt.Println(i) 15 | }() 16 | } 17 | 18 | func main() { 19 | var wg sync.WaitGroup 20 | 21 | incr(&wg) 22 | wg.Wait() 23 | fmt.Println("done..") 24 | } 25 | -------------------------------------------------------------------------------- /01-class-codewalk/03-channel/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | go func(a, b int) { 5 | c := a + b 6 | }(1, 2) 7 | // TODO: get the value computed from goroutine 8 | // fmt.Printf("computed value %v\n", c) 9 | } 10 | -------------------------------------------------------------------------------- /01-class-codewalk/04-deep-dive-channels/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | var wg sync.WaitGroup 9 | 10 | func main() { 11 | ch := make(chan int, 3) 12 | 13 | wg.Add(2) 14 | go G1(ch) 15 | go G2(ch) 16 | wg.Wait() 17 | } 18 | 19 | //G1 - goroutine 20 | func G1(ch chan<- int) { 21 | defer wg.Done() 22 | for _, v := range []int{1, 2, 3, 4} { 23 | ch <- v 24 | } 25 | close(ch) 26 | } 27 | 28 | //G2 - goroutine 29 | func G2(ch <-chan int) { 30 | defer wg.Done() 31 | for v := range ch { 32 | fmt.Println(v) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /01-class-codewalk/05-select-timeout/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func main() { 9 | ch := make(chan int, 1) 10 | 11 | go func() { 12 | time.Sleep(5 * time.Second) 13 | ch <- 1 14 | }() 15 | 16 | select { 17 | case v := <-ch: 18 | fmt.Println(v) 19 | case <-time.After(3 * time.Second): 20 | fmt.Println("timeout") 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /01-class-codewalk/06-select-deadline/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func main() { 9 | ch := make(chan string) 10 | 11 | go func() { 12 | time.Sleep(1 * time.Second) 13 | ch <- "message" 14 | }() 15 | 16 | select { 17 | case m := <-ch: 18 | fmt.Println("received message", m) 19 | default: 20 | fmt.Println("no message received") 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /01-class-codewalk/07-sync-cond/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | var sharedRsc = make(map[string]string) 10 | 11 | func main() { 12 | var wg sync.WaitGroup 13 | mu := sync.Mutex{} 14 | 15 | wg.Add(1) 16 | go func() { 17 | defer wg.Done() 18 | mu.Lock() 19 | for len(sharedRsc) == 0 { 20 | mu.Unlock() 21 | time.Sleep(1 * time.Millisecond) 22 | mu.Lock() 23 | } 24 | fmt.Println(sharedRsc["rsc1"]) 25 | mu.Unlock() 26 | }() 27 | 28 | mu.Lock() 29 | sharedRsc["rsc1"] = "foo" 30 | mu.Unlock() 31 | 32 | wg.Wait() 33 | } 34 | -------------------------------------------------------------------------------- /01-class-codewalk/08-sync-cond/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | var sharedRsc = make(map[string]string) 9 | 10 | func main() { 11 | var wg sync.WaitGroup 12 | m := sync.Mutex{} 13 | c := sync.NewCond(&m) 14 | 15 | wg.Add(1) 16 | go func() { 17 | defer wg.Done() 18 | c.L.Lock() 19 | for len(sharedRsc) == 0 { 20 | c.Wait() 21 | } 22 | // Do processing.. 23 | fmt.Println(sharedRsc["rsc1"]) 24 | c.L.Unlock() 25 | }() 26 | 27 | // writes changes to sharedRsc 28 | wg.Add(1) 29 | go func() { 30 | defer wg.Done() 31 | c.L.Lock() 32 | sharedRsc["rsc1"] = "foo" 33 | c.Signal() 34 | c.L.Unlock() 35 | }() 36 | 37 | wg.Wait() 38 | } 39 | -------------------------------------------------------------------------------- /01-class-codewalk/09-sync-cond/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | var sharedRsc = make(map[string]string) 9 | 10 | func main() { 11 | var wg sync.WaitGroup 12 | m := sync.Mutex{} 13 | c := sync.NewCond(&m) 14 | 15 | wg.Add(1) 16 | go func() { 17 | defer wg.Done() 18 | c.L.Lock() 19 | for len(sharedRsc) == 0 { 20 | c.Wait() 21 | } 22 | 23 | fmt.Println(sharedRsc["rsc1"]) 24 | c.L.Unlock() 25 | }() 26 | 27 | wg.Add(1) 28 | go func() { 29 | defer wg.Done() 30 | c.L.Lock() 31 | for len(sharedRsc) == 0 { 32 | c.Wait() 33 | } 34 | 35 | fmt.Println(sharedRsc["rsc2"]) 36 | c.L.Unlock() 37 | }() 38 | 39 | // writes changes to sharedRsc 40 | wg.Add(1) 41 | go func() { 42 | defer wg.Done() 43 | c.L.Lock() 44 | sharedRsc["rsc1"] = "foo" 45 | sharedRsc["rsc2"] = "bar" 46 | c.Broadcast() 47 | c.L.Unlock() 48 | }() 49 | 50 | wg.Wait() 51 | } 52 | -------------------------------------------------------------------------------- /01-exercise-solution/01-goroutines/01-hello/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func fun(s string) { 9 | for i := 0; i < 3; i++ { 10 | fmt.Println(s) 11 | time.Sleep(1 * time.Millisecond) 12 | } 13 | } 14 | 15 | func main() { 16 | // Direct call 17 | fun("direct call") 18 | 19 | // write goroutine with different variants for function call. 20 | 21 | // goroutine function call 22 | go fun("goroutine-1") 23 | 24 | // goroutine with anonymous function 25 | go func() { 26 | fun("goroutine-2") 27 | }() 28 | 29 | // goroutine with function value call 30 | fv := fun 31 | go fv("goroutine-3") 32 | 33 | // wait for goroutines to end 34 | fmt.Println("waiting for goroutines to complete..") 35 | time.Sleep(1 * time.Second) 36 | 37 | fmt.Println("done..") 38 | } 39 | -------------------------------------------------------------------------------- /01-exercise-solution/01-goroutines/02-client-server/client/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io" 5 | "log" 6 | "net" 7 | "os" 8 | ) 9 | 10 | func main() { 11 | // connect to server on localhost port 8000 12 | conn, err := net.Dial("tcp", "localhost:8000") 13 | 14 | if err != nil { 15 | log.Fatal(err) 16 | } 17 | 18 | defer conn.Close() 19 | 20 | mustCopy(os.Stdout, conn) 21 | 22 | } 23 | 24 | // mustCopy - utility function 25 | func mustCopy(dst io.Writer, src io.Reader) { 26 | if _, err := io.Copy(dst, src); err != nil { 27 | log.Fatal(err) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /01-exercise-solution/01-goroutines/02-client-server/server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io" 5 | "log" 6 | "net" 7 | "time" 8 | ) 9 | 10 | func main() { 11 | // write server program to handle concurrent client connections. 12 | listener, err := net.Listen("tcp", "localhost:8000") 13 | 14 | if err != nil { 15 | log.Fatal(err) 16 | } 17 | 18 | for { 19 | conn, err := listener.Accept() 20 | 21 | if err != nil { 22 | continue 23 | } 24 | go handleConn(conn) 25 | } 26 | } 27 | 28 | // handleConn - utility function 29 | func handleConn(c net.Conn) { 30 | defer c.Close() 31 | for { 32 | _, err := io.WriteString(c, "response from server\n") 33 | if err != nil { 34 | return 35 | } 36 | time.Sleep(time.Second) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /01-exercise-solution/01-goroutines/03-join/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | func main() { 9 | // program to print the value as 1 10 | // deterministically. 11 | var wg sync.WaitGroup 12 | var data int 13 | 14 | wg.Add(1) 15 | go func() { 16 | defer wg.Done() 17 | data++ 18 | }() 19 | wg.Wait() 20 | fmt.Printf("the value of data is %v\n", data) 21 | 22 | fmt.Println("Done..") 23 | } 24 | -------------------------------------------------------------------------------- /01-exercise-solution/01-goroutines/04-add/counting/count.go: -------------------------------------------------------------------------------- 1 | package counting 2 | 3 | import ( 4 | "math/rand" 5 | "runtime" 6 | "sync" 7 | "sync/atomic" 8 | "time" 9 | ) 10 | 11 | func init() { 12 | rand.Seed(time.Now().UnixNano()) 13 | } 14 | 15 | // GenerateNumbers - random number generation 16 | func GenerateNumbers(max int) []int { 17 | rand.Seed(time.Now().UnixNano()) 18 | numbers := make([]int, max) 19 | for i := 0; i < max; i++ { 20 | numbers[i] = rand.Intn(10) 21 | } 22 | return numbers 23 | } 24 | 25 | // Add - sequential code to add numbers 26 | func Add(numbers []int) int64 { 27 | var sum int64 28 | for _, n := range numbers { 29 | sum += int64(n) 30 | } 31 | return sum 32 | } 33 | 34 | // AddConcurrent - concurrent code to add numbers 35 | func AddConcurrent(numbers []int) int64 { 36 | 37 | // Utilize all cores on machine 38 | numOfCores := runtime.NumCPU() 39 | runtime.GOMAXPROCS(numOfCores) 40 | 41 | var sum int64 42 | max := len(numbers) 43 | 44 | sizeOfParts := max / numOfCores 45 | 46 | var wg sync.WaitGroup 47 | 48 | for i := 0; i < numOfCores; i++ { 49 | 50 | // Divide the input into parts 51 | start := i * sizeOfParts 52 | end := start + sizeOfParts 53 | part := numbers[start:end] 54 | 55 | // Run computation for each part in seperate goroutine. 56 | wg.Add(1) 57 | go func(nums []int) { 58 | defer wg.Done() 59 | 60 | var partSum int64 61 | 62 | // Calculate sum for each part 63 | for _, n := range nums { 64 | partSum += int64(n) 65 | } 66 | 67 | // Add sum of each part to cummulative sum 68 | atomic.AddInt64(&sum, partSum) 69 | }(part) 70 | } 71 | 72 | wg.Wait() 73 | return sum 74 | } 75 | -------------------------------------------------------------------------------- /01-exercise-solution/01-goroutines/04-add/counting/count_test.go: -------------------------------------------------------------------------------- 1 | package counting 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func BenchmarkAdd(b *testing.B) { 8 | numbers := GenerateNumbers(1e7) 9 | for i := 0; i < b.N; i++ { 10 | Add(numbers) 11 | } 12 | } 13 | 14 | func BenchmarkAddConcurrent(b *testing.B) { 15 | numbers := GenerateNumbers(1e7) 16 | for i := 0; i < b.N; i++ { 17 | AddConcurrent(numbers) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /01-exercise-solution/01-goroutines/04-add/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "go-concurrency-exercises/01-exercise-solution/01-goroutines/04-add/counting" 8 | ) 9 | 10 | func main() { 11 | numbers := counting.GenerateNumbers(1e7) 12 | 13 | t := time.Now() 14 | sum := counting.Add(numbers) 15 | fmt.Printf("Sequential Add, Sum: %d, Time Taken: %s\n", sum, time.Since(t)) 16 | 17 | t = time.Now() 18 | sum = counting.AddConcurrent(numbers) 19 | fmt.Printf("Concurrent Add, Sum: %d, Time Taken: %s\n", sum, time.Since(t)) 20 | } 21 | -------------------------------------------------------------------------------- /01-exercise-solution/01-goroutines/05-closure/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | // run the program and check that variable i 9 | // was pinned for access from goroutine even after 10 | // enclosing function returns. 11 | 12 | func main() { 13 | var wg sync.WaitGroup 14 | 15 | incr := func(wg *sync.WaitGroup) { 16 | var i int 17 | wg.Add(1) 18 | go func() { 19 | defer wg.Done() 20 | i++ 21 | fmt.Printf("value of i: %v\n", i) 22 | }() 23 | fmt.Println("return from function") 24 | return 25 | } 26 | 27 | incr(&wg) 28 | wg.Wait() 29 | fmt.Println("done..") 30 | } 31 | -------------------------------------------------------------------------------- /01-exercise-solution/01-goroutines/06-closure/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | func main() { 9 | var wg sync.WaitGroup 10 | 11 | // what is the output 12 | // fix the issue. 13 | 14 | for i := 1; i <= 3; i++ { 15 | wg.Add(1) 16 | go func(i int) { 17 | defer wg.Done() 18 | fmt.Println(i) 19 | }(i) 20 | } 21 | wg.Wait() 22 | } 23 | -------------------------------------------------------------------------------- /01-exercise-solution/02-channel/01-channel/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | ch := make(chan int) 7 | 8 | go func(a, b int) { 9 | c := a + b 10 | ch <- c 11 | }(1, 2) 12 | // get the value computed from goroutine 13 | c := <-ch 14 | fmt.Printf("computed value %v\n", c) 15 | } 16 | -------------------------------------------------------------------------------- /01-exercise-solution/02-channel/02-channel/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | ch := make(chan int) 7 | go func() { 8 | for i := 0; i < 6; i++ { 9 | // send iterator over channel 10 | ch <- i 11 | } 12 | close(ch) 13 | }() 14 | // range over channel to recv values 15 | for i := range ch { 16 | fmt.Println(i) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /01-exercise-solution/02-channel/03-channel/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func main() { 8 | ch := make(chan int, 6) 9 | 10 | go func() { 11 | defer close(ch) 12 | 13 | // send all iterator values on channel without blocking 14 | for i := 0; i < 6; i++ { 15 | fmt.Printf("Sending: %d\n", i) 16 | ch <- i 17 | } 18 | }() 19 | 20 | for v := range ch { 21 | fmt.Printf("Received: %v\n", v) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /01-exercise-solution/02-channel/04-channel/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // TODO: Implement ping pong with Channel Direction 6 | 7 | func ping(out chan<- string) { 8 | // send message on ch1 9 | out <- "ping" 10 | } 11 | 12 | func pong(in <-chan string, out chan<- string) { 13 | // recv message on ch1 14 | msg := <-in 15 | msg = msg + " pong" 16 | // send it on ch2 17 | out <- msg 18 | } 19 | 20 | func main() { 21 | // create ch1 and ch2 22 | ch1 := make(chan string) 23 | ch2 := make(chan string) 24 | 25 | // spine goroutine ping and pong 26 | go ping(ch1) 27 | go pong(ch1, ch2) 28 | 29 | // recv message on ch2 30 | msg := <-ch2 31 | 32 | fmt.Println(msg) 33 | } 34 | -------------------------------------------------------------------------------- /01-exercise-solution/02-channel/05-channel/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | // create channel owner goroutine which return channel and 7 | // writes data into channel and 8 | // closes the channel when done. 9 | 10 | owner := func() chan int { 11 | ch := make(chan int) 12 | go func() { 13 | defer close(ch) 14 | for i := 0; i < 6; i++ { 15 | ch <- i 16 | } 17 | }() 18 | return ch 19 | } 20 | 21 | consumer := func(ch <-chan int) { 22 | // read values from channel 23 | for v := range ch { 24 | fmt.Printf("Received: %d\n", v) 25 | } 26 | fmt.Println("Done receiving!") 27 | } 28 | 29 | ch := owner() 30 | consumer(ch) 31 | } 32 | -------------------------------------------------------------------------------- /01-exercise-solution/03-select/01-select/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func main() { 9 | ch1 := make(chan string) 10 | ch2 := make(chan string) 11 | 12 | go func() { 13 | time.Sleep(1 * time.Second) 14 | ch1 <- "one" 15 | }() 16 | 17 | go func() { 18 | time.Sleep(2 * time.Second) 19 | ch2 <- "two" 20 | }() 21 | 22 | // multiplex recv on channel - ch1, ch2 23 | 24 | for i := 0; i < 2; i++ { 25 | select { 26 | case msg1 := <-ch1: 27 | fmt.Println(msg1) 28 | case msg2 := <-ch2: 29 | fmt.Println(msg2) 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /01-exercise-solution/03-select/02-select/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func main() { 9 | ch := make(chan string, 1) 10 | 11 | go func() { 12 | time.Sleep(2 * time.Second) 13 | ch <- "one" 14 | }() 15 | 16 | //implement timeout for recv on channel ch 17 | select { 18 | case m := <-ch: 19 | fmt.Println(m) 20 | case <-time.After(1 * time.Second): 21 | fmt.Println("timeout") 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /01-exercise-solution/03-select/03-select/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func main() { 9 | ch := make(chan string) 10 | 11 | go func() { 12 | for i := 0; i < 3; i++ { 13 | time.Sleep(1 * time.Second) 14 | ch <- "message" 15 | } 16 | 17 | }() 18 | 19 | // if there is no value on channel, do not block. 20 | for i := 0; i < 2; i++ { 21 | select { 22 | case m := <-ch: 23 | fmt.Println(m) 24 | default: 25 | fmt.Println("no message received") 26 | } 27 | 28 | // Do some processing.. 29 | fmt.Println("processing..") 30 | time.Sleep(1500 * time.Millisecond) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /01-exercise-solution/04-sync/01-mutex/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | "sync" 7 | ) 8 | 9 | func main() { 10 | 11 | runtime.GOMAXPROCS(4) 12 | 13 | var balance int 14 | var wg sync.WaitGroup 15 | var mu sync.Mutex 16 | 17 | deposit := func(amount int) { 18 | mu.Lock() 19 | balance += amount 20 | mu.Unlock() 21 | } 22 | 23 | withdrawal := func(amount int) { 24 | mu.Lock() 25 | defer mu.Unlock() 26 | balance -= amount 27 | } 28 | 29 | // we are making 100 times deposits of $1 30 | // and 100 times withdrawal of $1, concurrently. 31 | // run the program and check result. 32 | 33 | // TODO: fix the issue for consistent output. 34 | 35 | wg.Add(100) 36 | for i := 0; i < 100; i++ { 37 | go func() { 38 | defer wg.Done() 39 | deposit(1) 40 | }() 41 | } 42 | 43 | wg.Add(100) 44 | for i := 0; i < 100; i++ { 45 | go func() { 46 | defer wg.Done() 47 | withdrawal(1) 48 | }() 49 | } 50 | 51 | wg.Wait() 52 | fmt.Println(balance) 53 | } 54 | -------------------------------------------------------------------------------- /01-exercise-solution/04-sync/02-mutex/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | func main() { 11 | 12 | runtime.GOMAXPROCS(4) 13 | 14 | var balance int 15 | var wg sync.WaitGroup 16 | var mu sync.RWMutex 17 | 18 | deposit := func(amount int) { 19 | mu.Lock() 20 | balance += amount 21 | mu.Unlock() 22 | } 23 | 24 | read := func() int { 25 | mu.RLock() 26 | defer mu.RUnlock() 27 | return balance 28 | } 29 | 30 | wg.Add(10) 31 | for i := 0; i < 10; i++ { 32 | time.Sleep(1 * time.Millisecond) 33 | go func() { 34 | defer wg.Done() 35 | deposit(1) 36 | }() 37 | } 38 | 39 | // implement concurrent read. 40 | // allow multiple reads, writes holds the lock exclusively. 41 | wg.Add(10) 42 | for i := 0; i < 10; i++ { 43 | go func() { 44 | defer wg.Done() 45 | fmt.Println(read()) 46 | }() 47 | } 48 | wg.Wait() 49 | fmt.Println(balance) 50 | } 51 | -------------------------------------------------------------------------------- /01-exercise-solution/04-sync/11-atomic/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | "sync" 7 | "sync/atomic" 8 | ) 9 | 10 | func main() { 11 | runtime.GOMAXPROCS(4) 12 | 13 | var counter uint64 14 | var wg sync.WaitGroup 15 | 16 | // implement concurrency safe counter 17 | 18 | for i := 0; i < 50; i++ { 19 | wg.Add(1) 20 | go func() { 21 | defer wg.Done() 22 | for c := 0; c < 1000; c++ { 23 | atomic.AddUint64(&counter, 1) 24 | } 25 | }() 26 | } 27 | wg.Wait() 28 | fmt.Println("counter: ", counter) 29 | } 30 | -------------------------------------------------------------------------------- /01-exercise-solution/04-sync/21-cond/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | var sharedRsc = make(map[string]interface{}) 9 | 10 | func main() { 11 | var wg sync.WaitGroup 12 | 13 | m := sync.Mutex{} 14 | c := sync.NewCond(&m) 15 | 16 | wg.Add(1) 17 | go func() { 18 | defer wg.Done() 19 | 20 | // suspend goroutine until sharedRsc is populated. 21 | c.L.Lock() 22 | for len(sharedRsc) == 0 { 23 | c.Wait() 24 | } 25 | 26 | fmt.Println(sharedRsc["rsc1"]) 27 | c.L.Unlock() 28 | }() 29 | 30 | // TODO: writes changes to sharedRsc 31 | c.L.Lock() 32 | sharedRsc["rsc1"] = "foo" 33 | c.Signal() 34 | c.L.Unlock() 35 | wg.Wait() 36 | } 37 | -------------------------------------------------------------------------------- /01-exercise-solution/04-sync/22-cond/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | var sharedRsc = make(map[string]interface{}) 9 | 10 | func main() { 11 | var wg sync.WaitGroup 12 | m := sync.Mutex{} 13 | c := sync.NewCond(&m) 14 | 15 | wg.Add(1) 16 | go func() { 17 | defer wg.Done() 18 | 19 | // suspend goroutine until sharedRsc is populated. 20 | 21 | c.L.Lock() 22 | for len(sharedRsc) == 0 { 23 | c.Wait() 24 | } 25 | 26 | fmt.Println(sharedRsc["rsc1"]) 27 | c.L.Unlock() 28 | }() 29 | 30 | wg.Add(1) 31 | go func() { 32 | defer wg.Done() 33 | 34 | // suspend goroutine until sharedRsc is populated. 35 | 36 | c.L.Lock() 37 | for len(sharedRsc) == 0 { 38 | c.Wait() 39 | } 40 | 41 | fmt.Println(sharedRsc["rsc2"]) 42 | c.L.Unlock() 43 | }() 44 | 45 | // writes changes to sharedRsc 46 | c.L.Lock() 47 | sharedRsc["rsc1"] = "foo" 48 | sharedRsc["rsc2"] = "bar" 49 | c.Broadcast() 50 | c.L.Unlock() 51 | 52 | wg.Wait() 53 | } 54 | -------------------------------------------------------------------------------- /01-exercise-solution/04-sync/31-once/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | func main() { 9 | var wg sync.WaitGroup 10 | 11 | var once sync.Once 12 | 13 | load := func() { 14 | fmt.Println("Run only once initialization function") 15 | } 16 | 17 | wg.Add(10) 18 | for i := 0; i < 10; i++ { 19 | go func() { 20 | defer wg.Done() 21 | 22 | //TODO: modify so that load function gets called only once. 23 | once.Do(load) 24 | }() 25 | } 26 | wg.Wait() 27 | } 28 | -------------------------------------------------------------------------------- /01-exercise-solution/04-sync/41-pool/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "os" 8 | "sync" 9 | "time" 10 | ) 11 | 12 | // create pool of bytes.Buffers which can be reused. 13 | 14 | var bufPool = sync.Pool{ 15 | New: func() interface{} { 16 | fmt.Println("allocate new bytes.Buffer") 17 | return new(bytes.Buffer) 18 | }, 19 | } 20 | 21 | func log(w io.Writer, val string) { 22 | b := bufPool.Get().(*bytes.Buffer) 23 | b.Reset() 24 | 25 | b.WriteString(time.Now().Format("15:04:05")) 26 | b.WriteString(" : ") 27 | b.WriteString(val) 28 | b.WriteString("\n") 29 | 30 | w.Write(b.Bytes()) 31 | bufPool.Put(b) 32 | } 33 | 34 | func main() { 35 | log(os.Stdout, "debug-string1") 36 | log(os.Stdout, "debug-string2") 37 | } 38 | -------------------------------------------------------------------------------- /01-exercise-solution/05-race/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | //TODO: identify the data race 10 | // fix the issue. 11 | 12 | func main() { 13 | start := time.Now() 14 | reset := make(chan bool) 15 | var t *time.Timer 16 | t = time.AfterFunc(randomDuration(), func() { 17 | fmt.Println(time.Now().Sub(start)) 18 | reset <- true 19 | }) 20 | for time.Since(start) < 5*time.Second { 21 | <-reset 22 | t.Reset(randomDuration()) 23 | } 24 | } 25 | 26 | func randomDuration() time.Duration { 27 | return time.Duration(rand.Int63n(1e9)) 28 | } 29 | -------------------------------------------------------------------------------- /01-exercise-solution/07-exercise-web-crawler-solution/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "time" 7 | 8 | "golang.org/x/net/html" 9 | ) 10 | 11 | var fetched map[string]bool 12 | 13 | type result struct { 14 | url string 15 | urls []string 16 | err error 17 | depth int 18 | } 19 | 20 | // Crawl uses findLinks to recursively crawl 21 | // pages starting with url, to a maximum of depth. 22 | func Crawl(url string, depth int) { 23 | results := make(chan *result) 24 | 25 | fetch := func(url string, depth int) { 26 | urls, err := findLinks(url) 27 | results <- &result{url, urls, err, depth} 28 | } 29 | 30 | go fetch(url, depth) 31 | fetched[url] = true 32 | 33 | for fetching := 1; fetching > 0; fetching-- { 34 | res := <-results 35 | if res.err != nil { 36 | // fmt.Println(res.err) 37 | continue 38 | } 39 | 40 | fmt.Printf("found: %s\n", res.url) 41 | if res.depth > 0 { 42 | for _, u := range res.urls { 43 | if !fetched[u] { 44 | fetching++ 45 | go fetch(u, res.depth-1) 46 | fetched[u] = true 47 | } 48 | } 49 | } 50 | } 51 | close(results) 52 | } 53 | 54 | func main() { 55 | fetched = make(map[string]bool) 56 | now := time.Now() 57 | Crawl("http://andcloud.io", 2) 58 | fmt.Println("time taken:", time.Since(now)) 59 | } 60 | 61 | func findLinks(url string) ([]string, error) { 62 | resp, err := http.Get(url) 63 | if err != nil { 64 | return nil, err 65 | } 66 | if resp.StatusCode != http.StatusOK { 67 | resp.Body.Close() 68 | return nil, fmt.Errorf("getting %s: %s", url, resp.Status) 69 | } 70 | doc, err := html.Parse(resp.Body) 71 | resp.Body.Close() 72 | if err != nil { 73 | return nil, fmt.Errorf("parsing %s as HTML: %v", url, err) 74 | } 75 | return visit(nil, doc), nil 76 | } 77 | 78 | // visit appends to links each link found in n, and returns the result. 79 | func visit(links []string, n *html.Node) []string { 80 | if n.Type == html.ElementNode && n.Data == "a" { 81 | for _, a := range n.Attr { 82 | if a.Key == "href" { 83 | links = append(links, a.Val) 84 | } 85 | } 86 | } 87 | for c := n.FirstChild; c != nil; c = c.NextSibling { 88 | links = visit(links, c) 89 | } 90 | return links 91 | } 92 | -------------------------------------------------------------------------------- /01-exercise-solution/07-exercise-web-crawler/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "time" 7 | 8 | "golang.org/x/net/html" 9 | ) 10 | 11 | var fetched map[string]bool 12 | 13 | // Crawl uses findLinks to recursively crawl 14 | // pages starting with url, to a maximum of depth. 15 | func Crawl(url string, depth int) { 16 | // TODO: Fetch URLs in parallel. 17 | 18 | if depth < 0 { 19 | return 20 | } 21 | urls, err := findLinks(url) 22 | if err != nil { 23 | // fmt.Println(err) 24 | return 25 | } 26 | fmt.Printf("found: %s\n", url) 27 | fetched[url] = true 28 | for _, u := range urls { 29 | if !fetched[u] { 30 | Crawl(u, depth-1) 31 | } 32 | } 33 | return 34 | } 35 | 36 | func main() { 37 | fetched = make(map[string]bool) 38 | 39 | now := time.Now() 40 | Crawl("http://andcloud.io", 2) 41 | fmt.Println("time taken:", time.Since(now)) 42 | } 43 | 44 | func findLinks(url string) ([]string, error) { 45 | resp, err := http.Get(url) 46 | if err != nil { 47 | return nil, err 48 | } 49 | if resp.StatusCode != http.StatusOK { 50 | resp.Body.Close() 51 | return nil, fmt.Errorf("getting %s: %s", url, resp.Status) 52 | } 53 | doc, err := html.Parse(resp.Body) 54 | resp.Body.Close() 55 | if err != nil { 56 | return nil, fmt.Errorf("parsing %s as HTML: %v", url, err) 57 | } 58 | return visit(nil, doc), nil 59 | } 60 | 61 | // visit appends to links each link found in n, and returns the result. 62 | func visit(links []string, n *html.Node) []string { 63 | if n.Type == html.ElementNode && n.Data == "a" { 64 | for _, a := range n.Attr { 65 | if a.Key == "href" { 66 | links = append(links, a.Val) 67 | } 68 | } 69 | } 70 | for c := n.FirstChild; c != nil; c = c.NextSibling { 71 | links = visit(links, c) 72 | } 73 | return links 74 | } 75 | -------------------------------------------------------------------------------- /01-exercise/01-goroutines/01-hello/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func fun(s string) { 9 | for i := 0; i < 3; i++ { 10 | fmt.Println(s) 11 | time.Sleep(1 * time.Millisecond) 12 | } 13 | } 14 | 15 | func main() { 16 | // Direct call 17 | fun("direct call") 18 | 19 | // TODO: write goroutine with different variants for function call. 20 | 21 | // goroutine function call 22 | 23 | // goroutine with anonymous function 24 | 25 | // goroutine with function value call 26 | 27 | // wait for goroutines to end 28 | 29 | fmt.Println("done..") 30 | } 31 | -------------------------------------------------------------------------------- /01-exercise/01-goroutines/02-client-server/client/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io" 5 | "log" 6 | ) 7 | 8 | func main() { 9 | // TODO: connect to server on localhost port 8000 10 | 11 | } 12 | 13 | // mustCopy - utility function 14 | func mustCopy(dst io.Writer, src io.Reader) { 15 | if _, err := io.Copy(dst, src); err != nil { 16 | log.Fatal(err) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /01-exercise/01-goroutines/02-client-server/server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io" 5 | "net" 6 | "time" 7 | ) 8 | 9 | func main() { 10 | // TODO: write server program to handle concurrent client connections. 11 | 12 | } 13 | 14 | // handleConn - utility function 15 | func handleConn(c net.Conn) { 16 | defer c.Close() 17 | for { 18 | _, err := io.WriteString(c, "response from server\n") 19 | if err != nil { 20 | return 21 | } 22 | time.Sleep(time.Second) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /01-exercise/01-goroutines/03-join/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func main() { 8 | //TODO: modify the program 9 | // to print the value as 1 10 | // deterministically. 11 | 12 | var data int 13 | 14 | go func() { 15 | data++ 16 | }() 17 | 18 | fmt.Printf("the value of data is %v\n", data) 19 | 20 | fmt.Println("Done..") 21 | } 22 | -------------------------------------------------------------------------------- /01-exercise/01-goroutines/04-add/counting/count.go: -------------------------------------------------------------------------------- 1 | package counting 2 | 3 | import ( 4 | "math/rand" 5 | "time" 6 | ) 7 | 8 | func init() { 9 | rand.Seed(time.Now().UnixNano()) 10 | } 11 | 12 | // GenerateNumbers - random number generation 13 | func GenerateNumbers(max int) []int { 14 | rand.Seed(time.Now().UnixNano()) 15 | numbers := make([]int, max) 16 | for i := 0; i < max; i++ { 17 | numbers[i] = rand.Intn(10) 18 | } 19 | return numbers 20 | } 21 | 22 | // Add - sequential code to add numbers 23 | func Add(numbers []int) int64 { 24 | var sum int64 25 | for _, n := range numbers { 26 | sum += int64(n) 27 | } 28 | return sum 29 | } 30 | 31 | //TODO: complete the concurrent version of add function. 32 | 33 | // AddConcurrent - concurrent code to add numbers 34 | func AddConcurrent(numbers []int) int64 { 35 | var sum int64 36 | // Utilize all cores on machine 37 | 38 | // Divide the input into parts 39 | 40 | // Run computation for each part in seperate goroutine. 41 | 42 | // Add part sum to cummulative sum 43 | 44 | return sum 45 | } 46 | -------------------------------------------------------------------------------- /01-exercise/01-goroutines/04-add/counting/count_test.go: -------------------------------------------------------------------------------- 1 | package counting 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func BenchmarkAdd(b *testing.B) { 8 | numbers := GenerateNumbers(1e7) 9 | for i := 0; i < b.N; i++ { 10 | Add(numbers) 11 | } 12 | } 13 | 14 | func BenchmarkAddConcurrent(b *testing.B) { 15 | numbers := GenerateNumbers(1e7) 16 | for i := 0; i < b.N; i++ { 17 | AddConcurrent(numbers) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /01-exercise/01-goroutines/04-add/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "go-concurrency-exercises/01-exercise/01-goroutines/04-add/counting" 8 | ) 9 | 10 | func main() { 11 | numbers := counting.GenerateNumbers(1e7) 12 | 13 | t := time.Now() 14 | sum := counting.Add(numbers) 15 | fmt.Printf("Sequential Add, Sum: %d, Time Taken: %s\n", sum, time.Since(t)) 16 | 17 | t = time.Now() 18 | sum = counting.AddConcurrent(numbers) 19 | fmt.Printf("Concurrent Add, Sum: %d, Time Taken: %s\n", sum, time.Since(t)) 20 | } 21 | -------------------------------------------------------------------------------- /01-exercise/01-goroutines/05-closure/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | //TODO: run the program and check that variable i 9 | // was pinned for access from goroutine even after 10 | // enclosing function returns. 11 | 12 | func main() { 13 | var wg sync.WaitGroup 14 | 15 | incr := func(wg *sync.WaitGroup) { 16 | var i int 17 | wg.Add(1) 18 | go func() { 19 | defer wg.Done() 20 | i++ 21 | fmt.Printf("value of i: %v\n", i) 22 | }() 23 | fmt.Println("return from function") 24 | return 25 | } 26 | 27 | incr(&wg) 28 | wg.Wait() 29 | fmt.Println("done..") 30 | } 31 | -------------------------------------------------------------------------------- /01-exercise/01-goroutines/06-closure/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | func main() { 9 | var wg sync.WaitGroup 10 | 11 | // what is the output 12 | //TODO: fix the issue. 13 | 14 | for i := 1; i <= 3; i++ { 15 | wg.Add(1) 16 | go func() { 17 | defer wg.Done() 18 | fmt.Println(i) 19 | }() 20 | } 21 | wg.Wait() 22 | } 23 | -------------------------------------------------------------------------------- /01-exercise/02-channel/01-channel/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | go func(a, b int) { 5 | c := a + b 6 | }(1, 2) 7 | // TODO: get the value computed from goroutine 8 | // fmt.Printf("computed value %v\n", c) 9 | } 10 | -------------------------------------------------------------------------------- /01-exercise/02-channel/02-channel/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | go func() { 5 | for i := 0; i < 6; i++ { 6 | // TODO: send iterator over channel 7 | } 8 | }() 9 | 10 | // TODO: range over channel to recv values 11 | 12 | } 13 | -------------------------------------------------------------------------------- /01-exercise/02-channel/03-channel/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func main() { 8 | ch := make(chan int) 9 | 10 | go func() { 11 | defer close(ch) 12 | 13 | // TODO: send all iterator values on channel without blocking 14 | for i := 0; i < 6; i++ { 15 | fmt.Printf("Sending: %d\n", i) 16 | ch <- i 17 | } 18 | }() 19 | 20 | for v := range ch { 21 | fmt.Printf("Received: %v\n", v) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /01-exercise/02-channel/04-channel/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // TODO: Implement relaying of message with Channel Direction 4 | 5 | func genMsg() { 6 | // send message on ch1 7 | } 8 | 9 | func relayMsg() { 10 | // recv message on ch1 11 | // send it on ch2 12 | } 13 | 14 | func main() { 15 | // create ch1 and ch2 16 | 17 | // spine goroutine genMsg and relayMsg 18 | 19 | // recv message on ch2 20 | } 21 | -------------------------------------------------------------------------------- /01-exercise/02-channel/05-channel/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | //TODO: create channel owner goroutine which return channel and 7 | // writes data into channel and 8 | // closes the channel when done. 9 | 10 | consumer := func(ch <-chan int) { 11 | // read values from channel 12 | for v := range ch { 13 | fmt.Printf("Received: %d\n", v) 14 | } 15 | fmt.Println("Done receiving!") 16 | } 17 | 18 | ch := owner() 19 | consumer(ch) 20 | } 21 | -------------------------------------------------------------------------------- /01-exercise/03-select/01-select/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | func main() { 8 | ch1 := make(chan string) 9 | ch2 := make(chan string) 10 | 11 | go func() { 12 | time.Sleep(1 * time.Second) 13 | ch1 <- "one" 14 | }() 15 | 16 | go func() { 17 | time.Sleep(2 * time.Second) 18 | ch2 <- "two" 19 | }() 20 | 21 | // TODO: multiplex recv on channel - ch1, ch2 22 | 23 | } 24 | -------------------------------------------------------------------------------- /01-exercise/03-select/02-select/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func main() { 9 | ch := make(chan string, 1) 10 | 11 | go func() { 12 | time.Sleep(2 * time.Second) 13 | ch <- "one" 14 | }() 15 | 16 | // TODO: implement timeout for recv on channel ch 17 | 18 | m := <-ch 19 | fmt.Println(m) 20 | } 21 | -------------------------------------------------------------------------------- /01-exercise/03-select/03-select/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func main() { 9 | ch := make(chan string) 10 | 11 | go func() { 12 | for i := 0; i < 3; i++ { 13 | time.Sleep(1 * time.Second) 14 | ch <- "message" 15 | } 16 | 17 | }() 18 | 19 | // TODO: if there is no value on channel, do not block. 20 | for i := 0; i < 2; i++ { 21 | m := <-ch 22 | fmt.Println(m) 23 | 24 | // Do some processing.. 25 | fmt.Println("processing..") 26 | time.Sleep(1500 * time.Millisecond) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /01-exercise/04-sync/01-mutex/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | "sync" 7 | ) 8 | 9 | func main() { 10 | 11 | runtime.GOMAXPROCS(4) 12 | 13 | var balance int 14 | var wg sync.WaitGroup 15 | 16 | deposit := func(amount int) { 17 | balance += amount 18 | } 19 | 20 | withdrawal := func(amount int) { 21 | balance -= amount 22 | } 23 | 24 | // make 100 deposits of $1 25 | // and 100 withdrawal of $1 concurrently. 26 | // run the program and check result. 27 | 28 | // TODO: fix the issue for consistent output. 29 | 30 | wg.Add(100) 31 | for i := 0; i < 100; i++ { 32 | go func() { 33 | defer wg.Done() 34 | deposit(1) 35 | }() 36 | } 37 | 38 | wg.Add(100) 39 | for i := 0; i < 100; i++ { 40 | go func() { 41 | defer wg.Done() 42 | withdrawal(1) 43 | }() 44 | } 45 | 46 | wg.Wait() 47 | fmt.Println(balance) 48 | } 49 | -------------------------------------------------------------------------------- /01-exercise/04-sync/02-mutex/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | "sync" 7 | ) 8 | 9 | func main() { 10 | 11 | runtime.GOMAXPROCS(4) 12 | 13 | var balance int 14 | var wg sync.WaitGroup 15 | var mu sync.Mutex 16 | 17 | deposit := func(amount int) { 18 | mu.Lock() 19 | balance += amount 20 | mu.Unlock() 21 | } 22 | 23 | wg.Add(10) 24 | for i := 0; i < 10; i++ { 25 | go func() { 26 | defer wg.Done() 27 | deposit(1) 28 | }() 29 | } 30 | 31 | //TODO: implement concurrent read. 32 | // allow multiple reads, writes holds the lock exclusively. 33 | 34 | wg.Wait() 35 | fmt.Println(balance) 36 | } 37 | -------------------------------------------------------------------------------- /01-exercise/04-sync/11-atomic/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | "sync" 7 | ) 8 | 9 | func main() { 10 | runtime.GOMAXPROCS(4) 11 | 12 | var counter uint64 13 | var wg sync.WaitGroup 14 | 15 | // TODO: implement concurrency safe counter 16 | 17 | for i := 0; i < 50; i++ { 18 | wg.Add(1) 19 | go func() { 20 | defer wg.Done() 21 | for c := 0; c < 1000; c++ { 22 | counter++ 23 | } 24 | }() 25 | } 26 | wg.Wait() 27 | fmt.Println("counter: ", counter) 28 | } 29 | -------------------------------------------------------------------------------- /01-exercise/04-sync/21-cond/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | var sharedRsc = make(map[string]interface{}) 10 | 11 | func main() { 12 | var wg sync.WaitGroup 13 | 14 | wg.Add(1) 15 | go func() { 16 | defer wg.Done() 17 | 18 | //TODO: suspend goroutine until sharedRsc is populated. 19 | 20 | for len(sharedRsc) == 0 { 21 | time.Sleep(1 * time.Millisecond) 22 | } 23 | 24 | fmt.Println(sharedRsc["rsc1"]) 25 | }() 26 | 27 | // writes changes to sharedRsc 28 | sharedRsc["rsc1"] = "foo" 29 | 30 | wg.Wait() 31 | } 32 | -------------------------------------------------------------------------------- /01-exercise/04-sync/22-cond/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | var sharedRsc = make(map[string]interface{}) 10 | 11 | func main() { 12 | var wg sync.WaitGroup 13 | 14 | wg.Add(1) 15 | go func() { 16 | defer wg.Done() 17 | 18 | //TODO: suspend goroutine until sharedRsc is populated. 19 | 20 | for len(sharedRsc) == 0 { 21 | time.Sleep(1 * time.Millisecond) 22 | } 23 | 24 | fmt.Println(sharedRsc["rsc1"]) 25 | }() 26 | 27 | wg.Add(1) 28 | go func() { 29 | defer wg.Done() 30 | 31 | //TODO: suspend goroutine until sharedRsc is populated. 32 | 33 | for len(sharedRsc) == 0 { 34 | time.Sleep(1 * time.Millisecond) 35 | } 36 | 37 | fmt.Println(sharedRsc["rsc2"]) 38 | }() 39 | 40 | // writes changes to sharedRsc 41 | sharedRsc["rsc1"] = "foo" 42 | sharedRsc["rsc2"] = "bar" 43 | 44 | wg.Wait() 45 | } 46 | -------------------------------------------------------------------------------- /01-exercise/04-sync/31-once/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | func main() { 9 | var wg sync.WaitGroup 10 | 11 | load := func() { 12 | fmt.Println("Run only once initialization function") 13 | } 14 | 15 | wg.Add(10) 16 | for i := 0; i < 10; i++ { 17 | go func() { 18 | defer wg.Done() 19 | 20 | //TODO: modify so that load function gets called only once. 21 | load() 22 | }() 23 | } 24 | wg.Wait() 25 | } 26 | -------------------------------------------------------------------------------- /01-exercise/04-sync/41-pool/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | "os" 7 | "time" 8 | ) 9 | 10 | //TODO: create pool of bytes.Buffers which can be reused. 11 | 12 | func log(w io.Writer, val string) { 13 | var b bytes.Buffer 14 | 15 | b.WriteString(time.Now().Format("15:04:05")) 16 | b.WriteString(" : ") 17 | b.WriteString(val) 18 | b.WriteString("\n") 19 | 20 | w.Write(b.Bytes()) 21 | } 22 | 23 | func main() { 24 | log(os.Stdout, "debug-string1") 25 | log(os.Stdout, "debug-string2") 26 | } 27 | -------------------------------------------------------------------------------- /01-exercise/05-race/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | //TODO: identify the data race 10 | // fix the issue. 11 | 12 | func main() { 13 | start := time.Now() 14 | var t *time.Timer 15 | t = time.AfterFunc(randomDuration(), func() { 16 | fmt.Println(time.Now().Sub(start)) 17 | t.Reset(randomDuration()) 18 | }) 19 | time.Sleep(5 * time.Second) 20 | } 21 | 22 | func randomDuration() time.Duration { 23 | return time.Duration(rand.Int63n(1e9)) 24 | } 25 | 26 | //---------------------------------------------------- 27 | // (main goroutine) -> t <- (time.AfterFunc goroutine) 28 | //---------------------------------------------------- 29 | // (working condition) 30 | // main goroutine.. 31 | // t = time.AfterFunc() // returns a timer.. 32 | 33 | // AfterFunc goroutine 34 | // t.Reset() // timer reset 35 | //---------------------------------------------------- 36 | // (race condition- random duration is very small) 37 | // AfterFunc goroutine 38 | // t.Reset() // t = nil 39 | 40 | // main goroutine.. 41 | // t = time.AfterFunc() 42 | //---------------------------------------------------- 43 | -------------------------------------------------------------------------------- /01-exercise/07-exercise-web-crawler-solution/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "time" 7 | 8 | "golang.org/x/net/html" 9 | ) 10 | 11 | var fetched map[string]bool 12 | 13 | type result struct { 14 | url string 15 | urls []string 16 | err error 17 | depth int 18 | } 19 | 20 | // Crawl uses findLinks to recursively crawl 21 | // pages starting with url, to a maximum of depth. 22 | func Crawl(url string, depth int) { 23 | results := make(chan *result) 24 | 25 | fetch := func(url string, depth int) { 26 | urls, err := findLinks(url) 27 | results <- &result{url, urls, err, depth} 28 | } 29 | 30 | go fetch(url, depth) 31 | fetched[url] = true 32 | 33 | for fetching := 1; fetching > 0; fetching-- { 34 | res := <-results 35 | if res.err != nil { 36 | // fmt.Println(res.err) 37 | continue 38 | } 39 | 40 | fmt.Printf("found: %s\n", res.url) 41 | if res.depth > 0 { 42 | for _, u := range res.urls { 43 | if !fetched[u] { 44 | fetching++ 45 | go fetch(u, res.depth-1) 46 | fetched[u] = true 47 | } 48 | } 49 | } 50 | } 51 | close(results) 52 | } 53 | 54 | func main() { 55 | fetched = make(map[string]bool) 56 | now := time.Now() 57 | Crawl("http://andcloud.io", 2) 58 | fmt.Println("time taken:", time.Since(now)) 59 | } 60 | 61 | func findLinks(url string) ([]string, error) { 62 | resp, err := http.Get(url) 63 | if err != nil { 64 | return nil, err 65 | } 66 | if resp.StatusCode != http.StatusOK { 67 | resp.Body.Close() 68 | return nil, fmt.Errorf("getting %s: %s", url, resp.Status) 69 | } 70 | doc, err := html.Parse(resp.Body) 71 | resp.Body.Close() 72 | if err != nil { 73 | return nil, fmt.Errorf("parsing %s as HTML: %v", url, err) 74 | } 75 | return visit(nil, doc), nil 76 | } 77 | 78 | // visit appends to links each link found in n, and returns the result. 79 | func visit(links []string, n *html.Node) []string { 80 | if n.Type == html.ElementNode && n.Data == "a" { 81 | for _, a := range n.Attr { 82 | if a.Key == "href" { 83 | links = append(links, a.Val) 84 | } 85 | } 86 | } 87 | for c := n.FirstChild; c != nil; c = c.NextSibling { 88 | links = visit(links, c) 89 | } 90 | return links 91 | } 92 | -------------------------------------------------------------------------------- /01-exercise/07-exercise-web-crawler/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "time" 7 | 8 | "golang.org/x/net/html" 9 | ) 10 | 11 | var fetched map[string]bool 12 | 13 | // Crawl uses findLinks to recursively crawl 14 | // pages starting with url, to a maximum of depth. 15 | func Crawl(url string, depth int) { 16 | // TODO: Fetch URLs in parallel. 17 | 18 | if depth < 0 { 19 | return 20 | } 21 | urls, err := findLinks(url) 22 | if err != nil { 23 | // fmt.Println(err) 24 | return 25 | } 26 | fmt.Printf("found: %s\n", url) 27 | fetched[url] = true 28 | for _, u := range urls { 29 | if !fetched[u] { 30 | Crawl(u, depth-1) 31 | } 32 | } 33 | return 34 | } 35 | 36 | func main() { 37 | fetched = make(map[string]bool) 38 | now := time.Now() 39 | Crawl("http://andcloud.io", 2) 40 | fmt.Println("time taken:", time.Since(now)) 41 | } 42 | 43 | func findLinks(url string) ([]string, error) { 44 | resp, err := http.Get(url) 45 | if err != nil { 46 | return nil, err 47 | } 48 | if resp.StatusCode != http.StatusOK { 49 | resp.Body.Close() 50 | return nil, fmt.Errorf("getting %s: %s", url, resp.Status) 51 | } 52 | doc, err := html.Parse(resp.Body) 53 | resp.Body.Close() 54 | if err != nil { 55 | return nil, fmt.Errorf("parsing %s as HTML: %v", url, err) 56 | } 57 | return visit(nil, doc), nil 58 | } 59 | 60 | // visit appends to links each link found in n, and returns the result. 61 | func visit(links []string, n *html.Node) []string { 62 | if n.Type == html.ElementNode && n.Data == "a" { 63 | for _, a := range n.Attr { 64 | if a.Key == "href" { 65 | links = append(links, a.Val) 66 | } 67 | } 68 | } 69 | for c := n.FirstChild; c != nil; c = c.NextSibling { 70 | links = visit(links, c) 71 | } 72 | return links 73 | } 74 | -------------------------------------------------------------------------------- /02-class-codewalk/01-pipeline/main.go: -------------------------------------------------------------------------------- 1 | // Squaring numbers. 2 | 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | "sync" 8 | ) 9 | 10 | func generator(nums ...int) <-chan int { 11 | out := make(chan int) 12 | go func() { 13 | for _, n := range nums { 14 | out <- n 15 | } 16 | close(out) 17 | }() 18 | return out 19 | } 20 | 21 | func square(in <-chan int) <-chan int { 22 | out := make(chan int) 23 | go func() { 24 | for n := range in { 25 | out <- n * n 26 | } 27 | close(out) 28 | }() 29 | return out 30 | } 31 | 32 | func merge(cs ...<-chan int) <-chan int { 33 | out := make(chan int) 34 | var wg sync.WaitGroup 35 | 36 | output := func(c <-chan int) { 37 | for n := range c { 38 | out <- n 39 | } 40 | wg.Done() 41 | } 42 | 43 | wg.Add(len(cs)) 44 | for _, c := range cs { 45 | go output(c) 46 | } 47 | 48 | go func() { 49 | wg.Wait() 50 | close(out) 51 | }() 52 | return out 53 | } 54 | 55 | func main() { 56 | in := generator(2, 3) 57 | 58 | c1 := square(in) 59 | c2 := square(in) 60 | 61 | out := merge(c1, c2) 62 | fmt.Println(<-out) 63 | } 64 | -------------------------------------------------------------------------------- /02-class-codewalk/02-context/01-withcancel/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | ) 8 | 9 | func main() { 10 | generator := func(ctx context.Context) <-chan int { 11 | dst := make(chan int) 12 | n := 1 13 | go func() { 14 | defer close(dst) 15 | for { 16 | select { 17 | case <-ctx.Done(): 18 | log.Println(ctx.Err()) 19 | return 20 | case dst <- n: 21 | n++ 22 | } 23 | } 24 | }() 25 | return dst 26 | } 27 | 28 | // Create a context that is cancellable. 29 | ctx, cancel := context.WithCancel(context.Background()) 30 | 31 | ch := generator(ctx) 32 | for n := range ch { 33 | fmt.Println(n) 34 | if n == 5 { 35 | cancel() 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /02-class-codewalk/02-context/02-withdeadline/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | ) 8 | 9 | func main() { 10 | gen := func(ctx context.Context) <-chan int { 11 | dst := make(chan int) 12 | n := 1 13 | go func() { 14 | defer close(dst) 15 | 16 | numGenerator := func() error { 17 | deadline, ok := ctx.Deadline() 18 | if ok { 19 | if deadline.Sub(time.Now().Add(10*time.Millisecond)) <= 0 { 20 | return context.DeadlineExceeded 21 | } 22 | } 23 | for { 24 | select { 25 | case <-ctx.Done(): 26 | return ctx.Err() 27 | case dst <- n: 28 | n++ 29 | } 30 | } 31 | } 32 | 33 | err := numGenerator() 34 | if err != nil { 35 | fmt.Println(err) 36 | } 37 | }() 38 | return dst 39 | } 40 | 41 | deadline := time.Now().Add(5 * time.Millisecond) 42 | ctx, cancel := context.WithDeadline(context.Background(), deadline) 43 | defer cancel() 44 | 45 | for n := range gen(ctx) { 46 | fmt.Println(n) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /02-class-codewalk/02-context/03-withvalue/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | ) 7 | 8 | type userIDKey string 9 | type database map[string]bool 10 | 11 | var db database = database{ 12 | "jane": true, 13 | } 14 | 15 | func main() { 16 | ctx, cancel := context.WithCancel(context.Background()) 17 | defer cancel() 18 | processRequest(ctx, "jane") 19 | } 20 | 21 | func processRequest(ctx context.Context, userid string) { 22 | // send userID information to checkMemberShipStatus for 23 | // database lookup. 24 | vctx := context.WithValue(ctx, 25 | userIDKey("userIDKey"), 26 | userid) 27 | 28 | ch := checkMemberShipStatus(vctx) 29 | status := <-ch 30 | fmt.Printf("membership status of userid : %s : %v\n", userid, status) 31 | } 32 | 33 | // checkMemberShipStatus - takes context as input. 34 | // extracts the user id information from context. 35 | // spins a goroutine to do database lookup 36 | // sends the result on the returned channel. 37 | func checkMemberShipStatus(ctx context.Context) <-chan bool { 38 | ch := make(chan bool) 39 | go func() { 40 | defer close(ch) 41 | // do some database lookup 42 | userid := ctx.Value(userIDKey("userIDKey")).(string) 43 | status := db[userid] 44 | ch <- status 45 | }() 46 | return ch 47 | } 48 | -------------------------------------------------------------------------------- /02-exercise-solution/02-pipeline/01-pipeline/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func generator(nums ...int) <-chan int { 6 | out := make(chan int) 7 | 8 | go func() { 9 | for _, n := range nums { 10 | out <- n 11 | } 12 | close(out) 13 | }() 14 | return out 15 | } 16 | 17 | func square(in <-chan int) <-chan int { 18 | out := make(chan int) 19 | go func() { 20 | for n := range in { 21 | out <- n * n 22 | } 23 | close(out) 24 | }() 25 | return out 26 | } 27 | 28 | func main() { 29 | // set up the pipeline 30 | for n := range square(square(generator(2, 3))) { 31 | fmt.Println(n) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /02-exercise-solution/02-pipeline/02-pipeline/main.go: -------------------------------------------------------------------------------- 1 | // Squaring numbers. 2 | 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | "sync" 8 | ) 9 | 10 | func generator(nums ...int) <-chan int { 11 | out := make(chan int) 12 | go func() { 13 | for _, n := range nums { 14 | out <- n 15 | } 16 | close(out) 17 | }() 18 | return out 19 | } 20 | 21 | func square(in <-chan int) <-chan int { 22 | out := make(chan int) 23 | go func() { 24 | for n := range in { 25 | out <- n * n 26 | } 27 | close(out) 28 | }() 29 | return out 30 | } 31 | 32 | func merge(cs ...<-chan int) <-chan int { 33 | out := make(chan int) 34 | var wg sync.WaitGroup 35 | 36 | output := func(c <-chan int) { 37 | for n := range c { 38 | out <- n 39 | } 40 | wg.Done() 41 | } 42 | 43 | wg.Add(len(cs)) 44 | for _, c := range cs { 45 | go output(c) 46 | } 47 | 48 | go func() { 49 | wg.Wait() 50 | close(out) 51 | }() 52 | return out 53 | } 54 | 55 | func main() { 56 | in := generator(2, 3) 57 | 58 | c1 := square(in) 59 | c2 := square(in) 60 | 61 | for n := range merge(c1, c2) { 62 | fmt.Println(n) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /02-exercise-solution/02-pipeline/03-pipeline/main.go: -------------------------------------------------------------------------------- 1 | // Squaring numbers. 2 | 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | "sync" 8 | ) 9 | 10 | func generator(done <-chan struct{}, nums ...int) <-chan int { 11 | out := make(chan int) 12 | 13 | go func() { 14 | defer close(out) 15 | for _, n := range nums { 16 | select { 17 | case out <- n: 18 | case <-done: 19 | return 20 | } 21 | } 22 | 23 | }() 24 | return out 25 | } 26 | 27 | func square(done <-chan struct{}, in <-chan int) <-chan int { 28 | out := make(chan int) 29 | go func() { 30 | defer close(out) 31 | for n := range in { 32 | select { 33 | case out <- n * n: 34 | case <-done: 35 | return 36 | } 37 | } 38 | }() 39 | return out 40 | } 41 | 42 | func merge(done <-chan struct{}, cs ...<-chan int) <-chan int { 43 | out := make(chan int) 44 | var wg sync.WaitGroup 45 | 46 | output := func(c <-chan int) { 47 | defer wg.Done() 48 | for n := range c { 49 | select { 50 | case out <- n: 51 | case <-done: 52 | return 53 | } 54 | } 55 | } 56 | 57 | wg.Add(len(cs)) 58 | for _, c := range cs { 59 | go output(c) 60 | } 61 | 62 | go func() { 63 | wg.Wait() 64 | close(out) 65 | }() 66 | return out 67 | } 68 | 69 | func main() { 70 | done := make(chan struct{}) 71 | defer close(done) 72 | 73 | in := generator(done, 2, 3) 74 | 75 | c1 := square(done, in) 76 | c2 := square(done, in) 77 | 78 | out := merge(done, c1, c2) 79 | 80 | fmt.Println(<-out) 81 | } 82 | 83 | // guidelines for pipeline construction 84 | 85 | // stages close their outbound channels when all the send operations are done. 86 | // stages keep receiving values from inbound channels until those channels are closed or the senders are unblocked. 87 | -------------------------------------------------------------------------------- /02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/1.jpg -------------------------------------------------------------------------------- /02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/10.jpg -------------------------------------------------------------------------------- /02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/11.jpg -------------------------------------------------------------------------------- /02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/12.jpg -------------------------------------------------------------------------------- /02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/13.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/13.jpg -------------------------------------------------------------------------------- /02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/14.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/14.jpg -------------------------------------------------------------------------------- /02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/15.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/15.jpg -------------------------------------------------------------------------------- /02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/16.jpg -------------------------------------------------------------------------------- /02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/17.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/17.jpg -------------------------------------------------------------------------------- /02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/18.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/18.jpg -------------------------------------------------------------------------------- /02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/19.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/19.jpg -------------------------------------------------------------------------------- /02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/2.jpg -------------------------------------------------------------------------------- /02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/20.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/20.jpg -------------------------------------------------------------------------------- /02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/21.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/21.jpg -------------------------------------------------------------------------------- /02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/22.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/22.jpg -------------------------------------------------------------------------------- /02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/23.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/23.jpg -------------------------------------------------------------------------------- /02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/24.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/24.jpg -------------------------------------------------------------------------------- /02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/25.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/25.jpg -------------------------------------------------------------------------------- /02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/26.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/26.jpg -------------------------------------------------------------------------------- /02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/27.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/27.jpg -------------------------------------------------------------------------------- /02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/28.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/28.jpg -------------------------------------------------------------------------------- /02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/29.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/29.jpg -------------------------------------------------------------------------------- /02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/3.jpg -------------------------------------------------------------------------------- /02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/30.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/30.jpg -------------------------------------------------------------------------------- /02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/31.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/31.jpg -------------------------------------------------------------------------------- /02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/32.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/32.jpg -------------------------------------------------------------------------------- /02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/33.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/33.jpg -------------------------------------------------------------------------------- /02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/34.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/34.jpg -------------------------------------------------------------------------------- /02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/35.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/35.jpg -------------------------------------------------------------------------------- /02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/36.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/36.jpg -------------------------------------------------------------------------------- /02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/37.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/37.jpg -------------------------------------------------------------------------------- /02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/38.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/38.jpg -------------------------------------------------------------------------------- /02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/39.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/39.jpg -------------------------------------------------------------------------------- /02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/4.jpg -------------------------------------------------------------------------------- /02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/40.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/40.jpg -------------------------------------------------------------------------------- /02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/5.jpg -------------------------------------------------------------------------------- /02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/6.jpg -------------------------------------------------------------------------------- /02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/7.jpg -------------------------------------------------------------------------------- /02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/8.jpg -------------------------------------------------------------------------------- /02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise-solution/02-pipeline/04-image-processing-pipeline/imgs/9.jpg -------------------------------------------------------------------------------- /02-exercise-solution/02-pipeline/04-image-processing-pipeline/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "image" 6 | "log" 7 | "net/http" 8 | "os" 9 | "path/filepath" 10 | "sync" 11 | "time" 12 | 13 | "github.com/disintegration/imaging" 14 | ) 15 | 16 | // Pipeline 17 | // walkfile ----------> processImage -----------> saveImage 18 | // (paths) (results) 19 | 20 | type result struct { 21 | srcImagePath string 22 | thumbnailImage *image.NRGBA 23 | err error 24 | } 25 | 26 | // Image processing - Pipeline 27 | // Input - directory with images. 28 | // output - thumbnail images 29 | func main() { 30 | if len(os.Args) < 2 { 31 | log.Fatal("need to send directory path of images") 32 | } 33 | start := time.Now() 34 | err := setupPipeLine(os.Args[1]) 35 | 36 | if err != nil { 37 | log.Fatal(err) 38 | } 39 | fmt.Printf("Time taken: %s\n", time.Since(start)) 40 | } 41 | 42 | func setupPipeLine(root string) error { 43 | done := make(chan struct{}) 44 | defer close(done) 45 | 46 | // do the file walk 47 | paths, errc := walkFiles(done, root) 48 | 49 | // process the image 50 | results := processImage(done, paths) 51 | 52 | // save thumbnail images 53 | for r := range results { 54 | if r.err != nil { 55 | return r.err 56 | } 57 | saveThumbnail(r.srcImagePath, r.thumbnailImage) 58 | } 59 | 60 | // check for error on the channel, from walkfiles stage. 61 | if err := <-errc; err != nil { 62 | return err 63 | } 64 | return nil 65 | } 66 | 67 | func walkFiles(done <-chan struct{}, root string) (<-chan string, <-chan error) { 68 | 69 | // create output channels 70 | paths := make(chan string) 71 | errc := make(chan error, 1) 72 | 73 | go func() { 74 | defer close(paths) 75 | errc <- filepath.Walk(root, func(path string, info os.FileInfo, err error) error { 76 | 77 | // filter out error 78 | if err != nil { 79 | return err 80 | } 81 | 82 | // check if it is file 83 | if !info.Mode().IsRegular() { 84 | return nil 85 | } 86 | 87 | // check if it is image/jpeg 88 | contentType, _ := getFileContentType(path) 89 | if contentType != "image/jpeg" { 90 | return nil 91 | } 92 | 93 | // send file path to next stage 94 | select { 95 | case paths <- path: 96 | case <-done: 97 | return fmt.Errorf("walk cancelled") 98 | } 99 | return nil 100 | }) 101 | }() 102 | return paths, errc 103 | } 104 | 105 | func processImage(done <-chan struct{}, paths <-chan string) <-chan result { 106 | results := make(chan result) 107 | var wg sync.WaitGroup 108 | 109 | thumbnailer := func() { 110 | for srcImagePath := range paths { 111 | srcImage, err := imaging.Open(srcImagePath) 112 | if err != nil { 113 | select { 114 | case results <- result{srcImagePath, nil, err}: 115 | case <-done: 116 | return 117 | } 118 | } 119 | thumbnailImage := imaging.Thumbnail(srcImage, 100, 100, imaging.Lanczos) 120 | 121 | select { 122 | case results <- result{srcImagePath, thumbnailImage, err}: 123 | case <-done: 124 | return 125 | } 126 | } 127 | } 128 | 129 | const numThumbnailer = 5 130 | for i := 0; i < numThumbnailer; i++ { 131 | wg.Add(1) 132 | go func() { 133 | thumbnailer() 134 | wg.Done() 135 | }() 136 | } 137 | 138 | go func() { 139 | wg.Wait() 140 | close(results) 141 | }() 142 | return results 143 | } 144 | 145 | // saveThumbnail - save the thumnail image to folder 146 | func saveThumbnail(srcImagePath string, thumbnailImage *image.NRGBA) error { 147 | filename := filepath.Base(srcImagePath) 148 | dstImagePath := "thumbnail/" + filename 149 | 150 | // save the image in the thumbnail folder. 151 | err := imaging.Save(thumbnailImage, dstImagePath) 152 | if err != nil { 153 | return err 154 | } 155 | fmt.Printf("%s -> %s\n", srcImagePath, dstImagePath) 156 | return nil 157 | } 158 | 159 | // getFileContentType - return content type and error status 160 | func getFileContentType(file string) (string, error) { 161 | 162 | out, err := os.Open(file) 163 | if err != nil { 164 | return "", err 165 | } 166 | defer out.Close() 167 | 168 | // Only the first 512 bytes are used to sniff the content type. 169 | buffer := make([]byte, 512) 170 | 171 | _, err = out.Read(buffer) 172 | if err != nil { 173 | return "", err 174 | } 175 | 176 | // Use the net/http package's handy DectectContentType function. Always returns a valid 177 | // content-type by returning "application/octet-stream" if no others seemed to match. 178 | contentType := http.DetectContentType(buffer) 179 | 180 | return contentType, nil 181 | } 182 | -------------------------------------------------------------------------------- /02-exercise-solution/02-pipeline/04-image-processing-pipeline/main_premature_termination.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "image" 6 | "log" 7 | "net/http" 8 | "os" 9 | "path/filepath" 10 | "sync" 11 | "time" 12 | 13 | "github.com/disintegration/imaging" 14 | ) 15 | 16 | // Pipeline 17 | // walkfile ----------> processImage -----------> saveImage 18 | // (paths) (results) 19 | 20 | type result struct { 21 | srcImagePath string 22 | thumbnailImage *image.NRGBA 23 | err error 24 | } 25 | 26 | // Image processing - Pipeline 27 | // Input - directory with images. 28 | // output - thumbnail images 29 | func main() { 30 | if len(os.Args) < 2 { 31 | log.Fatal("need to send directory path of images") 32 | } 33 | start := time.Now() 34 | err := setupPipeLine(os.Args[1]) 35 | 36 | if err != nil { 37 | log.Fatal(err) 38 | } 39 | fmt.Printf("Time taken: %s\n", time.Since(start)) 40 | } 41 | 42 | type StageError struct { 43 | Stage string 44 | Err error 45 | } 46 | 47 | func (s *StageError) Error() string { 48 | return fmt.Sprintf("stage = %s: error = %v", s.Stage, s.Err) 49 | } 50 | 51 | func setupPipeLine(root string) error { 52 | done := make(chan struct{}) 53 | // defer close(done) 54 | 55 | // do the file walk 56 | paths, errc := walkFiles(done, root) 57 | 58 | // check for error on the errc channel, 59 | // but dont block on errc channel. 60 | select { 61 | case err := <-errc: 62 | if err != nil { 63 | return &StageError{ 64 | Stage: "walkFiles", 65 | Err: err, 66 | } 67 | } 68 | default: 69 | } 70 | 71 | // process the image 72 | results := processImage(done, paths) 73 | 74 | counter := 0 75 | // save thumbnail images 76 | for r := range results { 77 | if r.err != nil { 78 | return &StageError{ 79 | Stage: "processImage", 80 | Err: r.err, 81 | } 82 | } 83 | saveThumbnail(r.srcImagePath, r.thumbnailImage) 84 | counter++ 85 | if counter == 5 { 86 | close(done) 87 | } 88 | } 89 | 90 | // check for error on the errc channel, 91 | // return error if filepath.Walk returned error. 92 | if err := <-errc; err != nil { 93 | return &StageError{ 94 | Stage: "walkFiles", 95 | Err: err, 96 | } 97 | } 98 | return nil 99 | } 100 | 101 | func walkFiles(done <-chan struct{}, root string) (<-chan string, <-chan error) { 102 | 103 | // create output channels 104 | paths := make(chan string) 105 | errc := make(chan error, 1) 106 | 107 | go func() { 108 | defer close(paths) 109 | errc <- filepath.Walk(root, func(path string, info os.FileInfo, err error) error { 110 | 111 | // filter out error 112 | if err != nil { 113 | return err 114 | } 115 | 116 | // check if it is file 117 | if !info.Mode().IsRegular() { 118 | return nil 119 | } 120 | 121 | // check if it is image/jpeg 122 | contentType, _ := getFileContentType(path) 123 | if contentType != "image/jpeg" { 124 | return nil 125 | } 126 | 127 | // send file path to next stage 128 | select { 129 | case paths <- path: 130 | case <-done: 131 | return fmt.Errorf("walk cancelled") 132 | } 133 | return nil 134 | }) 135 | }() 136 | return paths, errc 137 | } 138 | 139 | func processImage(done <-chan struct{}, paths <-chan string) <-chan result { 140 | results := make(chan result) 141 | var wg sync.WaitGroup 142 | 143 | thumbnailer := func() { 144 | for srcImagePath := range paths { 145 | srcImage, err := imaging.Open(srcImagePath) 146 | if err != nil { 147 | select { 148 | case results <- result{srcImagePath, nil, err}: 149 | case <-done: 150 | return 151 | } 152 | } 153 | thumbnailImage := imaging.Thumbnail(srcImage, 100, 100, imaging.Lanczos) 154 | 155 | select { 156 | case results <- result{srcImagePath, thumbnailImage, err}: 157 | case <-done: 158 | return 159 | } 160 | } 161 | } 162 | 163 | const numThumbnailer = 5 164 | for i := 0; i < numThumbnailer; i++ { 165 | wg.Add(1) 166 | go func() { 167 | thumbnailer() 168 | wg.Done() 169 | }() 170 | } 171 | 172 | go func() { 173 | wg.Wait() 174 | close(results) 175 | }() 176 | return results 177 | } 178 | 179 | // saveThumbnail - save the thumnail image to folder 180 | func saveThumbnail(srcImagePath string, thumbnailImage *image.NRGBA) error { 181 | filename := filepath.Base(srcImagePath) 182 | dstImagePath := "thumbnail/" + filename 183 | 184 | // save the image in the thumbnail folder. 185 | err := imaging.Save(thumbnailImage, dstImagePath) 186 | if err != nil { 187 | return err 188 | } 189 | fmt.Printf("%s -> %s\n", srcImagePath, dstImagePath) 190 | return nil 191 | } 192 | 193 | // getFileContentType - return content type and error status 194 | func getFileContentType(file string) (string, error) { 195 | 196 | out, err := os.Open(file) 197 | if err != nil { 198 | return "", err 199 | } 200 | defer out.Close() 201 | 202 | // Only the first 512 bytes are used to sniff the content type. 203 | buffer := make([]byte, 512) 204 | 205 | _, err = out.Read(buffer) 206 | if err != nil { 207 | return "", err 208 | } 209 | 210 | // Use the net/http package's handy DectectContentType function. Always returns a valid 211 | // content-type by returning "application/octet-stream" if no others seemed to match. 212 | contentType := http.DetectContentType(buffer) 213 | 214 | return contentType, nil 215 | } 216 | -------------------------------------------------------------------------------- /02-exercise-solution/02-pipeline/04-image-processing-pipeline/thumbnail/testfile: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise-solution/02-pipeline/04-image-processing-pipeline/thumbnail/testfile -------------------------------------------------------------------------------- /02-exercise-solution/03-context/01-withcancel/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | ) 7 | 8 | func main() { 9 | 10 | // TODO: gen generates integers in a separate goroutine and 11 | // sends them to the returned channel. 12 | // The callers of gen need to cancel the context once 13 | // they consume 5 integers 14 | // so that internal goroutine 15 | // started by gen is not leaked. 16 | gen := func(ctx context.Context) <-chan int { 17 | dst := make(chan int) 18 | n := 1 19 | go func() { 20 | defer close(dst) 21 | for { 22 | select { 23 | case <-ctx.Done(): 24 | return 25 | case dst <- n: 26 | n++ 27 | } 28 | } 29 | }() 30 | return dst 31 | } 32 | 33 | // Create a context that is cancellable. 34 | ctx, cancel := context.WithCancel(context.Background()) 35 | 36 | ch := gen(ctx) 37 | for n := range ch { 38 | fmt.Println(n) 39 | if n == 5 { 40 | cancel() 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /02-exercise-solution/03-context/02-withdeadline/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | ) 8 | 9 | type data struct { 10 | result string 11 | } 12 | 13 | func main() { 14 | 15 | // set deadline for goroutine to return computational result. 16 | deadline := time.Now().Add(5 * time.Millisecond) 17 | ctx, cancel := context.WithDeadline(context.Background(), deadline) 18 | defer cancel() 19 | 20 | compute := func() <-chan data { 21 | ch := make(chan data) 22 | go func() { 23 | defer close(ch) 24 | 25 | deadline, ok := ctx.Deadline() 26 | if ok { 27 | if deadline.Sub(time.Now().Add(50*time.Millisecond)) < 0 { 28 | fmt.Println("not sufficient time given, terminating") 29 | return 30 | } 31 | } 32 | 33 | // Simulate work. 34 | time.Sleep(50 * time.Millisecond) 35 | 36 | // Report result. 37 | select { 38 | case ch <- data{"123"}: 39 | case <-ctx.Done(): 40 | fmt.Println("work cancelled") 41 | return 42 | } 43 | 44 | }() 45 | return ch 46 | } 47 | 48 | // Wait for the work to finish. If it takes too long move on. 49 | 50 | ch := compute() 51 | d, ok := <-ch 52 | if ok { 53 | fmt.Println("work complete", d) 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /02-exercise-solution/03-context/03-timeout/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "io" 6 | "log" 7 | "net/http" 8 | "os" 9 | "time" 10 | ) 11 | 12 | func main() { 13 | 14 | // set a timeout for http get operation. 15 | req, err := http.NewRequest("GET", "https://andcloud.io", nil) 16 | if err != nil { 17 | log.Fatal(err) 18 | } 19 | 20 | // Create a context with a timeout of 100 milliseconds. 21 | ctx, cancel := context.WithTimeout(req.Context(), 100*time.Millisecond) 22 | defer cancel() 23 | 24 | // Bind the new context into the request. 25 | req = req.WithContext(ctx) 26 | 27 | resp, err := http.DefaultClient.Do(req) 28 | if err != nil { 29 | log.Println("ERROR:", err) 30 | return 31 | } 32 | 33 | // Close the response body on the return. 34 | defer resp.Body.Close() 35 | 36 | // Write the response to stdout. 37 | io.Copy(os.Stdout, resp.Body) 38 | } 39 | -------------------------------------------------------------------------------- /02-exercise-solution/03-context/04-value/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | ) 7 | 8 | type userIDKey string 9 | type database map[string]bool 10 | 11 | var db database = database{ 12 | "jane": true, 13 | } 14 | 15 | func main() { 16 | ctx, cancel := context.WithCancel(context.Background()) 17 | defer cancel() 18 | processRequest(ctx, "jane") 19 | } 20 | 21 | func processRequest(ctx context.Context, userid string) { 22 | // send userID information to checkMemberShipStatus for 23 | // database lookup. 24 | vctx := context.WithValue(ctx, 25 | userIDKey("userIDKey"), 26 | userid) 27 | 28 | ch := checkMemberShipStatus(vctx) 29 | status := <-ch 30 | fmt.Printf("membership status of userid : %s : %v\n", userid, status) 31 | } 32 | 33 | // checkMemberShipStatus - takes context as input. 34 | // extracts the user id information from context. 35 | // spins a goroutine to do database lookup 36 | // sends the result on the returned channel. 37 | func checkMemberShipStatus(ctx context.Context) <-chan bool { 38 | ch := make(chan bool) 39 | go func() { 40 | defer close(ch) 41 | // do some database lookup 42 | userid := ctx.Value(userIDKey("userIDKey")).(string) 43 | status := db[userid] 44 | ch <- status 45 | }() 46 | return ch 47 | } 48 | -------------------------------------------------------------------------------- /02-exercise-solution/03-context/05-server-timeout/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "errors" 7 | "fmt" 8 | "log" 9 | "net/http" 10 | "time" 11 | 12 | _ "github.com/lib/pq" 13 | ) 14 | 15 | var db *sql.DB 16 | 17 | func slowQuery(ctx context.Context) error { 18 | _, err := db.ExecContext(ctx, "SELECT pg_sleep(5)") 19 | return err 20 | } 21 | 22 | func slowHandler(w http.ResponseWriter, req *http.Request) { 23 | start := time.Now() 24 | err := slowQuery(req.Context()) 25 | if err != nil { 26 | switch { 27 | case errors.Is(err, context.Canceled): 28 | log.Printf("Warning: %s\n", err.Error()) 29 | default: 30 | log.Printf("Error: %s\n", err.Error()) 31 | } 32 | return 33 | } 34 | fmt.Fprintln(w, "OK") 35 | fmt.Printf("slowHandler took: %v\n", time.Since(start)) 36 | } 37 | 38 | func main() { 39 | var err error 40 | 41 | connstr := "host=localhost port=5432 user=alice password=pa$$word dbname=wonderland sslmode=disable" 42 | 43 | db, err = sql.Open("postgres", connstr) 44 | if err != nil { 45 | log.Fatal(err) 46 | } 47 | 48 | ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 49 | defer cancel() 50 | 51 | if err = db.PingContext(ctx); err != nil { 52 | log.Fatal(err) 53 | } 54 | srv := http.Server{ 55 | Addr: "localhost:8000", 56 | WriteTimeout: 2 * time.Second, 57 | Handler: http.TimeoutHandler(http.HandlerFunc(slowHandler), 1*time.Second, "Timeout!\n"), 58 | } 59 | 60 | if err := srv.ListenAndServe(); err != nil { 61 | fmt.Printf("Server failed: %s\n", err) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /02-exercise/02-pipeline/01-pipeline/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // TODO: Build a Pipeline 4 | // generator() -> square() -> print 5 | 6 | // generator - convertes a list of integers to a channel 7 | func generator(nums ...int) { 8 | 9 | } 10 | 11 | // square - receive on inbound channel 12 | // square the number 13 | // output on outbound channel 14 | func square() { 15 | 16 | } 17 | 18 | func main() { 19 | // set up the pipeline 20 | 21 | // run the last stage of pipeline 22 | // receive the values from square stage 23 | // print each one, until channel is closed. 24 | 25 | } 26 | -------------------------------------------------------------------------------- /02-exercise/02-pipeline/02-pipeline/main.go: -------------------------------------------------------------------------------- 1 | // generator() -> square() -> print 2 | 3 | package main 4 | 5 | func generator(nums ...int) <-chan int { 6 | out := make(chan int) 7 | 8 | go func() { 9 | for _, n := range nums { 10 | out <- n 11 | } 12 | close(out) 13 | }() 14 | return out 15 | } 16 | 17 | func square(in <-chan int) <-chan int { 18 | out := make(chan int) 19 | go func() { 20 | for n := range in { 21 | out <- n * n 22 | } 23 | close(out) 24 | }() 25 | return out 26 | } 27 | 28 | func merge(cs ...<-chan int) <-chan int { 29 | // Implement fan-in 30 | // merge a list of channels to a single channel 31 | } 32 | 33 | func main() { 34 | in := generator(2, 3) 35 | 36 | // TODO: fan out square stage to run two instances. 37 | 38 | // TODO: fan in the results of square stages. 39 | 40 | } 41 | -------------------------------------------------------------------------------- /02-exercise/02-pipeline/03-pipeline/main.go: -------------------------------------------------------------------------------- 1 | // generator() -> square() -> 2 | // -> merge -> print 3 | // -> square() -> 4 | package main 5 | 6 | import ( 7 | "fmt" 8 | "sync" 9 | ) 10 | 11 | func generator(nums ...int) <-chan int { 12 | out := make(chan int) 13 | 14 | go func() { 15 | for _, n := range nums { 16 | out <- n 17 | } 18 | close(out) 19 | }() 20 | return out 21 | } 22 | 23 | func square(in <-chan int) <-chan int { 24 | out := make(chan int) 25 | go func() { 26 | for n := range in { 27 | out <- n * n 28 | } 29 | close(out) 30 | }() 31 | return out 32 | } 33 | 34 | func merge(cs ...<-chan int) <-chan int { 35 | out := make(chan int) 36 | var wg sync.WaitGroup 37 | 38 | output := func(c <-chan int) { 39 | for n := range c { 40 | out <- n 41 | } 42 | wg.Done() 43 | } 44 | 45 | wg.Add(len(cs)) 46 | for _, c := range cs { 47 | go output(c) 48 | } 49 | 50 | go func() { 51 | wg.Wait() 52 | close(out) 53 | }() 54 | return out 55 | } 56 | 57 | func main() { 58 | in := generator(2, 3) 59 | 60 | c1 := square(in) 61 | c2 := square(in) 62 | 63 | out := merge(c1, c2) 64 | 65 | // TODO: cancel goroutines after receiving one value. 66 | 67 | fmt.Println(<-out) 68 | } 69 | -------------------------------------------------------------------------------- /02-exercise/02-pipeline/04-image-processing-sequential/imgs/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise/02-pipeline/04-image-processing-sequential/imgs/1.jpg -------------------------------------------------------------------------------- /02-exercise/02-pipeline/04-image-processing-sequential/imgs/10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise/02-pipeline/04-image-processing-sequential/imgs/10.jpg -------------------------------------------------------------------------------- /02-exercise/02-pipeline/04-image-processing-sequential/imgs/11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise/02-pipeline/04-image-processing-sequential/imgs/11.jpg -------------------------------------------------------------------------------- /02-exercise/02-pipeline/04-image-processing-sequential/imgs/12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise/02-pipeline/04-image-processing-sequential/imgs/12.jpg -------------------------------------------------------------------------------- /02-exercise/02-pipeline/04-image-processing-sequential/imgs/13.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise/02-pipeline/04-image-processing-sequential/imgs/13.jpg -------------------------------------------------------------------------------- /02-exercise/02-pipeline/04-image-processing-sequential/imgs/14.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise/02-pipeline/04-image-processing-sequential/imgs/14.jpg -------------------------------------------------------------------------------- /02-exercise/02-pipeline/04-image-processing-sequential/imgs/15.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise/02-pipeline/04-image-processing-sequential/imgs/15.jpg -------------------------------------------------------------------------------- /02-exercise/02-pipeline/04-image-processing-sequential/imgs/16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise/02-pipeline/04-image-processing-sequential/imgs/16.jpg -------------------------------------------------------------------------------- /02-exercise/02-pipeline/04-image-processing-sequential/imgs/17.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise/02-pipeline/04-image-processing-sequential/imgs/17.jpg -------------------------------------------------------------------------------- /02-exercise/02-pipeline/04-image-processing-sequential/imgs/18.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise/02-pipeline/04-image-processing-sequential/imgs/18.jpg -------------------------------------------------------------------------------- /02-exercise/02-pipeline/04-image-processing-sequential/imgs/19.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise/02-pipeline/04-image-processing-sequential/imgs/19.jpg -------------------------------------------------------------------------------- /02-exercise/02-pipeline/04-image-processing-sequential/imgs/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise/02-pipeline/04-image-processing-sequential/imgs/2.jpg -------------------------------------------------------------------------------- /02-exercise/02-pipeline/04-image-processing-sequential/imgs/20.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise/02-pipeline/04-image-processing-sequential/imgs/20.jpg -------------------------------------------------------------------------------- /02-exercise/02-pipeline/04-image-processing-sequential/imgs/21.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise/02-pipeline/04-image-processing-sequential/imgs/21.jpg -------------------------------------------------------------------------------- /02-exercise/02-pipeline/04-image-processing-sequential/imgs/22.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise/02-pipeline/04-image-processing-sequential/imgs/22.jpg -------------------------------------------------------------------------------- /02-exercise/02-pipeline/04-image-processing-sequential/imgs/23.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise/02-pipeline/04-image-processing-sequential/imgs/23.jpg -------------------------------------------------------------------------------- /02-exercise/02-pipeline/04-image-processing-sequential/imgs/24.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise/02-pipeline/04-image-processing-sequential/imgs/24.jpg -------------------------------------------------------------------------------- /02-exercise/02-pipeline/04-image-processing-sequential/imgs/25.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise/02-pipeline/04-image-processing-sequential/imgs/25.jpg -------------------------------------------------------------------------------- /02-exercise/02-pipeline/04-image-processing-sequential/imgs/26.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise/02-pipeline/04-image-processing-sequential/imgs/26.jpg -------------------------------------------------------------------------------- /02-exercise/02-pipeline/04-image-processing-sequential/imgs/27.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise/02-pipeline/04-image-processing-sequential/imgs/27.jpg -------------------------------------------------------------------------------- /02-exercise/02-pipeline/04-image-processing-sequential/imgs/28.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise/02-pipeline/04-image-processing-sequential/imgs/28.jpg -------------------------------------------------------------------------------- /02-exercise/02-pipeline/04-image-processing-sequential/imgs/29.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise/02-pipeline/04-image-processing-sequential/imgs/29.jpg -------------------------------------------------------------------------------- /02-exercise/02-pipeline/04-image-processing-sequential/imgs/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise/02-pipeline/04-image-processing-sequential/imgs/3.jpg -------------------------------------------------------------------------------- /02-exercise/02-pipeline/04-image-processing-sequential/imgs/30.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise/02-pipeline/04-image-processing-sequential/imgs/30.jpg -------------------------------------------------------------------------------- /02-exercise/02-pipeline/04-image-processing-sequential/imgs/31.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise/02-pipeline/04-image-processing-sequential/imgs/31.jpg -------------------------------------------------------------------------------- /02-exercise/02-pipeline/04-image-processing-sequential/imgs/32.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise/02-pipeline/04-image-processing-sequential/imgs/32.jpg -------------------------------------------------------------------------------- /02-exercise/02-pipeline/04-image-processing-sequential/imgs/33.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise/02-pipeline/04-image-processing-sequential/imgs/33.jpg -------------------------------------------------------------------------------- /02-exercise/02-pipeline/04-image-processing-sequential/imgs/34.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise/02-pipeline/04-image-processing-sequential/imgs/34.jpg -------------------------------------------------------------------------------- /02-exercise/02-pipeline/04-image-processing-sequential/imgs/35.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise/02-pipeline/04-image-processing-sequential/imgs/35.jpg -------------------------------------------------------------------------------- /02-exercise/02-pipeline/04-image-processing-sequential/imgs/36.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise/02-pipeline/04-image-processing-sequential/imgs/36.jpg -------------------------------------------------------------------------------- /02-exercise/02-pipeline/04-image-processing-sequential/imgs/37.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise/02-pipeline/04-image-processing-sequential/imgs/37.jpg -------------------------------------------------------------------------------- /02-exercise/02-pipeline/04-image-processing-sequential/imgs/38.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise/02-pipeline/04-image-processing-sequential/imgs/38.jpg -------------------------------------------------------------------------------- /02-exercise/02-pipeline/04-image-processing-sequential/imgs/39.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise/02-pipeline/04-image-processing-sequential/imgs/39.jpg -------------------------------------------------------------------------------- /02-exercise/02-pipeline/04-image-processing-sequential/imgs/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise/02-pipeline/04-image-processing-sequential/imgs/4.jpg -------------------------------------------------------------------------------- /02-exercise/02-pipeline/04-image-processing-sequential/imgs/40.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise/02-pipeline/04-image-processing-sequential/imgs/40.jpg -------------------------------------------------------------------------------- /02-exercise/02-pipeline/04-image-processing-sequential/imgs/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise/02-pipeline/04-image-processing-sequential/imgs/5.jpg -------------------------------------------------------------------------------- /02-exercise/02-pipeline/04-image-processing-sequential/imgs/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise/02-pipeline/04-image-processing-sequential/imgs/6.jpg -------------------------------------------------------------------------------- /02-exercise/02-pipeline/04-image-processing-sequential/imgs/7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise/02-pipeline/04-image-processing-sequential/imgs/7.jpg -------------------------------------------------------------------------------- /02-exercise/02-pipeline/04-image-processing-sequential/imgs/8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise/02-pipeline/04-image-processing-sequential/imgs/8.jpg -------------------------------------------------------------------------------- /02-exercise/02-pipeline/04-image-processing-sequential/imgs/9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise/02-pipeline/04-image-processing-sequential/imgs/9.jpg -------------------------------------------------------------------------------- /02-exercise/02-pipeline/04-image-processing-sequential/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "image" 6 | "log" 7 | "net/http" 8 | "os" 9 | "path/filepath" 10 | "time" 11 | 12 | "github.com/disintegration/imaging" 13 | ) 14 | 15 | // Image processing - sequential 16 | // Input - directory with images. 17 | // output - thumbnail images 18 | func main() { 19 | if len(os.Args) < 2 { 20 | log.Fatal("need to send directory path of images") 21 | } 22 | start := time.Now() 23 | 24 | err := walkFiles(os.Args[1]) 25 | 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | fmt.Printf("Time taken: %s\n", time.Since(start)) 30 | } 31 | 32 | // walfiles - take diretory path as input 33 | // does the file walk 34 | // generates thumbnail images 35 | // saves the image to thumbnail directory. 36 | func walkFiles(root string) error { 37 | err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error { 38 | 39 | // filter out error 40 | if err != nil { 41 | return err 42 | } 43 | 44 | // check if it is file 45 | if !info.Mode().IsRegular() { 46 | return nil 47 | } 48 | 49 | // check if it is image/jpeg 50 | contentType, _ := getFileContentType(path) 51 | if contentType != "image/jpeg" { 52 | return nil 53 | } 54 | 55 | // process the image 56 | thumbnailImage, err := processImage(path) 57 | if err != nil { 58 | return err 59 | } 60 | 61 | // save the thumbnail image to disk 62 | err = saveThumbnail(path, thumbnailImage) 63 | if err != nil { 64 | return err 65 | } 66 | return nil 67 | }) 68 | 69 | if err != nil { 70 | return err 71 | } 72 | return nil 73 | } 74 | 75 | // processImage - takes image file as input 76 | // return pointer to thumbnail image in memory. 77 | func processImage(path string) (*image.NRGBA, error) { 78 | 79 | // load the image from file 80 | srcImage, err := imaging.Open(path) 81 | if err != nil { 82 | return nil, err 83 | } 84 | 85 | // scale the image to 100px * 100px 86 | thumbnailImage := imaging.Thumbnail(srcImage, 100, 100, imaging.Lanczos) 87 | 88 | return thumbnailImage, nil 89 | } 90 | 91 | // saveThumbnail - save the thumnail image to folder 92 | func saveThumbnail(srcImagePath string, thumbnailImage *image.NRGBA) error { 93 | filename := filepath.Base(srcImagePath) 94 | dstImagePath := "thumbnail/" + filename 95 | 96 | // save the image in the thumbnail folder. 97 | err := imaging.Save(thumbnailImage, dstImagePath) 98 | if err != nil { 99 | return err 100 | } 101 | fmt.Printf("%s -> %s\n", srcImagePath, dstImagePath) 102 | return nil 103 | } 104 | 105 | // getFileContentType - return content type and error status 106 | func getFileContentType(file string) (string, error) { 107 | 108 | out, err := os.Open(file) 109 | if err != nil { 110 | return "", err 111 | } 112 | defer out.Close() 113 | 114 | // Only the first 512 bytes are used to sniff the content type. 115 | buffer := make([]byte, 512) 116 | 117 | _, err = out.Read(buffer) 118 | if err != nil { 119 | return "", err 120 | } 121 | 122 | // Use the net/http package's handy DectectContentType function. Always returns a valid 123 | // content-type by returning "application/octet-stream" if no others seemed to match. 124 | contentType := http.DetectContentType(buffer) 125 | 126 | return contentType, nil 127 | } 128 | -------------------------------------------------------------------------------- /02-exercise/02-pipeline/04-image-processing-sequential/thumbnail/testfile: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/02-exercise/02-pipeline/04-image-processing-sequential/thumbnail/testfile -------------------------------------------------------------------------------- /02-exercise/03-context/01-withcancel/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | 5 | // TODO: generator - generates integers in a separate goroutine and 6 | // sends them to the returned channel. 7 | // The callers of gen need to cancel the goroutine once 8 | // they consume 5th integer value 9 | // so that internal goroutine 10 | // started by gen is not leaked. 11 | generator := func() <-chan int { 12 | 13 | } 14 | 15 | // Create a context that is cancellable. 16 | 17 | } 18 | -------------------------------------------------------------------------------- /02-exercise/03-context/02-withdeadline/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | type data struct { 9 | result string 10 | } 11 | 12 | func main() { 13 | 14 | // TODO: set deadline for goroutine to return computational result. 15 | 16 | compute := func() <-chan data { 17 | ch := make(chan data) 18 | go func() { 19 | defer close(ch) 20 | // Simulate work. 21 | time.Sleep(50 * time.Millisecond) 22 | 23 | // Report result. 24 | ch <- data{"123"} 25 | }() 26 | return ch 27 | } 28 | 29 | // Wait for the work to finish. If it takes too long move on. 30 | ch := compute() 31 | d := <-ch 32 | fmt.Printf("work complete: %s\n", d) 33 | 34 | } 35 | -------------------------------------------------------------------------------- /02-exercise/03-context/03-timeout/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io" 5 | "log" 6 | "net/http" 7 | "os" 8 | ) 9 | 10 | func main() { 11 | 12 | // TODO: set a http client timeout 13 | 14 | req, err := http.NewRequest("GET", "https://andcloud.io", nil) 15 | if err != nil { 16 | log.Fatal(err) 17 | } 18 | 19 | resp, err := http.DefaultClient.Do(req) 20 | if err != nil { 21 | log.Println("ERROR:", err) 22 | return 23 | } 24 | 25 | // Close the response body on the return. 26 | defer resp.Body.Close() 27 | 28 | // Write the response to stdout. 29 | io.Copy(os.Stdout, resp.Body) 30 | } 31 | -------------------------------------------------------------------------------- /02-exercise/03-context/04-value/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type database map[string]bool 8 | 9 | var db database = database{ 10 | "jane": true, 11 | } 12 | 13 | func main() { 14 | processRequest("jane") 15 | } 16 | 17 | func processRequest(userid string) { 18 | // TODO: send userID information to checkMemberShip through context for 19 | // map lookup. 20 | ch := checkMemberShip() 21 | status := <-ch 22 | fmt.Printf("membership status of userid : %s : %v\n", userid, status) 23 | } 24 | 25 | // checkMemberShip - takes context as input. 26 | // extracts the user id information from context. 27 | // spins a goroutine to do map lookup 28 | // sends the result on the returned channel. 29 | func checkMemberShip() <-chan bool { 30 | ch := make(chan bool) 31 | go func() { 32 | defer close(ch) 33 | // do some database lookup 34 | status := db[userid] 35 | ch <- status 36 | }() 37 | return ch 38 | } 39 | -------------------------------------------------------------------------------- /02-exercise/03-context/05-server-timeout/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | "log" 7 | "net/http" 8 | "time" 9 | 10 | _ "github.com/lib/pq" 11 | ) 12 | 13 | var db *sql.DB 14 | 15 | func slowQuery() error { 16 | _, err := db.Exec("SELECT pg_sleep(5)") 17 | return err 18 | } 19 | 20 | func slowHandler(w http.ResponseWriter, req *http.Request) { 21 | start := time.Now() 22 | err := slowQuery() 23 | if err != nil { 24 | log.Printf("Error: %s\n", err.Error()) 25 | return 26 | } 27 | fmt.Fprintln(w, "OK") 28 | fmt.Printf("slowHandler took: %v\n", time.Since(start)) 29 | } 30 | 31 | func main() { 32 | var err error 33 | 34 | connstr := "host=localhost port=5432 user=alice password=pa$$word dbname=wonderland sslmode=disable" 35 | 36 | db, err = sql.Open("postgres", connstr) 37 | if err != nil { 38 | log.Fatal(err) 39 | } 40 | 41 | if err = db.Ping(); err != nil { 42 | log.Fatal(err) 43 | } 44 | 45 | srv := http.Server{ 46 | Addr: "localhost:8000", 47 | WriteTimeout: 2 * time.Second, 48 | Handler: http.HandlerFunc(slowHandler), 49 | } 50 | 51 | if err := srv.ListenAndServe(); err != nil { 52 | fmt.Printf("Server failed: %s\n", err) 53 | } 54 | } 55 | 56 | // --> Installing postgres - macos 57 | // brew install postgresql 58 | 59 | // --> start 60 | // pg_ctl -D /usr/local/var/postgres start 61 | 62 | // --> create db and user 63 | // psql postgres 64 | // CREATE DATABASE wonderland; 65 | // CREATE USER alice WITH ENCRYPTED PASSWORD 'pa$$word'; 66 | // GRANT ALL PRIVILEGES ON DATABASE wonderland TO alice; 67 | 68 | // --> stop 69 | // pg_ctl -D /usr/local/var/postgres stop 70 | 71 | // --> postgresql download link 72 | // https://www.postgresql.org/download/ 73 | 74 | // start postgresql - Windows 75 | // pg_ctl -D "C:\Program Files\PostgreSQL\13\data" start 76 | 77 | // stop postgresql - Windows 78 | // pg_ctl -D "C:\Program Files\PostgreSQL\13\data" stop 79 | 80 | // --> Linux 81 | // sudo apt-get update 82 | // sudo apt-get install postgresql-13 83 | 84 | // sudo -u postgres psql -c "ALTER USER alice PASSWORD 'pa$$word';" 85 | // sudo -u postgres psql -c "CREATE DATABASE wonderland;" 86 | 87 | // sudo service postgresql start 88 | 89 | // sudo service postgresql stop 90 | -------------------------------------------------------------------------------- /03-class-codewalk/01-interface/01-Fprintf/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "os" 7 | ) 8 | 9 | func main() { 10 | var buf bytes.Buffer 11 | 12 | fmt.Fprintf(os.Stdout, "hello ") 13 | fmt.Fprintf(&buf, "world") 14 | } 15 | -------------------------------------------------------------------------------- /03-class-codewalk/01-interface/02-assign/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "os" 8 | "time" 9 | ) 10 | 11 | func main() { 12 | var w io.Writer 13 | w = os.Stdout 14 | w = new(bytes.Buffer) 15 | w = time.Second 16 | fmt.Println(w) 17 | } 18 | -------------------------------------------------------------------------------- /03-class-codewalk/01-interface/04-printer/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io" 5 | "os" 6 | ) 7 | 8 | func main() { 9 | os.Stdout.Write([]byte("hello")) 10 | os.Stdout.Close() 11 | 12 | printer(os.Stdout) 13 | } 14 | 15 | func printer(w io.Writer) { 16 | w.Write([]byte("hello")) 17 | w.Close() 18 | } 19 | -------------------------------------------------------------------------------- /03-class-codewalk/01-interface/05-empty-interface/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | describe(42) 7 | describe("hello") 8 | } 9 | 10 | func describe(i interface{}) { 11 | fmt.Printf("(%v, %T)\n", i, i) 12 | } 13 | -------------------------------------------------------------------------------- /03-exercise-solution/01-Interfaces/01-density/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // Metal - mass and volume information 6 | type Metal struct { 7 | mass float64 8 | volume float64 9 | } 10 | 11 | // Density - return density of metal 12 | func (m *Metal) Density() float64 { 13 | return m.mass / m.volume 14 | } 15 | 16 | // Gas - measurements for Gas 17 | type Gas struct { 18 | pressure float64 19 | temperature float64 20 | molecularMass float64 21 | } 22 | 23 | // Density - return density of liquid 24 | func (g *Gas) Density() float64 { 25 | var density float64 26 | density = (g.molecularMass * g.pressure) / (0.0821 * (g.temperature + 273)) 27 | return density 28 | } 29 | 30 | // IsDenser - compare density of two objects 31 | func IsDenser(a, b Dense) bool { 32 | return a.Density() > b.Density() 33 | } 34 | 35 | // Dense - interface 36 | type Dense interface { 37 | Density() float64 38 | } 39 | 40 | func main() { 41 | gold := Metal{478, 24} 42 | silver := Metal{100, 10} 43 | 44 | result := IsDenser(&gold, &silver) 45 | if result { 46 | fmt.Println("gold has higher density than silver") 47 | } else { 48 | fmt.Println("silver has higher density than gold") 49 | } 50 | 51 | oxygen := Gas{pressure: 5, 52 | temperature: 27, 53 | molecularMass: 32} 54 | 55 | hydrogen := Gas{pressure: 1, 56 | temperature: 0, 57 | molecularMass: 2} 58 | 59 | result = IsDenser(&oxygen, &hydrogen) 60 | 61 | if result { 62 | fmt.Println("oxygen has higher density than hydrogen") 63 | } else { 64 | fmt.Println("hydrogen has higher density than oxygen") 65 | } 66 | } 67 | 68 | // Formula for density of metal 69 | // density = (mass)/(volume) 70 | 71 | // Formula for density of Gas 72 | // density = ((molecular mass) * (pressure)) / ((gas constant) * (temperature in kelvin)) 73 | -------------------------------------------------------------------------------- /03-exercise-solution/01-Interfaces/02-Fprintf/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "os" 7 | ) 8 | 9 | func main() { 10 | var buf bytes.Buffer 11 | 12 | fmt.Fprintf(os.Stdout, "hello ") 13 | fmt.Fprintf(&buf, "world") 14 | } 15 | -------------------------------------------------------------------------------- /03-exercise-solution/01-Interfaces/03-byte-counter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // ByteCounter - counts bytes written 6 | type ByteCounter int 7 | 8 | func (b *ByteCounter) Write(p []byte) (int, error) { 9 | *b += ByteCounter(len(p)) 10 | return len(p), nil 11 | } 12 | 13 | func main() { 14 | var b ByteCounter 15 | fmt.Fprintf(&b, "hello world") 16 | fmt.Println(b) 17 | } 18 | 19 | // output: 11 20 | -------------------------------------------------------------------------------- /03-exercise-solution/01-Interfaces/04-String/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type user struct { 6 | name string 7 | email string 8 | } 9 | 10 | func (u user) String() string { 11 | return fmt.Sprintf("%s <%s>", u.name, u.email) 12 | } 13 | 14 | func main() { 15 | u := user{ 16 | name: "John Doe", 17 | email: "johndoe@example.com", 18 | } 19 | fmt.Println(u) 20 | } 21 | -------------------------------------------------------------------------------- /03-exercise-solution/01-Interfaces/05-assign/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "os" 8 | "time" 9 | ) 10 | 11 | func main() { 12 | var w io.Writer 13 | w = os.Stdout 14 | w = new(bytes.Buffer) 15 | w = time.Second 16 | fmt.Println(w) 17 | } 18 | -------------------------------------------------------------------------------- /03-exercise-solution/01-Interfaces/05-printer/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io" 5 | "os" 6 | ) 7 | 8 | func main() { 9 | os.Stdout.Write([]byte("hello")) 10 | os.Stdout.Close() 11 | 12 | printer(os.Stdout) 13 | } 14 | 15 | func printer(w io.Writer) { 16 | w.Write([]byte("hello")) 17 | w.Close() 18 | } 19 | -------------------------------------------------------------------------------- /03-exercise-solution/01-Interfaces/06-nil/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | ) 7 | 8 | const debug = true 9 | 10 | func main() { 11 | var buf *bytes.Buffer 12 | 13 | if debug { 14 | buf = new(bytes.Buffer) 15 | } 16 | f(buf) 17 | } 18 | 19 | func f(out io.Writer) { 20 | 21 | // ... some computation 22 | 23 | if out != nil { 24 | out.Write([]byte("done!\n")) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /03-exercise-solution/01-Interfaces/09-triangle/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | ) 7 | 8 | type circle struct { 9 | radius float64 10 | } 11 | type triangle struct { 12 | // lengths of the sides of a triangle. 13 | a, b, c float64 14 | } 15 | type rectangle struct { 16 | a, b float64 17 | } 18 | 19 | func (c circle) area() float64 { 20 | return math.Pi * c.radius * c.radius 21 | } 22 | 23 | // Heron's Formula for the area of a triangle 24 | func (t triangle) area() float64 { 25 | p := (t.a + t.b + t.c) / 2.0 // perimeter half 26 | return math.Sqrt(p * (p - t.a) * (p - t.b) * (p - t.c)) 27 | } 28 | 29 | func (t rectangle) area() float64 { 30 | return t.a * t.b 31 | } 32 | 33 | func (t triangle) angles() []float64 { 34 | return []float64{angle(t.b, t.c, t.a), angle(t.a, t.c, t.b), angle(t.a, t.b, t.c)} 35 | } 36 | func angle(a, b, c float64) float64 { 37 | return math.Acos((a*a+b*b-c*c)/(2*a*b)) * 180.0 / math.Pi 38 | } 39 | 40 | type shape interface { 41 | area() float64 42 | } 43 | 44 | func main() { 45 | shapes := []shape{ 46 | circle{1.0}, 47 | rectangle{5, 10}, 48 | triangle{10, 4, 7}, 49 | } 50 | for _, v := range shapes { 51 | fmt.Println(v, "\tArea:", v.area()) 52 | if t, ok := v.(triangle); ok { 53 | fmt.Println("Angles:", t.angles()) 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /03-exercise-solution/01-Interfaces/10-empty-interface/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | describe(42) 7 | describe("hello") 8 | } 9 | 10 | func describe(i interface{}) { 11 | fmt.Printf("(%v, %T)\n", i, i) 12 | } 13 | -------------------------------------------------------------------------------- /03-exercise/01-Interfaces/01-density/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // Metal - mass and volume information 6 | type Metal struct { 7 | mass float64 8 | volume float64 9 | } 10 | 11 | // Density - return density of metal 12 | func (m *Metal) Density() float64 { 13 | // density = mass/volume 14 | return m.mass / m.volume 15 | } 16 | 17 | // IsDenser - compare density of two objects 18 | func IsDenser(a, b *Metal) bool { 19 | return a.Density() > b.Density() 20 | } 21 | 22 | func main() { 23 | gold := Metal{478, 24} 24 | silver := Metal{100, 10} 25 | 26 | result := IsDenser(&gold, &silver) 27 | if result { 28 | fmt.Println("gold has higher density than silver") 29 | } else { 30 | fmt.Println("silver has higher density than gold") 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /03-exercise/01-Interfaces/02-Fprintf/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "os" 7 | ) 8 | 9 | func main() { 10 | var buf bytes.Buffer 11 | 12 | fmt.Fprintf(os.Stdout, "hello ") 13 | fmt.Fprintf(&buf, "world") 14 | } 15 | -------------------------------------------------------------------------------- /03-exercise/01-Interfaces/03-byte-counter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // ByteCounter type 6 | type ByteCounter int 7 | 8 | // TODO: Implement Write method for ByteCounter 9 | // to count the number of bytes written. 10 | 11 | func main() { 12 | var b ByteCounter 13 | fmt.Fprintf(&b, "hello world") 14 | fmt.Println(b) 15 | } 16 | -------------------------------------------------------------------------------- /03-exercise/01-Interfaces/04-String/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type user struct { 6 | name string 7 | email string 8 | } 9 | 10 | // TODO: Implement custom formating for user struct values. 11 | 12 | func main() { 13 | u := user{ 14 | name: "John Doe", 15 | email: "johndoe@example.com", 16 | } 17 | fmt.Println(u) 18 | } 19 | -------------------------------------------------------------------------------- /03-exercise/01-Interfaces/05-assign/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "os" 8 | "time" 9 | ) 10 | 11 | func main() { 12 | var w io.Writer 13 | w = os.Stdout 14 | w = new(bytes.Buffer) 15 | w = time.Second 16 | fmt.Println(w) 17 | } 18 | -------------------------------------------------------------------------------- /03-exercise/01-Interfaces/05-printer/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | ) 6 | 7 | func main() { 8 | printer(os.Stdout, "hello") 9 | } 10 | 11 | func printer(f *os.File, str string) { 12 | f.Write([]byte(str)) 13 | f.Close() 14 | } 15 | 16 | // os.Stdout.Write([]byte("hello")) 17 | // os.Stdout.Close() 18 | -------------------------------------------------------------------------------- /03-exercise/01-Interfaces/05-printer01/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io" 5 | "os" 6 | ) 7 | 8 | func main() { 9 | printer(os.Stdout, "hello") 10 | } 11 | 12 | func printer(w io.Writer, str string) { 13 | w.Write([]byte(str)) 14 | w.Close() 15 | } 16 | -------------------------------------------------------------------------------- /03-exercise/01-Interfaces/06-nil/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | ) 7 | 8 | const debug = true 9 | 10 | func main() { 11 | var buf *bytes.Buffer 12 | 13 | if debug { 14 | buf = new(bytes.Buffer) 15 | } 16 | f(buf) 17 | } 18 | 19 | func f(out io.Writer) { 20 | 21 | // ... some computation 22 | 23 | if out != nil { 24 | out.Write([]byte("done!\n")) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /03-exercise/01-Interfaces/09-triangle/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | ) 7 | 8 | type circle struct { 9 | radius float64 10 | } 11 | type triangle struct { 12 | a, b, c float64 // lengths of the sides of a triangle. 13 | } 14 | type rectangle struct { 15 | h, w float64 16 | } 17 | 18 | func (c circle) String() string { 19 | return fmt.Sprint("Circle (Radius: ", c.radius, ")") 20 | } 21 | func (t triangle) String() string { 22 | return fmt.Sprint("Triangle (Sides: ", t.a, ", ", t.b, ", ", t.c, ")") 23 | } 24 | func (r rectangle) String() string { 25 | return fmt.Sprint("Rectangle (Sides: ", r.h, ", ", r.w, ")") 26 | } 27 | 28 | func (c circle) area() float64 { 29 | return math.Pi * c.radius * c.radius 30 | } 31 | 32 | func (t triangle) area() float64 { 33 | // Heron's formula 34 | p := (t.a + t.b + t.c) / 2.0 35 | return math.Sqrt(p * (p - t.a) * (p - t.b) * (p - t.c)) 36 | } 37 | 38 | func (r rectangle) area() float64 { 39 | return r.h * r.w 40 | } 41 | 42 | func (t triangle) angles() []float64 { 43 | return []float64{angle(t.b, t.c, t.a), angle(t.a, t.c, t.b), angle(t.a, t.b, t.c)} 44 | } 45 | 46 | func angle(a, b, c float64) float64 { 47 | return math.Acos((a*a+b*b-c*c)/(2*a*b)) * 180.0 / math.Pi 48 | } 49 | 50 | // TODO: Instantiate circle, triangle, rectangle with values 51 | // Print area of each. 52 | // print angles of the triangle 53 | 54 | func main() { 55 | 56 | } 57 | -------------------------------------------------------------------------------- /03-exercise/01-Interfaces/10-empty-interface/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | describe(42) 7 | describe("hello") 8 | } 9 | 10 | func describe(value interface{}) { 11 | switch v := value.(type) { 12 | case int: 13 | fmt.Printf("v is integer with value %d\n", v) 14 | case string: 15 | fmt.Printf("v is a string, whose length is %d\n", len(v)) 16 | default: 17 | fmt.Println("we dont know what 'v' is!") 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Go concurrency exercises 2 | 3 | Exercises and code walks included in this repository are part of Udemy course "concurrency in Go (Golang)". 4 | 5 | https://www.udemy.com/course/concurrency-in-go-golang/?referralCode=5AE5A041D5793C048954 6 | 7 | 8 | -------------------------------------------------------------------------------- /concurrency-in-go-slides.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andcloudio/go-concurrency-exercises/96592113a1f4a4c498e1efb60ddb2325d6ce2b3b/concurrency-in-go-slides.pdf --------------------------------------------------------------------------------