Skip to content

Commit 8fba348

Browse files
authored
Merge pull request #869 from devlights/add-countdownlatch-example
2 parents 2c95941 + 00ab815 commit 8fba348

File tree

3 files changed

+187
-0
lines changed

3 files changed

+187
-0
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# https://taskfile.dev
2+
3+
version: '3'
4+
5+
tasks:
6+
default:
7+
cmds:
8+
- go run .
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package main
2+
3+
import (
4+
"sync"
5+
"sync/atomic"
6+
)
7+
8+
// CountdownLatch は、C#-のCountdownEventやJavaのCountDownLatchと同様の機能を提供する構造体です.
9+
type CountdownLatch struct {
10+
count int32
11+
mutex sync.Mutex
12+
cond *sync.Cond
13+
}
14+
15+
// NewCountdownLatch は、指定されたカウント数でCountdownLatchを初期化します.
16+
func NewCountdownLatch(initialCount int) *CountdownLatch {
17+
if initialCount < 0 {
18+
panic("初期カウントは0以上である必要があります")
19+
}
20+
21+
var (
22+
ce CountdownLatch
23+
)
24+
ce.count = int32(initialCount)
25+
ce.cond = sync.NewCond(&ce.mutex)
26+
27+
return &ce
28+
}
29+
30+
// Signal は、カウントを1減らします.
31+
// 戻り値として、カウントダウンが満了したかどうかを返します.
32+
func (me *CountdownLatch) Signal() bool {
33+
return me.SignalCount(1)
34+
}
35+
36+
// SignalCount は、指定された数だけカウントを減らします.
37+
// 戻り値として、カウントダウンが満了したかどうかを返します.
38+
func (me *CountdownLatch) SignalCount(count int) bool {
39+
if count <= 0 {
40+
return false
41+
}
42+
43+
me.mutex.Lock()
44+
defer me.mutex.Unlock()
45+
46+
newCount := atomic.AddInt32(&me.count, -int32(count))
47+
if newCount <= 0 {
48+
me.cond.Broadcast()
49+
return true
50+
}
51+
52+
return false
53+
}
54+
55+
// Wait は、カウントが0になるまでブロックします.
56+
func (me *CountdownLatch) Wait() {
57+
me.mutex.Lock()
58+
defer me.mutex.Unlock()
59+
60+
for atomic.LoadInt32(&me.count) > 0 {
61+
me.cond.Wait()
62+
}
63+
}
64+
65+
// CurrentCount は、現在のカウント値を返します.
66+
func (me *CountdownLatch) CurrentCount() int {
67+
return int(atomic.LoadInt32(&me.count))
68+
}
69+
70+
// Reset は、カウントを指定された値にリセットします.
71+
func (me *CountdownLatch) Reset(count int) {
72+
if count < 0 {
73+
panic("リセットカウントは0以上である必要があります")
74+
}
75+
76+
me.mutex.Lock()
77+
defer me.mutex.Unlock()
78+
79+
atomic.StoreInt32(&me.count, int32(count))
80+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"errors"
6+
"log"
7+
"sync"
8+
"time"
9+
)
10+
11+
const (
12+
MainTimeout = 20 * time.Second
13+
ProcTimeout = 10 * time.Second
14+
)
15+
16+
var (
17+
ErrMainTooSlow = errors.New("(MAIN) TOO SLOW")
18+
ErrProcTooSlow = errors.New("(PROC) TOO SLOW")
19+
)
20+
21+
func init() {
22+
log.SetFlags(0)
23+
}
24+
25+
func main() {
26+
var (
27+
rootCtx = context.Background()
28+
mainCtx, mainCxl = context.WithTimeoutCause(rootCtx, MainTimeout, ErrMainTooSlow)
29+
procCtx = run(mainCtx)
30+
err error
31+
)
32+
defer mainCxl()
33+
34+
select {
35+
case <-mainCtx.Done():
36+
err = context.Cause(mainCtx)
37+
case <-procCtx.Done():
38+
if err = context.Cause(procCtx); errors.Is(err, context.Canceled) {
39+
err = nil
40+
}
41+
}
42+
43+
if err != nil {
44+
log.Fatal(err)
45+
}
46+
}
47+
48+
func run(pCtx context.Context) context.Context {
49+
var (
50+
ctx, cxl = context.WithCancelCause(pCtx)
51+
)
52+
53+
go func() {
54+
cxl(proc(ctx))
55+
}()
56+
go func() {
57+
<-time.After(ProcTimeout)
58+
cxl(ErrProcTooSlow)
59+
}()
60+
61+
return ctx
62+
}
63+
64+
func proc(_ context.Context) error {
65+
const (
66+
latchCount = 3
67+
)
68+
var (
69+
ce = NewCountdownLatch(latchCount)
70+
wg sync.WaitGroup
71+
)
72+
73+
for range 2 {
74+
ce.Reset(latchCount)
75+
76+
for i := range 5 {
77+
wg.Add(1)
78+
go func(i int) {
79+
defer wg.Done()
80+
81+
log.Printf("[%2d] 待機開始", i)
82+
ce.Wait()
83+
log.Printf("[%2d] 待機解除", i)
84+
}(i)
85+
}
86+
87+
for range 3 {
88+
<-time.After(time.Second)
89+
90+
log.Printf("現在のカウント: %d\n", ce.CurrentCount())
91+
ce.Signal()
92+
}
93+
94+
wg.Wait()
95+
log.Println("-------------------------------------")
96+
}
97+
98+
return nil
99+
}

0 commit comments

Comments
 (0)