├── go.mod ├── cover.png ├── coverReleased.png ├── chapter10 ├── resources │ ├── cover.png │ └── index.html ├── listing10.1 │ └── filehash.go ├── listing10.14 │ └── cupcakeoneman.go ├── listing10.3 │ └── dirhashsequential.go ├── listing10.11 │ └── httpserver.go ├── listing10.2 │ └── dirfilehash.go ├── listing10.12 │ └── httpservernonblocking.go ├── listing10.4 │ └── dirhashconcurrent.go ├── listing10.15_16 │ └── cupcakefactory.go ├── listing10.9 │ └── httputils.go ├── listing10.13 │ └── cupcakepipeline.go └── listing10.5_6_7_8 │ └── deepestnestedfile.go ├── chapter8 ├── listing8.10 │ └── blockingnils.go ├── listing8.3 │ └── nonblocking.go ├── listing8.1_2 │ └── selectmanychannels.go ├── listing8.7 │ └── selecttimer.go ├── listing8.8_9 │ └── selectsender.go ├── listing8.11_12 │ └── selectwithnil.go ├── listing8.4_5_6 │ └── passwordguesser.go └── listing8.13_14 │ └── charcountermessagepass.go ├── chapter12 ├── listing12.6 │ └── flight.go ├── listing12.3 │ └── atomicbench_test.go ├── listing12.8 │ └── atomiccompareandswap.go ├── listing12.9 │ └── spinlocks.go ├── listing12.10 │ └── createflight.go ├── listing12.11 │ └── usingfutex1.md ├── listing12.12_13 │ └── usingfutex2.md ├── listing12.1_2 │ └── atomicstingyspendy.go ├── listing12.14 │ └── simplifiedsemaphore.md ├── listing12.7 │ └── flightbooking.go └── listing12.4_5 │ └── atomiccharcounter.go ├── chapter2 ├── listing2.4 │ └── cpucheck.go ├── listing2.5 │ └── goscheduler.go ├── listing2.1_2 │ └── sequential.go └── listing2.3 │ └── parallel.go ├── exercises ├── chapter9 │ ├── exercise9.4 │ │ └── drain.go │ ├── exercise9.1 │ │ └── generatesquares.go │ ├── exercise9.3 │ │ └── printchannel.go │ ├── exercise9.5 │ │ └── squaresuntil.go │ └── exercise9.2 │ │ └── takeuntil.go ├── chapter4 │ ├── exercise4.1 │ │ └── countdown.go │ ├── exercise4.1solution │ │ └── countdown.go │ ├── exercise4.2 │ │ └── readtrywritemutex.go │ ├── exercise4.4 │ │ └── wordfrequencymutex.go │ └── exercise4.3 │ │ └── tryreadwritemutex.go ├── chapter12 │ ├── exercise12.2main │ │ └── spinlockboolmain.go │ ├── exercise12.1 │ │ └── spinlockbool.go │ ├── exercise12.2 │ │ └── spinlockbooltry.go │ ├── exercise12.3 │ │ └── spinsemaphore.go │ └── exercise12.3main │ │ └── spinsemaphoremain.go ├── chapter7 │ ├── exercise7.1 │ │ └── messagepassingstop.go │ ├── exercise7.2a │ │ └── closing.go │ ├── exercise7.2b │ │ └── closing.go │ ├── exercise7.3 │ │ └── collectresults.go │ └── exercise7.4 │ │ └── channelcondvar.go ├── chapter8 │ ├── exercise8.2 │ │ └── stopreadingafter.go │ ├── exercise8.1 │ │ └── latesttemp.go │ └── exercise8.3 │ │ └── twoormore.go ├── chapter2 │ ├── exercise2.1 │ │ └── catfiles.go │ ├── exercise2.2 │ │ └── grepfiles.go │ ├── exercise2.3 │ │ └── grepdir.go │ └── exercise2.4 │ │ └── grepdirrec.go ├── chapter3 │ ├── exercise3.3 │ │ └── waitfornextnum.go │ └── exercise3.1 │ │ └── wordfrequency.go ├── chapter10 │ ├── exercise10.3 │ │ └── totallines.go │ ├── exercise10.2 │ │ └── httpservernonblocking.go │ ├── exercise10.3solution │ │ └── totallines.go │ └── exercise10.1 │ │ └── dirhashconcurrent.go ├── chapter6 │ ├── exercise6.1 │ │ └── orderedfilesearch.go │ ├── exercise6.3single │ │ └── matrixmultiplytime.go │ ├── exercise6.4 │ │ └── matrixmultiplywg.go │ ├── exercise6.2 │ │ └── waitgrptrywait.go │ └── exercise6.3multi │ │ └── matrixmultiplytime.go ├── chapter5 │ ├── exercise5.1 │ │ └── stingyspendysignalon50.go │ ├── exercise5.2 │ │ └── gamesynctimeout.go │ └── exercise5.3 │ │ └── weightedsemaphore.go ├── commonfiles │ ├── txtfile2 │ ├── subdir │ │ ├── txtfile4 │ │ └── subsubdir │ │ │ └── txtfile6 │ ├── txtfile1 │ └── txtfile3 └── chapter11 │ ├── exercise11.1 │ └── scoreupdate.go │ ├── exercise11.1solution │ └── scoreupdate.go │ └── exercise11.2 │ └── allfilesinfoselect.go ├── .gitignore ├── chapter7 ├── listing7.4 │ └── nosender.go ├── listing7.3 │ └── noreceiver.go ├── listing7.12_13 │ └── collectresults.go ├── listing7.1_2 │ └── messagepassing.go ├── listing7.8_9 │ └── closing.go ├── listing7.7 │ └── directional.go ├── listing7.11 │ └── forchannel.go ├── listing7.10 │ └── closingFlag.go ├── listing7.14main │ └── channelmain.go ├── listing7.5_6 │ └── bufferchannel.go └── listing7.14 │ └── channel.go ├── chapter6 ├── listing6.4 │ └── waitgrpsemamain.go ├── listing6.3 │ └── waitgrpsema.go ├── listing6.7main │ └── waitgrpmain.go ├── listing6.1 │ └── waitforgroup.go ├── listing6.10 │ └── barrier.go ├── listing6.12_13 │ └── simplebarrierexample.go ├── listing6.2 │ └── charcounterwaitgroup.go ├── listing6.5_6 │ └── filesearch.go ├── listing6.7 │ └── waitgrp.go ├── listing6.14_15 │ └── matrixmultiplysimple.go └── listing6.16_17 │ └── matrixmultiply.go ├── chapter3 ├── listing3.1 │ └── countdown.go ├── listing3.5_6 │ └── stingyspendy.go ├── listing3.7 │ └── stingyspendysched.go ├── listing3.2_3 │ └── charcountersequential.go └── listing3.4 │ └── charcounterconcurrent.go ├── chapter11 ├── listing11.6 │ └── deadlockdetection.go ├── listing11.7 │ └── deadlocknodetection.go ├── listing11.3_4 │ └── bankaccountmutex.go ├── listing11.5 │ └── ledgermutex.go ├── listing11.1_2 │ └── simpledeadlock.go ├── listing11.13 │ └── simplenodeadlockorder.go ├── listing11.15_16_17 │ └── allfilesinfo.go ├── listing11.18 │ └── allfilesinfoseparate.go ├── listing11.19 │ └── allfilesinfoselect.go ├── listing11.14 │ └── ledgermutexorder.go └── listing11.8_9_10_11_12 │ └── ledgermutexarb.go ├── README.md ├── chapter5 ├── listing5.7 │ └── waitforonetask.go ├── listing5.18 │ └── releasebeforeacquire.go ├── listing5.10 │ └── writestarvation.go ├── listing5.16 │ └── semaphore.go ├── listing5.6 │ └── signalbeforewait.go ├── listing5.8_9 │ └── gamesync.go ├── listing5.1 │ └── stingyspendynegative.go ├── listing5.3_4_5 │ └── stingyspendycond.go ├── listing5.2 │ └── stingyspendyretry.go └── listing5.11_12_13_14_15 │ └── readwritewpref.go ├── chapter9 ├── listing9.1_2 │ └── closingchannel.go ├── listing9.18 │ └── take.go ├── listing9.10 │ └── fanIn.go ├── listing9.3_4 │ └── generateurls.go ├── listing9.20_21 │ └── primesieve.go ├── listing9.14 │ └── broadcast.go ├── listing9.5_6 │ └── downloadpages.go ├── listing9.7_8 │ └── extractwords.go ├── listing9.9_11 │ └── extractwordsmulti.go ├── listing9.12_13 │ └── longestwords.go ├── listing9.16_17 │ └── wordstats.go └── listing9.19 │ └── wordstatsearlyquit.go ├── chapter4 ├── listing4.5main │ └── charcountermutexmain.go ├── listing4.12main │ └── readwritemain.go ├── listing4.1_2 │ └── stingyspendymutex.go ├── listing4.5 │ └── charcountermutex.go ├── listing4.6 │ └── nonblockingmutex.go ├── listing4.12 │ └── readwritemutex.go ├── listing4.3_4 │ └── charcountermutexslow.go ├── listing4.7_8_9 │ └── matchmonitor.go └── listing4.10_11 │ └── matchmonitorrw.go └── LICENSE /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/cutajarj/ConcurrentProgrammingWithGo 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cutajarj/ConcurrentProgrammingWithGo/HEAD/cover.png -------------------------------------------------------------------------------- /coverReleased.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cutajarj/ConcurrentProgrammingWithGo/HEAD/coverReleased.png -------------------------------------------------------------------------------- /chapter10/resources/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cutajarj/ConcurrentProgrammingWithGo/HEAD/chapter10/resources/cover.png -------------------------------------------------------------------------------- /chapter8/listing8.10/blockingnils.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | var ch chan string = nil 7 | ch <- "message" 8 | fmt.Println("This is never printed") 9 | } 10 | -------------------------------------------------------------------------------- /chapter12/listing12.6/flight.go: -------------------------------------------------------------------------------- 1 | package listing12_6 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | type Flight struct { 8 | Origin, Dest string 9 | SeatsLeft int 10 | Locker sync.Locker 11 | } 12 | 13 | -------------------------------------------------------------------------------- /chapter10/resources/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Learn Concurrent Programming with Go 5 | 6 |

Learn Concurrent Programming with Go

7 | -------------------------------------------------------------------------------- /chapter2/listing2.4/cpucheck.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | ) 7 | 8 | func main() { 9 | fmt.Println("Number of CPUs:", runtime.NumCPU()) 10 | 11 | fmt.Println("GOMAXPROCS:", runtime.GOMAXPROCS(0)) 12 | } 13 | -------------------------------------------------------------------------------- /exercises/chapter9/exercise9.4/drain.go: -------------------------------------------------------------------------------- 1 | package exercise9_4 2 | 3 | func Drain[T any](quit <-chan int, input <-chan T) { 4 | go func() { 5 | for { 6 | select { 7 | case <-input: 8 | case <-quit: 9 | return 10 | } 11 | } 12 | }() 13 | } 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | /.idea 8 | *.iml 9 | 10 | # Test binary, build with `go test -c` 11 | *.test 12 | 13 | # Output of the go coverage tool, specifically when used with LiteIDE 14 | *.out 15 | -------------------------------------------------------------------------------- /chapter10/listing10.1/filehash.go: -------------------------------------------------------------------------------- 1 | package listing10_1 2 | 3 | import ( 4 | "crypto/sha256" 5 | "io" 6 | "os" 7 | ) 8 | 9 | func FHash(filepath string) []byte { 10 | file, _ := os.Open(filepath) 11 | defer file.Close() 12 | 13 | sha := sha256.New() 14 | io.Copy(sha, file) 15 | 16 | return sha.Sum(nil) 17 | } 18 | -------------------------------------------------------------------------------- /exercises/chapter9/exercise9.1/generatesquares.go: -------------------------------------------------------------------------------- 1 | package exercise9_1 2 | 3 | func GenerateSquares(quit <-chan int) <-chan int { 4 | squares := make(chan int) 5 | go func() { 6 | i := 0 7 | defer close(squares) 8 | for { 9 | i += 1 10 | select { 11 | case squares <- i * i: 12 | case <-quit: 13 | return 14 | } 15 | } 16 | }() 17 | return squares 18 | } 19 | -------------------------------------------------------------------------------- /chapter2/listing2.5/goscheduler.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | ) 7 | /* 8 | Note: this program is an example of what not to do; using go scheduler 9 | to synchronize executions 10 | */ 11 | func sayHello() { 12 | fmt.Println("Hello") 13 | } 14 | 15 | func main() { 16 | go sayHello() 17 | runtime.Gosched() 18 | fmt.Println("Finished") 19 | } 20 | -------------------------------------------------------------------------------- /chapter12/listing12.3/atomicbench_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "sync/atomic" 5 | "testing" 6 | ) 7 | 8 | var total = int64(0) 9 | 10 | func BenchmarkNormal(bench *testing.B) { 11 | for i := 0; i < bench.N; i++ { 12 | total += 1 13 | } 14 | } 15 | 16 | func BenchmarkAtomic(bench *testing.B) { 17 | for i := 0; i < bench.N; i++ { 18 | atomic.AddInt64(&total, 1) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /chapter2/listing2.1_2/sequential.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func doWork(id int) { 9 | fmt.Printf("Work %d started at %s\n", id, time.Now().Format("15:04:05")) 10 | time.Sleep(1 * time.Second) 11 | fmt.Printf("Work %d finished at %s\n", id, time.Now().Format("15:04:05")) 12 | } 13 | 14 | func main() { 15 | for i := 0; i < 5; i++ { 16 | doWork(i) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /chapter7/listing7.4/nosender.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func main() { 9 | msgChannel := make(chan string) 10 | go sender(msgChannel) 11 | fmt.Println("Reading message from channel...") 12 | msg := <-msgChannel 13 | fmt.Println("Received:", msg) 14 | } 15 | 16 | func sender(messages chan string) { 17 | time.Sleep(5 * time.Second) 18 | fmt.Println("Sender slept for 5 seconds") 19 | } 20 | -------------------------------------------------------------------------------- /exercises/chapter4/exercise4.1/countdown.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func countdown(seconds *int) { 9 | for *seconds > 0 { 10 | time.Sleep(1 * time.Second) 11 | *seconds -= 1 12 | } 13 | } 14 | 15 | func main() { 16 | count := 5 17 | go countdown(&count) 18 | for count > 0 { 19 | time.Sleep(500 * time.Millisecond) 20 | fmt.Println(count) 21 | } 22 | } 23 | 24 | -------------------------------------------------------------------------------- /chapter2/listing2.3/parallel.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func doWork(id int) { 9 | fmt.Printf("Work %d started at %s\n", id, time.Now().Format("15:04:05")) 10 | time.Sleep(1 * time.Second) 11 | fmt.Printf("Work %d finished at %s\n", id, time.Now().Format("15:04:05")) 12 | } 13 | 14 | func main() { 15 | for i := 0; i < 5; i++ { 16 | go doWork(i) 17 | } 18 | time.Sleep(2 * time.Second) 19 | } 20 | -------------------------------------------------------------------------------- /chapter12/listing12.8/atomiccompareandswap.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync/atomic" 6 | ) 7 | 8 | func main() { 9 | number := int32(17) 10 | result := atomic.CompareAndSwapInt32(&number, 17, 19) 11 | fmt.Printf("17 <- swap(17,19): result %t, value: %d\n", result, number) 12 | number = int32(23) 13 | result = atomic.CompareAndSwapInt32(&number, 17, 19) 14 | fmt.Printf("23 <- swap(17,19): result %t, value: %d\n", result, number) 15 | } 16 | -------------------------------------------------------------------------------- /chapter6/listing6.4/waitgrpsemamain.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/cutajarj/ConcurrentProgrammingWithGo/chapter6/listing6.3" 6 | ) 7 | 8 | func doWork(id int, wg *listing6_3.WaitGrp) { 9 | fmt.Println(id, "Done working ") 10 | wg.Done() 11 | } 12 | 13 | func main() { 14 | wg := listing6_3.NewWaitGrp(4) 15 | for i := 1; i <= 4; i++ { 16 | go doWork(i, wg) 17 | } 18 | wg.Wait() 19 | fmt.Println("All complete") 20 | } 21 | -------------------------------------------------------------------------------- /exercises/chapter12/exercise12.2main/spinlockboolmain.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/cutajarj/ConcurrentProgrammingWithGo/exercises/chapter12/exercise12.2" 6 | ) 7 | 8 | func main() { 9 | spin := exercise12_2.SpinLock{} 10 | 11 | spin.Lock() 12 | 13 | fmt.Println("This should be false: ", spin.TryLock()) 14 | 15 | spin.Unlock() 16 | 17 | fmt.Println("This should be true: ", spin.TryLock()) 18 | 19 | spin.Unlock() 20 | } 21 | -------------------------------------------------------------------------------- /chapter6/listing6.3/waitgrpsema.go: -------------------------------------------------------------------------------- 1 | package listing6_3 2 | 3 | import ( 4 | "github.com/cutajarj/ConcurrentProgrammingWithGo/chapter5/listing5.16" 5 | ) 6 | 7 | type WaitGrp struct { 8 | sema *listing5_16.Semaphore 9 | } 10 | 11 | func NewWaitGrp(size int) *WaitGrp { 12 | return &WaitGrp{sema: listing5_16.NewSemaphore(1 - size)} 13 | } 14 | 15 | func (wg *WaitGrp) Wait() { 16 | wg.sema.Acquire() 17 | } 18 | 19 | func (wg *WaitGrp) Done() { 20 | wg.sema.Release() 21 | } 22 | -------------------------------------------------------------------------------- /chapter12/listing12.9/spinlocks.go: -------------------------------------------------------------------------------- 1 | package listing12_9 2 | 3 | import ( 4 | "runtime" 5 | "sync" 6 | "sync/atomic" 7 | ) 8 | 9 | type SpinLock int32 10 | 11 | func (s *SpinLock) Lock() { 12 | for !atomic.CompareAndSwapInt32((*int32)(s), 0, 1) { 13 | runtime.Gosched() 14 | } 15 | } 16 | 17 | func (s *SpinLock) Unlock() { 18 | atomic.StoreInt32((*int32)(s), 0) 19 | } 20 | 21 | func NewSpinLock() sync.Locker { 22 | var lock SpinLock 23 | return &lock 24 | } 25 | -------------------------------------------------------------------------------- /chapter12/listing12.10/createflight.go: -------------------------------------------------------------------------------- 1 | package listing12_10 2 | 3 | import ( 4 | "github.com/cutajarj/ConcurrentProgrammingWithGo/chapter12/listing12.6" 5 | "github.com/cutajarj/ConcurrentProgrammingWithGo/chapter12/listing12.9" 6 | ) 7 | 8 | func NewFlight(origin, dest string) *listing12_6.Flight { 9 | return &listing12_6.Flight{ 10 | Origin: origin, 11 | Dest: dest, 12 | SeatsLeft: 200, 13 | Locker: listing12_9.NewSpinLock(), 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /chapter10/listing10.14/cupcakeoneman.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/cutajarj/ConcurrentProgrammingWithGo/chapter10/listing10.13" 6 | ) 7 | 8 | func main() { 9 | for i := 0; i < 10; i++ { 10 | result := listing10_13.Box( 11 | listing10_13.AddToppings( 12 | listing10_13.Bake( 13 | listing10_13.Mixture( 14 | listing10_13.PrepareTray(i))))) 15 | fmt.Println("Accepting", result) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /exercises/chapter12/exercise12.1/spinlockbool.go: -------------------------------------------------------------------------------- 1 | package exercise12_1 2 | 3 | import ( 4 | "runtime" 5 | "sync" 6 | "sync/atomic" 7 | ) 8 | 9 | type SpinLock atomic.Bool 10 | 11 | func (s *SpinLock) Lock() { 12 | for !(*atomic.Bool)(s).CompareAndSwap(false, true) { 13 | runtime.Gosched() 14 | } 15 | } 16 | 17 | func (s *SpinLock) Unlock() { 18 | (*atomic.Bool)(s).Store(false) 19 | } 20 | 21 | func NewSpinLock() sync.Locker { 22 | var lock SpinLock 23 | return &lock 24 | } 25 | 26 | -------------------------------------------------------------------------------- /exercises/chapter9/exercise9.3/printchannel.go: -------------------------------------------------------------------------------- 1 | package exercise9_3 2 | 3 | import "fmt" 4 | 5 | func Print[T any](quit <-chan int, input <-chan T) <-chan T { 6 | output := make(chan T) 7 | go func() { 8 | defer close(output) 9 | moreData := true 10 | var msg T 11 | for moreData { 12 | select { 13 | case msg, moreData = <-input: 14 | if moreData { 15 | fmt.Println(msg) 16 | output <- msg 17 | } 18 | case <-quit: 19 | return 20 | } 21 | } 22 | }() 23 | return output 24 | } 25 | -------------------------------------------------------------------------------- /chapter6/listing6.7main/waitgrpmain.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/cutajarj/ConcurrentProgrammingWithGo/chapter6/listing6.7" 6 | ) 7 | 8 | func doWork(id int, wg *listing6_7.WaitGrp) { 9 | fmt.Println(id, "Done working ") 10 | wg.Done() 11 | } 12 | 13 | func main() { 14 | wg := listing6_7.NewWaitGrp() 15 | for i := 1; i <= 4; i++ { 16 | wg.Add(2) 17 | go doWork(i, wg) 18 | go doWork(i, wg) 19 | } 20 | wg.Wait() 21 | fmt.Println("All complete") 22 | } 23 | -------------------------------------------------------------------------------- /chapter7/listing7.3/noreceiver.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func main() { 9 | msgChannel := make(chan string) 10 | go receiver(msgChannel) 11 | fmt.Println("Sending HELLO...") 12 | msgChannel <- "HELLO" 13 | fmt.Println("Sending THERE...") 14 | msgChannel <- "THERE" 15 | fmt.Println("Sending STOP...") 16 | msgChannel <- "STOP" 17 | } 18 | 19 | func receiver(messages chan string) { 20 | time.Sleep(5 * time.Second) 21 | fmt.Println("Receiver slept for 5 seconds") 22 | } 23 | -------------------------------------------------------------------------------- /chapter6/listing6.1/waitforgroup.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | func doWork(id int, wg *sync.WaitGroup) { 11 | i := rand.Intn(5) 12 | time.Sleep(time.Duration(i) * time.Second) 13 | fmt.Println(id, "Done working after", i, "seconds") 14 | wg.Done() 15 | } 16 | 17 | func main() { 18 | wg := sync.WaitGroup{} 19 | wg.Add(4) 20 | for i := 1; i <= 4; i++ { 21 | go doWork(i, &wg) 22 | } 23 | wg.Wait() 24 | fmt.Println("All complete") 25 | } 26 | -------------------------------------------------------------------------------- /chapter7/listing7.12_13/collectresults.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func findFactors(number int) []int { 8 | result := make([]int, 0) 9 | for i := 1; i <= number; i++ { 10 | if number%i == 0 { 11 | result = append(result, i) 12 | } 13 | } 14 | return result 15 | } 16 | 17 | func main() { 18 | resultCh := make(chan []int) 19 | go func() { 20 | resultCh <- findFactors(3419110721) 21 | }() 22 | fmt.Println(findFactors(4033836233)) 23 | fmt.Println(<-resultCh) 24 | } 25 | -------------------------------------------------------------------------------- /chapter7/listing7.1_2/messagepassing.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | msgChannel := make(chan string) 7 | go receiver(msgChannel) 8 | fmt.Println("Sending HELLO...") 9 | msgChannel <- "HELLO" 10 | fmt.Println("Sending THERE...") 11 | msgChannel <- "THERE" 12 | fmt.Println("Sending STOP...") 13 | msgChannel <- "STOP" 14 | } 15 | 16 | func receiver(messages chan string) { 17 | msg := "" 18 | for msg != "STOP" { 19 | msg = <-messages 20 | fmt.Println("Received:", msg) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /chapter3/listing3.1/countdown.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | /* 8 | Note: this program has a race condition for demonstration purposes 9 | This is then fixed later as an exercise in chapter 4 10 | */ 11 | func countdown(seconds *int) { 12 | for *seconds > 0 { 13 | time.Sleep(1 * time.Second) 14 | *seconds -= 1 15 | } 16 | } 17 | 18 | func main() { 19 | count := 5 20 | go countdown(&count) 21 | for count > 0 { 22 | time.Sleep(500 * time.Millisecond) 23 | fmt.Println(count) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /chapter11/listing11.6/deadlockdetection.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | func lockBoth(lock1, lock2 *sync.Mutex, wg *sync.WaitGroup) { 9 | for i := 0; i < 10000; i++ { 10 | lock1.Lock(); lock2.Lock() 11 | lock1.Unlock(); lock2.Unlock() 12 | } 13 | wg.Done() 14 | } 15 | 16 | func main() { 17 | lockA, lockB := sync.Mutex{}, sync.Mutex{} 18 | wg := sync.WaitGroup{} 19 | wg.Add(2) 20 | go lockBoth(&lockA, &lockB, &wg) 21 | go lockBoth(&lockB, &lockA, &wg) 22 | wg.Wait() 23 | fmt.Println("Done") 24 | } 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Learn Concurrent Programming With Go 2 | Listings from the manning book 3 | Title: Learn Concurrent Programming With Go 4 | Author: James Cutajar 5 | ISBN: 9781633438385 6 | [Book can be purchased here.](https://manning.com/books/learn-concurrent-programming-with-go?utm_source=cutajarj&utm_medium=affiliate&utm_campaign=book_cutajar_learn_12_14_22&a_aid=cutajarj&a_bid=4360d6bb) 7 | You can use the 35% discount code on any manning book: **au35cut** 8 | 9 | #### Please note this repo is not for the Udemy course. 10 | 11 | ![Book Cover](coverReleased.png) 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /chapter6/listing6.10/barrier.go: -------------------------------------------------------------------------------- 1 | package listing6_10 2 | 3 | import "sync" 4 | 5 | type Barrier struct { 6 | size int 7 | waitCount int 8 | cond *sync.Cond 9 | } 10 | 11 | func NewBarrier(size int) *Barrier { 12 | condVar := sync.NewCond(&sync.Mutex{}) 13 | return &Barrier{size, 0, condVar} 14 | } 15 | 16 | func (b *Barrier) Wait() { 17 | b.cond.L.Lock() 18 | b.waitCount += 1 19 | if b.waitCount == b.size { 20 | b.waitCount = 0 21 | b.cond.Broadcast() 22 | } else { 23 | b.cond.Wait() 24 | } 25 | b.cond.L.Unlock() 26 | } 27 | -------------------------------------------------------------------------------- /exercises/chapter7/exercise7.1/messagepassingstop.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | msgChannel := make(chan string) 7 | go receiver(msgChannel) 8 | fmt.Println("Sending HELLO...") 9 | msgChannel <- "HELLO" 10 | fmt.Println("Sending THERE...") 11 | msgChannel <- "THERE" 12 | fmt.Println("Sending STOP...") 13 | msgChannel <- "STOP" 14 | <-msgChannel 15 | close(msgChannel) 16 | } 17 | 18 | func receiver(messages chan string) { 19 | msg := "" 20 | for msg != "STOP" { 21 | msg = <-messages 22 | fmt.Println("Received:", msg) 23 | } 24 | messages <- "" 25 | } 26 | -------------------------------------------------------------------------------- /chapter12/listing12.11/usingfutex1.md: -------------------------------------------------------------------------------- 1 | # Developing a mutex using a futex attempt 1 2 | 3 | Note: This code will not compile because we cannot access futexes from Go. This code is to demonstrate the mutex algorithms. 4 | 5 | ```go 6 | package listing12_11 7 | 8 | import "sync/atomic" 9 | 10 | type FutexLock int32 11 | 12 | func (f *FutexLock) Lock() { 13 | for !atomic.CompareAndSwapInt32((*int32)(f), 0, 1) { 14 | futex_wait((*int32)(f), 1) 15 | } 16 | } 17 | 18 | func (f *FutexLock) Unlock() { 19 | atomic.StoreInt32((*int32)(f), 0) 20 | futex_wakeup((*int32)(f), 1) 21 | } 22 | ``` 23 | -------------------------------------------------------------------------------- /chapter5/listing5.7/waitforonetask.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | func doWork(cond *sync.Cond) { 9 | fmt.Println("Work started") 10 | fmt.Println("Work finished") 11 | cond.L.Lock() 12 | cond.Signal() 13 | cond.L.Unlock() 14 | } 15 | 16 | func main() { 17 | cond := sync.NewCond(&sync.Mutex{}) 18 | cond.L.Lock() 19 | for i := 0; i < 50000; i++ { 20 | go doWork(cond) 21 | fmt.Println("Waiting for child goroutine") 22 | cond.Wait() 23 | fmt.Println("Child goroutine finished") 24 | } 25 | cond.L.Unlock() 26 | } 27 | -------------------------------------------------------------------------------- /exercises/chapter7/exercise7.2a/closing.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func receiver(messages <-chan string) { 9 | for { 10 | msg := <-messages 11 | fmt.Println(time.Now().Format("15:04:05"), "Received:", msg) 12 | time.Sleep(1 * time.Second) 13 | } 14 | } 15 | 16 | func main() { 17 | msgChannel := make(chan string) 18 | go receiver(msgChannel) 19 | for i := 1; i <= 3; i++ { 20 | fmt.Println(time.Now().Format("15:04:05"), "Sending:", i) 21 | msgChannel <- "test" 22 | time.Sleep(1 * time.Second) 23 | } 24 | close(msgChannel) 25 | time.Sleep(3 * time.Second) 26 | } 27 | -------------------------------------------------------------------------------- /exercises/chapter7/exercise7.2b/closing.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func receiver(messages <-chan []int) { 9 | for { 10 | msg := <-messages 11 | fmt.Println(time.Now().Format("15:04:05"), "Received:", msg) 12 | time.Sleep(1 * time.Second) 13 | } 14 | } 15 | 16 | func main() { 17 | msgChannel := make(chan []int) 18 | go receiver(msgChannel) 19 | for i := 1; i <= 3; i++ { 20 | fmt.Println(time.Now().Format("15:04:05"), "Sending:", i) 21 | msgChannel <- make([]int, 10) 22 | time.Sleep(1 * time.Second) 23 | } 24 | close(msgChannel) 25 | time.Sleep(3 * time.Second) 26 | } 27 | -------------------------------------------------------------------------------- /chapter5/listing5.18/releasebeforeacquire.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/cutajarj/ConcurrentProgrammingWithGo/chapter5/listing5.16" 6 | ) 7 | 8 | func main() { 9 | semaphore := listing5_16.NewSemaphore(0) 10 | for i := 0; i < 50000; i++ { 11 | go doWork(semaphore) 12 | fmt.Println("Waiting for child goroutine") 13 | semaphore.Acquire() 14 | fmt.Println("Child goroutine finished") 15 | } 16 | } 17 | 18 | func doWork(semaphore *listing5_16.Semaphore) { 19 | fmt.Println("Work started") 20 | fmt.Println("Work finished") 21 | semaphore.Release() 22 | } 23 | -------------------------------------------------------------------------------- /exercises/chapter12/exercise12.2/spinlockbooltry.go: -------------------------------------------------------------------------------- 1 | package exercise12_2 2 | 3 | import ( 4 | "runtime" 5 | "sync" 6 | "sync/atomic" 7 | ) 8 | 9 | type SpinLock atomic.Bool 10 | 11 | func (s *SpinLock) Lock() { 12 | for !(*atomic.Bool)(s).CompareAndSwap(false, true) { 13 | runtime.Gosched() 14 | } 15 | } 16 | 17 | func (s *SpinLock) Unlock() { 18 | (*atomic.Bool)(s).Store(false) 19 | } 20 | 21 | func (s *SpinLock) TryLock() bool { 22 | return (*atomic.Bool)(s).CompareAndSwap(false, true) 23 | } 24 | 25 | func NewSpinLock() sync.Locker { 26 | var lock SpinLock 27 | return &lock 28 | } 29 | 30 | -------------------------------------------------------------------------------- /exercises/chapter7/exercise7.3/collectresults.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | ) 7 | 8 | func findFactors(number int) []int { 9 | result := make([]int, 0) 10 | for i := 1; i <= number; i++ { 11 | if number%i == 0 { 12 | result = append(result, i) 13 | } 14 | } 15 | return result 16 | } 17 | 18 | func main() { 19 | resultChs := make([]chan []int, 10) 20 | for i := 0; i < 10; i++ { 21 | resultChs[i] = make(chan []int) 22 | go func(n int) { 23 | resultChs[n] <- findFactors(rand.Intn(1000000000)) 24 | }(i) 25 | } 26 | for i := 0; i < 10; i++ { 27 | fmt.Println(<-resultChs[i]) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /exercises/chapter8/exercise8.2/stopreadingafter.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | func generateNumbers() chan int { 10 | output := make(chan int) 11 | go func() { 12 | for { 13 | output <- rand.Intn(10) 14 | time.Sleep(200 * time.Millisecond) 15 | } 16 | }() 17 | return output 18 | } 19 | 20 | func main() { 21 | numbers := generateNumbers() 22 | timeout := time.After(5 * time.Second) 23 | for { 24 | select { 25 | case n := <-numbers: 26 | fmt.Println(n) 27 | case <-timeout: 28 | fmt.Println("Stopping reading after timeout") 29 | return 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /chapter10/listing10.3/dirhashsequential.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/sha256" 5 | "fmt" 6 | "github.com/cutajarj/ConcurrentProgrammingWithGo/chapter10/listing10.1" 7 | "os" 8 | "path/filepath" 9 | ) 10 | 11 | func main() { 12 | dir := os.Args[1] 13 | files, _ := os.ReadDir(dir) 14 | sha := sha256.New() 15 | for _, file := range files { 16 | if !file.IsDir() { 17 | fpath := filepath.Join(dir, file.Name()) 18 | hashOnFile := listing10_1.FHash(fpath) 19 | sha.Write(hashOnFile) 20 | } 21 | } 22 | fmt.Printf("%s - %x\n", dir, sha.Sum(nil)) 23 | } 24 | -------------------------------------------------------------------------------- /exercises/chapter12/exercise12.3/spinsemaphore.go: -------------------------------------------------------------------------------- 1 | package exercise12_3 2 | 3 | import ( 4 | "sync/atomic" 5 | ) 6 | 7 | type SpinSemaphore int32 8 | 9 | func (s *SpinSemaphore) Acquire() { 10 | for { 11 | v := atomic.LoadInt32((*int32)(s)) 12 | if v != 0 && atomic.CompareAndSwapInt32((*int32)(s), v, v - 1) { 13 | break 14 | } 15 | } 16 | } 17 | 18 | func (s *SpinSemaphore) Release() { 19 | atomic.AddInt32((*int32)(s), 1) 20 | } 21 | 22 | func NewSpinSemaphore(permits int32) *SpinSemaphore { 23 | var sema SpinSemaphore 24 | atomic.StoreInt32((*int32)(&sema), permits) 25 | return &sema 26 | } 27 | -------------------------------------------------------------------------------- /chapter5/listing5.10/writestarvation.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/cutajarj/ConcurrentProgrammingWithGo/chapter4/listing4.12" 6 | "time" 7 | ) 8 | 9 | func main() { 10 | rwMutex := listing4_12.ReadWriteMutex{} 11 | for i := 0; i < 2; i++ { 12 | go func() { 13 | for { 14 | rwMutex.ReadLock() 15 | time.Sleep(1 * time.Second) 16 | fmt.Println("Read done") 17 | rwMutex.ReadUnlock() 18 | } 19 | }() 20 | } 21 | time.Sleep(1 * time.Second) 22 | rwMutex.WriteLock() 23 | fmt.Println("Write finished") 24 | } 25 | -------------------------------------------------------------------------------- /chapter7/listing7.8_9/closing.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func receiver(messages <-chan int) { 9 | for { 10 | msg := <-messages 11 | fmt.Println(time.Now().Format("15:04:05"), "Received:", msg) 12 | time.Sleep(1 * time.Second) 13 | } 14 | } 15 | 16 | func main() { 17 | msgChannel := make(chan int) 18 | go receiver(msgChannel) 19 | for i := 1; i <= 3; i++ { 20 | fmt.Println(time.Now().Format("15:04:05"), "Sending:", i) 21 | msgChannel <- i 22 | time.Sleep(1 * time.Second) 23 | } 24 | close(msgChannel) 25 | time.Sleep(3 * time.Second) 26 | } 27 | -------------------------------------------------------------------------------- /chapter10/listing10.11/httpserver.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/cutajarj/ConcurrentProgrammingWithGo/chapter10/listing10.9" 5 | "net" 6 | ) 7 | 8 | /* 9 | If you're having problems running this, please make sure that the port 8080 is available 10 | Note that error handling is omitted so that listing is brief 11 | */ 12 | func main() { 13 | incomingConnections := make(chan net.Conn) 14 | listing10_9.StartHttpWorkers(3, incomingConnections) 15 | server, _ := net.Listen("tcp", "localhost:8080") 16 | defer server.Close() 17 | for { 18 | conn, _ := server.Accept() 19 | incomingConnections <- conn 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /chapter7/listing7.7/directional.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func receiver(messages <-chan int) { 9 | for { 10 | msg := <-messages 11 | fmt.Println(time.Now().Format("15:04:05"), "Received:", msg) 12 | } 13 | } 14 | 15 | func sender(messages chan<- int) { 16 | for i := 1; ; i++ { 17 | fmt.Println(time.Now().Format("15:04:05"), "Sending:", i) 18 | messages <- i 19 | time.Sleep(1 * time.Second) 20 | } 21 | } 22 | 23 | func main() { 24 | msgChannel := make(chan int) 25 | go receiver(msgChannel) 26 | go sender(msgChannel) 27 | time.Sleep(5 * time.Second) 28 | } 29 | -------------------------------------------------------------------------------- /exercises/chapter9/exercise9.5/squaresuntil.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/cutajarj/ConcurrentProgrammingWithGo/exercises/chapter9/exercise9.1" 5 | "github.com/cutajarj/ConcurrentProgrammingWithGo/exercises/chapter9/exercise9.2" 6 | "github.com/cutajarj/ConcurrentProgrammingWithGo/exercises/chapter9/exercise9.3" 7 | "github.com/cutajarj/ConcurrentProgrammingWithGo/exercises/chapter9/exercise9.4" 8 | ) 9 | 10 | func main() { 11 | quit := make(chan int) 12 | exercise9_4.Drain(quit, 13 | exercise9_3.Print(quit, 14 | exercise9_2.TakeUntil(quit, func(s int) bool { return s <= 1000000 } , 15 | exercise9_1.GenerateSquares(quit)))) 16 | <-quit 17 | } 18 | -------------------------------------------------------------------------------- /chapter9/listing9.1_2/closingchannel.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func printNumbers(numbers <-chan int, quit chan int) { 6 | go func() { 7 | for i := 0; i < 10; i++ { 8 | fmt.Println(<-numbers) 9 | } 10 | close(quit) 11 | }() 12 | } 13 | 14 | func main() { 15 | numbers := make(chan int) 16 | quit := make(chan int) 17 | printNumbers(numbers, quit) 18 | next := 0 19 | for i := 1; ; i++ { 20 | next += i 21 | select { 22 | case numbers <- next: 23 | case <-quit: 24 | fmt.Println("Quitting number generation") 25 | return 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /chapter4/listing4.5main/charcountermutexmain.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/cutajarj/ConcurrentProgrammingWithGo/chapter4/listing4.5" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | func main() { 11 | mutex := sync.Mutex{} 12 | var frequency = make([]int, 26) 13 | for i := 1000; i <= 1030; i++ { 14 | url := fmt.Sprintf("https://rfc-editor.org/rfc/rfc%d.txt", i) 15 | go listing4_5.CountLetters(url, frequency, &mutex) 16 | } 17 | time.Sleep(10 * time.Second) 18 | mutex.Lock() 19 | for i, c := range listing4_5.AllLetters { 20 | fmt.Printf("%c-%d ", c, frequency[i]) 21 | } 22 | mutex.Unlock() 23 | } 24 | -------------------------------------------------------------------------------- /chapter9/listing9.18/take.go: -------------------------------------------------------------------------------- 1 | package listing9_18 2 | 3 | func Take[K any](quit chan int, n int, input <-chan K) <-chan K { 4 | output := make(chan K) 5 | go func() { 6 | defer close(output) 7 | moreData := true 8 | var msg K 9 | for n > 0 && moreData { 10 | select { 11 | case msg, moreData = <-input: 12 | if moreData { 13 | output <- msg 14 | n-- 15 | } 16 | case <-quit: 17 | return 18 | } 19 | } 20 | if n == 0 { 21 | close(quit) 22 | } 23 | }() 24 | return output 25 | } 26 | -------------------------------------------------------------------------------- /exercises/chapter2/exercise2.1/catfiles.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | "time" 8 | ) 9 | 10 | /* 11 | You can run this by executing: 12 | go run catfiles.go ../../commonfiles/txtfile1 ../../commonfiles/txtfile2 ../../commonfiles/txtfile3 13 | */ 14 | 15 | func printFile(filename string) { 16 | content, err := os.ReadFile(filename) 17 | if err != nil { 18 | log.Fatal(err) 19 | } 20 | fmt.Println(string(content)) 21 | } 22 | 23 | func main() { 24 | filenames := os.Args[1:] 25 | for _, filename := range filenames { 26 | go printFile(filename) 27 | } 28 | time.Sleep(2 * time.Second) 29 | } 30 | -------------------------------------------------------------------------------- /chapter4/listing4.12main/readwritemain.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/cutajarj/ConcurrentProgrammingWithGo/chapter4/listing4.12" 6 | "time" 7 | ) 8 | 9 | func main() { 10 | rwMutex := listing4_12.ReadWriteMutex{} 11 | for i := 0; i < 10; i++ { 12 | go func() { 13 | rwMutex.ReadLock() 14 | fmt.Println("Read started") 15 | time.Sleep(5 * time.Second) 16 | fmt.Println("Read done") 17 | rwMutex.ReadUnlock() 18 | }() 19 | } 20 | time.Sleep(1 * time.Second) 21 | fmt.Println("Write started") 22 | rwMutex.WriteLock() 23 | fmt.Println("Write finished") 24 | } 25 | -------------------------------------------------------------------------------- /chapter5/listing5.16/semaphore.go: -------------------------------------------------------------------------------- 1 | package listing5_16 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | type Semaphore struct { 8 | permits int 9 | cond *sync.Cond 10 | } 11 | 12 | func NewSemaphore(n int) *Semaphore { 13 | return &Semaphore{ 14 | permits: n, 15 | cond: sync.NewCond(&sync.Mutex{}), 16 | } 17 | } 18 | 19 | func (rw *Semaphore) Acquire() { 20 | rw.cond.L.Lock() 21 | for rw.permits <= 0 { 22 | rw.cond.Wait() 23 | } 24 | rw.permits-- 25 | rw.cond.L.Unlock() 26 | } 27 | 28 | func (rw *Semaphore) Release() { 29 | rw.cond.L.Lock() 30 | rw.permits++ 31 | rw.cond.Signal() 32 | rw.cond.L.Unlock() 33 | } 34 | -------------------------------------------------------------------------------- /chapter7/listing7.11/forchannel.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func receiver(messages <-chan int) { 9 | for msg := range messages { 10 | fmt.Println(time.Now().Format("15:04:05"), "Received:", msg) 11 | time.Sleep(1 * time.Second) 12 | } 13 | fmt.Println("Receiver finished.") 14 | } 15 | 16 | func main() { 17 | msgChannel := make(chan int) 18 | go receiver(msgChannel) 19 | for i := 1; i <= 3; i++ { 20 | fmt.Println(time.Now().Format("15:04:05"), "Sending:", i) 21 | msgChannel <- i 22 | time.Sleep(1 * time.Second) 23 | } 24 | close(msgChannel) 25 | time.Sleep(3 * time.Second) 26 | } 27 | -------------------------------------------------------------------------------- /chapter8/listing8.3/nonblocking.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func sendMsgAfter(seconds time.Duration) <-chan string { 9 | messages := make(chan string) 10 | go func() { 11 | time.Sleep(seconds) 12 | messages <- "Hello" 13 | }() 14 | return messages 15 | } 16 | 17 | func main() { 18 | messages := sendMsgAfter(3 * time.Second) 19 | for { 20 | select { 21 | case msg := <-messages: 22 | fmt.Println("Message received:", msg) 23 | return 24 | default: 25 | fmt.Println("No messages waiting") 26 | time.Sleep(1 * time.Second) 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /chapter5/listing5.6/signalbeforewait.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | /* 9 | Note: this program has a bug for demonstration purposes 10 | We demonstrate how to fix this problem in the next listing 11 | */ 12 | func doWork(cond *sync.Cond) { 13 | fmt.Println("Work started") 14 | fmt.Println("Work finished") 15 | cond.Signal() 16 | } 17 | 18 | func main() { 19 | cond := sync.NewCond(&sync.Mutex{}) 20 | cond.L.Lock() 21 | for i := 0; i < 50000; i++ { 22 | go doWork(cond) 23 | fmt.Println("Waiting for child goroutine") 24 | cond.Wait() 25 | fmt.Println("Child goroutine finished") 26 | } 27 | cond.L.Unlock() 28 | } 29 | -------------------------------------------------------------------------------- /exercises/chapter3/exercise3.3/waitfornextnum.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | /* 9 | Note: this program has a race condition for demonstration purposes 10 | You might need to run this multiple times to trigger the race condition 11 | */ 12 | 13 | func addNextNumber(nextNum *[101]int) { 14 | i := 0 15 | for nextNum[i] != 0 { 16 | i++ 17 | } 18 | nextNum[i] = nextNum[i-1] + 1 19 | } 20 | 21 | func main() { 22 | nextNum := [101]int{1} 23 | for i := 0; i < 100; i++ { 24 | go addNextNumber(&nextNum) 25 | } 26 | for nextNum[100] == 0 { 27 | fmt.Println("Waiting for goroutines to complete") 28 | time.Sleep(10 * time.Millisecond) 29 | } 30 | fmt.Println(nextNum) 31 | } 32 | -------------------------------------------------------------------------------- /chapter9/listing9.10/fanIn.go: -------------------------------------------------------------------------------- 1 | package listing9_10 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | func FanIn[K any](quit <-chan int, allChannels ...<-chan K) chan K { 8 | wg := sync.WaitGroup{} 9 | wg.Add(len(allChannels)) 10 | output := make(chan K) 11 | for _, c := range allChannels { 12 | go func(channel <-chan K) { 13 | defer wg.Done() 14 | for i := range channel { 15 | select { 16 | case output <- i: 17 | case <-quit: 18 | return 19 | } 20 | } 21 | }(c) 22 | } 23 | go func() { 24 | wg.Wait() 25 | close(output) 26 | }() 27 | return output 28 | } 29 | -------------------------------------------------------------------------------- /chapter9/listing9.3_4/generateurls.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func generateUrls(quit <-chan int) <-chan string { 6 | urls := make(chan string) 7 | go func() { 8 | defer close(urls) 9 | for i := 100; i <= 130; i++ { 10 | url := fmt.Sprintf("https://rfc-editor.org/rfc/rfc%d.txt", i) 11 | select { 12 | case urls <- url: 13 | case <-quit: 14 | return 15 | } 16 | } 17 | }() 18 | return urls 19 | } 20 | 21 | func main() { 22 | quit := make(chan int) 23 | defer close(quit) 24 | results := generateUrls(quit) 25 | for result := range results { 26 | fmt.Println(result) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /chapter11/listing11.7/deadlocknodetection.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | func lockBoth(lock1, lock2 *sync.Mutex, wg *sync.WaitGroup) { 10 | for i := 0; i < 10000; i++ { 11 | lock1.Lock(); lock2.Lock() 12 | lock1.Unlock(); lock2.Unlock() 13 | } 14 | wg.Done() 15 | } 16 | 17 | func main() { 18 | lockA, lockB := sync.Mutex{}, sync.Mutex{} 19 | wg := sync.WaitGroup{} 20 | wg.Add(2) 21 | go lockBoth(&lockA, &lockB, &wg) 22 | go lockBoth(&lockB, &lockA, &wg) 23 | go func() { 24 | wg.Wait() 25 | fmt.Println("Done waiting on waitgroup") 26 | }() 27 | time.Sleep(30 * time.Second) 28 | fmt.Println("Done") 29 | } 30 | -------------------------------------------------------------------------------- /chapter7/listing7.10/closingFlag.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func receiver(messages <-chan int) { 9 | for { 10 | msg, more := <-messages 11 | fmt.Println(time.Now().Format("15:04:05"), "Received:", msg, more) 12 | time.Sleep(1 * time.Second) 13 | if !more { 14 | return 15 | } 16 | } 17 | } 18 | 19 | func main() { 20 | msgChannel := make(chan int) 21 | go receiver(msgChannel) 22 | for i := 1; i <= 3; i++ { 23 | fmt.Println(time.Now().Format("15:04:05"), "Sending:", i) 24 | msgChannel <- i 25 | time.Sleep(1 * time.Second) 26 | } 27 | close(msgChannel) 28 | time.Sleep(3 * time.Second) 29 | } 30 | -------------------------------------------------------------------------------- /chapter8/listing8.1_2/selectmanychannels.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func writeEvery(msg string, seconds time.Duration) <-chan string { 9 | messages := make(chan string) 10 | go func() { 11 | for { 12 | time.Sleep(seconds) 13 | messages <- msg 14 | } 15 | }() 16 | return messages 17 | } 18 | 19 | func main() { 20 | messagesFromA := writeEvery("Tick", 1 * time.Second) 21 | messagesFromB := writeEvery("Tock", 3 * time.Second) 22 | for { 23 | select { 24 | case msg1 := <-messagesFromA: 25 | fmt.Println(msg1) 26 | case msg2 := <-messagesFromB: 27 | fmt.Println(msg2) 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /chapter10/listing10.2/dirfilehash.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/cutajarj/ConcurrentProgrammingWithGo/chapter10/listing10.1" 6 | "os" 7 | "path/filepath" 8 | "sync" 9 | ) 10 | 11 | func main() { 12 | dir := os.Args[1] 13 | files, _ := os.ReadDir(dir) 14 | wg := sync.WaitGroup{} 15 | for _, file := range files { 16 | if !file.IsDir() { 17 | wg.Add(1) 18 | go func(filename string) { 19 | fPath := filepath.Join(dir, filename) 20 | hash := listing10_1.FHash(fPath) 21 | fmt.Printf("%s - %x\n", filename, hash) 22 | wg.Done() 23 | }(file.Name()) 24 | } 25 | } 26 | wg.Wait() 27 | } 28 | -------------------------------------------------------------------------------- /chapter6/listing6.12_13/simplebarrierexample.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/cutajarj/ConcurrentProgrammingWithGo/chapter6/listing6.10" 6 | "time" 7 | ) 8 | 9 | func workAndWait(name string, timeToWork int, barrier *listing6_10.Barrier) { 10 | start := time.Now() 11 | for { 12 | fmt.Println(time.Since(start), name, "is running") 13 | time.Sleep(time.Duration(timeToWork) * time.Second) 14 | fmt.Println(time.Since(start), name, "is waiting on barrier") 15 | barrier.Wait() 16 | } 17 | } 18 | 19 | func main() { 20 | barrier := listing6_10.NewBarrier(2) 21 | go workAndWait("Red", 4, barrier) 22 | go workAndWait("Blue", 10, barrier) 23 | time.Sleep(100 * time.Second) 24 | } 25 | -------------------------------------------------------------------------------- /chapter3/listing3.5_6/stingyspendy.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | /* 9 | Note: this program has a race condition for demonstration purposes 10 | In later chapters we cover how to wait for threads to complete their work 11 | */ 12 | func stingy(money *int) { 13 | for i := 0; i < 1000000; i++ { 14 | *money += 10 15 | } 16 | fmt.Println("Stingy Done") 17 | } 18 | 19 | func spendy(money *int) { 20 | for i := 0; i < 1000000; i++ { 21 | *money -= 10 22 | } 23 | fmt.Println("Spendy Done") 24 | } 25 | 26 | func main() { 27 | money := 100 28 | go stingy(&money) 29 | go spendy(&money) 30 | time.Sleep(2 * time.Second) 31 | fmt.Println("Money in bank account: ", money) 32 | } 33 | -------------------------------------------------------------------------------- /chapter6/listing6.2/charcounterwaitgroup.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/cutajarj/ConcurrentProgrammingWithGo/chapter4/listing4.5" 6 | "sync" 7 | ) 8 | 9 | func main() { 10 | wg := sync.WaitGroup{} 11 | wg.Add(31) 12 | mutex := sync.Mutex{} 13 | var frequency = make([]int, 26) 14 | for i := 1000; i <= 1030; i++ { 15 | url := fmt.Sprintf("https://rfc-editor.org/rfc/rfc%d.txt", i) 16 | go func() { 17 | listing4_5.CountLetters(url, frequency, &mutex) 18 | wg.Done() 19 | }() 20 | } 21 | wg.Wait() 22 | mutex.Lock() 23 | for i, c := range listing4_5.AllLetters { 24 | fmt.Printf("%c-%d ", c, frequency[i]) 25 | } 26 | mutex.Unlock() 27 | } 28 | -------------------------------------------------------------------------------- /exercises/chapter10/exercise10.3/totallines.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "net/http" 7 | "strings" 8 | ) 9 | 10 | func main() { 11 | const pagesToDownload = 30 12 | totalLines := 0 13 | for i := 1000; i < 1000 + pagesToDownload; i++ { 14 | url := fmt.Sprintf("https://rfc-editor.org/rfc/rfc%d.txt", i) 15 | fmt.Println("Downloading", url) 16 | resp, _ := http.Get(url) 17 | if resp.StatusCode != 200 { 18 | panic("Server's error: " + resp.Status) 19 | } 20 | bodyBytes, _ := io.ReadAll(resp.Body) 21 | totalLines += strings.Count(string(bodyBytes), "\n") 22 | resp.Body.Close() 23 | } 24 | fmt.Println("Total lines:", totalLines) 25 | } 26 | 27 | -------------------------------------------------------------------------------- /chapter6/listing6.5_6/filesearch.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | "strings" 8 | "sync" 9 | ) 10 | 11 | func fileSearch(dir string, filename string, wg *sync.WaitGroup) { 12 | files, _ := os.ReadDir(dir) 13 | for _, file := range files { 14 | fpath := filepath.Join(dir, file.Name()) 15 | if strings.Contains(file.Name(), filename) { 16 | fmt.Println(fpath) 17 | } 18 | if file.IsDir() { 19 | wg.Add(1) 20 | go fileSearch(fpath, filename, wg) 21 | } 22 | } 23 | wg.Done() 24 | } 25 | 26 | func main() { 27 | wg := sync.WaitGroup{} 28 | wg.Add(1) 29 | go fileSearch(os.Args[1], os.Args[2], &wg) 30 | wg.Wait() 31 | } 32 | -------------------------------------------------------------------------------- /chapter12/listing12.12_13/usingfutex2.md: -------------------------------------------------------------------------------- 1 | # Developing a mutex using a futex attempt 2 2 | 3 | Note: This code will not compile because we cannot access futexes from Go. 4 | This code is to demonstrate the mutex algorithms. It is our second attempt to 5 | implement a mutex using futexes. 6 | 7 | ```go 8 | package listing12_12 9 | 10 | import "sync/atomic" 11 | 12 | type FutexLock int32 13 | 14 | func (f *FutexLock) Unlock() { 15 | oldValue := atomic.SwapInt32((*int32)(f), 0) 16 | if oldValue == 2 { 17 | futex_wakeup((*int32)(f), 1) 18 | } 19 | } 20 | 21 | func (f *FutexLock) Lock() { 22 | if !atomic.CompareAndSwapInt32((*int32)(f), 0, 1) { 23 | for atomic.SwapInt32((*int32)(f), 2) != 0 { 24 | futex_wait((*int32)(f), 2) 25 | } 26 | } 27 | } 28 | ``` 29 | -------------------------------------------------------------------------------- /chapter6/listing6.7/waitgrp.go: -------------------------------------------------------------------------------- 1 | package listing6_7 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | type WaitGrp struct { 8 | groupSize int 9 | cond *sync.Cond 10 | } 11 | 12 | func NewWaitGrp() *WaitGrp { 13 | return &WaitGrp{ 14 | cond: sync.NewCond(&sync.Mutex{}), 15 | } 16 | } 17 | 18 | func (wg *WaitGrp) Add(delta int) { 19 | wg.cond.L.Lock() 20 | wg.groupSize += delta 21 | wg.cond.L.Unlock() 22 | } 23 | 24 | func (wg *WaitGrp) Wait() { 25 | wg.cond.L.Lock() 26 | for wg.groupSize > 0 { 27 | wg.cond.Wait() 28 | } 29 | wg.cond.L.Unlock() 30 | } 31 | 32 | func (wg *WaitGrp) Done() { 33 | wg.cond.L.Lock() 34 | wg.groupSize-- 35 | if wg.groupSize == 0 { 36 | wg.cond.Broadcast() 37 | } 38 | wg.cond.L.Unlock() 39 | } 40 | -------------------------------------------------------------------------------- /exercises/chapter10/exercise10.2/httpservernonblocking.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/cutajarj/ConcurrentProgrammingWithGo/chapter10/listing10.9" 6 | "net" 7 | ) 8 | 9 | 10 | func main() { 11 | incomingConnections := make(chan net.Conn, 10) 12 | listing10_9.StartHttpWorkers(3, incomingConnections) 13 | server, _ := net.Listen("tcp", "localhost:8080") 14 | defer server.Close() 15 | for { 16 | conn, _ := server.Accept() 17 | select { 18 | case incomingConnections <- conn: 19 | default: 20 | fmt.Println("Server is busy") 21 | conn.Write([]byte("HTTP/1.1 503 Service Unavailable\r\n\r\n" + 22 | "Busy\n")) 23 | conn.Close() 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /exercises/chapter12/exercise12.3main/spinsemaphoremain.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/cutajarj/ConcurrentProgrammingWithGo/exercises/chapter12/exercise12.3" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | func acquireAndWait(id int, sema *exercise12_3.SpinSemaphore) { 11 | sema.Acquire() 12 | fmt.Println(id, "has acquired the semaphore") 13 | time.Sleep(2 * time.Second) 14 | fmt.Println(id, "releasing the semaphore") 15 | sema.Release() 16 | } 17 | 18 | func main() { 19 | sema := exercise12_3.NewSpinSemaphore(2) 20 | wg := sync.WaitGroup{} 21 | wg.Add(10) 22 | for i := 0; i < 10; i++ { 23 | go func(id int) { 24 | acquireAndWait(id, sema) 25 | wg.Done() 26 | }(i) 27 | } 28 | wg.Wait() 29 | } 30 | -------------------------------------------------------------------------------- /exercises/chapter8/exercise8.1/latesttemp.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | func generateTemp() chan int { 10 | output := make(chan int) 11 | go func() { 12 | temp := 50 //fahrenheit 13 | for { 14 | output <- temp 15 | temp += rand.Intn(3) - 1 16 | time.Sleep(200 * time.Millisecond) 17 | } 18 | }() 19 | return output 20 | } 21 | 22 | func outputTemp(input chan int) { 23 | go func() { 24 | for { 25 | fmt.Println("Current temp:", <-input) 26 | time.Sleep(2 * time.Second) 27 | } 28 | }() 29 | } 30 | 31 | func main() { 32 | temps := generateTemp() 33 | display := make(chan int) 34 | outputTemp(display) 35 | t := <-temps 36 | for { 37 | select { 38 | case t = <-temps: 39 | case display <- t: 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /exercises/chapter4/exercise4.1solution/countdown.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | func countdown(seconds *int, mutex *sync.Mutex) { 10 | mutex.Lock() 11 | remaining := *seconds 12 | mutex.Unlock() 13 | for remaining > 0 { 14 | time.Sleep(1 * time.Second) 15 | mutex.Lock() 16 | *seconds -= 1 17 | remaining = *seconds 18 | mutex.Unlock() 19 | } 20 | } 21 | 22 | func main() { 23 | mutex := sync.Mutex{} 24 | count := 5 25 | go countdown(&count, &mutex) 26 | remaining := count 27 | for remaining > 0 { 28 | time.Sleep(500 * time.Millisecond) 29 | mutex.Lock() 30 | fmt.Println(count) 31 | remaining = count 32 | mutex.Unlock() 33 | } 34 | } 35 | 36 | -------------------------------------------------------------------------------- /chapter7/listing7.14main/channelmain.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/cutajarj/ConcurrentProgrammingWithGo/chapter7/listing7.14" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | func receiver(messages *listing7_14.Channel[int], wGroup *sync.WaitGroup) { 11 | msg := 0 12 | for msg != -1 { 13 | time.Sleep(1 * time.Second) 14 | msg = messages.Receive() 15 | fmt.Println("Received:", msg) 16 | } 17 | wGroup.Done() 18 | } 19 | 20 | func main() { 21 | channel := listing7_14.NewChannel[int](10) 22 | wGroup := sync.WaitGroup{} 23 | wGroup.Add(1) 24 | go receiver(channel, &wGroup) 25 | for i := 1; i <= 6; i++ { 26 | fmt.Println("Sending: ", i) 27 | channel.Send(i) 28 | } 29 | channel.Send(-1) 30 | wGroup.Wait() 31 | } 32 | -------------------------------------------------------------------------------- /exercises/chapter9/exercise9.2/takeuntil.go: -------------------------------------------------------------------------------- 1 | package exercise9_2 2 | 3 | func TakeUntil[K any](quit chan int, f func(K) bool, input <-chan K) <-chan K { 4 | output := make(chan K) 5 | go func() { 6 | defer close(output) 7 | moreData := true 8 | fValue := true 9 | var msg K 10 | for fValue && moreData { 11 | select { 12 | case msg, moreData = <-input: 13 | if moreData { 14 | fValue = f(msg) 15 | if fValue { 16 | output <- msg 17 | } 18 | } 19 | case <-quit: 20 | return 21 | } 22 | } 23 | if !fValue { 24 | close(quit) 25 | } 26 | }() 27 | return output 28 | } 29 | -------------------------------------------------------------------------------- /chapter12/listing12.1_2/atomicstingyspendy.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "sync/atomic" 7 | ) 8 | 9 | func stingy(money *int32) { 10 | for i := 0; i < 1000000; i++ { 11 | atomic.AddInt32(money, 10) 12 | } 13 | fmt.Println("Stingy Done") 14 | } 15 | 16 | func spendy(money *int32) { 17 | for i := 0; i < 1000000; i++ { 18 | atomic.AddInt32(money, -10) 19 | } 20 | fmt.Println("Spendy Done") 21 | } 22 | 23 | func main() { 24 | money := int32(100) 25 | wg := sync.WaitGroup{} 26 | wg.Add(2) 27 | go func() { 28 | stingy(&money) 29 | wg.Done() 30 | }() 31 | go func() { 32 | spendy(&money) 33 | wg.Done() 34 | }() 35 | wg.Wait() 36 | fmt.Println("Money in account: ", atomic.LoadInt32(&money)) 37 | } 38 | -------------------------------------------------------------------------------- /chapter3/listing3.7/stingyspendysched.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | "time" 7 | ) 8 | 9 | /* 10 | Note: this program has a race condition for demonstration purposes 11 | In later chapters we cover how to wait for threads to complete their work 12 | */ 13 | func stingy(money *int) { 14 | for i := 0; i < 1000000; i++ { 15 | *money += 10 16 | runtime.Gosched() 17 | } 18 | fmt.Println("Stingy Done") 19 | } 20 | 21 | func spendy(money *int) { 22 | for i := 0; i < 1000000; i++ { 23 | *money -= 10 24 | runtime.Gosched() 25 | } 26 | fmt.Println("Spendy Done") 27 | } 28 | 29 | func main() { 30 | money := 100 31 | go stingy(&money) 32 | go spendy(&money) 33 | time.Sleep(2 * time.Second) 34 | fmt.Print("Money in bank account: ", money) 35 | } 36 | -------------------------------------------------------------------------------- /chapter8/listing8.7/selecttimer.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strconv" 7 | "time" 8 | ) 9 | 10 | func sendMsgAfter(seconds time.Duration) <-chan string { 11 | messages := make(chan string) 12 | go func() { 13 | time.Sleep(seconds) 14 | messages <- "Hello" 15 | }() 16 | return messages 17 | } 18 | 19 | func main() { 20 | t, _ := strconv.Atoi(os.Args[1]) 21 | messages := sendMsgAfter(3 * time.Second) 22 | timeoutDuration := time.Duration(t) * time.Second 23 | fmt.Printf("Waiting for message for %d seconds...\n", t) 24 | select { 25 | case msg := <-messages: 26 | fmt.Println("Message received:", msg) 27 | case tNow := <-time.After(timeoutDuration): 28 | fmt.Println("Timed out. Waited until:", tNow.Format("15:04:05")) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /chapter9/listing9.20_21/primesieve.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func primeMultipleFilter(numbers <-chan int, quit chan<- int) { 6 | var right chan int 7 | p := <-numbers 8 | fmt.Println(p) 9 | for n := range numbers { 10 | if n%p != 0 { 11 | if right == nil { 12 | right = make(chan int) 13 | go primeMultipleFilter(right, quit) 14 | } 15 | right <- n 16 | } 17 | } 18 | if right == nil { 19 | close(quit) 20 | } else { 21 | close(right) 22 | } 23 | } 24 | 25 | func main() { 26 | numbers := make(chan int) 27 | quit := make(chan int) 28 | go primeMultipleFilter(numbers, quit) 29 | for i := 2; i < 100000; i++ { 30 | numbers <- i 31 | } 32 | close(numbers) 33 | <-quit 34 | } 35 | -------------------------------------------------------------------------------- /chapter7/listing7.5_6/bufferchannel.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | func receiver(messages chan int, wGroup *sync.WaitGroup) { 10 | msg := 0 11 | for msg != -1 { 12 | time.Sleep(1 * time.Second) 13 | msg = <-messages 14 | fmt.Println(time.Now().Format("15:04:05"), "Received:", msg) 15 | } 16 | wGroup.Done() 17 | } 18 | 19 | func main() { 20 | msgChannel := make(chan int, 3) 21 | wGroup := sync.WaitGroup{} 22 | wGroup.Add(1) 23 | go receiver(msgChannel, &wGroup) 24 | for i := 1; i <= 6; i++ { 25 | size := len(msgChannel) 26 | fmt.Printf("%s Sending: %d. Buffer Size: %d\n", 27 | time.Now().Format("15:04:05"), i, size) 28 | msgChannel <- i 29 | } 30 | msgChannel <- -1 31 | wGroup.Wait() 32 | } 33 | -------------------------------------------------------------------------------- /chapter4/listing4.1_2/stingyspendymutex.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | func stingy(money *int, mutex *sync.Mutex) { 10 | for i := 0; i < 1000000; i++ { 11 | mutex.Lock() 12 | *money += 10 13 | mutex.Unlock() 14 | } 15 | fmt.Println("Stingy Done") 16 | } 17 | 18 | func spendy(money *int, mutex *sync.Mutex) { 19 | for i := 0; i < 1000000; i++ { 20 | mutex.Lock() 21 | *money -= 10 22 | mutex.Unlock() 23 | } 24 | fmt.Println("Spendy Done") 25 | } 26 | 27 | func main() { 28 | money := 100 29 | mutex := sync.Mutex{} 30 | go stingy(&money, &mutex) 31 | go spendy(&money, &mutex) 32 | time.Sleep(2 * time.Second) 33 | mutex.Lock() 34 | fmt.Println("Money in bank account: ", money) 35 | mutex.Unlock() 36 | } 37 | -------------------------------------------------------------------------------- /chapter11/listing11.3_4/bankaccountmutex.go: -------------------------------------------------------------------------------- 1 | package listing11_3_4 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | type BankAccount struct { 9 | id string 10 | balance int 11 | mutex sync.Mutex 12 | } 13 | 14 | func NewBankAccount(id string) *BankAccount { 15 | return &BankAccount{ 16 | id: id, 17 | balance: 100, 18 | mutex: sync.Mutex{}, 19 | } 20 | } 21 | 22 | func (src *BankAccount) Transfer(to *BankAccount, amount int, exId int) { 23 | fmt.Printf("%d Locking %s's account\n", exId, src.id) 24 | src.mutex.Lock() 25 | fmt.Printf("%d Locking %s's account\n", exId, to.id) 26 | to.mutex.Lock() 27 | src.balance -= amount 28 | to.balance += amount 29 | to.mutex.Unlock() 30 | src.mutex.Unlock() 31 | fmt.Printf("%d Unlocked %s and %s\n", exId, src.id, to.id) 32 | } 33 | 34 | -------------------------------------------------------------------------------- /chapter4/listing4.5/charcountermutex.go: -------------------------------------------------------------------------------- 1 | package listing4_5 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "net/http" 7 | "strings" 8 | "sync" 9 | "time" 10 | ) 11 | 12 | const AllLetters = "abcdefghijklmnopqrstuvwxyz" 13 | 14 | func CountLetters(url string, frequency []int, mutex *sync.Mutex) { 15 | resp, _ := http.Get(url) 16 | defer resp.Body.Close() 17 | if resp.StatusCode != 200 { 18 | panic("Server returning error code: " + resp.Status) 19 | } 20 | body, _ := io.ReadAll(resp.Body) 21 | mutex.Lock() 22 | for _, b := range body { 23 | c := strings.ToLower(string(b)) 24 | cIndex := strings.Index(AllLetters, c) 25 | if cIndex >= 0 { 26 | frequency[cIndex] += 1 27 | } 28 | } 29 | mutex.Unlock() 30 | fmt.Println("Completed:", url, time.Now().Format("15:04:05")) 31 | } 32 | -------------------------------------------------------------------------------- /chapter5/listing5.8_9/gamesync.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | func main() { 10 | cond := sync.NewCond(&sync.Mutex{}) 11 | playersInGame := 4 12 | for playerId := 0; playerId < 4; playerId++ { 13 | go playerHandler(cond, &playersInGame, playerId) 14 | time.Sleep(1 * time.Second) 15 | } 16 | } 17 | 18 | func playerHandler(cond *sync.Cond, playersRemaining *int, playerId int) { 19 | cond.L.Lock() 20 | fmt.Println(playerId, ": Connected") 21 | *playersRemaining-- 22 | if *playersRemaining == 0 { 23 | cond.Broadcast() 24 | } 25 | for *playersRemaining > 0 { 26 | fmt.Println(playerId, ": Waiting for more players") 27 | cond.Wait() 28 | } 29 | cond.L.Unlock() 30 | fmt.Println("All players connected. Ready player", playerId) 31 | //Game started 32 | } 33 | -------------------------------------------------------------------------------- /chapter4/listing4.6/nonblockingmutex.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/cutajarj/ConcurrentProgrammingWithGo/chapter4/listing4.5" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | func main() { 11 | mutex := sync.Mutex{} 12 | var frequency = make([]int, 26) 13 | for i := 2000; i <= 2200; i++ { 14 | url := fmt.Sprintf("https://rfc-editor.org/rfc/rfc%d.txt", i) 15 | go listing4_5.CountLetters(url, frequency, &mutex) 16 | } 17 | for i := 0; i < 100; i++ { 18 | time.Sleep(100 * time.Millisecond) 19 | if mutex.TryLock() { 20 | for i, c := range listing4_5.AllLetters { 21 | fmt.Printf("%c-%d ", c, frequency[i]) 22 | } 23 | fmt.Println() 24 | mutex.Unlock() 25 | } else { 26 | fmt.Println("Mutex already being used") 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /exercises/chapter2/exercise2.2/grepfiles.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | "strings" 8 | "time" 9 | ) 10 | 11 | /* 12 | You can run this by executing: 13 | go run grepfiles.go Mozilla ../../commonfiles/txtfile1 ../../commonfiles/txtfile2 ../../commonfiles/txtfile3 14 | */ 15 | 16 | func grepFile(filename string, searchStr string) { 17 | content, err := os.ReadFile(filename) 18 | if err != nil { 19 | log.Fatal(err) 20 | } 21 | if strings.Contains(string(content), searchStr) { 22 | fmt.Println(filename, "contains a match with", searchStr) 23 | } else { 24 | fmt.Println(filename, "does NOT contain a match with", searchStr) 25 | } 26 | } 27 | 28 | func main() { 29 | searchStr := os.Args[1] 30 | filenames := os.Args[2:] 31 | for _, filename := range filenames { 32 | go grepFile(filename, searchStr) 33 | } 34 | time.Sleep(2 * time.Second) 35 | } 36 | -------------------------------------------------------------------------------- /chapter12/listing12.14/simplifiedsemaphore.md: -------------------------------------------------------------------------------- 1 | # Simplifying Go's internal semaphore to understand how it works 2 | 3 | Note: This code will not compile as since it is pseudocode. It is meant to illustrate how 4 | Go's internal semaphore works without the extra noise of the runtime functionality 5 | 6 | ```go 7 | package listing12_14 8 | 9 | import "sync/atomic" 10 | 11 | func semaphoreAcquire(permits *int32, queueAtTheBack bool) { 12 | for { 13 | v := atomic.LoadInt32(permits) 14 | if v != 0 && atomic.CompareAndSwapInt32(permits, v, v-1) { 15 | break 16 | } 17 | //The queue functions will only queue and park the 18 | //goroutine if the permits atomic variable is zero 19 | if queueAtTheBack { 20 | queueAndSuspendGoroutineAtTheEnd(permits) 21 | } else { 22 | queueAndSuspendGoroutineInFront(permits) 23 | } 24 | } 25 | } 26 | ``` 27 | -------------------------------------------------------------------------------- /chapter4/listing4.12/readwritemutex.go: -------------------------------------------------------------------------------- 1 | package listing4_12 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | // Listing 4.12 8 | type ReadWriteMutex struct { 9 | readersCounter int 10 | readersLock sync.Mutex 11 | globalLock sync.Mutex 12 | } 13 | 14 | // Listing 4.13 15 | func (rw *ReadWriteMutex) ReadLock() { 16 | rw.readersLock.Lock() 17 | rw.readersCounter++ 18 | if rw.readersCounter == 1 { 19 | rw.globalLock.Lock() 20 | } 21 | rw.readersLock.Unlock() 22 | } 23 | 24 | func (rw *ReadWriteMutex) WriteLock() { 25 | rw.globalLock.Lock() 26 | } 27 | 28 | // Listing 4.14 29 | func (rw *ReadWriteMutex) ReadUnlock() { 30 | rw.readersLock.Lock() 31 | rw.readersCounter-- 32 | if rw.readersCounter == 0 { 33 | rw.globalLock.Unlock() 34 | } 35 | rw.readersLock.Unlock() 36 | } 37 | 38 | func (rw *ReadWriteMutex) WriteUnlock() { 39 | rw.globalLock.Unlock() 40 | } 41 | 42 | -------------------------------------------------------------------------------- /chapter10/listing10.12/httpservernonblocking.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/cutajarj/ConcurrentProgrammingWithGo/chapter10/listing10.9" 6 | "net" 7 | ) 8 | 9 | /* 10 | To trigger the "Too Many Requests" message try running this: 11 | seq 1 2000 | xargs -Iname -P100 curl -s "http://localhost:8080/index.html" | grep Busy 12 | */ 13 | func main() { 14 | incomingConnections := make(chan net.Conn) 15 | listing10_9.StartHttpWorkers(3, incomingConnections) 16 | server, _ := net.Listen("tcp", "localhost:8080") 17 | defer server.Close() 18 | for { 19 | conn, _ := server.Accept() 20 | select { 21 | case incomingConnections <- conn: 22 | default: 23 | fmt.Println("Server is busy") 24 | conn.Write([]byte("HTTP/1.1 429 Too Many Requests\r\n\r\n" + 25 | "Busy\n")) 26 | conn.Close() 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /chapter12/listing12.7/flightbooking.go: -------------------------------------------------------------------------------- 1 | package listing12_7 2 | 3 | import ( 4 | "github.com/cutajarj/ConcurrentProgrammingWithGo/chapter12/listing12.6" 5 | "sort" 6 | ) 7 | 8 | func Book(flights []*listing12_6.Flight, seatsToBook int) bool { 9 | bookable := true 10 | sort.Slice(flights, func(a, b int) bool { 11 | flightA := flights[a].Origin + flights[a].Dest 12 | flightB := flights[b].Origin + flights[b].Dest 13 | return flightA < (flightB) 14 | }) 15 | for _, f := range flights { 16 | f.Locker.Lock() 17 | } 18 | for i := 0; i < len(flights) && bookable; i++ { 19 | if flights[i].SeatsLeft < seatsToBook { 20 | bookable = false 21 | } 22 | } 23 | for i := 0; i < len(flights) && bookable; i++ { 24 | flights[i].SeatsLeft-=seatsToBook 25 | } 26 | for _, f := range flights { 27 | f.Locker.Unlock() 28 | } 29 | return bookable 30 | } 31 | -------------------------------------------------------------------------------- /exercises/chapter4/exercise4.2/readtrywritemutex.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | type ReadWriteMutex struct { 8 | readersCounter int 9 | readersLock sync.Mutex 10 | globalLock sync.Mutex 11 | } 12 | 13 | func (rw *ReadWriteMutex) ReadLock() { 14 | rw.readersLock.Lock() 15 | rw.readersCounter++ 16 | if rw.readersCounter == 1 { 17 | rw.globalLock.Lock() 18 | } 19 | rw.readersLock.Unlock() 20 | } 21 | 22 | func (rw *ReadWriteMutex) WriteLock() { 23 | rw.globalLock.Lock() 24 | } 25 | 26 | func (rw *ReadWriteMutex) TryWriteLock() bool { 27 | return rw.globalLock.TryLock() 28 | } 29 | 30 | func (rw *ReadWriteMutex) ReadUnlock() { 31 | rw.readersLock.Lock() 32 | rw.readersCounter-- 33 | if rw.readersCounter == 0 { 34 | rw.globalLock.Unlock() 35 | } 36 | rw.readersLock.Unlock() 37 | } 38 | 39 | func (rw *ReadWriteMutex) WriteUnlock() { 40 | rw.globalLock.Unlock() 41 | } 42 | -------------------------------------------------------------------------------- /chapter10/listing10.4/dirhashconcurrent.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/sha256" 5 | "fmt" 6 | "github.com/cutajarj/ConcurrentProgrammingWithGo/chapter10/listing10.1" 7 | "os" 8 | "path/filepath" 9 | ) 10 | 11 | func main() { 12 | dir := os.Args[1] 13 | files, _ := os.ReadDir(dir) 14 | sha := sha256.New() 15 | var prev, next chan int 16 | for _, file := range files { 17 | if !file.IsDir() { 18 | next = make(chan int) 19 | go func(filename string, prev, next chan int) { 20 | fpath := filepath.Join(dir, filename) 21 | hashOnFile := listing10_1.FHash(fpath) 22 | if prev != nil { 23 | <-prev 24 | } 25 | sha.Write(hashOnFile) 26 | next <- 0 27 | }(file.Name(), prev, next) 28 | prev = next 29 | } 30 | } 31 | <-next 32 | fmt.Printf("%x\n", sha.Sum(nil)) 33 | } 34 | -------------------------------------------------------------------------------- /chapter5/listing5.1/stingyspendynegative.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | func stingy(money *int, mutex *sync.Mutex) { 11 | for i := 0; i < 1000000; i++ { 12 | mutex.Lock() 13 | *money += 10 14 | mutex.Unlock() 15 | } 16 | fmt.Println("Stingy Done") 17 | } 18 | 19 | func spendy(money *int, mutex *sync.Mutex) { 20 | for i := 0; i < 200000; i++ { 21 | mutex.Lock() 22 | *money -= 50 23 | if *money < 0 { 24 | fmt.Println("Money is negative!") 25 | os.Exit(1) 26 | } 27 | mutex.Unlock() 28 | } 29 | fmt.Println("Spendy Done") 30 | } 31 | 32 | func main() { 33 | money := 100 34 | mutex := sync.Mutex{} 35 | go stingy(&money, &mutex) 36 | go spendy(&money, &mutex) 37 | time.Sleep(2 * time.Second) 38 | mutex.Lock() 39 | fmt.Println("Money in bank account: ", money) 40 | mutex.Unlock() 41 | } 42 | -------------------------------------------------------------------------------- /chapter11/listing11.5/ledgermutex.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/cutajarj/ConcurrentProgrammingWithGo/chapter11/listing11.3_4" 6 | "math/rand" 7 | "time" 8 | ) 9 | 10 | func main() { 11 | accounts := []listing11_3_4.BankAccount{ 12 | *listing11_3_4.NewBankAccount("Sam"), 13 | *listing11_3_4.NewBankAccount("Paul"), 14 | *listing11_3_4.NewBankAccount("Amy"), 15 | *listing11_3_4.NewBankAccount("Mia"), 16 | } 17 | total := len(accounts) 18 | for i := 0; i < 4; i++ { 19 | go func(eId int) { 20 | for j := 1; j < 1000; j++ { 21 | from, to := rand.Intn(total), rand.Intn(total) 22 | for from == to { 23 | to = rand.Intn(total) 24 | } 25 | accounts[from].Transfer(&accounts[to], 10, eId) 26 | } 27 | fmt.Println(eId, "COMPLETE") 28 | }(i) 29 | } 30 | time.Sleep(60 * time.Second) 31 | } 32 | -------------------------------------------------------------------------------- /chapter8/listing8.8_9/selectsender.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | "math/rand" 7 | ) 8 | 9 | func primesOnly(inputs <-chan int) <-chan int { 10 | results := make(chan int) 11 | go func() { 12 | for c := range inputs { 13 | isPrime := c != 1 14 | for i := 2; i <= int(math.Sqrt(float64(c))); i++ { 15 | if c%i == 0 { 16 | isPrime = false 17 | break 18 | } 19 | } 20 | if isPrime { 21 | results <- c 22 | } 23 | } 24 | }() 25 | return results 26 | } 27 | 28 | func main() { 29 | numbersChannel := make(chan int) 30 | primes := primesOnly(numbersChannel) 31 | for i := 0; i < 100; { 32 | select { 33 | case numbersChannel <- rand.Intn(1000000000) + 1: 34 | case p := <-primes: 35 | fmt.Println("Found prime:", p) 36 | i++ 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /exercises/chapter6/exercise6.1/orderedfilesearch.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | "sort" 8 | "strings" 9 | "sync" 10 | ) 11 | 12 | func fileSearch(dir string, filename string, 13 | wg *sync.WaitGroup, mutex *sync.Mutex, matches *[]string) { 14 | files, _ := os.ReadDir(dir) 15 | for _, file := range files { 16 | fpath := filepath.Join(dir, file.Name()) 17 | if strings.Contains(file.Name(), filename) { 18 | mutex.Lock() 19 | *matches = append(*matches, fpath) 20 | mutex.Unlock() 21 | } 22 | if file.IsDir() { 23 | wg.Add(1) 24 | go fileSearch(fpath, filename, wg, mutex, matches) 25 | } 26 | } 27 | wg.Done() 28 | } 29 | 30 | func main() { 31 | results := make([]string, 0) 32 | mutex := sync.Mutex{} 33 | wg := sync.WaitGroup{} 34 | wg.Add(1) 35 | go fileSearch(os.Args[1], os.Args[2], &wg, &mutex, &results) 36 | wg.Wait() 37 | mutex.Lock() 38 | sort.Strings(results) 39 | fmt.Println(strings.Join(results, "\n")) 40 | mutex.Unlock() 41 | } 42 | -------------------------------------------------------------------------------- /exercises/chapter5/exercise5.1/stingyspendysignalon50.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | func main() { 11 | money := 100 12 | mutex := sync.Mutex{} 13 | cond := sync.NewCond(&mutex) 14 | go stingy(&money, cond) 15 | go spendy(&money, cond) 16 | time.Sleep(2 * time.Second) 17 | mutex.Lock() 18 | fmt.Println("Money in bank account: ", money) 19 | mutex.Unlock() 20 | } 21 | 22 | func stingy(money *int, cond *sync.Cond) { 23 | for i := 0; i < 1000000; i++ { 24 | cond.L.Lock() 25 | *money += 10 26 | if *money >= 50 { 27 | cond.Signal() 28 | } 29 | cond.L.Unlock() 30 | } 31 | fmt.Println("Stingy Done") 32 | } 33 | 34 | func spendy(money *int, cond *sync.Cond) { 35 | for i := 0; i < 200000; i++ { 36 | cond.L.Lock() 37 | for *money < 50 { 38 | cond.Wait() 39 | } 40 | *money -= 50 41 | if *money < 0 { 42 | fmt.Println("Money is negative!") 43 | os.Exit(1) 44 | } 45 | cond.L.Unlock() 46 | } 47 | fmt.Println("Spendy Done") 48 | } 49 | -------------------------------------------------------------------------------- /chapter9/listing9.14/broadcast.go: -------------------------------------------------------------------------------- 1 | package listing9_14 2 | 3 | func Broadcast[K any](quit <-chan int, input <-chan K, n int) []chan K { 4 | outputs := CreateAll[K](n) 5 | go func() { 6 | defer CloseAll(outputs...) 7 | var msg K 8 | moreData := true 9 | for moreData { 10 | select { 11 | case msg, moreData = <-input: 12 | if moreData { 13 | for _, output := range outputs { 14 | output <- msg 15 | } 16 | } 17 | case <-quit: 18 | return 19 | } 20 | } 21 | }() 22 | return outputs 23 | } 24 | 25 | func CreateAll[K any](n int) []chan K { 26 | channels := make([]chan K, n) 27 | for i, _ := range channels { 28 | channels[i] = make(chan K) 29 | } 30 | return channels 31 | } 32 | 33 | func CloseAll[K any](channels ...chan K) { 34 | for _, output := range channels { 35 | close(output) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /exercises/chapter6/exercise6.3single/matrixmultiplytime.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | const matrixSize = 1200 10 | 11 | func generateRandMatrix(matrix *[matrixSize][matrixSize]int) { 12 | for row := 0; row < matrixSize; row++ { 13 | for col := 0; col < matrixSize; col++ { 14 | matrix[row][col] = rand.Intn(10) - 5 15 | } 16 | } 17 | } 18 | 19 | func matrixMultiply(matrixA, matrixB, result *[matrixSize][matrixSize]int) { 20 | for row := 0; row < matrixSize; row++ { 21 | for col := 0; col < matrixSize; col++ { 22 | sum := 0 23 | for i := 0; i < matrixSize; i++ { 24 | sum += matrixA[row][i] * matrixB[i][col] 25 | } 26 | result[row][col] = sum 27 | } 28 | } 29 | } 30 | 31 | func main() { 32 | var matrixA, matrixB, result [matrixSize][matrixSize]int 33 | start := time.Now() 34 | for i := 0; i < 4; i++ { 35 | generateRandMatrix(&matrixA) 36 | generateRandMatrix(&matrixB) 37 | matrixMultiply(&matrixA, &matrixB, &result) 38 | } 39 | fmt.Println("Complete in", time.Since(start)) 40 | } 41 | -------------------------------------------------------------------------------- /chapter11/listing11.1_2/simpledeadlock.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | func red(lock1, lock2 *sync.Mutex) { 10 | for { 11 | fmt.Println("Red: Acquiring lock1") 12 | lock1.Lock() 13 | fmt.Println("Red: Acquiring lock2") 14 | lock2.Lock() 15 | fmt.Println("Red: Both locks Acquired") 16 | lock1.Unlock(); lock2.Unlock() 17 | fmt.Println("Red: Locks Released") 18 | } 19 | } 20 | 21 | func blue(lock1, lock2 *sync.Mutex) { 22 | for { 23 | fmt.Println("Blue: Acquiring lock2") 24 | lock2.Lock() 25 | fmt.Println("Blue: Acquiring lock1") 26 | lock1.Lock() 27 | fmt.Println("Blue: Both locks Acquired") 28 | lock1.Unlock(); lock2.Unlock() 29 | fmt.Println("Blue: Locks Released") 30 | } 31 | } 32 | 33 | func main() { 34 | lockA := sync.Mutex{} 35 | lockB := sync.Mutex{} 36 | go red(&lockA, &lockB) 37 | go blue(&lockA, &lockB) 38 | time.Sleep(20 * time.Second) 39 | fmt.Println("Done") 40 | } -------------------------------------------------------------------------------- /exercises/chapter2/exercise2.3/grepdir.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "log" 7 | "os" 8 | "path/filepath" 9 | "strings" 10 | "time" 11 | ) 12 | 13 | /* 14 | You can run this by executing: 15 | go run grepdir.go Mozilla ../../commonfiles 16 | */ 17 | 18 | func grepPath(path string, fileInfo os.FileInfo, searchStr string) { 19 | fullPath := filepath.Join(path, fileInfo.Name()) 20 | if !fileInfo.IsDir() { 21 | content, err := os.ReadFile(fullPath) 22 | if err != nil { 23 | log.Fatal(err) 24 | } 25 | if strings.Contains(string(content), searchStr) { 26 | fmt.Println(fullPath, "contains a match with", searchStr) 27 | } else { 28 | fmt.Println(fullPath, "does NOT contain a match with", searchStr) 29 | } 30 | } 31 | } 32 | 33 | func main() { 34 | searchStr := os.Args[1] 35 | path := os.Args[2] 36 | files, err := ioutil.ReadDir(path) 37 | if err != nil { 38 | log.Fatal(err) 39 | } 40 | for _, fileInfo := range files { 41 | go grepPath(path, fileInfo, searchStr) 42 | } 43 | time.Sleep(2 * time.Second) 44 | } 45 | -------------------------------------------------------------------------------- /chapter11/listing11.13/simplenodeadlockorder.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | func red(lock1, lock2 *sync.Mutex) { 10 | for { 11 | fmt.Println("Red: Acquiring lock1") 12 | lock1.Lock() 13 | fmt.Println("Red: Acquiring lock2") 14 | lock2.Lock() 15 | fmt.Println("Red: Both locks Acquired") 16 | lock1.Unlock(); lock2.Unlock() 17 | fmt.Println("Red: Locks Released") 18 | } 19 | } 20 | 21 | func blue(lock1, lock2 *sync.Mutex) { 22 | for { 23 | fmt.Println("Blue: Acquiring lock1") 24 | lock1.Lock() 25 | fmt.Println("Blue: Acquiring lock2") 26 | lock2.Lock() 27 | fmt.Println("Blue: Both locks Acquired") 28 | lock1.Unlock(); lock2.Unlock() 29 | fmt.Println("Blue: Locks Released") 30 | } 31 | } 32 | 33 | func main() { 34 | lockA := sync.Mutex{} 35 | lockB := sync.Mutex{} 36 | go red(&lockA, &lockB) 37 | go blue(&lockA, &lockB) 38 | time.Sleep(20 * time.Second) 39 | fmt.Println("Done") 40 | } -------------------------------------------------------------------------------- /chapter7/listing7.14/channel.go: -------------------------------------------------------------------------------- 1 | package listing7_14 2 | 3 | import ( 4 | "container/list" 5 | "github.com/cutajarj/ConcurrentProgrammingWithGo/chapter5/listing5.16" 6 | "sync" 7 | ) 8 | 9 | type Channel[M any] struct { 10 | capacitySema *listing5_16.Semaphore 11 | sizeSema *listing5_16.Semaphore 12 | mutex sync.Mutex 13 | buffer *list.List 14 | } 15 | 16 | func NewChannel[M any](capacity int) *Channel[M] { 17 | return &Channel[M]{ 18 | capacitySema: listing5_16.NewSemaphore(capacity), 19 | sizeSema: listing5_16.NewSemaphore(0), 20 | buffer: list.New(), 21 | } 22 | } 23 | 24 | func (c *Channel[M]) Send(message M) { 25 | c.capacitySema.Acquire() 26 | c.mutex.Lock() 27 | c.buffer.PushBack(message) 28 | c.mutex.Unlock() 29 | c.sizeSema.Release() 30 | } 31 | 32 | func (c *Channel[M]) Receive() M { 33 | c.capacitySema.Release() 34 | c.sizeSema.Acquire() 35 | c.mutex.Lock() 36 | v := c.buffer.Remove(c.buffer.Front()).(M) 37 | c.mutex.Unlock() 38 | return v 39 | } 40 | -------------------------------------------------------------------------------- /chapter3/listing3.2_3/charcountersequential.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "net/http" 7 | "strings" 8 | ) 9 | 10 | const allLetters = "abcdefghijklmnopqrstuvwxyz" 11 | 12 | func countLetters(url string, frequency []int) { 13 | resp, _ := http.Get(url) 14 | defer resp.Body.Close() 15 | if resp.StatusCode != 200 { 16 | panic("Server returning error status code: " + resp.Status) 17 | } 18 | body, _ := io.ReadAll(resp.Body) 19 | for _, b := range body { 20 | c := strings.ToLower(string(b)) 21 | cIndex := strings.Index(allLetters, c) 22 | if cIndex >= 0 { 23 | frequency[cIndex] += 1 24 | } 25 | } 26 | fmt.Println("Completed:", url) 27 | } 28 | 29 | func main() { 30 | var frequency = make([]int, 26) 31 | for i := 1000; i <= 1030; i++ { 32 | url := fmt.Sprintf("https://rfc-editor.org/rfc/rfc%d.txt", i) 33 | countLetters(url, frequency) 34 | } 35 | for i, c := range allLetters { 36 | fmt.Printf("%c-%d ", c, frequency[i]) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /chapter5/listing5.3_4_5/stingyspendycond.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | func main() { 11 | money := 100 12 | mutex := sync.Mutex{} 13 | cond := sync.NewCond(&mutex) 14 | go stingy(&money, cond) 15 | go spendy(&money, cond) 16 | time.Sleep(2 * time.Second) 17 | mutex.Lock() 18 | fmt.Println("Money in bank account: ", money) 19 | mutex.Unlock() 20 | } 21 | 22 | func stingy(money *int, cond *sync.Cond) { 23 | for i := 0; i < 1000000; i++ { 24 | cond.L.Lock() 25 | *money += 10 26 | cond.Signal() 27 | cond.L.Unlock() 28 | } 29 | fmt.Println("Stingy Done") 30 | } 31 | 32 | func spendy(money *int, cond *sync.Cond) { 33 | for i := 0; i < 200000; i++ { 34 | cond.L.Lock() 35 | for *money < 50 { 36 | cond.Wait() 37 | } 38 | *money -= 50 39 | if *money < 0 { 40 | fmt.Println("Money is negative!") 41 | os.Exit(1) 42 | } 43 | cond.L.Unlock() 44 | } 45 | fmt.Println("Spendy Done") 46 | } 47 | -------------------------------------------------------------------------------- /exercises/chapter10/exercise10.3solution/totallines.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "net/http" 7 | "strings" 8 | ) 9 | 10 | func main() { 11 | const pagesToDownload = 30 12 | linesOnPage := make(chan int) 13 | finalResult := make(chan int) 14 | for i := 1000; i < 1000 + pagesToDownload; i++ { 15 | go func(id int) { 16 | url := fmt.Sprintf("https://rfc-editor.org/rfc/rfc%d.txt", id) 17 | fmt.Println("Downloading", url) 18 | resp, _ := http.Get(url) 19 | if resp.StatusCode != 200 { 20 | panic("Server's error: " + resp.Status) 21 | } 22 | bodyBytes, _ := io.ReadAll(resp.Body) 23 | linesOnPage <- strings.Count(string(bodyBytes), "\n") 24 | resp.Body.Close() 25 | }(i) 26 | } 27 | go func() { 28 | totalLines := 0 29 | for i := 0; i < pagesToDownload; i++ { 30 | totalLines += <-linesOnPage 31 | } 32 | finalResult <- totalLines 33 | }() 34 | fmt.Println("Total lines:", <-finalResult) 35 | } 36 | 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 James Cutajar 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /chapter5/listing5.2/stingyspendyretry.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | func stingy(money *int, mutex *sync.Mutex) { 11 | for i := 0; i < 1000000; i++ { 12 | mutex.Lock() 13 | *money += 10 14 | mutex.Unlock() 15 | } 16 | fmt.Println("Stingy Done") 17 | } 18 | 19 | func spendy(money *int, mutex *sync.Mutex) { 20 | for i := 0; i < 200000; i++ { 21 | mutex.Lock() 22 | for *money < 50 { 23 | mutex.Unlock() 24 | time.Sleep(10 * time.Millisecond) 25 | mutex.Lock() 26 | } 27 | *money -= 50 28 | if *money < 0 { 29 | fmt.Println("Money is negative!") 30 | os.Exit(1) 31 | } 32 | mutex.Unlock() 33 | } 34 | fmt.Println("Spendy Done") 35 | } 36 | 37 | func main() { 38 | money := 100 39 | mutex := sync.Mutex{} 40 | go stingy(&money, &mutex) 41 | go spendy(&money, &mutex) 42 | time.Sleep(2 * time.Second) 43 | mutex.Lock() 44 | fmt.Println("Money in bank account: ", money) 45 | mutex.Unlock() 46 | } 47 | -------------------------------------------------------------------------------- /exercises/commonfiles/txtfile2: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 James Cutajar 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /chapter10/listing10.15_16/cupcakefactory.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/cutajarj/ConcurrentProgrammingWithGo/chapter10/listing10.13" 6 | ) 7 | 8 | func AddOnPipe[X, Y any](q <-chan int, f func(X) Y, in <-chan X) chan Y { 9 | output := make(chan Y) 10 | go func() { 11 | defer close(output) 12 | for { 13 | select { 14 | case <-q: 15 | return 16 | case input := <-in: 17 | output <- f(input) 18 | } 19 | } 20 | }() 21 | return output 22 | } 23 | 24 | func main() { 25 | input := make(chan int) 26 | quit := make(chan int) 27 | output := AddOnPipe(quit, listing10_13.Box, 28 | AddOnPipe(quit, listing10_13.AddToppings, 29 | AddOnPipe(quit, listing10_13.Bake, 30 | AddOnPipe(quit, listing10_13.Mixture, 31 | AddOnPipe(quit, listing10_13.PrepareTray, input))))) 32 | go func() { 33 | for i := 0; i < 10; i++ { 34 | input <- i 35 | } 36 | }() 37 | for i := 0; i < 10; i++ { 38 | fmt.Println(<-output, "received") 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /exercises/chapter10/exercise10.1/dirhashconcurrent.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/md5" 5 | "fmt" 6 | "github.com/cutajarj/ConcurrentProgrammingWithGo/chapter10/listing10.1" 7 | "os" 8 | "path/filepath" 9 | "sync" 10 | ) 11 | 12 | func main() { 13 | dir := os.Args[1] 14 | files, _ := os.ReadDir(dir) 15 | hMd5 := md5.New() 16 | var prev, next *sync.WaitGroup 17 | for _, file := range files { 18 | if !file.IsDir() { 19 | next = &sync.WaitGroup{} 20 | next.Add(1) 21 | go func(filename string, prev, next *sync.WaitGroup) { 22 | fpath := filepath.Join(dir, filename) 23 | fmt.Println("Processing", fpath) 24 | hashOnFile := listing10_1.FHash(fpath) 25 | // If not the first iteration 26 | if prev != nil { 27 | prev.Wait() 28 | } 29 | hMd5.Write(hashOnFile) 30 | next.Done() 31 | }(file.Name(), prev, next) 32 | prev = next 33 | } 34 | } 35 | next.Wait() 36 | fmt.Printf("%s - %x\n", dir, hMd5.Sum(nil)) 37 | } 38 | -------------------------------------------------------------------------------- /exercises/chapter5/exercise5.2/gamesynctimeout.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | func playerHandler(cond *sync.Cond, playersRemaining *int, 10 | playerId int, cancel *bool) { 11 | cond.L.Lock() 12 | fmt.Println(playerId, ": Connected") 13 | *playersRemaining-- 14 | if *playersRemaining == 0 { 15 | cond.Broadcast() 16 | } 17 | for *playersRemaining > 0 && !*cancel { 18 | fmt.Println(playerId, ": Waiting for more players") 19 | cond.Wait() 20 | } 21 | cond.L.Unlock() 22 | if *cancel { 23 | fmt.Println(playerId, ": Game cancelled") 24 | } else { 25 | fmt.Println("All players connected. Ready player", playerId) 26 | } 27 | } 28 | 29 | func timeout(cond *sync.Cond, cancel *bool) { 30 | time.Sleep(10 * time.Second) 31 | cond.L.Lock() 32 | *cancel = true 33 | cond.Broadcast() 34 | cond.L.Unlock() 35 | } 36 | 37 | func main() { 38 | cond := sync.NewCond(&sync.Mutex{}) 39 | cancel := false 40 | go timeout(cond, &cancel) 41 | playersInGame := 5 42 | for i := 0; i < 4; i++ { 43 | go playerHandler(cond, &playersInGame, i, &cancel) 44 | time.Sleep(1 * time.Second) 45 | } 46 | time.Sleep(60 * time.Second) 47 | } 48 | -------------------------------------------------------------------------------- /chapter10/listing10.9/httputils.go: -------------------------------------------------------------------------------- 1 | package listing10_9 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "os" 7 | "regexp" 8 | ) 9 | 10 | var r, _ = regexp.Compile("GET (.+) HTTP/1.1\r\n") 11 | 12 | func handleHttpRequest(conn net.Conn) { 13 | buff := make([]byte, 1024) 14 | size, _ := conn.Read(buff) 15 | if r.Match(buff[:size]) { 16 | file, err := os.ReadFile( 17 | fmt.Sprintf("../resources/%s", r.FindSubmatch(buff[:size])[1])) 18 | if err == nil { 19 | conn.Write([]byte(fmt.Sprintf( 20 | "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n\r\n", len(file)))) 21 | conn.Write(file) 22 | } else { 23 | conn.Write([]byte( 24 | "HTTP/1.1 404 Not Found\r\n\r\nNot Found")) 25 | } 26 | } else { 27 | conn.Write([]byte("HTTP/1.1 500 Internal Server Error\r\n\r\n")) 28 | } 29 | conn.Close() 30 | } 31 | 32 | func StartHttpWorkers(n int, incomingConnections <-chan net.Conn) { 33 | for i := 0; i < n; i++ { 34 | go func() { 35 | for c := range incomingConnections { 36 | handleHttpRequest(c) 37 | } 38 | }() 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /exercises/chapter6/exercise6.4/matrixmultiplywg.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "sync" 7 | ) 8 | 9 | const matrixSize = 3 10 | 11 | func generateRandMatrix(matrix *[matrixSize][matrixSize]int) { 12 | for row := 0; row < matrixSize; row++ { 13 | for col := 0; col < matrixSize; col++ { 14 | matrix[row][col] = rand.Intn(10) - 5 15 | } 16 | } 17 | } 18 | 19 | func rowMultiply(matrixA, matrixB, result *[matrixSize][matrixSize]int, row int, wg *sync.WaitGroup) { 20 | for col := 0; col < matrixSize; col++ { 21 | sum := 0 22 | for i := 0; i < matrixSize; i++ { 23 | sum += matrixA[row][i] * matrixB[i][col] 24 | } 25 | result[row][col] = sum 26 | } 27 | wg.Done() 28 | } 29 | 30 | func main() { 31 | var matrixA, matrixB, result [matrixSize][matrixSize]int 32 | for i := 0; i < 4; i++ { 33 | generateRandMatrix(&matrixA) 34 | generateRandMatrix(&matrixB) 35 | wg := sync.WaitGroup{} 36 | wg.Add(matrixSize) 37 | for row := 0; row < matrixSize; row++ { 38 | go rowMultiply(&matrixA, &matrixB, &result, row, &wg) 39 | } 40 | wg.Wait() 41 | for i := 0; i < matrixSize; i++ { 42 | fmt.Println(matrixA[i], matrixB[i], result[i]) 43 | } 44 | fmt.Println() 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /chapter6/listing6.14_15/matrixmultiplysimple.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | ) 7 | 8 | const matrixSize = 3 9 | 10 | func generateRandMatrix(matrix *[matrixSize][matrixSize]int) { 11 | for row := 0; row < matrixSize; row++ { 12 | for col := 0; col < matrixSize; col++ { 13 | matrix[row][col] = rand.Intn(10) - 5 14 | } 15 | } 16 | } 17 | 18 | func matrixMultiply(matrixA, matrixB, result *[matrixSize][matrixSize]int) { 19 | for row := 0; row < matrixSize; row++ { 20 | for col := 0; col < matrixSize; col++ { 21 | sum := 0 22 | for i := 0; i < matrixSize; i++ { 23 | sum += matrixA[row][i] * matrixB[i][col] 24 | } 25 | result[row][col] = sum 26 | } 27 | } 28 | } 29 | 30 | func main() { 31 | var matrixA, matrixB, result [matrixSize][matrixSize]int 32 | for i := 0; i < 4; i++ { 33 | generateRandMatrix(&matrixA) 34 | generateRandMatrix(&matrixB) 35 | matrixMultiply(&matrixA, &matrixB, &result) 36 | for i := 0; i < matrixSize; i++ { 37 | fmt.Println(matrixA[i], matrixB[i], result[i]) 38 | } 39 | fmt.Println() 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /chapter8/listing8.11_12/selectwithnil.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | func generateAmounts(n int) <-chan int { 10 | amounts := make(chan int) 11 | go func() { 12 | defer close(amounts) 13 | for i := 0; i < n; i++ { 14 | amounts <- rand.Intn(100) + 1 15 | time.Sleep(100 * time.Millisecond) 16 | } 17 | }() 18 | return amounts 19 | } 20 | 21 | func main() { 22 | sales := generateAmounts(50) 23 | expenses := generateAmounts(40) 24 | endOfDayAmount := 0 25 | for sales != nil || expenses != nil { 26 | select { 27 | case sale, moreData := <-sales: 28 | if moreData { 29 | fmt.Println("Sale of:", sale) 30 | endOfDayAmount += sale 31 | } else { 32 | sales = nil 33 | } 34 | case expense, moreData := <-expenses: 35 | if moreData { 36 | fmt.Println("Expense of:", expense) 37 | endOfDayAmount -= expense 38 | } else { 39 | expenses = nil 40 | } 41 | } 42 | } 43 | fmt.Println("End of day profit and loss:", endOfDayAmount) 44 | } 45 | -------------------------------------------------------------------------------- /exercises/chapter2/exercise2.4/grepdirrec.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | "path/filepath" 8 | "strings" 9 | "time" 10 | ) 11 | 12 | /* 13 | You can run this by executing: 14 | go run grepdirrec.go Mozilla ../../commonfiles 15 | */ 16 | 17 | func grepPath(path string, dirEntry os.DirEntry, searchStr string) { 18 | fullPath := filepath.Join(path, dirEntry.Name()) 19 | if dirEntry.IsDir() { 20 | files, err := os.ReadDir(fullPath) 21 | if err != nil { 22 | log.Fatal(err) 23 | } 24 | for _, file := range files { 25 | go grepPath(fullPath, file, searchStr) 26 | } 27 | } else { 28 | content, err := os.ReadFile(fullPath) 29 | if err != nil { 30 | log.Fatal(err) 31 | } 32 | if strings.Contains(string(content), searchStr) { 33 | fmt.Println(fullPath, "contains a match with ", searchStr) 34 | } else { 35 | fmt.Println(fullPath, "does NOT contain a match with", searchStr) 36 | } 37 | } 38 | } 39 | 40 | func main() { 41 | searchStr := os.Args[1] 42 | path := os.Args[2] 43 | files, err := os.ReadDir(path) 44 | if err != nil { 45 | log.Fatal(err) 46 | } 47 | for _, dirEntry := range files { 48 | go grepPath(path, dirEntry, searchStr) 49 | } 50 | time.Sleep(2 * time.Second) 51 | } 52 | -------------------------------------------------------------------------------- /chapter10/listing10.13/cupcakepipeline.go: -------------------------------------------------------------------------------- 1 | package listing10_13 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | const ( 9 | ovenTime = 5 10 | everyThingElseTime = 2 11 | ) 12 | 13 | func PrepareTray(trayNumber int) string { 14 | fmt.Println("Preparing empty tray", trayNumber) 15 | time.Sleep(everyThingElseTime * time.Second) 16 | return fmt.Sprintf("tray number %d", trayNumber) 17 | } 18 | 19 | func Mixture(tray string) string { 20 | fmt.Println("Pouring cupcake Mixture in", tray) 21 | time.Sleep(everyThingElseTime * time.Second) 22 | return fmt.Sprintf("cupcake in %s", tray) 23 | } 24 | 25 | func Bake(mixture string) string { 26 | fmt.Println("Baking", mixture) 27 | time.Sleep(ovenTime * time.Second) 28 | return fmt.Sprintf("baked %s", mixture) 29 | } 30 | 31 | func AddToppings(bakedCupCake string) string { 32 | fmt.Println("Adding topping to", bakedCupCake) 33 | time.Sleep(everyThingElseTime * time.Second) 34 | return fmt.Sprintf("topping on %s", bakedCupCake) 35 | } 36 | 37 | func Box(finishedCupCake string) string { 38 | fmt.Println("Boxing", finishedCupCake) 39 | time.Sleep(everyThingElseTime * time.Second) 40 | return fmt.Sprintf("%s boxed", finishedCupCake) 41 | } 42 | -------------------------------------------------------------------------------- /exercises/chapter3/exercise3.1/wordfrequency.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "net/http" 7 | "regexp" 8 | "strings" 9 | "time" 10 | ) 11 | 12 | /* 13 | Note: this program has a race condition for demonstration purposes 14 | Additionally we have a timer at the end which you might need to adjust 15 | depending on how fast your internet connection is. 16 | In later chapters we cover how to wait for threads to complete their work 17 | */ 18 | func countLetters(url string, frequency map[string]int) { 19 | resp, _ := http.Get(url) 20 | defer resp.Body.Close() 21 | if resp.StatusCode != 200 { 22 | panic("Server's error: " + resp.Status) 23 | } 24 | body, _ := io.ReadAll(resp.Body) 25 | wordRegex := regexp.MustCompile(`[a-zA-Z]+`) 26 | for _, word := range wordRegex.FindAllString(string(body), -1) { 27 | wordLower := strings.ToLower(word) 28 | frequency[wordLower] += 1 29 | } 30 | fmt.Println("Completed:", url) 31 | } 32 | 33 | func main() { 34 | var frequency = make(map[string]int) 35 | for i := 1000; i <= 1020; i++ { 36 | url := fmt.Sprintf("https://rfc-editor.org/rfc/rfc%d.txt", i) 37 | go countLetters(url, frequency) 38 | } 39 | time.Sleep(10 * time.Second) 40 | for k, v := range frequency { 41 | fmt.Println(k, "->", v) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /exercises/chapter6/exercise6.2/waitgrptrywait.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | type WaitGrp struct { 10 | groupSize int 11 | cond *sync.Cond 12 | } 13 | 14 | func NewWaitGrp() *WaitGrp { 15 | return &WaitGrp{ 16 | cond: sync.NewCond(&sync.Mutex{}), 17 | } 18 | } 19 | 20 | func (wg *WaitGrp) Add(delta int) { 21 | wg.cond.L.Lock() 22 | wg.groupSize += delta 23 | wg.cond.L.Unlock() 24 | } 25 | 26 | func (wg *WaitGrp) TryWait() bool { 27 | wg.cond.L.Lock() 28 | result := wg.groupSize == 0 29 | wg.cond.L.Unlock() 30 | return result 31 | } 32 | 33 | func (wg *WaitGrp) Wait() { 34 | wg.cond.L.Lock() 35 | for wg.groupSize > 0 { 36 | wg.cond.Wait() 37 | } 38 | wg.cond.L.Unlock() 39 | } 40 | 41 | func (wg *WaitGrp) Done() { 42 | wg.cond.L.Lock() 43 | wg.groupSize-- 44 | if wg.groupSize == 0 { 45 | wg.cond.Broadcast() 46 | } 47 | wg.cond.L.Unlock() 48 | } 49 | 50 | func main() { 51 | wg := NewWaitGrp() 52 | wg.Add(1) 53 | go func() { 54 | time.Sleep(5 * time.Second) 55 | fmt.Println("Marking Wait group as done.") 56 | wg.Done() 57 | }() 58 | for !wg.TryWait() { 59 | fmt.Println("Wait group is not done. Trying again later.") 60 | time.Sleep(1 * time.Second) 61 | } 62 | fmt.Println("Wait group is done.") 63 | } 64 | -------------------------------------------------------------------------------- /chapter12/listing12.4_5/atomiccharcounter.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "net/http" 7 | "strings" 8 | "sync" 9 | "sync/atomic" 10 | ) 11 | 12 | const allLetters = "abcdefghijklmnopqrstuvwxyz" 13 | 14 | func countLetters(url string, frequency []int32) { 15 | resp, _ := http.Get(url) 16 | defer resp.Body.Close() 17 | if resp.StatusCode != 200 { 18 | panic("Server returning error code: " + resp.Status) 19 | } 20 | body, _ := io.ReadAll(resp.Body) 21 | for _, b := range body { 22 | c := strings.ToLower(string(b)) 23 | cIndex := strings.Index(allLetters, c) 24 | if cIndex >= 0 { 25 | atomic.AddInt32(&frequency[cIndex], 1) 26 | } 27 | } 28 | fmt.Println("Completed:", url) 29 | } 30 | 31 | 32 | func main() { 33 | wg := sync.WaitGroup{} 34 | wg.Add(31) 35 | var frequency = make([]int32, 26) 36 | for i := 1000; i <= 1030; i++ { 37 | url := fmt.Sprintf("https://rfc-editor.org/rfc/rfc%d.txt", i) 38 | go func() { 39 | countLetters(url, frequency) 40 | wg.Done() 41 | }() 42 | } 43 | wg.Wait() 44 | for i, c := range allLetters { 45 | fmt.Printf("%c-%d ", c, atomic.LoadInt32(&frequency[i])) 46 | } 47 | } 48 | 49 | -------------------------------------------------------------------------------- /exercises/chapter6/exercise6.3multi/matrixmultiplytime.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/cutajarj/ConcurrentProgrammingWithGo/chapter6/listing6.10" 6 | "math/rand" 7 | "time" 8 | ) 9 | 10 | const matrixSize = 1200 11 | 12 | func generateRandMatrix(matrix *[matrixSize][matrixSize]int) { 13 | for row := 0; row < matrixSize; row++ { 14 | for col := 0; col < matrixSize; col++ { 15 | matrix[row][col] = rand.Intn(10) - 5 16 | } 17 | } 18 | } 19 | 20 | func rowMultiply(matrixA, matrixB, result *[matrixSize][matrixSize]int, 21 | row int, barrier *listing6_10.Barrier) { 22 | for { 23 | barrier.Wait() 24 | for col := 0; col < matrixSize; col++ { 25 | sum := 0 26 | for i := 0; i < matrixSize; i++ { 27 | sum += matrixA[row][i] * matrixB[i][col] 28 | } 29 | result[row][col] = sum 30 | } 31 | barrier.Wait() 32 | } 33 | } 34 | 35 | func main() { 36 | var matrixA, matrixB, result [matrixSize][matrixSize]int 37 | barrier := listing6_10.NewBarrier(matrixSize + 1) 38 | for row := 0; row < matrixSize; row++ { 39 | go rowMultiply(&matrixA, &matrixB, &result, row, barrier) 40 | } 41 | start := time.Now() 42 | for i := 0; i < 4; i++ { 43 | generateRandMatrix(&matrixA) 44 | generateRandMatrix(&matrixB) 45 | barrier.Wait() 46 | barrier.Wait() 47 | } 48 | fmt.Println("Complete in", time.Since(start)) 49 | } 50 | -------------------------------------------------------------------------------- /exercises/chapter4/exercise4.4/wordfrequencymutex.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "net/http" 7 | "regexp" 8 | "strings" 9 | "sync" 10 | "time" 11 | ) 12 | 13 | /* 14 | Note: In this program we have a timer at the end which you might need to adjust 15 | depending on how fast your internet connection is. 16 | In later chapters we cover how to wait for threads to complete their work 17 | */ 18 | func countLetters(url string, frequency map[string]int, mutex *sync.Mutex) { 19 | resp, _ := http.Get(url) 20 | defer resp.Body.Close() 21 | if resp.StatusCode != 200 { 22 | panic("Server's error: " + resp.Status) 23 | } 24 | body, _ := io.ReadAll(resp.Body) 25 | wordRegex := regexp.MustCompile(`[a-zA-Z]+`) 26 | mutex.Lock() 27 | for _, word := range wordRegex.FindAllString(string(body), -1) { 28 | wordLower := strings.ToLower(word) 29 | frequency[wordLower] += 1 30 | } 31 | mutex.Unlock() 32 | fmt.Println("Completed:", url) 33 | } 34 | 35 | func main() { 36 | mutex := sync.Mutex{} 37 | var frequency = make(map[string]int) 38 | for i := 1000; i <= 1020; i++ { 39 | url := fmt.Sprintf("https://rfc-editor.org/rfc/rfc%d.txt", i) 40 | go countLetters(url, frequency, &mutex) 41 | } 42 | time.Sleep(10 * time.Second) 43 | mutex.Lock() 44 | for k, v := range frequency { 45 | fmt.Println(k, "->", v) 46 | } 47 | mutex.Unlock() 48 | } 49 | -------------------------------------------------------------------------------- /chapter8/listing8.4_5_6/passwordguesser.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | const ( 9 | passwordToGuess = "go far" 10 | alphabet = " abcdefghijklmnopqrstuvwxyz" 11 | ) 12 | 13 | func toBase27(n int) string { 14 | result := "" 15 | for n > 0 { 16 | result = string(alphabet[n%27]) + result 17 | n /= 27 18 | } 19 | return result 20 | } 21 | 22 | func guessPassword(from int, upto int, stop chan int, result chan string) { 23 | for guessN := from; guessN < upto; guessN += 1 { 24 | 25 | select { 26 | 27 | case <-stop: 28 | fmt.Printf("Stopped at %d [%d,%d)\n", guessN, from, upto) 29 | return 30 | 31 | default: 32 | if toBase27(guessN) == passwordToGuess { 33 | result <- toBase27(guessN) 34 | close(stop) 35 | return 36 | } 37 | } 38 | } 39 | fmt.Printf("Not found between [%d,%d)\n", from, upto) 40 | } 41 | 42 | func main() { 43 | finished := make(chan int) 44 | 45 | passwordFound := make(chan string) 46 | 47 | for i := 1; i <= 387_420_488; i += 10_000_000 { 48 | go guessPassword(i, i+10_000_000, finished, passwordFound) 49 | } 50 | 51 | fmt.Println("password found:", <-passwordFound) 52 | close(passwordFound) 53 | time.Sleep(5 * time.Second) 54 | } 55 | -------------------------------------------------------------------------------- /chapter3/listing3.4/charcounterconcurrent.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "net/http" 7 | "strings" 8 | "time" 9 | ) 10 | 11 | const allLetters = "abcdefghijklmnopqrstuvwxyz" 12 | 13 | /* 14 | Note: this program has a race condition for demonstration purposes 15 | Additionally we have a timer at the end which you might need to adjust 16 | depending on how fast your internet connection is. 17 | In later chapters we cover how to wait for threads to complete their work 18 | */ 19 | func countLetters(url string, frequency []int) { 20 | resp, _ := http.Get(url) 21 | defer resp.Body.Close() 22 | if resp.StatusCode != 200 { 23 | panic("Server returning error status code: " + resp.Status) 24 | } 25 | body, _ := io.ReadAll(resp.Body) 26 | for _, b := range body { 27 | c := strings.ToLower(string(b)) 28 | cIndex := strings.Index(allLetters, c) 29 | if cIndex >= 0 { 30 | frequency[cIndex] += 1 31 | } 32 | } 33 | fmt.Println("Completed:", url) 34 | } 35 | 36 | func main() { 37 | var frequency = make([]int, 26) 38 | for i := 1000; i <= 1030; i++ { 39 | url := fmt.Sprintf("https://rfc-editor.org/rfc/rfc%d.txt", i) 40 | go countLetters(url, frequency) 41 | } 42 | time.Sleep(10 * time.Second) 43 | for i, c := range allLetters { 44 | fmt.Printf("%c-%d ", c, frequency[i]) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /chapter11/listing11.15_16_17/allfilesinfo.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | "time" 8 | ) 9 | 10 | func handleDirectories(dirs <-chan string, files chan<- string) { 11 | for fullpath := range dirs { 12 | fmt.Println("Reading all files from", fullpath) 13 | filesInDir, _ := os.ReadDir(fullpath) 14 | fmt.Printf("Pushing %d files from %s\n", len(filesInDir), fullpath) 15 | for _, file := range filesInDir { 16 | files <- filepath.Join(fullpath, file.Name()) 17 | } 18 | } 19 | } 20 | 21 | func handleFiles(files <-chan string, dirs chan<- string) { 22 | for path := range files { 23 | file, _ := os.Open(path) 24 | fileInfo, _ := file.Stat() 25 | if fileInfo.IsDir() { 26 | fmt.Printf("Pushing %s directory\n", fileInfo.Name()) 27 | dirs <- path 28 | } else { 29 | fmt.Printf("File %s, size: %.2fKB, last modified: %s\n", 30 | fileInfo.Name(), float64(fileInfo.Size()) / 1024.0, 31 | fileInfo.ModTime().Format(time.ANSIC)) 32 | } 33 | } 34 | } 35 | 36 | func main() { 37 | filesChannel := make(chan string) 38 | dirsChannel := make(chan string) 39 | go handleFiles(filesChannel, dirsChannel) 40 | go handleDirectories(dirsChannel, filesChannel) 41 | dirsChannel <- os.Args[1] 42 | time.Sleep(60 * time.Second) 43 | } -------------------------------------------------------------------------------- /exercises/chapter5/exercise5.3/weightedsemaphore.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | type WeightedSemaphore struct { 10 | permits int 11 | cond *sync.Cond 12 | } 13 | 14 | func NewSemaphore(n int) *WeightedSemaphore { 15 | return &WeightedSemaphore{ 16 | permits: n, 17 | cond: sync.NewCond(&sync.Mutex{}), 18 | } 19 | } 20 | 21 | func (rw *WeightedSemaphore) Acquire(permits int) { 22 | rw.cond.L.Lock() 23 | for rw.permits-permits < 0 { 24 | rw.cond.Wait() 25 | } 26 | rw.permits -= permits 27 | rw.cond.L.Unlock() 28 | } 29 | 30 | func (rw *WeightedSemaphore) Release(permits int) { 31 | rw.cond.L.Lock() 32 | rw.permits += permits 33 | rw.cond.Broadcast() 34 | rw.cond.L.Unlock() 35 | } 36 | 37 | func main() { 38 | sema := NewSemaphore(2) 39 | sema.Acquire(2) 40 | fmt.Println("Parent thread A acquired semaphore") 41 | go func() { 42 | sema.Acquire(1) 43 | fmt.Println("Child B thread acquired semaphore") 44 | time.Sleep(1 * time.Second) 45 | sema.Release(1) 46 | fmt.Println("Child B thread released semaphore") 47 | }() 48 | go func() { 49 | sema.Acquire(1) 50 | fmt.Println("Child C thread acquired semaphore") 51 | time.Sleep(1 * time.Second) 52 | sema.Release(1) 53 | fmt.Println("Child C thread released semaphore") 54 | }() 55 | time.Sleep(3 * time.Second) 56 | fmt.Println("Parent thread A releasing semaphore") 57 | sema.Release(2) 58 | time.Sleep(3 * time.Second) 59 | } 60 | -------------------------------------------------------------------------------- /exercises/chapter11/exercise11.1/scoreupdate.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "sync" 7 | ) 8 | 9 | type Player struct { 10 | name string 11 | score int 12 | mutex sync.Mutex 13 | } 14 | 15 | func incrementScores(players []*Player, increment int) { 16 | for _, player := range players { 17 | player.mutex.Lock() 18 | } 19 | for _, player := range players { 20 | player.score += increment 21 | } 22 | for _, player := range players { 23 | player.mutex.Unlock() 24 | } 25 | } 26 | 27 | func main() { 28 | players := []*Player{ 29 | {"James", 0, sync.Mutex{}}, 30 | {"Ann", 0, sync.Mutex{}}, 31 | {"Paul", 0, sync.Mutex{}}, 32 | {"Isabel", 0, sync.Mutex{}}, 33 | {"Peter", 0, sync.Mutex{}}, 34 | {"Jane", 0, sync.Mutex{}}, 35 | } 36 | wg := sync.WaitGroup{} 37 | for i := 0; i < 1000; i++ { 38 | n := rand.Intn(len(players)) + 1 39 | rand.Shuffle(len(players), func(i, j int) { players[i], players[j] = players[j], players[i] }) 40 | wg.Add(1) 41 | sublist := make([]*Player, n) 42 | copy(sublist, players[:n]) 43 | go func(players []*Player) { 44 | incrementScores(players, 10) 45 | wg.Done() 46 | }(sublist) 47 | } 48 | wg.Wait() 49 | for _, player := range players { 50 | fmt.Printf("Score for %s is %d\n", player.name, player.score) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /chapter11/listing11.18/allfilesinfoseparate.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | "time" 8 | ) 9 | 10 | func handleDirectories(dirs <-chan string, files chan<- string) { 11 | for fullpath := range dirs { 12 | fmt.Println("Reading all files from", fullpath) 13 | filesInDir, _ := os.ReadDir(fullpath) 14 | fmt.Printf("Pushing %d files from %s\n", len(filesInDir), fullpath) 15 | for _, file := range filesInDir { 16 | go func(fp string) { 17 | files <- fp 18 | }(filepath.Join(fullpath, file.Name())) 19 | } 20 | } 21 | } 22 | 23 | func handleFiles(files <-chan string, dirs chan<- string) { 24 | for path := range files { 25 | file, _ := os.Open(path) 26 | fileInfo, _ := file.Stat() 27 | if fileInfo.IsDir() { 28 | fmt.Printf("Pushing %s directory\n", fileInfo.Name()) 29 | dirs <- path 30 | } else { 31 | fmt.Printf("File %s, size: %.2fKB, last modified: %s\n", 32 | fileInfo.Name(), float64(fileInfo.Size()) / 1024.0, 33 | fileInfo.ModTime().Format(time.ANSIC)) 34 | } 35 | } 36 | } 37 | 38 | func main() { 39 | filesChannel := make(chan string) 40 | dirsChannel := make(chan string) 41 | go handleFiles(filesChannel, dirsChannel) 42 | go handleDirectories(dirsChannel, filesChannel) 43 | dirsChannel <- os.Args[1] 44 | time.Sleep(60 * time.Second) 45 | } -------------------------------------------------------------------------------- /exercises/chapter7/exercise7.4/channelcondvar.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "container/list" 5 | "fmt" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | type Channel[M any] struct { 11 | cond *sync.Cond 12 | maxCapacity int 13 | buffer *list.List 14 | } 15 | 16 | func NewChannel[M any](capacity int) *Channel[M] { 17 | return &Channel[M]{ 18 | cond: sync.NewCond(&sync.Mutex{}), 19 | maxCapacity: capacity, 20 | buffer: list.New(), 21 | } 22 | } 23 | 24 | func (c *Channel[M]) Send(message M) { 25 | c.cond.L.Lock() 26 | for c.buffer.Len() == c.maxCapacity { 27 | c.cond.Wait() 28 | } 29 | c.buffer.PushBack(message) 30 | c.cond.Broadcast() 31 | c.cond.L.Unlock() 32 | } 33 | 34 | func (c *Channel[M]) Receive() M { 35 | c.cond.L.Lock() 36 | c.maxCapacity++ 37 | c.cond.Broadcast() 38 | for c.buffer.Len() == 0 { 39 | c.cond.Wait() 40 | } 41 | c.maxCapacity-- 42 | v := c.buffer.Remove(c.buffer.Front()).(M) 43 | c.cond.L.Unlock() 44 | return v 45 | } 46 | 47 | func receiver(messages *Channel[int], wGroup *sync.WaitGroup) { 48 | msg := 0 49 | for msg != -1 { 50 | time.Sleep(1 * time.Second) 51 | msg = messages.Receive() 52 | fmt.Println("Received:", msg) 53 | } 54 | wGroup.Done() 55 | } 56 | 57 | func main() { 58 | channel := NewChannel[int](2) 59 | wGroup := sync.WaitGroup{} 60 | wGroup.Add(1) 61 | go receiver(channel, &wGroup) 62 | for i := 1; i <= 6; i++ { 63 | fmt.Println("Sending: ", i) 64 | channel.Send(i) 65 | } 66 | channel.Send(-1) 67 | wGroup.Wait() 68 | } 69 | -------------------------------------------------------------------------------- /chapter4/listing4.3_4/charcountermutexslow.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "net/http" 7 | "strings" 8 | "sync" 9 | "time" 10 | ) 11 | 12 | const AllLetters = "abcdefghijklmnopqrstuvwxyz" 13 | 14 | func main() { 15 | mutex := sync.Mutex{} 16 | var frequency = make([]int, 26) 17 | for i := 1000; i <= 1030; i++ { 18 | url := fmt.Sprintf("https://rfc-editor.org/rfc/rfc%d.txt", i) 19 | go CountLetters(url, frequency, &mutex) 20 | } 21 | time.Sleep(60 * time.Second) 22 | mutex.Lock() 23 | for i, c := range AllLetters { 24 | fmt.Printf("%c-%d ", c, frequency[i]) 25 | } 26 | mutex.Unlock() 27 | } 28 | 29 | // CountLetters 30 | // Note: this program us locking the entire goroutine with mutex on purpose to demonstrate 31 | // bad placement of the lock and unlock. We fix this in the next listing 32 | func CountLetters(url string, frequency []int, mutex *sync.Mutex) { 33 | mutex.Lock() 34 | resp, _ := http.Get(url) 35 | defer resp.Body.Close() 36 | if resp.StatusCode != 200 { 37 | panic("Server returning error status code: " + resp.Status) 38 | } 39 | body, _ := io.ReadAll(resp.Body) 40 | for _, b := range body { 41 | c := strings.ToLower(string(b)) 42 | cIndex := strings.Index(AllLetters, c) 43 | if cIndex >= 0 { 44 | frequency[cIndex] += 1 45 | } 46 | } 47 | fmt.Println("Completed:", url, time.Now().Format("15:04:05")) 48 | mutex.Unlock() 49 | } 50 | -------------------------------------------------------------------------------- /chapter4/listing4.7_8_9/matchmonitor.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | func matchRecorder(matchEvents *[]string, mutex *sync.Mutex) { 11 | for i := 0; ; i++ { 12 | mutex.Lock() 13 | *matchEvents = append(*matchEvents, 14 | "Match event " + strconv.Itoa(i)) 15 | mutex.Unlock() 16 | time.Sleep(200 * time.Millisecond) 17 | fmt.Println("Appended match event") 18 | } 19 | } 20 | 21 | func clientHandler(mEvents *[]string, mutex *sync.Mutex, st time.Time) { 22 | for i := 0; i < 100; i ++ { 23 | mutex.Lock() 24 | allEvents := copyAllEvents(mEvents) 25 | mutex.Unlock() 26 | 27 | timeTaken := time.Since(st) 28 | fmt.Println(len(allEvents), "events copied in", timeTaken) 29 | } 30 | } 31 | 32 | func copyAllEvents(matchEvents *[]string) []string { 33 | allEvents := make([]string, 0, len(*matchEvents)) 34 | for _, e := range *matchEvents { 35 | allEvents = append(allEvents, e) 36 | } 37 | return allEvents 38 | } 39 | 40 | func main() { 41 | mutex := sync.Mutex{} 42 | var matchEvents = make([]string, 0, 10000) 43 | for j := 0; j < 10000; j++ { 44 | matchEvents = append(matchEvents, "Match event") 45 | } 46 | go matchRecorder(&matchEvents, &mutex) 47 | start := time.Now() 48 | for j := 0; j < 5000; j++ { 49 | go clientHandler(&matchEvents, &mutex, start) 50 | } 51 | time.Sleep(100 * time.Second) 52 | } 53 | -------------------------------------------------------------------------------- /chapter4/listing4.10_11/matchmonitorrw.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | func matchRecorder(matchEvents *[]string, mutex *sync.RWMutex) { 11 | for i := 0; ; i++ { 12 | mutex.Lock() 13 | *matchEvents = append(*matchEvents, 14 | "Match event " + strconv.Itoa(i)) 15 | mutex.Unlock() 16 | time.Sleep(200 * time.Millisecond) 17 | fmt.Println("Appended match event") 18 | } 19 | } 20 | 21 | func clientHandler(mEvents *[]string, mutex *sync.RWMutex, st time.Time) { 22 | for i := 0; i < 100; i ++ { 23 | mutex.RLock() 24 | allEvents := copyAllEvents(mEvents) 25 | mutex.RUnlock() 26 | timeTaken := time.Since(st) 27 | fmt.Println(len(allEvents), "events copied in", timeTaken) 28 | } 29 | } 30 | 31 | func copyAllEvents(matchEvents *[]string) []string { 32 | allEvents := make([]string, 0, len(*matchEvents)) 33 | for _, e := range *matchEvents { 34 | allEvents = append(allEvents, e) 35 | } 36 | return allEvents 37 | } 38 | 39 | func main() { 40 | mutex := sync.RWMutex{} 41 | var matchEvents = make([]string, 0, 10000) 42 | for j := 0; j < 10000; j++ { 43 | matchEvents = append(matchEvents, "Match event") 44 | } 45 | go matchRecorder(&matchEvents, &mutex) 46 | start := time.Now() 47 | for j := 0; j < 5000; j++ { 48 | go clientHandler(&matchEvents, &mutex, start) 49 | } 50 | time.Sleep(100 * time.Second) 51 | } 52 | -------------------------------------------------------------------------------- /chapter6/listing6.16_17/matrixmultiply.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/cutajarj/ConcurrentProgrammingWithGo/chapter6/listing6.10" 6 | "math/rand" 7 | ) 8 | 9 | const matrixSize = 3 10 | 11 | func generateRandMatrix(matrix *[matrixSize][matrixSize]int) { 12 | for row := 0; row < matrixSize; row++ { 13 | for col := 0; col < matrixSize; col++ { 14 | matrix[row][col] = rand.Intn(10) - 5 15 | } 16 | } 17 | } 18 | 19 | func rowMultiply(matrixA, matrixB, result *[matrixSize][matrixSize]int, 20 | row int, barrier *listing6_10.Barrier) { 21 | for { 22 | barrier.Wait() 23 | for col := 0; col < matrixSize; col++ { 24 | sum := 0 25 | for i := 0; i < matrixSize; i++ { 26 | sum += matrixA[row][i] * matrixB[i][col] 27 | } 28 | result[row][col] = sum 29 | } 30 | barrier.Wait() 31 | } 32 | } 33 | 34 | func main() { 35 | var matrixA, matrixB, result [matrixSize][matrixSize]int 36 | barrier := listing6_10.NewBarrier(matrixSize + 1) 37 | for row := 0; row < matrixSize; row++ { 38 | go rowMultiply(&matrixA, &matrixB, &result, row, barrier) 39 | } 40 | 41 | for i := 0; i < 4; i++ { 42 | generateRandMatrix(&matrixA) 43 | generateRandMatrix(&matrixB) 44 | barrier.Wait() 45 | barrier.Wait() 46 | for i := 0; i < matrixSize; i++ { 47 | fmt.Println(matrixA[i], matrixB[i], result[i]) 48 | } 49 | fmt.Println() 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /chapter8/listing8.13_14/charcountermessagepass.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "net/http" 7 | "strings" 8 | ) 9 | 10 | const allLetters = "abcdefghijklmnopqrstuvwxyz" 11 | 12 | func countLetters(url string) <-chan []int { 13 | result := make(chan []int) 14 | go func() { 15 | defer close(result) 16 | frequency := make([]int, 26) 17 | resp, _ := http.Get(url) 18 | defer resp.Body.Close() 19 | if resp.StatusCode != 200 { 20 | panic("Server returning error code: " + resp.Status) 21 | } 22 | body, _ := io.ReadAll(resp.Body) 23 | for _, b := range body { 24 | c := strings.ToLower(string(b)) 25 | cIndex := strings.Index(allLetters, c) 26 | if cIndex >= 0 { 27 | frequency[cIndex] += 1 28 | } 29 | } 30 | fmt.Println("Completed:", url) 31 | result <- frequency 32 | }() 33 | return result 34 | } 35 | 36 | func main() { 37 | results := make([]<-chan []int, 0) 38 | totalFrequencies := make([]int, 26) 39 | for i := 1000; i <= 1030; i++ { 40 | url := fmt.Sprintf("https://rfc-editor.org/rfc/rfc%d.txt", i) 41 | results = append(results, countLetters(url)) 42 | } 43 | for _, c := range results { 44 | frequencyResult := <-c 45 | for i := 0; i < 26; i++ { 46 | totalFrequencies[i] += frequencyResult[i] 47 | } 48 | } 49 | for i, c := range allLetters { 50 | fmt.Printf("%c-%d ", c, totalFrequencies[i]) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /exercises/chapter11/exercise11.1solution/scoreupdate.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "sort" 7 | "sync" 8 | ) 9 | 10 | type Player struct { 11 | name string 12 | score int 13 | mutex sync.Mutex 14 | } 15 | 16 | func incrementScores(players []*Player, increment int) { 17 | sort.Slice(players, func(a, b int) bool { 18 | return players[a].name < players[b].name 19 | }) 20 | for _, player := range players { 21 | player.mutex.Lock() 22 | } 23 | for _, player := range players { 24 | player.score += increment 25 | } 26 | for _, player := range players { 27 | player.mutex.Unlock() 28 | } 29 | } 30 | 31 | func main() { 32 | players := []*Player{ 33 | {"James", 0, sync.Mutex{}}, 34 | {"Ann", 0, sync.Mutex{}}, 35 | {"Paul", 0, sync.Mutex{}}, 36 | {"Isabel", 0, sync.Mutex{}}, 37 | {"Peter", 0, sync.Mutex{}}, 38 | {"Jane", 0, sync.Mutex{}}, 39 | } 40 | wg := sync.WaitGroup{} 41 | for i := 0; i < 1000; i++ { 42 | n := rand.Intn(len(players)) + 1 43 | rand.Shuffle(len(players), func(i, j int) { players[i], players[j] = players[j], players[i] }) 44 | wg.Add(1) 45 | sublist := make([]*Player, n) 46 | copy(sublist, players[:n]) 47 | go func(players []*Player) { 48 | incrementScores(players, 10) 49 | wg.Done() 50 | }(sublist) 51 | } 52 | wg.Wait() 53 | for _, player := range players { 54 | fmt.Printf("Score for %s is %d\n", player.name, player.score) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /chapter9/listing9.5_6/downloadpages.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "net/http" 7 | ) 8 | 9 | func downloadPages(quit <-chan int, urls <-chan string) <-chan string { 10 | pages := make(chan string) 11 | go func() { 12 | defer close(pages) 13 | moreData, url := true, "" 14 | for moreData { 15 | select { 16 | case url, moreData = <-urls: 17 | if moreData { 18 | resp, _ := http.Get(url) 19 | if resp.StatusCode != 200 { 20 | panic("Server's error: " + resp.Status) 21 | } 22 | body, _ := io.ReadAll(resp.Body) 23 | pages <- string(body) 24 | resp.Body.Close() 25 | } 26 | case <-quit: 27 | return 28 | } 29 | } 30 | }() 31 | return pages 32 | } 33 | 34 | func generateUrls(quit <-chan int) <-chan string { 35 | urls := make(chan string) 36 | go func() { 37 | defer close(urls) 38 | for i := 100; i <= 130; i++ { 39 | url := fmt.Sprintf("https://rfc-editor.org/rfc/rfc%d.txt", i) 40 | select { 41 | case urls <- url: 42 | case <-quit: 43 | return 44 | } 45 | } 46 | }() 47 | return urls 48 | } 49 | 50 | func main() { 51 | quit := make(chan int) 52 | defer close(quit) 53 | results := downloadPages(quit, generateUrls(quit)) 54 | for result := range results { 55 | fmt.Println(result) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /exercises/chapter8/exercise8.3/twoormore.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | func player() chan string { 10 | output := make(chan string) 11 | count := rand.Intn(100) 12 | move := []string{"UP", "DOWN", "LEFT", "RIGHT"} 13 | go func() { 14 | defer close(output) 15 | for i := 0; i < count; i++ { 16 | output <- move[rand.Intn(4)] 17 | d := time.Duration(rand.Intn(200)) 18 | time.Sleep(d * time.Millisecond) 19 | } 20 | }() 21 | return output 22 | } 23 | 24 | func handlePlayer(id int, moreData bool, move string, 25 | players []chan string, totalPlayers *int) { 26 | if moreData { 27 | fmt.Printf("Player %d: %s\n", id, move) 28 | } else { 29 | players[id] = nil 30 | *totalPlayers-- 31 | fmt.Printf("Player %d left the game. Remaining players: %d\n", id, *totalPlayers) 32 | } 33 | } 34 | 35 | func main() { 36 | players := []chan string {player(), player(), player(), player()} 37 | totalPlayers := 4 38 | for totalPlayers > 1 { 39 | select { 40 | case move, moreData := <-players[0]: 41 | handlePlayer(0, moreData, move, players, &totalPlayers) 42 | case move, moreData := <-players[1]: 43 | handlePlayer(1, moreData, move, players, &totalPlayers) 44 | case move, moreData := <-players[2]: 45 | handlePlayer(2, moreData, move, players, &totalPlayers) 46 | case move, moreData := <-players[3]: 47 | handlePlayer(3, moreData, move, players, &totalPlayers) 48 | } 49 | } 50 | fmt.Println("Game finished") 51 | } 52 | 53 | -------------------------------------------------------------------------------- /chapter5/listing5.11_12_13_14_15/readwritewpref.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | type ReadWriteMutex struct { 10 | readersCounter int 11 | writersWaiting int 12 | writerActive bool 13 | cond *sync.Cond 14 | } 15 | 16 | func NewReadWriteMutex() *ReadWriteMutex { 17 | return &ReadWriteMutex{cond: sync.NewCond(&sync.Mutex{})} 18 | } 19 | 20 | func (rw *ReadWriteMutex) ReadLock() { 21 | rw.cond.L.Lock() 22 | for rw.writersWaiting > 0 || rw.writerActive { 23 | rw.cond.Wait() 24 | } 25 | rw.readersCounter++ 26 | rw.cond.L.Unlock() 27 | } 28 | 29 | func (rw *ReadWriteMutex) WriteLock() { 30 | rw.cond.L.Lock() 31 | rw.writersWaiting++ 32 | for rw.readersCounter > 0 || rw.writerActive { 33 | rw.cond.Wait() 34 | } 35 | rw.writersWaiting-- 36 | rw.writerActive = true 37 | rw.cond.L.Unlock() 38 | } 39 | 40 | func (rw *ReadWriteMutex) ReadUnlock() { 41 | rw.cond.L.Lock() 42 | rw.readersCounter-- 43 | if rw.readersCounter == 0 { 44 | rw.cond.Broadcast() 45 | } 46 | rw.cond.L.Unlock() 47 | } 48 | 49 | func (rw *ReadWriteMutex) WriteUnlock() { 50 | rw.cond.L.Lock() 51 | rw.writerActive = false 52 | rw.cond.Broadcast() 53 | rw.cond.L.Unlock() 54 | } 55 | 56 | func main() { 57 | rwMutex := NewReadWriteMutex() 58 | for i := 0; i < 2; i++ { 59 | go func() { 60 | for { 61 | rwMutex.ReadLock() 62 | time.Sleep(1 * time.Second) 63 | fmt.Println("Read done") 64 | rwMutex.ReadUnlock() 65 | } 66 | }() 67 | } 68 | time.Sleep(1 * time.Second) 69 | rwMutex.WriteLock() 70 | fmt.Println("Write finished") 71 | } 72 | -------------------------------------------------------------------------------- /chapter11/listing11.19/allfilesinfoselect.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | "time" 8 | ) 9 | 10 | func handleDirectories(dirs <-chan string, files chan<- string) { 11 | toPush := make([]string, 0) 12 | appendAllFiles := func(path string) { 13 | fmt.Println("Reading all files from", path) 14 | filesInDir, _ := os.ReadDir(path) 15 | fmt.Printf("Pushing %d files from %s\n", len(filesInDir), path) 16 | for _, f := range filesInDir { 17 | toPush = append(toPush, filepath.Join(path, f.Name())) 18 | } 19 | } 20 | for { 21 | if len(toPush) == 0 { 22 | appendAllFiles(<-dirs) 23 | } else { 24 | select { 25 | case fullpath := <-dirs: 26 | appendAllFiles(fullpath) 27 | case files <- toPush[0]: 28 | toPush = toPush[1:] 29 | } 30 | } 31 | } 32 | } 33 | 34 | func handleFiles(files <-chan string, dirs chan<- string) { 35 | for path := range files { 36 | file, _ := os.Open(path) 37 | fileInfo, _ := file.Stat() 38 | if fileInfo.IsDir() { 39 | fmt.Printf("Pushing %s directory\n", fileInfo.Name()) 40 | dirs <- path 41 | } else { 42 | fmt.Printf("File %s, size: %.2fKB, last modified: %s\n", 43 | fileInfo.Name(), float64(fileInfo.Size()) / 1024.0, 44 | fileInfo.ModTime().Format(time.ANSIC)) 45 | } 46 | } 47 | } 48 | 49 | func main() { 50 | filesChannel := make(chan string) 51 | dirsChannel := make(chan string) 52 | go handleFiles(filesChannel, dirsChannel) 53 | go handleDirectories(dirsChannel, filesChannel) 54 | dirsChannel <- os.Args[1] 55 | time.Sleep(60 * time.Second) 56 | } 57 | -------------------------------------------------------------------------------- /chapter11/listing11.14/ledgermutexorder.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "sort" 7 | "sync" 8 | "time" 9 | ) 10 | 11 | type BankAccount struct { 12 | id string 13 | balance int 14 | mutex sync.Mutex 15 | } 16 | 17 | func NewBankAccount(id string) *BankAccount { 18 | return &BankAccount{ 19 | id: id, 20 | balance: 100, 21 | mutex: sync.Mutex{}, 22 | } 23 | } 24 | 25 | func (src *BankAccount) Transfer(to *BankAccount, amount int, tellerId int) { 26 | accounts := []*BankAccount{src, to} 27 | sort.Slice(accounts, func(a, b int) bool { 28 | return accounts[a].id < accounts[b].id 29 | }) 30 | fmt.Printf("%d Locking %s's account\n", tellerId, accounts[0].id) 31 | accounts[0].mutex.Lock() 32 | fmt.Printf("%d Locking %s's account\n", tellerId, accounts[1].id) 33 | accounts[1].mutex.Lock() 34 | src.balance -= amount 35 | to.balance += amount 36 | to.mutex.Unlock() 37 | src.mutex.Unlock() 38 | fmt.Printf("%d Unlocked %s and %s\n", tellerId, src.id, to.id) 39 | } 40 | 41 | func main() { 42 | accounts := []BankAccount{ 43 | *NewBankAccount("Sam"), 44 | *NewBankAccount("Paul"), 45 | *NewBankAccount("Amy"), 46 | *NewBankAccount("Mia"), 47 | } 48 | for i := 0; i < 4; i++ { 49 | go func(tellerId int) { 50 | for i := 1; i < 1000; i++ { 51 | from, to := rand.Intn(len(accounts)), rand.Intn(len(accounts)) 52 | for from == to { 53 | to = rand.Intn(len(accounts)) 54 | } 55 | accounts[from].Transfer(&accounts[to], 10, tellerId) 56 | } 57 | fmt.Println(tellerId,"COMPLETE") 58 | }(i) 59 | } 60 | time.Sleep(60 * time.Second) 61 | } -------------------------------------------------------------------------------- /chapter10/listing10.5_6_7_8/deepestnestedfile.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | "os" 7 | "path/filepath" 8 | "strings" 9 | "sync" 10 | ) 11 | 12 | type CodeDepth struct { 13 | file string 14 | level int 15 | } 16 | 17 | func deepestNestedBlock(filename string) CodeDepth { 18 | code, _ := os.ReadFile(filename) 19 | max := 0 20 | level := 0 21 | for _, c := range code { 22 | if c == '{' { 23 | level += 1 24 | max = int(math.Max(float64(max), float64(level))) 25 | } else if c == '}' { 26 | level -= 1 27 | } 28 | } 29 | return CodeDepth{filename, max} 30 | } 31 | 32 | func forkIfNeeded(path string, info os.FileInfo, 33 | wg *sync.WaitGroup, results chan CodeDepth) { 34 | if !info.IsDir() && strings.HasSuffix(path, ".go") { 35 | wg.Add(1) 36 | go func() { 37 | results <- deepestNestedBlock(path) 38 | wg.Done() 39 | }() 40 | } 41 | } 42 | 43 | func joinResults(partialResults chan CodeDepth) chan CodeDepth { 44 | finalResult := make(chan CodeDepth) 45 | max := CodeDepth{"", 0} 46 | go func() { 47 | for pr := range partialResults { 48 | if pr.level > max.level { 49 | max = pr 50 | } 51 | } 52 | finalResult <- max 53 | }() 54 | return finalResult 55 | } 56 | 57 | func main() { 58 | dir := os.Args[1] 59 | partialResults := make(chan CodeDepth) 60 | wg := sync.WaitGroup{} 61 | filepath.Walk(dir, 62 | func(path string, info os.FileInfo, err error) error { 63 | forkIfNeeded(path, info, &wg, partialResults) 64 | return nil 65 | }) 66 | finalResult := joinResults(partialResults) 67 | wg.Wait() 68 | close(partialResults) 69 | result := <-finalResult 70 | fmt.Printf("%s has the deepest nested code block of %d\n", 71 | result.file, result.level) 72 | } 73 | -------------------------------------------------------------------------------- /exercises/chapter4/exercise4.3/tryreadwritemutex.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | type ReadWriteMutex struct { 9 | readersCounter int 10 | readersLock sync.Mutex 11 | globalLock sync.Mutex 12 | } 13 | 14 | func (rw *ReadWriteMutex) ReadLock() { 15 | rw.readersLock.Lock() 16 | rw.readersCounter++ 17 | if rw.readersCounter == 1 { 18 | rw.globalLock.Lock() 19 | } 20 | rw.readersLock.Unlock() 21 | } 22 | 23 | func (rw *ReadWriteMutex) TryReadLock() bool { 24 | if rw.readersLock.TryLock() { 25 | globalSuccess := true 26 | if rw.readersCounter == 0 { 27 | globalSuccess = rw.globalLock.TryLock() 28 | } 29 | if globalSuccess { 30 | rw.readersCounter++ 31 | } 32 | rw.readersLock.Unlock() 33 | return globalSuccess 34 | } else { 35 | return false 36 | } 37 | } 38 | 39 | func (rw *ReadWriteMutex) WriteLock() { 40 | rw.globalLock.Lock() 41 | } 42 | 43 | func (rw *ReadWriteMutex) TryWriteLock() bool { 44 | return rw.globalLock.TryLock() 45 | } 46 | 47 | func (rw *ReadWriteMutex) ReadUnlock() { 48 | rw.readersLock.Lock() 49 | rw.readersCounter-- 50 | if rw.readersCounter == 0 { 51 | rw.globalLock.Unlock() 52 | } 53 | rw.readersLock.Unlock() 54 | } 55 | 56 | func (rw *ReadWriteMutex) WriteUnlock() { 57 | rw.globalLock.Unlock() 58 | } 59 | 60 | func main() { 61 | rwMutex := ReadWriteMutex{} 62 | fmt.Println("Acquiring Readlock") 63 | rwMutex.ReadLock() 64 | fmt.Println("Acquiring Readlock again") 65 | rwMutex.ReadLock() 66 | fmt.Println("Trying Readlock", rwMutex.TryReadLock()) 67 | fmt.Println("Trying Writelock", rwMutex.TryWriteLock()) 68 | rwMutex.ReadUnlock() 69 | rwMutex.ReadUnlock() 70 | rwMutex.ReadUnlock() 71 | fmt.Println("Trying Writelock", rwMutex.TryWriteLock()) 72 | fmt.Println("Trying Readlock", rwMutex.TryReadLock()) 73 | } 74 | -------------------------------------------------------------------------------- /exercises/chapter11/exercise11.2/allfilesinfoselect.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | "time" 8 | ) 9 | 10 | func handleDirectories(dirs <-chan string, files chan<- string) { 11 | filesToPush := make([]string, 0) 12 | appendAllFiles := func(fullpath string) { 13 | fmt.Println("Reading all files from", fullpath) 14 | filesInDir, _ := os.ReadDir(fullpath) 15 | fmt.Printf("Pushing %d files from %s\n", len(filesInDir), fullpath) 16 | for _, file := range filesInDir { 17 | filesToPush = append(filesToPush, filepath.Join(fullpath, file.Name())) 18 | } 19 | } 20 | for { 21 | if len(filesToPush) == 0 { 22 | appendAllFiles(<-dirs) 23 | } else { 24 | select { 25 | case fullpath := <-dirs: 26 | appendAllFiles(fullpath) 27 | case files <- filesToPush[0]: 28 | filesToPush = filesToPush[1:] 29 | } 30 | } 31 | } 32 | } 33 | 34 | func handleFiles(files <-chan string, dirs chan<- string) { 35 | dirsToPush := make([]string, 0) 36 | appendAnyDirs := func(path string) { 37 | file, _ := os.Open(path) 38 | fileInfo, _ := file.Stat() 39 | if fileInfo.IsDir() { 40 | fmt.Printf("Pushing %s directory\n", fileInfo.Name()) 41 | dirsToPush = append(dirsToPush, path) 42 | } else { 43 | fmt.Printf("File %s, size: %.2fKB, last modified: %s\n", 44 | fileInfo.Name(), float64(fileInfo.Size())/1024.0, 45 | fileInfo.ModTime().Format(time.ANSIC)) 46 | } 47 | } 48 | for { 49 | if len(dirsToPush) == 0 { 50 | appendAnyDirs(<-files) 51 | } else { 52 | select { 53 | case f := <- files: 54 | appendAnyDirs(f) 55 | case dirs <- dirsToPush[0]: 56 | dirsToPush = dirsToPush[1:] 57 | } 58 | } 59 | } 60 | } 61 | 62 | func main() { 63 | filesChannel := make(chan string) 64 | dirsChannel := make(chan string) 65 | go handleFiles(filesChannel, dirsChannel) 66 | go handleDirectories(dirsChannel, filesChannel) 67 | dirsChannel <- os.Args[1] 68 | time.Sleep(60 * time.Second) 69 | } 70 | -------------------------------------------------------------------------------- /chapter9/listing9.7_8/extractwords.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "net/http" 7 | "regexp" 8 | "strings" 9 | ) 10 | 11 | func extractWords(quit <-chan int, pages <-chan string) <-chan string { 12 | words := make(chan string) 13 | go func() { 14 | defer close(words) 15 | wordRegex := regexp.MustCompile(`[a-zA-Z]+`) 16 | moreData, pg := true, "" 17 | for moreData { 18 | select { 19 | case pg, moreData = <-pages: 20 | if moreData { 21 | for _, word := range wordRegex.FindAllString(pg, -1) { 22 | words <- strings.ToLower(word) 23 | } 24 | } 25 | case <-quit: 26 | return 27 | } 28 | } 29 | }() 30 | return words 31 | } 32 | 33 | func downloadPages(quit <-chan int, urls <-chan string) <-chan string { 34 | pages := make(chan string) 35 | go func() { 36 | defer close(pages) 37 | moreData, url := true, "" 38 | for moreData { 39 | select { 40 | case url, moreData = <-urls: 41 | if moreData { 42 | resp, _ := http.Get(url) 43 | if resp.StatusCode != 200 { 44 | panic("Server's error: " + resp.Status) 45 | } 46 | body, _ := io.ReadAll(resp.Body) 47 | pages <- string(body) 48 | resp.Body.Close() 49 | } 50 | case <-quit: 51 | return 52 | } 53 | } 54 | }() 55 | return pages 56 | } 57 | 58 | func generateUrls(quit <-chan int) <-chan string { 59 | urls := make(chan string) 60 | go func() { 61 | defer close(urls) 62 | for i := 100; i <= 130; i++ { 63 | url := fmt.Sprintf("https://rfc-editor.org/rfc/rfc%d.txt", i) 64 | select { 65 | case urls <- url: 66 | case <-quit: 67 | return 68 | } 69 | } 70 | }() 71 | return urls 72 | } 73 | 74 | func main() { 75 | quit := make(chan int) 76 | defer close(quit) 77 | results := extractWords(quit, downloadPages(quit, generateUrls(quit))) 78 | for result := range results { 79 | fmt.Println(result) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /chapter11/listing11.8_9_10_11_12/ledgermutexarb.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | func main() { 11 | accounts := []BankAccount{ 12 | *NewBankAccount("Sam"), 13 | *NewBankAccount("Paul"), 14 | *NewBankAccount("Amy"), 15 | *NewBankAccount("Mia"), 16 | } 17 | total := len(accounts) 18 | arb := NewArbitrator() 19 | for i := 0; i < 4; i++ { 20 | go func(tellerId int) { 21 | for i := 1; i < 1000; i++ { 22 | from, to := rand.Intn(total), rand.Intn(total) 23 | for from == to { 24 | to = rand.Intn(total) 25 | } 26 | accounts[from].Transfer(&accounts[to], 10, tellerId, arb) 27 | } 28 | fmt.Println(tellerId,"COMPLETE") 29 | }(i) 30 | } 31 | time.Sleep(60 * time.Second) 32 | } 33 | 34 | type BankAccount struct { 35 | id string 36 | balance int 37 | } 38 | 39 | func NewBankAccount(id string) *BankAccount { 40 | return &BankAccount{ 41 | id: id, 42 | balance: 100, 43 | } 44 | } 45 | 46 | type Arbitrator struct { 47 | accountsInUse map[string]bool 48 | cond *sync.Cond 49 | } 50 | 51 | func NewArbitrator() *Arbitrator{ 52 | return &Arbitrator{ 53 | accountsInUse: map[string]bool{}, 54 | cond: sync.NewCond(&sync.Mutex{}), 55 | } 56 | } 57 | 58 | func (a *Arbitrator) LockAccounts(ids... string) { 59 | a.cond.L.Lock() 60 | for allAvailable := false; !allAvailable; { 61 | allAvailable = true 62 | for _, id := range ids { 63 | if a.accountsInUse[id] { 64 | allAvailable = false 65 | a.cond.Wait() 66 | } 67 | } 68 | } 69 | for _, id := range ids { 70 | a.accountsInUse[id] = true 71 | } 72 | a.cond.L.Unlock() 73 | } 74 | 75 | func (a *Arbitrator) UnlockAccounts(ids... string) { 76 | a.cond.L.Lock() 77 | for _, id := range ids { 78 | a.accountsInUse[id] = false 79 | } 80 | a.cond.Broadcast() 81 | a.cond.L.Unlock() 82 | } 83 | 84 | func (src *BankAccount) Transfer(to *BankAccount, amount int, tellerId int, 85 | arb *Arbitrator) { 86 | fmt.Printf("%d Locking %s and %s\n", tellerId, src.id, to.id) 87 | arb.LockAccounts(src.id, to.id) 88 | src.balance -= amount 89 | to.balance += amount 90 | arb.UnlockAccounts(src.id, to.id) 91 | fmt.Printf("%d Unlocked %s and %s\n", tellerId, src.id, to.id) 92 | } 93 | 94 | -------------------------------------------------------------------------------- /chapter9/listing9.9_11/extractwordsmulti.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/cutajarj/ConcurrentProgrammingWithGo/chapter9/listing9.10" 6 | "io" 7 | "net/http" 8 | "regexp" 9 | "strings" 10 | ) 11 | 12 | func extractWords(quit <-chan int, pages <-chan string) <-chan string { 13 | words := make(chan string) 14 | go func() { 15 | defer close(words) 16 | wordRegex := regexp.MustCompile(`[a-zA-Z]+`) 17 | moreData, page := true, "" 18 | for moreData { 19 | select { 20 | case page, moreData = <-pages: 21 | if moreData { 22 | for _, word := range wordRegex.FindAllString(page, -1) { 23 | words <- strings.ToLower(word) 24 | } 25 | } 26 | case <-quit: 27 | return 28 | } 29 | } 30 | }() 31 | return words 32 | } 33 | 34 | func downloadPages(quit <-chan int, urls <-chan string) <-chan string { 35 | pages := make(chan string) 36 | go func() { 37 | defer close(pages) 38 | moreData, url := true, "" 39 | for moreData { 40 | select { 41 | case url, moreData = <-urls: 42 | if moreData { 43 | resp, _ := http.Get(url) 44 | if resp.StatusCode != 200 { 45 | panic("Server's error: " + resp.Status) 46 | } 47 | body, _ := io.ReadAll(resp.Body) 48 | pages <- string(body) 49 | resp.Body.Close() 50 | } 51 | case <-quit: 52 | return 53 | } 54 | } 55 | }() 56 | return pages 57 | } 58 | 59 | func generateUrls(quit <-chan int) <-chan string { 60 | urls := make(chan string) 61 | go func() { 62 | defer close(urls) 63 | for i := 100; i <= 130; i++ { 64 | url := fmt.Sprintf("https://rfc-editor.org/rfc/rfc%d.txt", i) 65 | select { 66 | case urls <- url: 67 | case <-quit: 68 | return 69 | } 70 | } 71 | }() 72 | return urls 73 | } 74 | 75 | const downloaders = 20 76 | 77 | func main() { 78 | quit := make(chan int) 79 | defer close(quit) 80 | urls := generateUrls(quit) 81 | pages := make([]<-chan string, downloaders) 82 | for i := 0; i < downloaders; i++ { 83 | pages[i] = downloadPages(quit, urls) 84 | } 85 | results := extractWords(quit, listing9_10.FanIn(quit, pages...)) 86 | for result := range results { 87 | fmt.Println(result) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /chapter9/listing9.12_13/longestwords.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/cutajarj/ConcurrentProgrammingWithGo/chapter9/listing9.10" 6 | "io" 7 | "net/http" 8 | "regexp" 9 | "sort" 10 | "strings" 11 | ) 12 | 13 | func longestWords(quit <-chan int, words <-chan string) <-chan string { 14 | longWords := make(chan string) 15 | go func() { 16 | defer close(longWords) 17 | uniqueWordsMap := make(map[string]bool) 18 | uniqueWords := make([]string, 0) 19 | moreData, word := true, "" 20 | for moreData { 21 | select { 22 | case word, moreData = <-words: 23 | if moreData && !uniqueWordsMap[word] { 24 | uniqueWordsMap[word] = true 25 | uniqueWords = append(uniqueWords, word) 26 | } 27 | case <-quit: 28 | return 29 | } 30 | } 31 | sort.Slice(uniqueWords, func(a, b int) bool { 32 | return len(uniqueWords[a]) > len(uniqueWords[b]) 33 | }) 34 | longWords <- strings.Join(uniqueWords[:10], ", ") 35 | }() 36 | return longWords 37 | } 38 | 39 | func extractWords(quit <-chan int, pages <-chan string) <-chan string { 40 | words := make(chan string) 41 | go func() { 42 | defer close(words) 43 | wordRegex := regexp.MustCompile(`[a-zA-Z]+`) 44 | moreData, page := true, "" 45 | for moreData { 46 | select { 47 | case page, moreData = <-pages: 48 | if moreData { 49 | for _, word := range wordRegex.FindAllString(page, -1) { 50 | words <- strings.ToLower(word) 51 | } 52 | } 53 | case <-quit: 54 | return 55 | } 56 | } 57 | }() 58 | return words 59 | } 60 | 61 | func downloadPages(quit <-chan int, urls <-chan string) <-chan string { 62 | pages := make(chan string) 63 | go func() { 64 | defer close(pages) 65 | moreData, url := true, "" 66 | for moreData { 67 | select { 68 | case url, moreData = <-urls: 69 | if moreData { 70 | resp, _ := http.Get(url) 71 | if resp.StatusCode != 200 { 72 | panic("Server's error: " + resp.Status) 73 | } 74 | body, _ := io.ReadAll(resp.Body) 75 | pages <- string(body) 76 | resp.Body.Close() 77 | } 78 | case <-quit: 79 | return 80 | } 81 | } 82 | }() 83 | return pages 84 | } 85 | 86 | func generateUrls(quit <-chan int) <-chan string { 87 | urls := make(chan string) 88 | go func() { 89 | defer close(urls) 90 | for i := 100; i <= 130; i++ { 91 | url := fmt.Sprintf("https://rfc-editor.org/rfc/rfc%d.txt", i) 92 | select { 93 | case urls <- url: 94 | case <-quit: 95 | return 96 | } 97 | } 98 | }() 99 | return urls 100 | } 101 | 102 | const downloaders = 20 103 | 104 | func main() { 105 | quit := make(chan int) 106 | defer close(quit) 107 | urls := generateUrls(quit) 108 | pages := make([]<-chan string, downloaders) 109 | for i := 0; i < downloaders; i++ { 110 | pages[i] = downloadPages(quit, urls) 111 | } 112 | results := longestWords(quit, 113 | extractWords(quit, listing9_10.FanIn(quit, pages...))) 114 | fmt.Println("Longest Words:", <-results) 115 | } 116 | -------------------------------------------------------------------------------- /chapter9/listing9.16_17/wordstats.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/cutajarj/ConcurrentProgrammingWithGo/chapter9/listing9.10" 6 | "github.com/cutajarj/ConcurrentProgrammingWithGo/chapter9/listing9.14" 7 | "io" 8 | "net/http" 9 | "regexp" 10 | "sort" 11 | "strings" 12 | ) 13 | 14 | func frequentWords(quit <-chan int, words <-chan string) <-chan string { 15 | mostFrequentWords := make(chan string) 16 | go func() { 17 | defer close(mostFrequentWords) 18 | freqMap := make(map[string]int) 19 | freqList := make([]string, 0) 20 | moreData, word := true, "" 21 | for moreData { 22 | select { 23 | case word, moreData = <-words: 24 | if moreData { 25 | if freqMap[word] == 0 { 26 | freqList = append(freqList, word) 27 | } 28 | freqMap[word] += 1 29 | } 30 | case <-quit: 31 | return 32 | } 33 | } 34 | sort.Slice(freqList, func(a, b int) bool { 35 | return freqMap[freqList[a]] > freqMap[freqList[b]] 36 | }) 37 | mostFrequentWords <- strings.Join(freqList[:10], ", ") 38 | }() 39 | return mostFrequentWords 40 | } 41 | 42 | func longestWords(quit <-chan int, words <-chan string) <-chan string { 43 | longWords := make(chan string) 44 | go func() { 45 | defer close(longWords) 46 | uniqueWordsMap := make(map[string]bool) 47 | uniqueWords := make([]string, 0) 48 | moreData, word := true, "" 49 | for moreData { 50 | select { 51 | case word, moreData = <-words: 52 | if moreData && !uniqueWordsMap[word] { 53 | uniqueWordsMap[word] = true 54 | uniqueWords = append(uniqueWords, word) 55 | } 56 | case <-quit: 57 | return 58 | } 59 | } 60 | sort.Slice(uniqueWords, func(a, b int) bool { 61 | return len(uniqueWords[a]) > len(uniqueWords[b]) 62 | }) 63 | longWords <- strings.Join(uniqueWords[:10], ", ") 64 | }() 65 | return longWords 66 | } 67 | 68 | func extractWords(quit <-chan int, pages <-chan string) <-chan string { 69 | words := make(chan string) 70 | go func() { 71 | defer close(words) 72 | wordRegex := regexp.MustCompile(`[a-zA-Z]+`) 73 | moreData, page := true, "" 74 | for moreData { 75 | select { 76 | case page, moreData = <-pages: 77 | if moreData { 78 | for _, word := range wordRegex.FindAllString(page, -1) { 79 | words <- strings.ToLower(word) 80 | } 81 | } 82 | case <-quit: 83 | return 84 | } 85 | } 86 | }() 87 | return words 88 | } 89 | 90 | func downloadPages(quit <-chan int, urls <-chan string) <-chan string { 91 | pages := make(chan string) 92 | go func() { 93 | defer close(pages) 94 | moreData, url := true, "" 95 | for moreData { 96 | select { 97 | case url, moreData = <-urls: 98 | if moreData { 99 | resp, _ := http.Get(url) 100 | if resp.StatusCode != 200 { 101 | panic("Server's error: " + resp.Status) 102 | } 103 | body, _ := io.ReadAll(resp.Body) 104 | pages <- string(body) 105 | resp.Body.Close() 106 | } 107 | case <-quit: 108 | return 109 | } 110 | } 111 | }() 112 | return pages 113 | } 114 | 115 | func generateUrls(quit <-chan int) <-chan string { 116 | urls := make(chan string) 117 | go func() { 118 | defer close(urls) 119 | for i := 100; i <= 130; i++ { 120 | url := fmt.Sprintf("https://rfc-editor.org/rfc/rfc%d.txt", i) 121 | select { 122 | case urls <- url: 123 | case <-quit: 124 | return 125 | } 126 | } 127 | }() 128 | return urls 129 | } 130 | 131 | const downloaders = 20 132 | 133 | func main() { 134 | quit := make(chan int) 135 | defer close(quit) 136 | urls := generateUrls(quit) 137 | pages := make([]<-chan string, downloaders) 138 | for i := 0; i < downloaders; i++ { 139 | pages[i] = downloadPages(quit, urls) 140 | } 141 | words := extractWords(quit, listing9_10.FanIn(quit, pages...)) 142 | wordsMulti := listing9_14.Broadcast(quit, words, 2) 143 | longestResults := longestWords(quit, wordsMulti[0]) 144 | frequentResults := frequentWords(quit, wordsMulti[1]) 145 | fmt.Println("Longest Words:", <-longestResults) 146 | fmt.Println("Most frequent Words:", <-frequentResults) 147 | } 148 | -------------------------------------------------------------------------------- /chapter9/listing9.19/wordstatsearlyquit.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/cutajarj/ConcurrentProgrammingWithGo/chapter9/listing9.10" 6 | "github.com/cutajarj/ConcurrentProgrammingWithGo/chapter9/listing9.14" 7 | "github.com/cutajarj/ConcurrentProgrammingWithGo/chapter9/listing9.18" 8 | "io" 9 | "net/http" 10 | "regexp" 11 | "sort" 12 | "strings" 13 | ) 14 | 15 | func frequentWords(quit <-chan int, words <-chan string) <-chan string { 16 | mostFrequentWords := make(chan string) 17 | go func() { 18 | defer close(mostFrequentWords) 19 | frequencyMap := make(map[string]int) 20 | frequencyList := make([]string, 0) 21 | moreData, word := true, "" 22 | for moreData { 23 | select { 24 | case word, moreData = <-words: 25 | if moreData { 26 | if frequencyMap[word] == 0 { 27 | frequencyList = append(frequencyList, word) 28 | } 29 | frequencyMap[word] += 1 30 | } 31 | case <-quit: 32 | return 33 | } 34 | } 35 | sort.Slice(frequencyList, func(a, b int) bool { 36 | return frequencyMap[frequencyList[a]] > frequencyMap[frequencyList[b]] 37 | }) 38 | mostFrequentWords <- strings.Join(frequencyList[:10], ", ") 39 | }() 40 | return mostFrequentWords 41 | } 42 | 43 | func longestWords(quit <-chan int, words <-chan string) <-chan string { 44 | longWords := make(chan string) 45 | go func() { 46 | defer close(longWords) 47 | uniqueWordsMap := make(map[string]bool) 48 | uniqueWords := make([]string, 0) 49 | moreData, word := true, "" 50 | for moreData { 51 | select { 52 | case word, moreData = <-words: 53 | if moreData && !uniqueWordsMap[word] { 54 | uniqueWordsMap[word] = true 55 | uniqueWords = append(uniqueWords, word) 56 | } 57 | case <-quit: 58 | return 59 | } 60 | } 61 | sort.Slice(uniqueWords, func(a, b int) bool { 62 | return len(uniqueWords[a]) > len(uniqueWords[b]) 63 | }) 64 | longWords <- strings.Join(uniqueWords[:10], ", ") 65 | }() 66 | return longWords 67 | } 68 | 69 | func extractWords(quit <-chan int, pages <-chan string) <-chan string { 70 | words := make(chan string) 71 | go func() { 72 | defer close(words) 73 | wordRegex := regexp.MustCompile(`[a-zA-Z]+`) 74 | moreData, page := true, "" 75 | for moreData { 76 | select { 77 | case page, moreData = <-pages: 78 | if moreData { 79 | for _, word := range wordRegex.FindAllString(page, -1) { 80 | words <- strings.ToLower(word) 81 | } 82 | } 83 | case <-quit: 84 | return 85 | } 86 | } 87 | }() 88 | return words 89 | } 90 | 91 | func downloadPages(quit <-chan int, urls <-chan string) <-chan string { 92 | pages := make(chan string) 93 | go func() { 94 | defer close(pages) 95 | moreData, url := true, "" 96 | for moreData { 97 | select { 98 | case url, moreData = <-urls: 99 | if moreData { 100 | resp, _ := http.Get(url) 101 | if resp.StatusCode != 200 { 102 | panic("Server's error: " + resp.Status) 103 | } 104 | body, _ := io.ReadAll(resp.Body) 105 | pages <- string(body) 106 | resp.Body.Close() 107 | } 108 | case <-quit: 109 | return 110 | } 111 | } 112 | }() 113 | return pages 114 | } 115 | 116 | func generateUrls(quit <-chan int) <-chan string { 117 | urls := make(chan string) 118 | go func() { 119 | defer close(urls) 120 | for i := 100; i <= 130; i++ { 121 | url := fmt.Sprintf("https://rfc-editor.org/rfc/rfc%d.txt", i) 122 | select { 123 | case urls <- url: 124 | case <-quit: 125 | return 126 | } 127 | } 128 | }() 129 | return urls 130 | } 131 | 132 | const downloaders = 20 133 | 134 | func main() { 135 | quitWords := make(chan int) 136 | quit := make(chan int) 137 | defer close(quit) 138 | urls := generateUrls(quitWords) 139 | pages := make([]<-chan string, downloaders) 140 | for i := 0; i < downloaders; i++ { 141 | pages[i] = downloadPages(quitWords, urls) 142 | } 143 | words := listing9_18.Take(quitWords, 10000, 144 | extractWords(quitWords, listing9_10.FanIn(quitWords, pages...))) 145 | wordsMulti := listing9_14.Broadcast(quit, words, 2) 146 | longestResults := longestWords(quit, wordsMulti[0]) 147 | frequentResults := frequentWords(quit, wordsMulti[1]) 148 | 149 | fmt.Println("Longest Words:", <-longestResults) 150 | fmt.Println("Most frequent Words:", <-frequentResults) 151 | } 152 | -------------------------------------------------------------------------------- /exercises/commonfiles/subdir/txtfile4: -------------------------------------------------------------------------------- 1 | The Open Software License 3.0 (OSL-3.0) 2 | 3 | This Open Software License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: 4 | 5 | Licensed under the Open Software License version 3.0 6 | 7 | 1) Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: 8 | 9 | a) to reproduce the Original Work in copies, either alone or as part of a collective work; 10 | 11 | b) to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; 12 | 13 | c) to distribute or communicate copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work or Derivative Works that You distribute or communicate shall be licensed under this Open Software License; 14 | 15 | d) to perform the Original Work publicly; and 16 | 17 | e) to display the Original Work publicly. 18 | 19 | 2) Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. 20 | 21 | 3) Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. 22 | 23 | 4) Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. 24 | 25 | 5) External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). 26 | 27 | 6) Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. 28 | 29 | 7) Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. 30 | 31 | 8) Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. 32 | 33 | 9) Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including "fair use" or "fair dealing"). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). 34 | 35 | 10) Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. 36 | 37 | 11) Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. 38 | 39 | 12) Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. 40 | 41 | 13) Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. 42 | 43 | 14) Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. 44 | 45 | 15) Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. 46 | 47 | 16) Modification of This License. This License is Copyright © 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Open Software License" or "OSL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under " or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. 48 | 49 | -------------------------------------------------------------------------------- /exercises/commonfiles/txtfile1: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. -------------------------------------------------------------------------------- /exercises/commonfiles/txtfile3: -------------------------------------------------------------------------------- 1 | Mozilla Public License 2 | Version 2.0 3 | 1. Definitions 4 | 1.1. “Contributor” 5 | means each individual or legal entity that creates, contributes to the creation of, or owns Covered Software. 6 | 7 | 1.2. “Contributor Version” 8 | means the combination of the Contributions of others (if any) used by a Contributor and that particular Contributor’s Contribution. 9 | 10 | 1.3. “Contribution” 11 | means Covered Software of a particular Contributor. 12 | 13 | 1.4. “Covered Software” 14 | means Source Code Form to which the initial Contributor has attached the notice in Exhibit A, the Executable Form of such Source Code Form, and Modifications of such Source Code Form, in each case including portions thereof. 15 | 16 | 1.5. “Incompatible With Secondary Licenses” 17 | means 18 | 19 | that the initial Contributor has attached the notice described in Exhibit B to the Covered Software; or 20 | 21 | that the Covered Software was made available under the terms of version 1.1 or earlier of the License, but not also under the terms of a Secondary License. 22 | 23 | 1.6. “Executable Form” 24 | means any form of the work other than Source Code Form. 25 | 26 | 1.7. “Larger Work” 27 | means a work that combines Covered Software with other material, in a separate file or files, that is not Covered Software. 28 | 29 | 1.8. “License” 30 | means this document. 31 | 32 | 1.9. “Licensable” 33 | means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently, any and all of the rights conveyed by this License. 34 | 35 | 1.10. “Modifications” 36 | means any of the following: 37 | 38 | any file in Source Code Form that results from an addition to, deletion from, or modification of the contents of Covered Software; or 39 | 40 | any new file in Source Code Form that contains any Covered Software. 41 | 42 | 1.11. “Patent Claims” of a Contributor 43 | means any patent claim(s), including without limitation, method, process, and apparatus claims, in any patent Licensable by such Contributor that would be infringed, but for the grant of the License, by the making, using, selling, offering for sale, having made, import, or transfer of either its Contributions or its Contributor Version. 44 | 45 | 1.12. “Secondary License” 46 | means either the GNU General Public License, Version 2.0, the GNU Lesser General Public License, Version 2.1, the GNU Affero General Public License, Version 3.0, or any later versions of those licenses. 47 | 48 | 1.13. “Source Code Form” 49 | means the form of the work preferred for making modifications. 50 | 51 | 1.14. “You” (or “Your”) 52 | means an individual or a legal entity exercising rights under this License. For legal entities, “You” includes any entity that controls, is controlled by, or is under common control with You. For purposes of this definition, “control” means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity. 53 | 54 | 2. License Grants and Conditions 55 | 2.1. Grants 56 | Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license: 57 | 58 | under intellectual property rights (other than patent or trademark) Licensable by such Contributor to use, reproduce, make available, modify, display, perform, distribute, and otherwise exploit its Contributions, either on an unmodified basis, with Modifications, or as part of a Larger Work; and 59 | 60 | under Patent Claims of such Contributor to make, use, sell, offer for sale, have made, import, and otherwise transfer either its Contributions or its Contributor Version. 61 | 62 | 2.2. Effective Date 63 | The licenses granted in Section 2.1 with respect to any Contribution become effective for each Contribution on the date the Contributor first distributes such Contribution. 64 | 65 | 2.3. Limitations on Grant Scope 66 | The licenses granted in this Section 2 are the only rights granted under this License. No additional rights or licenses will be implied from the distribution or licensing of Covered Software under this License. Notwithstanding Section 2.1(b) above, no patent license is granted by a Contributor: 67 | 68 | for any code that a Contributor has removed from Covered Software; or 69 | 70 | for infringements caused by: (i) Your and any other third party’s modifications of Covered Software, or (ii) the combination of its Contributions with other software (except as part of its Contributor Version); or 71 | 72 | under Patent Claims infringed by Covered Software in the absence of its Contributions. 73 | 74 | This License does not grant any rights in the trademarks, service marks, or logos of any Contributor (except as may be necessary to comply with the notice requirements in Section 3.4). 75 | 76 | 2.4. Subsequent Licenses 77 | No Contributor makes additional grants as a result of Your choice to distribute the Covered Software under a subsequent version of this License (see Section 10.2) or under the terms of a Secondary License (if permitted under the terms of Section 3.3). 78 | 79 | 2.5. Representation 80 | Each Contributor represents that the Contributor believes its Contributions are its original creation(s) or it has sufficient rights to grant the rights to its Contributions conveyed by this License. 81 | 82 | 2.6. Fair Use 83 | This License is not intended to limit any rights You have under applicable copyright doctrines of fair use, fair dealing, or other equivalents. 84 | 85 | 2.7. Conditions 86 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in Section 2.1. 87 | 88 | 3. Responsibilities 89 | 3.1. Distribution of Source Form 90 | All distribution of Covered Software in Source Code Form, including any Modifications that You create or to which You contribute, must be under the terms of this License. You must inform recipients that the Source Code Form of the Covered Software is governed by the terms of this License, and how they can obtain a copy of this License. You may not attempt to alter or restrict the recipients’ rights in the Source Code Form. 91 | 92 | 3.2. Distribution of Executable Form 93 | If You distribute Covered Software in Executable Form then: 94 | 95 | such Covered Software must also be made available in Source Code Form, as described in Section 3.1, and You must inform recipients of the Executable Form how they can obtain a copy of such Source Code Form by reasonable means in a timely manner, at a charge no more than the cost of distribution to the recipient; and 96 | 97 | You may distribute such Executable Form under the terms of this License, or sublicense it under different terms, provided that the license for the Executable Form does not attempt to limit or alter the recipients’ rights in the Source Code Form under this License. 98 | 99 | 3.3. Distribution of a Larger Work 100 | You may create and distribute a Larger Work under terms of Your choice, provided that You also comply with the requirements of this License for the Covered Software. If the Larger Work is a combination of Covered Software with a work governed by one or more Secondary Licenses, and the Covered Software is not Incompatible With Secondary Licenses, this License permits You to additionally distribute such Covered Software under the terms of such Secondary License(s), so that the recipient of the Larger Work may, at their option, further distribute the Covered Software under the terms of either this License or such Secondary License(s). 101 | 102 | 3.4. Notices 103 | You may not remove or alter the substance of any license notices (including copyright notices, patent notices, disclaimers of warranty, or limitations of liability) contained within the Source Code Form of the Covered Software, except that You may alter any license notices to the extent required to remedy known factual inaccuracies. 104 | 105 | 3.5. Application of Additional Terms 106 | You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, You may do so only on Your own behalf, and not on behalf of any Contributor. You must make it absolutely clear that any such warranty, support, indemnity, or liability obligation is offered by You alone, and You hereby agree to indemnify every Contributor for any liability incurred by such Contributor as a result of warranty, support, indemnity or liability terms You offer. You may include additional disclaimers of warranty and limitations of liability specific to any jurisdiction. 107 | 108 | 4. Inability to Comply Due to Statute or Regulation 109 | If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Software due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be placed in a text file included with all distributions of the Covered Software under this License. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it. 110 | 111 | 5. Termination 112 | 5.1. The rights granted under this License will terminate automatically if You fail to comply with any of its terms. However, if You become compliant, then the rights granted under this License from a particular Contributor are reinstated (a) provisionally, unless and until such Contributor explicitly and finally terminates Your grants, and (b) on an ongoing basis, if such Contributor fails to notify You of the non-compliance by some reasonable means prior to 60 days after You have come back into compliance. Moreover, Your grants from a particular Contributor are reinstated on an ongoing basis if such Contributor notifies You of the non-compliance by some reasonable means, this is the first time You have received notice of non-compliance with this License from such Contributor, and You become compliant prior to 30 days after Your receipt of the notice. 113 | 114 | 5.2. If You initiate litigation against any entity by asserting a patent infringement claim (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a Contributor Version directly or indirectly infringes any patent, then the rights granted to You by any and all Contributors for the Covered Software under Section 2.1 of this License shall terminate. 115 | 116 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or Your distributors under this License prior to termination shall survive termination. 117 | 118 | 6. Disclaimer of Warranty 119 | Covered Software is provided under this License on an “as is” basis, without warranty of any kind, either expressed, implied, or statutory, including, without limitation, warranties that the Covered Software is free of defects, merchantable, fit for a particular purpose or non-infringing. The entire risk as to the quality and performance of the Covered Software is with You. Should any Covered Software prove defective in any respect, You (not any Contributor) assume the cost of any necessary servicing, repair, or correction. This disclaimer of warranty constitutes an essential part of this License. No use of any Covered Software is authorized under this License except under this disclaimer. 120 | 121 | 7. Limitation of Liability 122 | Under no circumstances and under no legal theory, whether tort (including negligence), contract, or otherwise, shall any Contributor, or anyone who distributes Covered Software as permitted above, be liable to You for any direct, indirect, special, incidental, or consequential damages of any character including, without limitation, damages for lost profits, loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses, even if such party shall have been informed of the possibility of such damages. This limitation of liability shall not apply to liability for death or personal injury resulting from such party’s negligence to the extent applicable law prohibits such limitation. Some jurisdictions do not allow the exclusion or limitation of incidental or consequential damages, so this exclusion and limitation may not apply to You. 123 | 124 | 8. Litigation 125 | Any litigation relating to this License may be brought only in the courts of a jurisdiction where the defendant maintains its principal place of business and such litigation shall be governed by laws of that jurisdiction, without reference to its conflict-of-law provisions. Nothing in this Section shall prevent a party’s ability to bring cross-claims or counter-claims. 126 | 127 | 9. Miscellaneous 128 | This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not be used to construe this License against a Contributor. 129 | 130 | 10. Versions of the License 131 | 10.1. New Versions 132 | Mozilla Foundation is the license steward. Except as provided in Section 10.3, no one other than the license steward has the right to modify or publish new versions of this License. Each version will be given a distinguishing version number. 133 | 134 | 10.2. Effect of New Versions 135 | You may distribute the Covered Software under the terms of the version of the License under which You originally received the Covered Software, or under the terms of any subsequent version published by the license steward. 136 | 137 | 10.3. Modified Versions 138 | If you create software not governed by this License, and you want to create a new license for such software, you may create and use a modified version of this License if you rename the license and remove any references to the name of the license steward (except to note that such modified license differs from this License). 139 | 140 | 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses 141 | If You choose to distribute Source Code Form that is Incompatible With Secondary Licenses under the terms of this version of the License, the notice described in Exhibit B of this License must be attached. 142 | 143 | Exhibit A - Source Code Form License Notice 144 | This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/. 145 | 146 | If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice. 147 | 148 | You may add additional accurate notices of copyright ownership. 149 | 150 | Exhibit B - “Incompatible With Secondary Licenses” Notice 151 | This Source Code Form is “Incompatible With Secondary Licenses”, as defined by the Mozilla Public License, v. 2.0. -------------------------------------------------------------------------------- /exercises/commonfiles/subdir/subsubdir/txtfile6: -------------------------------------------------------------------------------- 1 | The LaTeX Project Public License 2 | =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 3 | 4 | LPPL Version 1.3c 2008-05-04 5 | 6 | Copyright 1999 2002-2008 LaTeX3 Project 7 | Everyone is allowed to distribute verbatim copies of this 8 | license document, but modification of it is not allowed. 9 | 10 | 11 | PREAMBLE 12 | ======== 13 | 14 | The LaTeX Project Public License (LPPL) is the primary license under 15 | which the LaTeX kernel and the base LaTeX packages are distributed. 16 | 17 | You may use this license for any work of which you hold the copyright 18 | and which you wish to distribute. This license may be particularly 19 | suitable if your work is TeX-related (such as a LaTeX package), but 20 | it is written in such a way that you can use it even if your work is 21 | unrelated to TeX. 22 | 23 | The section `WHETHER AND HOW TO DISTRIBUTE WORKS UNDER THIS LICENSE', 24 | below, gives instructions, examples, and recommendations for authors 25 | who are considering distributing their works under this license. 26 | 27 | This license gives conditions under which a work may be distributed 28 | and modified, as well as conditions under which modified versions of 29 | that work may be distributed. 30 | 31 | We, the LaTeX3 Project, believe that the conditions below give you 32 | the freedom to make and distribute modified versions of your work 33 | that conform with whatever technical specifications you wish while 34 | maintaining the availability, integrity, and reliability of 35 | that work. If you do not see how to achieve your goal while 36 | meeting these conditions, then read the document `cfgguide.tex' 37 | and `modguide.tex' in the base LaTeX distribution for suggestions. 38 | 39 | 40 | DEFINITIONS 41 | =========== 42 | 43 | In this license document the following terms are used: 44 | 45 | `Work' 46 | Any work being distributed under this License. 47 | 48 | `Derived Work' 49 | Any work that under any applicable law is derived from the Work. 50 | 51 | `Modification' 52 | Any procedure that produces a Derived Work under any applicable 53 | law -- for example, the production of a file containing an 54 | original file associated with the Work or a significant portion of 55 | such a file, either verbatim or with modifications and/or 56 | translated into another language. 57 | 58 | `Modify' 59 | To apply any procedure that produces a Derived Work under any 60 | applicable law. 61 | 62 | `Distribution' 63 | Making copies of the Work available from one person to another, in 64 | whole or in part. Distribution includes (but is not limited to) 65 | making any electronic components of the Work accessible by 66 | file transfer protocols such as FTP or HTTP or by shared file 67 | systems such as Sun's Network File System (NFS). 68 | 69 | `Compiled Work' 70 | A version of the Work that has been processed into a form where it 71 | is directly usable on a computer system. This processing may 72 | include using installation facilities provided by the Work, 73 | transformations of the Work, copying of components of the Work, or 74 | other activities. Note that modification of any installation 75 | facilities provided by the Work constitutes modification of the Work. 76 | 77 | `Current Maintainer' 78 | A person or persons nominated as such within the Work. If there is 79 | no such explicit nomination then it is the `Copyright Holder' under 80 | any applicable law. 81 | 82 | `Base Interpreter' 83 | A program or process that is normally needed for running or 84 | interpreting a part or the whole of the Work. 85 | 86 | A Base Interpreter may depend on external components but these 87 | are not considered part of the Base Interpreter provided that each 88 | external component clearly identifies itself whenever it is used 89 | interactively. Unless explicitly specified when applying the 90 | license to the Work, the only applicable Base Interpreter is a 91 | `LaTeX-Format' or in the case of files belonging to the 92 | `LaTeX-format' a program implementing the `TeX language'. 93 | 94 | 95 | 96 | CONDITIONS ON DISTRIBUTION AND MODIFICATION 97 | =========================================== 98 | 99 | 1. Activities other than distribution and/or modification of the Work 100 | are not covered by this license; they are outside its scope. In 101 | particular, the act of running the Work is not restricted and no 102 | requirements are made concerning any offers of support for the Work. 103 | 104 | 2. You may distribute a complete, unmodified copy of the Work as you 105 | received it. Distribution of only part of the Work is considered 106 | modification of the Work, and no right to distribute such a Derived 107 | Work may be assumed under the terms of this clause. 108 | 109 | 3. You may distribute a Compiled Work that has been generated from a 110 | complete, unmodified copy of the Work as distributed under Clause 2 111 | above, as long as that Compiled Work is distributed in such a way that 112 | the recipients may install the Compiled Work on their system exactly 113 | as it would have been installed if they generated a Compiled Work 114 | directly from the Work. 115 | 116 | 4. If you are the Current Maintainer of the Work, you may, without 117 | restriction, modify the Work, thus creating a Derived Work. You may 118 | also distribute the Derived Work without restriction, including 119 | Compiled Works generated from the Derived Work. Derived Works 120 | distributed in this manner by the Current Maintainer are considered to 121 | be updated versions of the Work. 122 | 123 | 5. If you are not the Current Maintainer of the Work, you may modify 124 | your copy of the Work, thus creating a Derived Work based on the Work, 125 | and compile this Derived Work, thus creating a Compiled Work based on 126 | the Derived Work. 127 | 128 | 6. If you are not the Current Maintainer of the Work, you may 129 | distribute a Derived Work provided the following conditions are met 130 | for every component of the Work unless that component clearly states 131 | in the copyright notice that it is exempt from that condition. Only 132 | the Current Maintainer is allowed to add such statements of exemption 133 | to a component of the Work. 134 | 135 | a. If a component of this Derived Work can be a direct replacement 136 | for a component of the Work when that component is used with the 137 | Base Interpreter, then, wherever this component of the Work 138 | identifies itself to the user when used interactively with that 139 | Base Interpreter, the replacement component of this Derived Work 140 | clearly and unambiguously identifies itself as a modified version 141 | of this component to the user when used interactively with that 142 | Base Interpreter. 143 | 144 | b. Every component of the Derived Work contains prominent notices 145 | detailing the nature of the changes to that component, or a 146 | prominent reference to another file that is distributed as part 147 | of the Derived Work and that contains a complete and accurate log 148 | of the changes. 149 | 150 | c. No information in the Derived Work implies that any persons, 151 | including (but not limited to) the authors of the original version 152 | of the Work, provide any support, including (but not limited to) 153 | the reporting and handling of errors, to recipients of the 154 | Derived Work unless those persons have stated explicitly that 155 | they do provide such support for the Derived Work. 156 | 157 | d. You distribute at least one of the following with the Derived Work: 158 | 159 | 1. A complete, unmodified copy of the Work; 160 | if your distribution of a modified component is made by 161 | offering access to copy the modified component from a 162 | designated place, then offering equivalent access to copy 163 | the Work from the same or some similar place meets this 164 | condition, even though third parties are not compelled to 165 | copy the Work along with the modified component; 166 | 167 | 2. Information that is sufficient to obtain a complete, 168 | unmodified copy of the Work. 169 | 170 | 7. If you are not the Current Maintainer of the Work, you may 171 | distribute a Compiled Work generated from a Derived Work, as long as 172 | the Derived Work is distributed to all recipients of the Compiled 173 | Work, and as long as the conditions of Clause 6, above, are met with 174 | regard to the Derived Work. 175 | 176 | 8. The conditions above are not intended to prohibit, and hence do not 177 | apply to, the modification, by any method, of any component so that it 178 | becomes identical to an updated version of that component of the Work as 179 | it is distributed by the Current Maintainer under Clause 4, above. 180 | 181 | 9. Distribution of the Work or any Derived Work in an alternative 182 | format, where the Work or that Derived Work (in whole or in part) is 183 | then produced by applying some process to that format, does not relax or 184 | nullify any sections of this license as they pertain to the results of 185 | applying that process. 186 | 187 | 10. a. A Derived Work may be distributed under a different license 188 | provided that license itself honors the conditions listed in 189 | Clause 6 above, in regard to the Work, though it does not have 190 | to honor the rest of the conditions in this license. 191 | 192 | b. If a Derived Work is distributed under a different license, that 193 | Derived Work must provide sufficient documentation as part of 194 | itself to allow each recipient of that Derived Work to honor the 195 | restrictions in Clause 6 above, concerning changes from the Work. 196 | 197 | 11. This license places no restrictions on works that are unrelated to 198 | the Work, nor does this license place any restrictions on aggregating 199 | such works with the Work by any means. 200 | 201 | 12. Nothing in this license is intended to, or may be used to, prevent 202 | complete compliance by all parties with all applicable laws. 203 | 204 | 205 | NO WARRANTY 206 | =========== 207 | 208 | There is no warranty for the Work. Except when otherwise stated in 209 | writing, the Copyright Holder provides the Work `as is', without 210 | warranty of any kind, either expressed or implied, including, but not 211 | limited to, the implied warranties of merchantability and fitness for a 212 | particular purpose. The entire risk as to the quality and performance 213 | of the Work is with you. Should the Work prove defective, you assume 214 | the cost of all necessary servicing, repair, or correction. 215 | 216 | In no event unless required by applicable law or agreed to in writing 217 | will The Copyright Holder, or any author named in the components of the 218 | Work, or any other party who may distribute and/or modify the Work as 219 | permitted above, be liable to you for damages, including any general, 220 | special, incidental or consequential damages arising out of any use of 221 | the Work or out of inability to use the Work (including, but not limited 222 | to, loss of data, data being rendered inaccurate, or losses sustained by 223 | anyone as a result of any failure of the Work to operate with any other 224 | programs), even if the Copyright Holder or said author or said other 225 | party has been advised of the possibility of such damages. 226 | 227 | 228 | MAINTENANCE OF THE WORK 229 | ======================= 230 | 231 | The Work has the status `author-maintained' if the Copyright Holder 232 | explicitly and prominently states near the primary copyright notice in 233 | the Work that the Work can only be maintained by the Copyright Holder 234 | or simply that it is `author-maintained'. 235 | 236 | The Work has the status `maintained' if there is a Current Maintainer 237 | who has indicated in the Work that they are willing to receive error 238 | reports for the Work (for example, by supplying a valid e-mail 239 | address). It is not required for the Current Maintainer to acknowledge 240 | or act upon these error reports. 241 | 242 | The Work changes from status `maintained' to `unmaintained' if there 243 | is no Current Maintainer, or the person stated to be Current 244 | Maintainer of the work cannot be reached through the indicated means 245 | of communication for a period of six months, and there are no other 246 | significant signs of active maintenance. 247 | 248 | You can become the Current Maintainer of the Work by agreement with 249 | any existing Current Maintainer to take over this role. 250 | 251 | If the Work is unmaintained, you can become the Current Maintainer of 252 | the Work through the following steps: 253 | 254 | 1. Make a reasonable attempt to trace the Current Maintainer (and 255 | the Copyright Holder, if the two differ) through the means of 256 | an Internet or similar search. 257 | 258 | 2. If this search is successful, then enquire whether the Work 259 | is still maintained. 260 | 261 | a. If it is being maintained, then ask the Current Maintainer 262 | to update their communication data within one month. 263 | 264 | b. If the search is unsuccessful or no action to resume active 265 | maintenance is taken by the Current Maintainer, then announce 266 | within the pertinent community your intention to take over 267 | maintenance. (If the Work is a LaTeX work, this could be 268 | done, for example, by posting to comp.text.tex.) 269 | 270 | 3a. If the Current Maintainer is reachable and agrees to pass 271 | maintenance of the Work to you, then this takes effect 272 | immediately upon announcement. 273 | 274 | b. If the Current Maintainer is not reachable and the Copyright 275 | Holder agrees that maintenance of the Work be passed to you, 276 | then this takes effect immediately upon announcement. 277 | 278 | 4. If you make an `intention announcement' as described in 2b. above 279 | and after three months your intention is challenged neither by 280 | the Current Maintainer nor by the Copyright Holder nor by other 281 | people, then you may arrange for the Work to be changed so as 282 | to name you as the (new) Current Maintainer. 283 | 284 | 5. If the previously unreachable Current Maintainer becomes 285 | reachable once more within three months of a change completed 286 | under the terms of 3b) or 4), then that Current Maintainer must 287 | become or remain the Current Maintainer upon request provided 288 | they then update their communication data within one month. 289 | 290 | A change in the Current Maintainer does not, of itself, alter the fact 291 | that the Work is distributed under the LPPL license. 292 | 293 | If you become the Current Maintainer of the Work, you should 294 | immediately provide, within the Work, a prominent and unambiguous 295 | statement of your status as Current Maintainer. You should also 296 | announce your new status to the same pertinent community as 297 | in 2b) above. 298 | 299 | 300 | WHETHER AND HOW TO DISTRIBUTE WORKS UNDER THIS LICENSE 301 | ====================================================== 302 | 303 | This section contains important instructions, examples, and 304 | recommendations for authors who are considering distributing their 305 | works under this license. These authors are addressed as `you' in 306 | this section. 307 | 308 | Choosing This License or Another License 309 | ---------------------------------------- 310 | 311 | If for any part of your work you want or need to use *distribution* 312 | conditions that differ significantly from those in this license, then 313 | do not refer to this license anywhere in your work but, instead, 314 | distribute your work under a different license. You may use the text 315 | of this license as a model for your own license, but your license 316 | should not refer to the LPPL or otherwise give the impression that 317 | your work is distributed under the LPPL. 318 | 319 | The document `modguide.tex' in the base LaTeX distribution explains 320 | the motivation behind the conditions of this license. It explains, 321 | for example, why distributing LaTeX under the GNU General Public 322 | License (GPL) was considered inappropriate. Even if your work is 323 | unrelated to LaTeX, the discussion in `modguide.tex' may still be 324 | relevant, and authors intending to distribute their works under any 325 | license are encouraged to read it. 326 | 327 | A Recommendation on Modification Without Distribution 328 | ----------------------------------------------------- 329 | 330 | It is wise never to modify a component of the Work, even for your own 331 | personal use, without also meeting the above conditions for 332 | distributing the modified component. While you might intend that such 333 | modifications will never be distributed, often this will happen by 334 | accident -- you may forget that you have modified that component; or 335 | it may not occur to you when allowing others to access the modified 336 | version that you are thus distributing it and violating the conditions 337 | of this license in ways that could have legal implications and, worse, 338 | cause problems for the community. It is therefore usually in your 339 | best interest to keep your copy of the Work identical with the public 340 | one. Many works provide ways to control the behavior of that work 341 | without altering any of its licensed components. 342 | 343 | How to Use This License 344 | ----------------------- 345 | 346 | To use this license, place in each of the components of your work both 347 | an explicit copyright notice including your name and the year the work 348 | was authored and/or last substantially modified. Include also a 349 | statement that the distribution and/or modification of that 350 | component is constrained by the conditions in this license. 351 | 352 | Here is an example of such a notice and statement: 353 | 354 | %% pig.dtx 355 | %% Copyright 2005 M. Y. Name 356 | % 357 | % This work may be distributed and/or modified under the 358 | % conditions of the LaTeX Project Public License, either version 1.3 359 | % of this license or (at your option) any later version. 360 | % The latest version of this license is in 361 | % http://www.latex-project.org/lppl.txt 362 | % and version 1.3 or later is part of all distributions of LaTeX 363 | % version 2005/12/01 or later. 364 | % 365 | % This work has the LPPL maintenance status `maintained'. 366 | % 367 | % The Current Maintainer of this work is M. Y. Name. 368 | % 369 | % This work consists of the files pig.dtx and pig.ins 370 | % and the derived file pig.sty. 371 | 372 | Given such a notice and statement in a file, the conditions 373 | given in this license document would apply, with the `Work' referring 374 | to the three files `pig.dtx', `pig.ins', and `pig.sty' (the last being 375 | generated from `pig.dtx' using `pig.ins'), the `Base Interpreter' 376 | referring to any `LaTeX-Format', and both `Copyright Holder' and 377 | `Current Maintainer' referring to the person `M. Y. Name'. 378 | 379 | If you do not want the Maintenance section of LPPL to apply to your 380 | Work, change `maintained' above into `author-maintained'. 381 | However, we recommend that you use `maintained', as the Maintenance 382 | section was added in order to ensure that your Work remains useful to 383 | the community even when you can no longer maintain and support it 384 | yourself. 385 | 386 | Derived Works That Are Not Replacements 387 | --------------------------------------- 388 | 389 | Several clauses of the LPPL specify means to provide reliability and 390 | stability for the user community. They therefore concern themselves 391 | with the case that a Derived Work is intended to be used as a 392 | (compatible or incompatible) replacement of the original Work. If 393 | this is not the case (e.g., if a few lines of code are reused for a 394 | completely different task), then clauses 6b and 6d shall not apply. 395 | 396 | 397 | Important Recommendations 398 | ------------------------- 399 | 400 | Defining What Constitutes the Work 401 | 402 | The LPPL requires that distributions of the Work contain all the 403 | files of the Work. It is therefore important that you provide a 404 | way for the licensee to determine which files constitute the Work. 405 | This could, for example, be achieved by explicitly listing all the 406 | files of the Work near the copyright notice of each file or by 407 | using a line such as: 408 | 409 | % This work consists of all files listed in manifest.txt. 410 | 411 | in that place. In the absence of an unequivocal list it might be 412 | impossible for the licensee to determine what is considered by you 413 | to comprise the Work and, in such a case, the licensee would be 414 | entitled to make reasonable conjectures as to which files comprise 415 | the Work. 416 | --------------------------------------------------------------------------------