source: libcfa/src/concurrency/locks.cfa@ c20533ea

ADT arm-eh ast-experimental enum forall-pointer-decay jacob/cs343-translation new-ast-unique-expr pthread-emulation qualifiedEnum stuck-waitfor-destruct
Last change on this file since c20533ea was c20533ea, checked in by Thierry Delisle <tdelisle@…>, 5 years ago

Fixed locks.hfa to no longer depend on private headers bits/algorithm.hfa and alarm.hfa

  • Property mode set to 100644
File size: 12.7 KB
Line 
1#include "locks.hfa"
2#include "kernel_private.hfa"
3
4#include <kernel.hfa>
5#include <stdlib.hfa>
6
7///////////////////////////////////////////////////////////////////
8//// info_thread
9///////////////////////////////////////////////////////////////////
10
11forall(dtype L | is_blocking_lock(L)) {
12 void ?{}( info_thread(L) & this, $thread * t ) {
13 ((Seqable &) this){};
14 this.t = t;
15 this.lock = 0p;
16 }
17
18 void ?{}( info_thread(L) & this, $thread * t, uintptr_t info ) {
19 ((Seqable &) this){};
20 this.t = t;
21 this.info = info;
22 this.lock = 0p;
23 }
24
25 void ^?{}( info_thread(L) & this ){ }
26
27 info_thread(L) *& Back( info_thread(L) * this ) {
28 return (info_thread(L) *)Back( (Seqable *)this );
29 }
30
31 info_thread(L) *& Next( info_thread(L) * this ) {
32 return (info_thread(L) *)Next( (Colable *)this );
33 }
34}
35
36///////////////////////////////////////////////////////////////////
37//// Blocking Locks
38///////////////////////////////////////////////////////////////////
39
40void ?{}( blocking_lock & this, bool multi_acquisition, bool strict_owner ) {
41 this.lock{};
42 this.blocked_threads{};
43 this.wait_count = 0;
44 this.multi_acquisition = multi_acquisition;
45 this.strict_owner = strict_owner;
46 this.owner = 0p;
47 this.recursion_count = 0;
48}
49
50void ^?{}( blocking_lock & this ) {}
51void ?{}( single_acquisition_lock & this ) {((blocking_lock &)this){ false, false };}
52void ^?{}( single_acquisition_lock & this ) {}
53void ?{}( owner_lock & this ) {((blocking_lock &)this){ true, true };}
54void ^?{}( owner_lock & this ) {}
55void ?{}( multiple_acquisition_lock & this ) {((blocking_lock &)this){ true, false };}
56void ^?{}( multiple_acquisition_lock & this ) {}
57
58void lock( blocking_lock & this ) with( this ) {
59 lock( lock __cfaabi_dbg_ctx2 );
60 if ( owner == active_thread() && !multi_acquisition) { // single acquisition lock is held by current thread
61 abort("A single acquisition lock holder attempted to reacquire the lock resulting in a deadlock.");
62 } else if ( owner != 0p && owner != active_thread() ) { // lock is held by some other thread
63 addTail( blocked_threads, *active_thread() );
64 wait_count++;
65 unlock( lock );
66 park( );
67 } else if ( owner == active_thread() && multi_acquisition ) { // multi acquisition lock is held by current thread
68 recursion_count++;
69 unlock( lock );
70 } else { // lock isn't held
71 owner = active_thread();
72 recursion_count = 1;
73 unlock( lock );
74 }
75}
76
77bool try_lock( blocking_lock & this ) with( this ) {
78 bool ret = false;
79 lock( lock __cfaabi_dbg_ctx2 );
80 if ( owner == 0p ) { // lock isn't held
81 owner = active_thread();
82 recursion_count = 1;
83 ret = true;
84 } else if ( owner == active_thread() && multi_acquisition ) { // multi acquisition lock is held by current thread
85 recursion_count++;
86 ret = true;
87 }
88 unlock( lock );
89 return ret;
90}
91
92void unlock_error_check( blocking_lock & this ) with( this ) {
93 if ( owner == 0p ){ // lock isn't held
94 abort( "There was an attempt to release a lock that isn't held" );
95 } else if ( strict_owner && owner != active_thread() ) {
96 abort( "A thread other than the owner attempted to release an owner lock" );
97 }
98}
99
100void pop_and_set_new_owner( blocking_lock & this ) with( this ) {
101 $thread * t = &dropHead( blocked_threads );
102 owner = t;
103 recursion_count = ( t ? 1 : 0 );
104 wait_count--;
105 unpark( t );
106}
107
108void unlock( blocking_lock & this ) with( this ) {
109 lock( lock __cfaabi_dbg_ctx2 );
110 unlock_error_check( this );
111 recursion_count--;
112 if ( recursion_count == 0 ) { // if recursion count is zero release lock and set new owner if one is waiting
113 pop_and_set_new_owner( this );
114 }
115 unlock( lock );
116}
117
118size_t wait_count( blocking_lock & this ) with( this ) {
119 return wait_count;
120}
121
122void set_recursion_count( blocking_lock & this, size_t recursion ) with( this ) {
123 recursion_count = recursion;
124}
125
126size_t get_recursion_count( blocking_lock & this ) with( this ) {
127 return recursion_count;
128}
129
130void on_notify( blocking_lock & this, $thread * t ) with( this ) {
131 lock( lock __cfaabi_dbg_ctx2 );
132 if ( owner != 0p ) { // lock held
133 addTail( blocked_threads, *t );
134 wait_count++;
135 unlock( lock );
136 } else { // lock not held
137 owner = t;
138 recursion_count = 1;
139 unpark( t );
140 unlock( lock );
141 }
142}
143
144void on_wait( blocking_lock & this ) with( this ) {
145 lock( lock __cfaabi_dbg_ctx2 );
146 unlock_error_check( this );
147 pop_and_set_new_owner( this );
148 unlock( lock );
149}
150
151///////////////////////////////////////////////////////////////////
152//// Overloaded routines for traits
153///////////////////////////////////////////////////////////////////
154
155// These routines are temporary until an inheritance bug is fixed
156
157void lock( single_acquisition_lock & this ){ lock( (blocking_lock &)this ); }
158void unlock( single_acquisition_lock & this ){ unlock( (blocking_lock &)this ); }
159void on_notify( single_acquisition_lock & this, struct $thread * t ){ on_notify( (blocking_lock &)this, t ); }
160void on_wait( single_acquisition_lock & this ){ on_wait( (blocking_lock &)this ); }
161void set_recursion_count( single_acquisition_lock & this, size_t recursion ){ set_recursion_count( (blocking_lock &)this, recursion ); }
162size_t get_recursion_count( single_acquisition_lock & this ){ return get_recursion_count( (blocking_lock &)this ); }
163
164void lock( owner_lock & this ){ lock( (blocking_lock &)this ); }
165void unlock( owner_lock & this ){ unlock( (blocking_lock &)this ); }
166void on_notify( owner_lock & this, struct $thread * t ){ on_notify( (blocking_lock &)this, t ); }
167void on_wait( owner_lock & this ){ on_wait( (blocking_lock &)this ); }
168void set_recursion_count( owner_lock & this, size_t recursion ){ set_recursion_count( (blocking_lock &)this, recursion ); }
169size_t get_recursion_count( owner_lock & this ){ return get_recursion_count( (blocking_lock &)this ); }
170
171void lock( multiple_acquisition_lock & this ){ lock( (blocking_lock &)this ); }
172void unlock( multiple_acquisition_lock & this ){ unlock( (blocking_lock &)this ); }
173void on_notify( multiple_acquisition_lock & this, struct $thread * t ){ on_notify( (blocking_lock &)this, t ); }
174void on_wait( multiple_acquisition_lock & this ){ on_wait( (blocking_lock &)this ); }
175void set_recursion_count( multiple_acquisition_lock & this, size_t recursion ){ set_recursion_count( (blocking_lock &)this, recursion ); }
176size_t get_recursion_count( multiple_acquisition_lock & this ){ return get_recursion_count( (blocking_lock &)this ); }
177
178
179forall(dtype L | is_blocking_lock(L)) {
180 struct alarm_node_wrap {
181 alarm_node_t alarm_node;
182 condition_variable(L) * cond;
183 info_thread(L) * i;
184 };
185
186 void ?{}( alarm_node_wrap(L) & this, Time alarm, Duration period, Alarm_Callback callback ) {
187 this.alarm_node{ callback, alarm, period };
188 }
189
190 void ^?{}( alarm_node_wrap(L) & this ) { }
191
192 void timeout_handler ( alarm_node_wrap(L) & this ) with( this ) {
193 // This condition_variable member is called from the kernel, and therefore, cannot block, but it can spin.
194 lock( cond->lock __cfaabi_dbg_ctx2 );
195
196 // this check is necessary to avoid a race condition since this timeout handler
197 // may still be called after a thread has been removed from the queue but
198 // before the alarm is unregistered
199 if ( listed(i) ) { // is thread on queue
200 i->signalled = false;
201 remove( cond->blocked_threads, *i ); // remove this thread O(1)
202 cond->count--;
203 if( i->lock ) {
204 on_notify(*i->lock, i->t); // call lock's on_notify if a lock was passed
205 } else {
206 unpark( i->t ); // otherwise wake thread
207 }
208 }
209 unlock( cond->lock );
210 }
211
212 // this casts the alarm node to our wrapped type since we used type erasure
213 void alarm_node_wrap_cast( alarm_node_t & a ) { timeout_handler( (alarm_node_wrap(L) &)a ); }
214
215}
216
217///////////////////////////////////////////////////////////////////
218//// condition variable
219///////////////////////////////////////////////////////////////////
220
221forall(dtype L | is_blocking_lock(L)) {
222
223 void ?{}( condition_variable(L) & this ){
224 this.lock{};
225 this.blocked_threads{};
226 this.count = 0;
227 }
228
229 void ^?{}( condition_variable(L) & this ){ }
230
231 void process_popped( condition_variable(L) & this, info_thread(L) & popped ) with( this ) {
232 if(&popped != 0p) {
233 popped.signalled = true;
234 count--;
235 if (popped.lock) {
236 on_notify(*popped.lock, popped.t); // if lock passed call on_notify
237 } else {
238 unpark(popped.t); // otherwise wake thread
239 }
240 }
241 }
242
243 bool notify_one( condition_variable(L) & this ) with( this ) {
244 lock( lock __cfaabi_dbg_ctx2 );
245 bool ret = !empty(blocked_threads);
246 process_popped(this, dropHead( blocked_threads ));
247 unlock( lock );
248 return ret;
249 }
250
251 bool notify_all( condition_variable(L) & this ) with(this) {
252 lock( lock __cfaabi_dbg_ctx2 );
253 bool ret = !empty(blocked_threads);
254 while( !empty(blocked_threads) ) {
255 process_popped(this, dropHead( blocked_threads ));
256 }
257 unlock( lock );
258 return ret;
259 }
260
261 uintptr_t front( condition_variable(L) & this ) with(this) {
262 return empty(blocked_threads) ? NULL : head(blocked_threads).info;
263 }
264
265 bool empty( condition_variable(L) & this ) with(this) { return empty(blocked_threads); }
266
267 int counter( condition_variable(L) & this ) with(this) { return count; }
268
269 size_t queue_and_get_recursion( condition_variable(L) & this, info_thread(L) * i ) with(this) {
270 addTail( blocked_threads, *i ); // add info_thread to waiting queue
271 count++;
272 size_t recursion_count = 0;
273 if (i->lock) { // if lock was passed get recursion count to reset to after waking thread
274 recursion_count = get_recursion_count(*i->lock);
275 on_wait( *i->lock );
276 }
277 return recursion_count;
278 }
279
280 // helper for wait()'s' with no timeout
281 void queue_info_thread( condition_variable(L) & this, info_thread(L) & i ) with(this) {
282 lock( lock __cfaabi_dbg_ctx2 );
283 size_t recursion_count = queue_and_get_recursion(this, &i);
284 unlock( lock );
285 park( ); // blocks here
286 if (i.lock) set_recursion_count(*i.lock, recursion_count); // resets recursion count here after waking
287 }
288
289 // helper for wait()'s' with a timeout
290 void queue_info_thread_timeout( condition_variable(L) & this, info_thread(L) & info, Time t ) with(this) {
291 lock( lock __cfaabi_dbg_ctx2 );
292 size_t recursion_count = queue_and_get_recursion(this, &info);
293 alarm_node_wrap(L) node_wrap = { t, 0`s, alarm_node_wrap_cast };
294 node_wrap.cond = &this;
295 node_wrap.i = &info;
296 register_self( &node_wrap.alarm_node );
297 unlock( lock );
298 park();
299 unregister_self( &node_wrap.alarm_node ); // unregisters alarm so it doesn't go off if this happens first
300 if (info.lock) set_recursion_count(*info.lock, recursion_count); // resets recursion count here after waking
301 }
302
303 void wait( condition_variable(L) & this ) with(this) {
304 info_thread( L ) i = { active_thread() };
305 queue_info_thread( this, i );
306 }
307
308 void wait( condition_variable(L) & this, uintptr_t info ) with(this) {
309 info_thread( L ) i = { active_thread(), info };
310 queue_info_thread( this, i );
311 }
312
313 bool wait( condition_variable(L) & this, Duration duration ) with(this) {
314 info_thread( L ) i = { active_thread() };
315 queue_info_thread_timeout(this, i, __kernel_get_time() + duration );
316 return i.signalled;
317 }
318
319 bool wait( condition_variable(L) & this, uintptr_t info, Duration duration ) with(this) {
320 info_thread( L ) i = { active_thread(), info };
321 queue_info_thread_timeout(this, i, __kernel_get_time() + duration );
322 return i.signalled;
323 }
324
325 bool wait( condition_variable(L) & this, Time time ) with(this) {
326 info_thread( L ) i = { active_thread() };
327 queue_info_thread_timeout(this, i, time);
328 return i.signalled;
329 }
330
331 bool wait( condition_variable(L) & this, uintptr_t info, Time time ) with(this) {
332 info_thread( L ) i = { active_thread(), info };
333 queue_info_thread_timeout(this, i, time);
334 return i.signalled;
335 }
336
337 void wait( condition_variable(L) & this, L & l ) with(this) {
338 info_thread(L) i = { active_thread() };
339 i.lock = &l;
340 queue_info_thread( this, i );
341 }
342
343 void wait( condition_variable(L) & this, L & l, uintptr_t info ) with(this) {
344 info_thread(L) i = { active_thread(), info };
345 i.lock = &l;
346 queue_info_thread( this, i );
347 }
348
349 bool wait( condition_variable(L) & this, L & l, Duration duration ) with(this) {
350 info_thread(L) i = { active_thread() };
351 i.lock = &l;
352 queue_info_thread_timeout(this, i, __kernel_get_time() + duration );
353 return i.signalled;
354 }
355
356 bool wait( condition_variable(L) & this, L & l, uintptr_t info, Duration duration ) with(this) {
357 info_thread(L) i = { active_thread(), info };
358 i.lock = &l;
359 queue_info_thread_timeout(this, i, __kernel_get_time() + duration );
360 return i.signalled;
361 }
362
363 bool wait( condition_variable(L) & this, L & l, Time time ) with(this) {
364 info_thread(L) i = { active_thread() };
365 i.lock = &l;
366 queue_info_thread_timeout(this, i, time );
367 return i.signalled;
368 }
369
370 bool wait( condition_variable(L) & this, L & l, uintptr_t info, Time time ) with(this) {
371 info_thread(L) i = { active_thread(), info };
372 i.lock = &l;
373 queue_info_thread_timeout(this, i, time );
374 return i.signalled;
375 }
376}
Note: See TracBrowser for help on using the repository browser.