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
Line 
1#include "locks.hfa"
2#include "kernel_private.hfa"
3
4#include <kernel.hfa>
5#include <stdlib.hfa>
6
7//-----------------------------------------------------------------------------
8// info_thread
9forall(dtype L | is_blocking_lock(L)) {
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 };
26
27 void ?{}( info_thread(L) & this, $thread * t, uintptr_t info, L * l ) {
28 ((Seqable &) this){};
29 this.t = t;
30 this.info = info;
31 this.lock = l;
32 }
33
34 void ^?{}( info_thread(L) & this ) {}
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 }
43}
44
45//-----------------------------------------------------------------------------
46// Blocking Locks
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
57void ^?{}( blocking_lock & this ) {}
58void ?{}( single_acquisition_lock & this ) {((blocking_lock &)this){ false, false };}
59void ^?{}( single_acquisition_lock & this ) {}
60void ?{}( owner_lock & this ) {((blocking_lock &)this){ true, true };}
61void ^?{}( owner_lock & this ) {}
62void ?{}( multiple_acquisition_lock & this ) {((blocking_lock &)this){ true, false };}
63void ^?{}( multiple_acquisition_lock & this ) {}
64
65void lock( blocking_lock & this ) with( this ) {
66 lock( lock __cfaabi_dbg_ctx2 );
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 );
75 wait_count++;
76 unlock( lock );
77 park( );
78 }
79 // multi acquisition lock is held by current thread
80 else if ( owner == thrd && multi_acquisition ) {
81 recursion_count++;
82 unlock( lock );
83 }
84 // lock isn't held
85 else {
86 owner = thrd;
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 );
95
96 // lock isn't held
97 if ( owner == 0p ) {
98 owner = active_thread();
99 recursion_count = 1;
100 ret = true;
101 }
102 // multi acquisition lock is held by current thread
103 else if ( owner == active_thread() && multi_acquisition ) {
104 recursion_count++;
105 ret = true;
106 }
107
108 unlock( lock );
109 return ret;
110}
111
112void pop_and_set_new_owner( blocking_lock & this ) with( this ) {
113 $thread * t = &dropHead( blocked_threads );
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 );
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
126 recursion_count--;
127 if ( recursion_count == 0 ) {
128 pop_and_set_new_owner( this );
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
145void on_notify( blocking_lock & this, $thread * t ) with( this ) {
146 lock( lock __cfaabi_dbg_ctx2 );
147 // lock held
148 if ( owner != 0p ) {
149 addTail( blocked_threads, *t );
150 wait_count++;
151 unlock( lock );
152 }
153 // lock not held
154 else {
155 owner = t;
156 recursion_count = 1;
157 unpark( t );
158 unlock( lock );
159 }
160}
161
162void on_wait( blocking_lock & this ) with( this ) {
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
167 pop_and_set_new_owner( this );
168 unlock( lock );
169}
170
171//-----------------------------------------------------------------------------
172// Overloaded routines for traits
173// These routines are temporary until an inheritance bug is fixed
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 ); }
193size_t get_recursion_count( multiple_acquisition_lock & this ){ return get_recursion_count( (blocking_lock &)this ); }
194
195//-----------------------------------------------------------------------------
196// alarm node wrapper
197forall(dtype L | is_blocking_lock(L)) {
198 struct alarm_node_wrap {
199 alarm_node_t alarm_node;
200 condition_variable(L) * cond;
201 info_thread(L) * i;
202 };
203
204 void ?{}( alarm_node_wrap(L) & this, Time alarm, Duration period, Alarm_Callback callback, condition_variable(L) * c, info_thread(L) * i ) {
205 this.alarm_node{ callback, alarm, period };
206 this.cond = c;
207 this.i = i;
208 }
209
210 void ^?{}( alarm_node_wrap(L) & this ) { }
211
212 void timeout_handler ( alarm_node_wrap(L) & this ) with( this ) {
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 );
223 cond->count--;
224 if( i->lock ) {
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 );
233 }
234
235 // this casts the alarm node to our wrapped type since we used type erasure
236 void alarm_node_wrap_cast( alarm_node_t & a ) { timeout_handler( (alarm_node_wrap(L) &)a ); }
237}
238
239//-----------------------------------------------------------------------------
240// condition variable
241forall(dtype L | is_blocking_lock(L)) {
242
243 void ?{}( condition_variable(L) & this ){
244 this.lock{};
245 this.blocked_threads{};
246 this.count = 0;
247 }
248
249 void ^?{}( condition_variable(L) & this ){ }
250
251 void process_popped( condition_variable(L) & this, info_thread(L) & popped ) with( this ) {
252 if(&popped != 0p) {
253 popped.signalled = true;
254 count--;
255 if (popped.lock) {
256 // if lock passed call on_notify
257 on_notify(*popped.lock, popped.t);
258 } else {
259 // otherwise wake thread
260 unpark(popped.t);
261 }
262 }
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 ));
269 unlock( lock );
270 return ret;
271 }
272
273 bool notify_all( condition_variable(L) & this ) with(this) {
274 lock( lock __cfaabi_dbg_ctx2 );
275 bool ret = !empty(blocked_threads);
276 while( !empty(blocked_threads) ) {
277 process_popped(this, dropHead( blocked_threads ));
278 }
279 unlock( lock );
280 return ret;
281 }
282
283 uintptr_t front( condition_variable(L) & this ) with(this) {
284 return empty(blocked_threads) ? NULL : head(blocked_threads).info;
285 }
286
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; }
290
291 size_t queue_and_get_recursion( condition_variable(L) & this, info_thread(L) * i ) with(this) {
292 // add info_thread to waiting queue
293 addTail( blocked_threads, *i );
294 count++;
295 size_t recursion_count = 0;
296 if (i->lock) {
297 // if lock was passed get recursion count to reset to after waking thread
298 recursion_count = get_recursion_count(*i->lock);
299 on_wait( *i->lock );
300 }
301 return recursion_count;
302 }
303
304 // helper for wait()'s' with no timeout
305 void queue_info_thread( condition_variable(L) & this, info_thread(L) & i ) with(this) {
306 lock( lock __cfaabi_dbg_ctx2 );
307 size_t recursion_count = queue_and_get_recursion(this, &i);
308 unlock( lock );
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);
315 }
316
317 #define WAIT( u, l ) \
318 info_thread( L ) i = { active_thread(), u, l }; \
319 queue_info_thread( this, i );
320
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 );
324 size_t recursion_count = queue_and_get_recursion(this, &info);
325 alarm_node_wrap(L) node_wrap = { t, 0`s, alarm_node_wrap_cast, &this, &info };
326 register_self( &node_wrap.alarm_node );
327 unlock( lock );
328
329 // blocks here
330 park();
331
332 // unregisters alarm so it doesn't go off if this happens first
333 unregister_self( &node_wrap.alarm_node );
334
335 // resets recursion count here after waking
336 if (info.lock) set_recursion_count(*info.lock, recursion_count);
337 }
338
339 #define WAIT_TIME( u, l, t ) \
340 info_thread( L ) i = { active_thread(), u, l }; \
341 queue_info_thread_timeout(this, i, t ); \
342 return i.signalled;
343
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 ) }
357}
Note: See TracBrowser for help on using the repository browser.