1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
|
#+private
#+build darwin
package sync
import "core:c"
import "core:sys/darwin"
import "core:time"
foreign import System "system:System"
foreign System {
// __ulock_wait is not available on 10.15
// See https://github.com/odin-lang/Odin/issues/1959
__ulock_wait :: proc "c" (operation: u32, addr: rawptr, value: u64, timeout_us: u32) -> c.int ---
// >= MacOS 11.
__ulock_wait2 :: proc "c" (operation: u32, addr: rawptr, value: u64, timeout_ns: u64, value2: u64) -> c.int ---
__ulock_wake :: proc "c" (operation: u32, addr: rawptr, wake_value: u64) -> c.int ---
}
UL_COMPARE_AND_WAIT :: 1
ULF_WAKE_ALL :: 0x00000100
ULF_NO_ERRNO :: 0x01000000
ENOENT :: -2
EINTR :: -4
EFAULT :: -14
ETIMEDOUT :: -60
_futex_wait :: proc "contextless" (f: ^Futex, expected: u32) -> bool {
return _futex_wait_with_timeout(f, expected, 0)
}
_futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, duration: time.Duration) -> bool {
when darwin.WAIT_ON_ADDRESS_AVAILABLE {
s: i32
if duration > 0 {
s = darwin.os_sync_wait_on_address_with_timeout(f, u64(expected), size_of(Futex), {}, .MACH_ABSOLUTE_TIME, u64(duration))
} else {
s = darwin.os_sync_wait_on_address(f, u64(expected), size_of(Futex), {})
}
if s >= 0 {
return true
}
switch darwin.errno() {
case -EINTR, -EFAULT:
return true
case -ETIMEDOUT:
return false
case:
panic_contextless("darwin.os_sync_wait_on_address_with_timeout failure")
}
} else {
when darwin.ULOCK_WAIT_2_AVAILABLE {
timeout_ns := u64(duration)
s := __ulock_wait2(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, f, u64(expected), timeout_ns, 0)
} else {
timeout_us := u32(duration / time.Microsecond)
s := __ulock_wait(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, f, u64(expected), timeout_us)
}
if s >= 0 {
return true
}
switch s {
case EINTR, EFAULT:
return true
case ETIMEDOUT:
return false
case:
panic_contextless("futex_wait failure")
}
return true
}
}
_futex_signal :: proc "contextless" (f: ^Futex) {
when darwin.WAIT_ON_ADDRESS_AVAILABLE {
loop: for {
s := darwin.os_sync_wake_by_address_any(f, size_of(Futex), {})
if s >= 0 {
return
}
switch darwin.errno() {
case -EINTR, -EFAULT:
continue loop
case -ENOENT:
return
case:
panic_contextless("darwin.os_sync_wake_by_address_any failure")
}
}
} else {
loop: for {
s := __ulock_wake(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, f, 0)
if s >= 0 {
return
}
switch s {
case EINTR, EFAULT:
continue loop
case ENOENT:
return
case:
panic_contextless("futex_wake_single failure")
}
}
}
}
_futex_broadcast :: proc "contextless" (f: ^Futex) {
when darwin.WAIT_ON_ADDRESS_AVAILABLE {
loop: for {
s := darwin.os_sync_wake_by_address_all(f, size_of(Futex), {})
if s >= 0 {
return
}
switch darwin.errno() {
case -EINTR, -EFAULT:
continue loop
case -ENOENT:
return
case:
panic_contextless("darwin.os_sync_wake_by_address_all failure")
}
}
} else {
loop: for {
s := __ulock_wake(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO | ULF_WAKE_ALL, f, 0)
if s >= 0 {
return
}
switch s {
case EINTR, EFAULT:
continue loop
case ENOENT:
return
case:
panic_contextless("futex_wake_all failure")
}
}
}
}
|