5
5
package runtime
6
6
7
7
import (
8
+ "internal/task"
9
+ "sync"
8
10
"sync/atomic"
9
- "unsafe"
10
11
)
11
12
12
13
// This file contains stub implementations for internal/poll.
@@ -24,22 +25,35 @@ import (
24
25
// This means we assume the following constant settings from the golang standard
25
26
// library: lifo=false,profile=semaBlock,skipframe=0,reason=waitReasonSemaquire
26
27
27
- type semaRoot struct {
28
- nwait atomic.Uint32
28
+ // The global state of the semaphore table.
29
+ // Semaphores are identified by their address.
30
+ // The table maps the address to the task that is currently holding the semaphore.
31
+ // The table is protected by a mutex.
32
+ // When a task acquires a semaphore, the mapping is added to the map.
33
+ // When a task releases a semaphore, the mapping is removed from the map.
34
+ //
35
+ // The table is used to implement the cansemacquire function.
36
+ // The cansemacquire function is called by the semacquire function.
37
+ // The cansemacquire function checks if the semaphore is available.
38
+ // If the semaphore is available, the function returns true.
39
+ // If the semaphore is not available, the function returns false.
40
+ type semTable struct {
41
+ table map [* uint32 ]* task.Task
42
+ lock sync.Mutex
29
43
}
30
44
31
45
var semtable semTable
32
46
33
- // Prime to not correlate with any user patterns.
34
- const semTabSize = 251
47
+ func init () {
48
+ semtable .table = make (map [* uint32 ]* task.Task )
49
+ }
35
50
36
- type semTable [semTabSize ]struct {
37
- root semaRoot
38
- pad [64 - unsafe .Sizeof (semaRoot {})]byte // only 64 x86_64, make this variable
51
+ func (s * semTable ) Lock () {
52
+ s .lock .Lock ()
39
53
}
40
54
41
- func (t * semTable ) rootFor ( addr * uint32 ) * semaRoot {
42
- return & t [( uintptr ( unsafe . Pointer ( addr )) >> 3 ) % semTabSize ]. root
55
+ func (s * semTable ) Unlock () {
56
+ s . lock . Unlock ()
43
57
}
44
58
45
59
//go:linkname semacquire internal/poll.runtime_Semacquire
@@ -51,22 +65,37 @@ func semacquire(sema *uint32) {
51
65
52
66
// Copied from src/runtime/sema.go
53
67
func cansemacquire (addr * uint32 ) bool {
54
- for {
55
- v := atomic .LoadUint32 (addr )
56
- if v == 0 {
57
- return false
58
- }
59
- if atomic .CompareAndSwapUint32 (addr , v , v - 1 ) {
60
- return true
61
- }
68
+ // Busy Looping until a lookup to the global semaphore table can be made
69
+ semtable .Lock ()
70
+
71
+ if _ , ok := semtable .table [addr ]; ! ok {
72
+ semtable .table [addr ] = task .Current ()
73
+ semtable .Unlock ()
74
+ return true
75
+ }
76
+
77
+ v := atomic .LoadUint32 (addr )
78
+ if v == 0 {
79
+ semtable .Unlock ()
80
+ return false
81
+ }
82
+ if atomic .CompareAndSwapUint32 (addr , v , v - 1 ) {
83
+ semtable .Unlock ()
84
+ return true
62
85
}
86
+ return true
63
87
}
64
88
65
89
//go:linkname semrelease internal/poll.runtime_Semrelease
66
90
func semrelease (sema * uint32 ) {
67
- root := semtable . rootFor ( sema )
68
- atomic . AddUint32 ( sema , 1 )
69
- if root . nwait . Load () == 0 {
70
- return
91
+ // Check if the semaphore is in the table
92
+ semtable . Lock ( )
93
+ if _ , ok := semtable . table [ sema ]; ! ok {
94
+ panic ( "invalid semaphore" )
71
95
}
96
+
97
+ atomic .AddUint32 (sema , 1 )
98
+ semtable .Unlock ()
99
+
100
+ Gosched ()
72
101
}
0 commit comments