source: libcfa/src/concurrency/locks.cfa@ 70f8bcd2

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

Some clean-up and format changes to make concurrency files consistent

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