aboutsummaryrefslogtreecommitdiff
path: root/core/sync/futex_darwin.odin
blob: 2b104fed609e83b27c8a7def579486009e042b0a (plain)
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")
		}
	}

	}
}