Skip to content

Commit 8974991

Browse files
committedAug 21, 2021
add more mutexes examples
1 parent 5c683a3 commit 8974991

File tree

17 files changed

+387
-30
lines changed

17 files changed

+387
-30
lines changed
 

Diff for: ‎mutexes/basic-mutex/main.go

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package main
2+
3+
func main() {
4+
}

Diff for: ‎mutexes/builtinmap-vs-syncmap/builtinmap_test.go

-25
This file was deleted.

Diff for: ‎mutexes/benchmarks/atomic_test.go renamed to ‎mutexes/mutex-vs-atomic/atomic_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package benchmarks
1+
package mutex_vs_atomic
22

33
import (
44
"sync"

Diff for: ‎mutexes/benchmarks/config_test.go renamed to ‎mutexes/mutex-vs-atomic/config_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package benchmarks
1+
package mutex_vs_atomic
22

33
type Config struct {
44
a []int

Diff for: ‎mutexes/benchmarks/mutex_test.go renamed to ‎mutexes/mutex-vs-atomic/mutex_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package benchmarks
1+
package mutex_vs_atomic
22

33
import (
44
"sync"

Diff for: ‎mutexes/once/README.md

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
- [Simple Example](https://github.com/golang-basics/concurrency/blob/master/mutexes/once/simple/main.go)
66
- [Increment/Decrement Example](https://github.com/golang-basics/concurrency/blob/master/mutexes/once/inc-dec/main.go)
7+
- [Race](https://github.com/golang-basics/concurrency/blob/master/mutexes/once/race/main.go)
78
- [Deadlock](https://github.com/golang-basics/concurrency/blob/master/mutexes/once/deadlock/main.go)
9+
- [Once Implementation](https://github.com/golang-basics/concurrency/blob/master/mutexes/once/once-implementation/main.go)
810

911
[Home](https://github.com/golang-basics/concurrency)

Diff for: ‎mutexes/once/caching/client/main.go

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package main
2+
3+
func main() {
4+
// fire some requests for products
5+
// for some requests for analytics
6+
}

Diff for: ‎mutexes/once/caching/server/main.go

+266
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"io/ioutil"
7+
"log"
8+
"net/http"
9+
"net/url"
10+
"os"
11+
"os/signal"
12+
"strings"
13+
"sync"
14+
"syscall"
15+
"time"
16+
)
17+
18+
func main() {
19+
errChan := make(chan error)
20+
quitChan := make(chan os.Signal)
21+
signal.Notify(quitChan, syscall.SIGINT, os.Interrupt)
22+
23+
go func() {
24+
fmt.Println("api gateway successfully started")
25+
err := http.ListenAndServe("localhost:8080", apiGateway())
26+
if err != nil {
27+
errChan <- err
28+
}
29+
}()
30+
go func() {
31+
fmt.Println("users api successfully started")
32+
err := http.ListenAndServe("localhost:8081", usersAPI())
33+
if err != nil {
34+
errChan <- err
35+
}
36+
}()
37+
go func() {
38+
fmt.Println("order api successfully started")
39+
err := http.ListenAndServe("localhost:8082", ordersAPI())
40+
if err != nil {
41+
errChan <- err
42+
}
43+
}()
44+
go func() {
45+
fmt.Println("products api successfully started")
46+
err := http.ListenAndServe("localhost:8083", productsAPI())
47+
if err != nil {
48+
errChan <- err
49+
}
50+
}()
51+
52+
select {
53+
case <-quitChan:
54+
fmt.Println("successfully exited")
55+
case err := <-errChan:
56+
fmt.Println("could not start server:", err)
57+
}
58+
}
59+
60+
type analyticsResponse struct {
61+
User user `json:"user"`
62+
Products []product `json:"products"`
63+
}
64+
65+
func apiGateway() *http.ServeMux {
66+
userClient := client{
67+
url: "localhost:8081/api/users",
68+
cache: map[string]*cacheEntry{},
69+
mu: new(sync.Mutex),
70+
}
71+
ordersClient := client{
72+
url: "localhost:8082/api/orders",
73+
cache: map[string]*cacheEntry{},
74+
mu: new(sync.Mutex),
75+
}
76+
productClient := client{
77+
url: "localhost:8083/api/products",
78+
cache: map[string]*cacheEntry{},
79+
mu: new(sync.Mutex),
80+
}
81+
82+
mux := http.NewServeMux()
83+
mux.HandleFunc("/api/gateway/products", func(w http.ResponseWriter, r *http.Request) {
84+
productID := r.URL.Query().Get("id")
85+
if strings.TrimSpace(productID) == "" {
86+
w.WriteHeader(http.StatusNotFound)
87+
return
88+
}
89+
res := productClient.query(productID, "id="+productID)
90+
w.Header().Set("content-type", "application/json")
91+
_, err := w.Write(append(res, '\n'))
92+
if err != nil {
93+
log.Fatalf("could not write json: %v", err)
94+
}
95+
})
96+
mux.HandleFunc("/api/gateway/analytics", func(w http.ResponseWriter, r *http.Request) {
97+
userID := r.URL.Query().Get("user_id")
98+
if strings.TrimSpace(userID) == "" {
99+
w.WriteHeader(http.StatusNotFound)
100+
return
101+
}
102+
103+
userRes := userClient.query(userID, "id="+userID)
104+
ordersRes := ordersClient.query(userID, "user_id="+userID)
105+
fmt.Println("userRes", string(userRes))
106+
fmt.Println("ordersRes", string(ordersRes))
107+
res := analyticsResponse{}
108+
109+
err := json.NewEncoder(w).Encode(res)
110+
if err != nil {
111+
log.Fatalf("could not encode json response: %v", err)
112+
}
113+
})
114+
return mux
115+
}
116+
117+
type user struct {
118+
ID string `json:"user_id"`
119+
Email string `json:"email"`
120+
}
121+
122+
func usersAPI() *http.ServeMux {
123+
usersDB := map[string]user{
124+
"1": {ID: "1", Email: "user1@example.com"},
125+
"2": {ID: "2", Email: "user2@example.com"},
126+
"3": {ID: "3", Email: "user3@example.com"},
127+
"4": {ID: "4", Email: "user4@example.com"},
128+
"5": {ID: "5", Email: "user5@example.com"},
129+
}
130+
131+
mux := http.NewServeMux()
132+
mux.HandleFunc("/api/users", func(w http.ResponseWriter, r *http.Request) {
133+
userID := r.URL.Query().Get("id")
134+
time.Sleep(time.Second)
135+
user, found := usersDB[userID]
136+
if !found {
137+
w.WriteHeader(http.StatusNotFound)
138+
return
139+
}
140+
err := json.NewEncoder(w).Encode(user)
141+
if err != nil {
142+
log.Fatalf("could not encode user: %v", err)
143+
}
144+
})
145+
return mux
146+
}
147+
148+
type order struct {
149+
ID string `json:"id"`
150+
UserID string `json:"user_id"`
151+
ProductIDs []string `json:"product_ids"`
152+
}
153+
154+
func ordersAPI() *http.ServeMux {
155+
ordersDB := map[string][]order{
156+
"1": {
157+
{ID: "1", UserID: "1", ProductIDs: []string{"1", "2"}},
158+
{ID: "2", UserID: "1", ProductIDs: []string{"3"}},
159+
},
160+
"2": {
161+
{ID: "3", UserID: "2", ProductIDs: []string{"1"}},
162+
},
163+
"3": {
164+
{ID: "4", UserID: "3", ProductIDs: []string{"2"}},
165+
},
166+
"4": {
167+
{ID: "5", UserID: "4", ProductIDs: []string{"3"}},
168+
},
169+
"5": {
170+
{ID: "6", UserID: "5", ProductIDs: []string{"1"}},
171+
{ID: "7", UserID: "5", ProductIDs: []string{"2"}},
172+
{ID: "8", UserID: "5", ProductIDs: []string{"3"}},
173+
},
174+
}
175+
176+
mux := http.NewServeMux()
177+
mux.HandleFunc("/api/orders", func(w http.ResponseWriter, r *http.Request) {
178+
userID := r.URL.Query().Get("user_id")
179+
orders, found := ordersDB[userID]
180+
if !found {
181+
w.WriteHeader(http.StatusNotFound)
182+
return
183+
}
184+
time.Sleep(time.Second)
185+
err := json.NewEncoder(w).Encode(orders)
186+
if err != nil {
187+
log.Fatalf("could not encode orders: %v", err)
188+
}
189+
})
190+
return mux
191+
}
192+
193+
type product struct {
194+
ID string `json:"id"`
195+
Name string `json:"name"`
196+
Price float32 `json:"price"`
197+
}
198+
199+
func productsAPI() *http.ServeMux {
200+
productsDB := map[string]product{
201+
"1": {ID: "1", Name: "Coca Cola - 2L", Price: 1.49},
202+
"2": {ID: "2", Name: "Red Bull - Original", Price: 3.99},
203+
"3": {ID: "3", Name: "Starbucks - Iced Coffee", Price: 2.25},
204+
}
205+
206+
mux := http.NewServeMux()
207+
mux.HandleFunc("/api/products", func(w http.ResponseWriter, r *http.Request) {
208+
productID := r.URL.Query().Get("id")
209+
product, found := productsDB[productID]
210+
fmt.Println("found", found)
211+
if !found {
212+
fmt.Println("here")
213+
w.WriteHeader(http.StatusNotFound)
214+
return
215+
}
216+
time.Sleep(time.Second)
217+
err := json.NewEncoder(w).Encode(product)
218+
if err != nil {
219+
log.Fatalf("could not encode products: %v", err)
220+
}
221+
})
222+
return mux
223+
}
224+
225+
type cacheEntry struct {
226+
data []byte
227+
once *sync.Once
228+
}
229+
230+
type client struct {
231+
cache map[string]*cacheEntry
232+
mu *sync.Mutex
233+
url string
234+
}
235+
236+
func (c *client) query(key, query string) []byte {
237+
c.mu.Lock()
238+
entry, found := c.cache[key]
239+
if !found {
240+
entry = &cacheEntry{once: new(sync.Once)}
241+
c.cache[key] = entry
242+
}
243+
c.mu.Unlock()
244+
245+
entry.once.Do(func() {
246+
uri := fmt.Sprintf("http://%s?%s", c.url, url.PathEscape(query))
247+
res, err := http.Get(uri)
248+
if err != nil {
249+
log.Fatalf("could not fetch data from external api %s: %v", c.url, err)
250+
}
251+
252+
if res.StatusCode >= 300 {
253+
c.mu.Lock()
254+
delete(c.cache, key)
255+
c.mu.Unlock()
256+
return
257+
}
258+
259+
entry.data, err = ioutil.ReadAll(res.Body)
260+
if err != nil {
261+
log.Fatalf("could not read response body: %v", err)
262+
}
263+
})
264+
265+
return entry.data
266+
}

Diff for: ‎mutexes/once/caching/simple/main.go

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package main
2+
3+
func main() {
4+
}

Diff for: ‎mutexes/once/once-implementation/main.go

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"sync"
6+
"sync/atomic"
7+
)
8+
9+
// sync.Once implementation
10+
// https://github.com/golang/go/blob/master/src/sync/once.go#L14
11+
type once struct {
12+
done uint32
13+
mu sync.Mutex
14+
}
15+
16+
// sync.Once.Do implementation
17+
// https://github.com/golang/go/blob/master/src/sync/once.go#L42
18+
func (o *once) Do(fn func()) {
19+
if atomic.LoadUint32(&o.done) == 0 {
20+
o.mu.Lock()
21+
defer o.mu.Unlock()
22+
if o.done == 0 {
23+
defer atomic.StoreUint32(&o.done, 1)
24+
fn()
25+
}
26+
}
27+
}
28+
29+
// try running this with the -race flag
30+
// go run -race main.go
31+
func main() {
32+
var o once
33+
f := func(i int) func() {
34+
return func() {
35+
fmt.Println("printing once:", i)
36+
}
37+
}
38+
39+
var wg sync.WaitGroup
40+
wg.Add(3)
41+
go func() {
42+
defer wg.Done()
43+
o.Do(f(1))
44+
}()
45+
go func() {
46+
defer wg.Done()
47+
o.Do(f(2))
48+
}()
49+
go func() {
50+
defer wg.Done()
51+
o.Do(f(3))
52+
}()
53+
54+
wg.Wait()
55+
}

0 commit comments

Comments
 (0)
Please sign in to comment.