aboutsummaryrefslogtreecommitdiff
path: root/core/sync/primitives.odin
blob: 00d7812a8b24dfd4540529b325a5a2a8f962695f (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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
package sync

import "core:runtime"
import "core:time"

current_thread_id :: proc "contextless" () -> int {
	return _current_thread_id()
}

// A Mutex is a mutual exclusion lock
// The zero value for a Mutex is an unlocked mutex
//
// A Mutex must not be copied after first use
Mutex :: struct {
	impl: _Mutex,
}

// mutex_lock locks m
mutex_lock :: proc "contextless" (m: ^Mutex) {
	_mutex_lock(m)
}

// mutex_unlock unlocks m
mutex_unlock :: proc "contextless" (m: ^Mutex) {
	_mutex_unlock(m)
}

// mutex_try_lock tries to lock m, will return true on success, and false on failure
mutex_try_lock :: proc "contextless" (m: ^Mutex) -> bool {
	return _mutex_try_lock(m)
}

/*
Example:
	if mutex_guard(&m) {
		...
	}
*/
@(deferred_in=mutex_unlock)
mutex_guard :: proc "contextless" (m: ^Mutex) -> bool {
	mutex_lock(m)
	return true
}

// A RW_Mutex is a reader/writer mutual exclusion lock
// The lock can be held by any arbitrary number of readers or a single writer
// The zero value for a RW_Mutex is an unlocked mutex
//
// A RW_Mutex must not be copied after first use
RW_Mutex :: struct {
	impl: _RW_Mutex,
}

// rw_mutex_lock locks rw for writing (with a single writer)
// If the mutex is already locked for reading or writing, the mutex blocks until the mutex is available.
rw_mutex_lock :: proc "contextless" (rw: ^RW_Mutex) {
	_rw_mutex_lock(rw)
}

// rw_mutex_unlock unlocks rw for writing (with a single writer)
rw_mutex_unlock :: proc "contextless" (rw: ^RW_Mutex) {
	_rw_mutex_unlock(rw)
}

// rw_mutex_try_lock tries to lock rw for writing (with a single writer)
rw_mutex_try_lock :: proc "contextless" (rw: ^RW_Mutex) -> bool {
	return _rw_mutex_try_lock(rw)
}

// rw_mutex_shared_lock locks rw for reading (with arbitrary number of readers)
rw_mutex_shared_lock :: proc "contextless" (rw: ^RW_Mutex) {
	_rw_mutex_shared_lock(rw)
}

// rw_mutex_shared_unlock unlocks rw for reading (with arbitrary number of readers)
rw_mutex_shared_unlock :: proc "contextless" (rw: ^RW_Mutex) {
	_rw_mutex_shared_unlock(rw)
}

// rw_mutex_try_shared_lock tries to lock rw for reading (with arbitrary number of readers)
rw_mutex_try_shared_lock :: proc "contextless" (rw: ^RW_Mutex) -> bool {
	return _rw_mutex_try_shared_lock(rw)
}
/*
Example:
	if rw_mutex_guard(&m) {
		...
	}
*/
@(deferred_in=rw_mutex_unlock)
rw_mutex_guard :: proc "contextless" (m: ^RW_Mutex) -> bool {
	rw_mutex_lock(m)
	return true
}

/*
Example:
	if rw_mutex_shared_guard(&m) {
		...
	}
*/
@(deferred_in=rw_mutex_shared_unlock)
rw_mutex_shared_guard :: proc "contextless" (m: ^RW_Mutex) -> bool {
	rw_mutex_shared_lock(m)
	return true
}



// A Recursive_Mutex is a recursive mutual exclusion lock
// The zero value for a Recursive_Mutex is an unlocked mutex
//
// A Recursive_Mutex must not be copied after first use
Recursive_Mutex :: struct {
	impl: _Recursive_Mutex,
}

recursive_mutex_lock :: proc "contextless" (m: ^Recursive_Mutex) {
	_recursive_mutex_lock(m)
}

recursive_mutex_unlock :: proc "contextless" (m: ^Recursive_Mutex) {
	_recursive_mutex_unlock(m)
}

recursive_mutex_try_lock :: proc "contextless" (m: ^Recursive_Mutex) -> bool {
	return _recursive_mutex_try_lock(m)
}

/*
Example:
	if recursive_mutex_guard(&m) {
		...
	}
*/
@(deferred_in=recursive_mutex_unlock)
recursive_mutex_guard :: proc "contextless" (m: ^Recursive_Mutex) -> bool {
	recursive_mutex_lock(m)
	return true
}


// Cond implements a condition variable, a rendezvous point for threads
// waiting for signalling the occurence of an event
//
// A Cond must not be copied after first use
Cond :: struct {
	impl: _Cond,
}

cond_wait :: proc "contextless" (c: ^Cond, m: ^Mutex) {
	_cond_wait(c, m)
}

cond_wait_with_timeout :: proc "contextless" (c: ^Cond, m: ^Mutex, duration: time.Duration) -> bool {
	if duration <= 0 {
		return false
	}
	return _cond_wait_with_timeout(c, m, duration)
}

cond_signal :: proc "contextless" (c: ^Cond) {
	_cond_signal(c)
}

cond_broadcast :: proc "contextless" (c: ^Cond) {
	_cond_broadcast(c)
}


// When waited upon, blocks until the internal count is greater than zero, then subtracts one.
// Posting to the semaphore increases the count by one, or the provided amount.
//
// A Sema must not be copied after first use
Sema :: struct {
	impl: _Sema,
}

sema_post :: proc "contextless" (s: ^Sema, count := 1) {
	_sema_post(s, count)
}

sema_wait :: proc "contextless" (s: ^Sema) {
	_sema_wait(s)
}

sema_wait_with_timeout :: proc "contextless" (s: ^Sema, duration: time.Duration) -> bool {
	return _sema_wait_with_timeout(s, duration)
}



// Futex is a fast userspace mutual exclusion lock, using a 32-bit memory address as a hint
// 
// An Futex must not be copied after first use
Futex :: distinct u32

futex_wait :: proc "contextless" (f: ^Futex, expected: u32) {
	if u32(atomic_load_explicit(f, .Acquire)) != expected {
		return
	}
	
	_assert(_futex_wait(f, expected), "futex_wait failure")
}

// returns true if the wait happened within the duration, false if it exceeded the time duration
futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, duration: time.Duration) -> bool {
	if u32(atomic_load_explicit(f, .Acquire)) != expected {
		return true
	}
	if duration <= 0 {
		return false
	}	
	
	return _futex_wait_with_timeout(f, expected, duration)
}

futex_signal :: proc "contextless" (f: ^Futex) {
	_futex_signal(f)
}

futex_broadcast :: proc "contextless" (f: ^Futex) {
	_futex_broadcast(f)
}


@(private)
_assert :: proc "contextless" (cond: bool, msg: string) {
	if !cond {
		_panic(msg)
	}
}

@(private)
_panic :: proc "contextless" (msg: string) -> ! {
	runtime.print_string(msg)
	runtime.print_byte('\n')
	runtime.trap()
}