Skip to content

Commit fc2b5d5

Browse files
committed
add sync.Cond
1 parent f10da24 commit fc2b5d5

File tree

2 files changed

+85
-0
lines changed

2 files changed

+85
-0
lines changed

src/sync/cond.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package sync
2+
3+
import "internal/task"
4+
5+
type Cond struct {
6+
L Locker
7+
8+
unlocking *earlySignal
9+
blocked task.Stack
10+
}
11+
12+
// earlySignal is a type used to implement a stack for signalling waiters while they are unlocking.
13+
type earlySignal struct {
14+
next *earlySignal
15+
16+
signaled bool
17+
}
18+
19+
func (c *Cond) trySignal() bool {
20+
// Pop a blocked task off of the stack, and schedule it if applicable.
21+
t := c.blocked.Pop()
22+
if t != nil {
23+
scheduleTask(t)
24+
return true
25+
}
26+
27+
// If there any tasks which are currently unlocking, signal one.
28+
if c.unlocking != nil {
29+
c.unlocking.signaled = true
30+
c.unlocking = c.unlocking.next
31+
return true
32+
}
33+
34+
// There was nothing to signal.
35+
return false
36+
}
37+
38+
func (c *Cond) Signal() {
39+
c.trySignal()
40+
}
41+
42+
func (c *Cond) Broadcast() {
43+
// Signal everything.
44+
for c.trySignal() {
45+
}
46+
}
47+
48+
func (c *Cond) Wait() {
49+
// Add an earlySignal frame to the stack so we can be signalled while unlocking.
50+
early := earlySignal{
51+
next: c.unlocking,
52+
}
53+
c.unlocking = &early
54+
55+
// Temporarily unlock L.
56+
c.L.Unlock()
57+
defer c.L.Lock()
58+
59+
// If we were signaled while unlocking, immediately complete.
60+
if early.signaled {
61+
return
62+
}
63+
64+
// Remove the earlySignal frame.
65+
if c.unlocking == &early {
66+
c.unlocking = early.next
67+
} else {
68+
// Something else happened after the unlock - the earlySignal is somewhere in the middle.
69+
// This would be faster but less space-efficient if it were a doubly linked list.
70+
prev := c.unlocking
71+
for prev.next != &early {
72+
prev = prev.next
73+
}
74+
prev.next = early.next
75+
}
76+
77+
// Wait for a signal.
78+
c.blocked.Push(task.Current())
79+
task.Pause()
80+
}

src/sync/mutex.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,8 @@ func (rw *RWMutex) RUnlock() {
6969
rw.m.Unlock()
7070
}
7171
}
72+
73+
type Locker interface {
74+
Lock()
75+
Unlock()
76+
}

0 commit comments

Comments
 (0)