Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added README & source from the first edition
- Loading branch information
Showing
79 changed files
with
3,584 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
This is the full sourcecode for the book, "Concurrency in Go" published by O'Reilly. | ||
|
||
For errata and more information, please refer to the book's hub at [[http://katherine.cox-buday.com/concurrency-in-go]]. |
59 changes: 59 additions & 0 deletions
59
...y-is-concurrency-hard/deadlocks-livelocks-and-starvation/livelock/fig-livelock-hallway.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
package main | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
"sync" | ||
"sync/atomic" | ||
"time" | ||
) | ||
|
||
func main() { | ||
cadence := sync.NewCond(&sync.Mutex{}) | ||
go func() { | ||
for range time.Tick(1 * time.Millisecond) { | ||
cadence.Broadcast() | ||
} | ||
}() | ||
|
||
takeStep := func() { | ||
cadence.L.Lock() | ||
cadence.Wait() | ||
cadence.L.Unlock() | ||
} | ||
|
||
tryDir := func(dirName string, dir *int32, out *bytes.Buffer) bool { // <1> | ||
fmt.Fprintf(out, " %v", dirName) | ||
atomic.AddInt32(dir, 1) // <2> | ||
takeStep() // <3> | ||
if atomic.LoadInt32(dir) == 1 { | ||
fmt.Fprint(out, ". Success!") | ||
return true | ||
} | ||
takeStep() | ||
atomic.AddInt32(dir, -1) // <4> | ||
return false | ||
} | ||
|
||
var left, right int32 | ||
tryLeft := func(out *bytes.Buffer) bool { return tryDir("left", &left, out) } | ||
tryRight := func(out *bytes.Buffer) bool { return tryDir("right", &right, out) } | ||
walk := func(walking *sync.WaitGroup, name string) { | ||
var out bytes.Buffer | ||
defer func() { fmt.Println(out.String()) }() | ||
defer walking.Done() | ||
fmt.Fprintf(&out, "%v is trying to scoot:", name) | ||
for i := 0; i < 5; i++ { // <1> | ||
if tryLeft(&out) || tryRight(&out) { // <2> | ||
return | ||
} | ||
} | ||
fmt.Fprintf(&out, "\n%v tosses her hands up in exasperation!", name) | ||
} | ||
|
||
var peopleInHallway sync.WaitGroup // <3> | ||
peopleInHallway.Add(2) | ||
go walk(&peopleInHallway, "Alice") | ||
go walk(&peopleInHallway, "Barbara") | ||
peopleInHallway.Wait() | ||
} |
56 changes: 56 additions & 0 deletions
56
...ency/why-is-concurrency-hard/deadlocks-livelocks-and-starvation/starvation/fig-example.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"sync" | ||
"time" | ||
) | ||
|
||
func main() { | ||
var wg sync.WaitGroup | ||
var sharedLock sync.Mutex | ||
const runtime = 1 * time.Second | ||
|
||
greedyWorker := func() { | ||
defer wg.Done() | ||
|
||
var count int | ||
for begin := time.Now(); time.Since(begin) <= runtime; { | ||
sharedLock.Lock() | ||
time.Sleep(3 * time.Nanosecond) | ||
sharedLock.Unlock() | ||
count++ | ||
} | ||
|
||
fmt.Printf("Greedy worker was able to execute %v work loops\n", count) | ||
} | ||
|
||
politeWorker := func() { | ||
defer wg.Done() | ||
|
||
var count int | ||
for begin := time.Now(); time.Since(begin) <= runtime; { | ||
sharedLock.Lock() | ||
time.Sleep(1 * time.Nanosecond) | ||
sharedLock.Unlock() | ||
|
||
sharedLock.Lock() | ||
time.Sleep(1 * time.Nanosecond) | ||
sharedLock.Unlock() | ||
|
||
sharedLock.Lock() | ||
time.Sleep(1 * time.Nanosecond) | ||
sharedLock.Unlock() | ||
|
||
count++ | ||
} | ||
|
||
fmt.Printf("Polite worker was able to execute %v work loops.\n", count) | ||
} | ||
|
||
wg.Add(2) | ||
go greedyWorker() | ||
go politeWorker() | ||
|
||
wg.Wait() | ||
} |
24 changes: 24 additions & 0 deletions
24
...ncy/why-is-concurrency-hard/memory-access-synchronization/fig-basic-memory-access-sync.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"sync" | ||
) | ||
|
||
func main() { | ||
var memoryAccess sync.Mutex // <1> | ||
var value int | ||
go func() { | ||
memoryAccess.Lock() // <2> | ||
value++ | ||
memoryAccess.Unlock() // <3> | ||
}() | ||
|
||
memoryAccess.Lock() // <4> | ||
if value == 0 { | ||
fmt.Printf("the value is %v.\n", value) | ||
} else { | ||
fmt.Printf("the value is %v.\n", value) | ||
} | ||
memoryAccess.Unlock() // <5> | ||
} |
15 changes: 15 additions & 0 deletions
15
...uction-to-concurrency/why-is-concurrency-hard/race-conditions/fig-basic-race-condition.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
) | ||
|
||
func main() { | ||
var data int | ||
go func() { // <1> | ||
data++ | ||
}() | ||
if data == 0 { | ||
fmt.Printf("the value is %v.\n", data) | ||
} | ||
} |
85 changes: 85 additions & 0 deletions
85
concurrency-at-scale/error-propagation/fig-error-propagation-corrected.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
"os" | ||
"os/exec" | ||
"runtime/debug" | ||
) | ||
|
||
type MyError struct { | ||
Inner error | ||
Message string | ||
StackTrace string | ||
Misc map[string]interface{} | ||
} | ||
|
||
func wrapError(err error, messagef string, msgArgs ...interface{}) MyError { | ||
return MyError{ | ||
Inner: err, //<1> | ||
Message: fmt.Sprintf(messagef, msgArgs...), | ||
StackTrace: string(debug.Stack()), // <2> | ||
Misc: make(map[string]interface{}), // <3> | ||
} | ||
} | ||
|
||
func (err MyError) Error() string { | ||
return err.Message | ||
} | ||
|
||
// "lowlevel" module | ||
|
||
type LowLevelErr struct { | ||
error | ||
} | ||
|
||
func isGloballyExec(path string) (bool, error) { | ||
info, err := os.Stat(path) | ||
if err != nil { | ||
return false, LowLevelErr{(wrapError(err, err.Error()))} // <1> | ||
} | ||
return info.Mode().Perm()&0100 == 0100, nil | ||
} | ||
|
||
// "intermediate" module | ||
|
||
type IntermediateErr struct { | ||
error | ||
} | ||
|
||
func runJob(id string) error { | ||
const jobBinPath = "/bad/job/binary" | ||
isExecutable, err := isGloballyExec(jobBinPath) | ||
if err != nil { | ||
return IntermediateErr{wrapError( | ||
err, | ||
"cannot run job %q: requisite binaries not available", | ||
id, | ||
)} // <1> | ||
} else if isExecutable == false { | ||
return wrapError(nil, "cannot run job %q: requisite binaries are not executable", id) | ||
} | ||
|
||
return exec.Command(jobBinPath, "--id="+id).Run() | ||
} | ||
|
||
func handleError(key int, err error, message string) { | ||
log.SetPrefix(fmt.Sprintf("[logID: %v]: ", key)) | ||
log.Printf("%#v", err) | ||
fmt.Printf("[%v] %v", key, message) | ||
} | ||
|
||
func main() { | ||
log.SetOutput(os.Stdout) | ||
log.SetFlags(log.Ltime | log.LUTC) | ||
|
||
err := runJob("1") | ||
if err != nil { | ||
msg := "There was an unexpected issue; please report this as a bug." | ||
if _, ok := err.(IntermediateErr); ok { | ||
msg = err.Error() | ||
} | ||
handleError(1, err, msg) | ||
} | ||
} |
81 changes: 81 additions & 0 deletions
81
concurrency-at-scale/error-propagation/fig-error-propagation.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
"os" | ||
"os/exec" | ||
"runtime/debug" | ||
) | ||
|
||
type MyError struct { | ||
Inner error | ||
Message string | ||
StackTrace string | ||
Misc map[string]interface{} | ||
} | ||
|
||
func wrapError(err error, messagef string, msgArgs ...interface{}) MyError { | ||
return MyError{ | ||
Inner: err, //<1> | ||
Message: fmt.Sprintf(messagef, msgArgs...), | ||
StackTrace: string(debug.Stack()), // <2> | ||
Misc: make(map[string]interface{}), // <3> | ||
} | ||
} | ||
|
||
func (err MyError) Error() string { | ||
return err.Message | ||
} | ||
|
||
// "lowlevel" module | ||
|
||
type LowLevelErr struct { | ||
error | ||
} | ||
|
||
func isGloballyExec(path string) (bool, error) { | ||
info, err := os.Stat(path) | ||
if err != nil { | ||
return false, LowLevelErr{(wrapError(err, err.Error()))} // <1> | ||
} | ||
return info.Mode().Perm()&0100 == 0100, nil | ||
} | ||
|
||
// "intermediate" module | ||
|
||
type IntermediateErr struct { | ||
error | ||
} | ||
|
||
func runJob(id string) error { | ||
const jobBinPath = "/bad/job/binary" | ||
isExecutable, err := isGloballyExec(jobBinPath) | ||
if err != nil { | ||
return err // <1> | ||
} else if isExecutable == false { | ||
return wrapError(nil, "job binary is not executable") | ||
} | ||
|
||
return exec.Command(jobBinPath, "--id="+id).Run() // <1> | ||
} | ||
|
||
func handleError(key int, err error, message string) { | ||
log.SetPrefix(fmt.Sprintf("[logID: %v]: ", key)) | ||
log.Printf("%#v", err) // <3> | ||
fmt.Printf("[%v] %v", key, message) | ||
} | ||
|
||
func main() { | ||
log.SetOutput(os.Stdout) | ||
log.SetFlags(log.Ltime | log.LUTC) | ||
|
||
err := runJob("1") | ||
if err != nil { | ||
msg := "There was an unexpected issue; please report this as a bug." | ||
if _, ok := err.(IntermediateErr); ok { // <1> | ||
msg = err.Error() | ||
} | ||
handleError(1, err, msg) // <2> | ||
} | ||
} |
Oops, something went wrong.