source: libcfa/src/concurrency/locks.cfa@ 5465377c

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 5465377c was 3959595, checked in by Thierry Delisle <tdelisle@…>, 5 years ago

Merge branch 'master' of plg.uwaterloo.ca:software/cfa/cfa-cc

  • Property mode set to 100644
File size: 11.5 KB
Line 
1#include "locks.hfa"
2#include "kernel_private.hfa"
3#include <stdlib.h>
4#include <stdio.h>
5
6#include <kernel.hfa>
7#include <stdlib.hfa>
8#include <thread.hfa>
9
10///////////////////////////////////////////////////////////////////
11//// info_thread
12///////////////////////////////////////////////////////////////////
13forall(dtype L | is_blocking_lock(L)) {
14 void ?{}( info_thread(L) & this, $thread * t ) {
15 this.t = t;
16 this.lock = 0p;
17 this.listed = false;
18 }
19
20 void ?{}( info_thread(L) & this, $thread * t, uintptr_t info ) {
21 this.t = t;
22 this.info = info;
23 this.lock = 0p;
24 this.listed = false;
25 }
26
27 void ^?{}( info_thread(L) & this ){
28 // default
29 }
30
31 info_thread(L) *& get_next( info_thread(L) & this ) {
32 return this.next;
33 }
34}
35///////////////////////////////////////////////////////////////////
36//// Blocking Locks
37///////////////////////////////////////////////////////////////////
38
39void ?{}( blocking_lock & this, bool multi_acquisition, bool strict_owner ) {
40 this.lock{};
41 this.blocked_threads{};
42 this.wait_count = 0;
43 this.multi_acquisition = multi_acquisition;
44 this.strict_owner = strict_owner;
45 this.owner = 0p;
46 this.recursion_count = 0;
47}
48
49void ^?{}( blocking_lock & this ) {
50 // default
51}
52
53void ?{}( mutex_lock & this ) {
54 ((blocking_lock &)this){ false, false };
55}
56
57void ^?{}( mutex_lock & this ) {
58 // default
59}
60
61void ?{}( owner_lock & this ) {
62 ((blocking_lock &)this){ true, true };
63}
64
65void ^?{}( owner_lock & this ) {
66 // default
67}
68
69void ?{}( recursive_mutex_lock & this ) {
70 ((blocking_lock &)this){ true, false };
71}
72
73void ^?{}( recursive_mutex_lock & this ) {
74 // default
75}
76
77void lock( blocking_lock & this ) with( this ) {
78 $thread * thrd = active_thread();
79 lock( lock __cfaabi_dbg_ctx2 );
80 if ( owner == thrd && !multi_acquisition) {
81 fprintf(stderr, "A single acquisition lock holder attempted to reacquire the lock resulting in a deadlock."); // Possibly throw instead
82 exit(EXIT_FAILURE);
83 } else if ( owner != 0p && owner != thrd ) {
84 append( blocked_threads, thrd );
85 wait_count++;
86 unlock( lock );
87 park( );
88 } else if ( owner == thrd && multi_acquisition ) {
89 recursion_count++;
90 unlock( lock );
91 } else {
92 owner = thrd;
93 recursion_count = 1;
94 unlock( lock );
95 }
96}
97
98bool try_lock( blocking_lock & this ) with( this ) {
99 $thread * thrd = active_thread();
100 bool ret = false;
101 lock( lock __cfaabi_dbg_ctx2 );
102 if ( owner == 0p ) {
103 owner = thrd;
104 if ( multi_acquisition ) recursion_count = 1;
105 ret = true;
106 } else if ( owner == thrd && multi_acquisition ) {
107 recursion_count++;
108 ret = true;
109 }
110 unlock( lock );
111 return ret;
112}
113
114void unlock( blocking_lock & this ) with( this ) {
115 lock( lock __cfaabi_dbg_ctx2 );
116 if ( owner == 0p ){ // no owner implies lock isn't held
117 fprintf( stderr, "There was an attempt to release a lock that isn't held" );
118 return;
119 } else if ( strict_owner && active_thread() ) {
120 fprintf( stderr, "A thread other than the owner attempted to release an owner lock" );
121 return;
122 }
123 recursion_count--;
124 if ( recursion_count == 0 ) {
125 $thread * thrd = pop_head( blocked_threads );
126 owner = thrd;
127 recursion_count = ( thrd && multi_acquisition ? 1 : 0 );
128 wait_count--;
129 unpark( thrd );
130 }
131 unlock( lock );
132}
133
134size_t wait_count( blocking_lock & this ) with( this ) {
135 return wait_count;
136}
137
138
139void set_recursion_count( blocking_lock & this, size_t recursion ) with( this ) {
140 recursion_count = recursion;
141}
142
143size_t get_recursion_count( blocking_lock & this ) with( this ) {
144 return recursion_count;
145}
146
147void add_( blocking_lock & this, $thread * t ) with( this ) {
148 lock( lock __cfaabi_dbg_ctx2 );
149 if ( owner != 0p ) {
150 append( blocked_threads, t );
151 wait_count++;
152 unlock( lock );
153 } else {
154 owner = t;
155 if ( multi_acquisition ) recursion_count = 1;
156 #if !defined( __CFA_NO_STATISTICS__ )
157 kernelTLS.this_stats = t->curr_cluster->stats;
158 #endif
159 unpark( t );
160 unlock( lock );
161 }
162}
163
164void remove_( blocking_lock & this ) with( this ) {
165 lock( lock __cfaabi_dbg_ctx2 );
166 if ( owner == 0p ){ // no owner implies lock isn't held
167 fprintf( stderr, "A lock that is not held was passed to a synchronization lock" );
168 } else if ( strict_owner && active_thread() ) {
169 fprintf( stderr, "A thread other than the owner of a lock passed it to a synchronization lock" );
170 } else {
171 $thread * thrd = pop_head( blocked_threads );
172 owner = thrd;
173 recursion_count = ( thrd && multi_acquisition ? 1 : 0 );
174 wait_count--;
175 unpark( thrd );
176 }
177 unlock( lock );
178}
179
180///////////////////////////////////////////////////////////////////
181//// Overloaded routines for traits
182///////////////////////////////////////////////////////////////////
183
184// This is temporary until an inheritance bug is fixed
185
186void lock( mutex_lock & this ){
187 lock( (blocking_lock &)this );
188}
189
190void unlock( mutex_lock & this ){
191 unlock( (blocking_lock &)this );
192}
193
194void add_( mutex_lock & this, struct $thread * t ){
195 add_( (blocking_lock &)this, t );
196}
197
198void remove_( mutex_lock & this ){
199 remove_( (blocking_lock &)this );
200}
201
202void set_recursion_count( mutex_lock & this, size_t recursion ){
203 set_recursion_count( (blocking_lock &)this, recursion );
204}
205
206size_t get_recursion_count( mutex_lock & this ){
207 get_recursion_count( (blocking_lock &)this );
208}
209
210void lock( recursive_mutex_lock & this ){
211 lock( (blocking_lock &)this );
212}
213
214void unlock( recursive_mutex_lock & this ){
215 unlock( (blocking_lock &)this );
216}
217
218void add_( recursive_mutex_lock & this, struct $thread * t ){
219 add_( (blocking_lock &)this, t );
220}
221
222void remove_( recursive_mutex_lock & this ){
223 remove_( (blocking_lock &)this );
224}
225
226void set_recursion_count( recursive_mutex_lock & this, size_t recursion ){
227 set_recursion_count( (blocking_lock &)this, recursion );
228}
229
230size_t get_recursion_count( recursive_mutex_lock & this ){
231 get_recursion_count( (blocking_lock &)this );
232}
233
234///////////////////////////////////////////////////////////////////
235//// condition variable
236///////////////////////////////////////////////////////////////////
237
238forall(dtype L | is_blocking_lock(L)) {
239
240 void timeout_handler ( alarm_node_wrap(L) & this ) with( this ) {
241 // This condition_variable member is called from the kernel, and therefore, cannot block, but it can spin.
242 lock( cond->lock __cfaabi_dbg_ctx2 );
243 if ( (*i)->listed ) { // is thread on queue
244 info_thread(L) * copy = *i;
245 remove( cond->blocked_threads, i ); //remove this thread O(1)
246 cond->wait_count--;
247 if( !copy->lock ) {
248 unlock( cond->lock );
249 #if !defined( __CFA_NO_STATISTICS__ )
250 #warning unprotected access to tls TODO discuss this
251 kernelTLS.this_stats = copy->t->curr_cluster->stats;
252 #endif
253 unpark( copy->t );
254 } else {
255 add_(*copy->lock, copy->t); // call lock's add_
256 }
257 }
258 unlock( cond->lock );
259 }
260
261 void alarm_node_wrap_cast( alarm_node_t & a ) {
262 timeout_handler( (alarm_node_wrap(L) &)a );
263 }
264
265 void ?{}( condition_variable(L) & this ){
266 this.lock{};
267 this.blocked_threads{};
268 this.count = 0;
269 }
270
271 void ^?{}( condition_variable(L) & this ){
272 // default
273 }
274
275 void ?{}( alarm_node_wrap(L) & this, $thread * thrd, Time alarm, Duration period, Alarm_Callback callback ) {
276 this.alarm_node{ thrd, alarm, period, callback };
277 }
278
279 void ^?{}( alarm_node_wrap(L) & this ) {
280 // default
281 }
282
283 bool notify_one( condition_variable(L) & this ) with( this ) {
284 lock( lock __cfaabi_dbg_ctx2 );
285 bool ret = !!blocked_threads;
286 info_thread(L) * popped = pop_head( blocked_threads );
287 popped->listed = false;
288 if(popped != 0p) {
289 count--;
290 if (popped->lock) {
291 add_(*popped->lock, popped->t);
292 } else {
293 unpark(popped->t);
294 }
295 }
296 unlock( lock );
297 return ret;
298 }
299
300 bool notify_all( condition_variable(L) & this ) with(this) {
301 lock( lock __cfaabi_dbg_ctx2 );
302 bool ret = blocked_threads ? true : false;
303 while( blocked_threads ) {
304 info_thread(L) * popped = pop_head( blocked_threads );
305 popped->listed = false;
306 if(popped != 0p){
307 count--;
308 if (popped->lock) {
309 add_(*popped->lock, popped->t);
310 } else {
311 unpark(popped->t);
312 }
313 }
314 }
315 unlock( lock );
316 return ret;
317 }
318
319 uintptr_t front( condition_variable(L) & this ) with(this) {
320 if(!blocked_threads) return NULL;
321 return peek(blocked_threads)->info;
322 }
323
324 bool empty( condition_variable(L) & this ) with(this) {
325 return blocked_threads ? false : true;
326 }
327
328 int counter( condition_variable(L) & this ) with(this) {
329 return count;
330 }
331
332 // helper for wait()'s' without a timeout
333 void queue_info_thread( condition_variable(L) & this, info_thread(L) & i ) with(this) {
334 lock( lock __cfaabi_dbg_ctx2 );
335 append( this.blocked_threads, &i );
336 count++;
337 i.listed = true;
338 size_t recursion_count;
339 if (i.lock) {
340 recursion_count = get_recursion_count(*i.lock);
341 remove_( *i.lock );
342 }
343
344 unlock( lock );
345 park( ); // blocks here
346
347 if (i.lock) set_recursion_count(*i.lock, recursion_count); // resets recursion count here after waking
348 }
349
350 // helper for wait()'s' with a timeout
351 void queue_info_thread_timeout( condition_variable(L) & this, info_thread(L) & info, Time t ) with(this) {
352 lock( lock __cfaabi_dbg_ctx2 );
353
354 info_thread(L) * queue_ptr = &info;
355
356 alarm_node_wrap(L) node_wrap = { info.t, t, 0`s, alarm_node_wrap_cast };
357 node_wrap.cond = &this;
358 node_wrap.i = &queue_ptr;
359
360 register_self( &node_wrap.alarm_node );
361
362 append( blocked_threads, queue_ptr );
363 info.listed = true;
364 count++;
365
366 size_t recursion_count;
367 if (info.lock) {
368 recursion_count = get_recursion_count(*info.lock);
369 remove_( *info.lock );
370 }
371
372 unlock( lock );
373 park();
374
375 if (info.lock) set_recursion_count(*info.lock, recursion_count);
376 }
377
378 void wait( condition_variable(L) & this ) with(this) {
379 info_thread( L ) i = { active_thread() };
380 queue_info_thread( this, i );
381 }
382
383 void wait( condition_variable(L) & this, uintptr_t info ) with(this) {
384 info_thread( L ) i = { active_thread(), info };
385 queue_info_thread( this, i );
386 }
387
388 void wait( condition_variable(L) & this, Duration duration ) with(this) {
389 info_thread( L ) i = { active_thread() };
390 queue_info_thread_timeout(this, i, __kernel_get_time() + duration );
391 }
392
393 void wait( condition_variable(L) & this, uintptr_t info, Duration duration ) with(this) {
394 info_thread( L ) i = { active_thread(), info };
395 queue_info_thread_timeout(this, i, __kernel_get_time() + duration );
396 }
397
398 void wait( condition_variable(L) & this, Time time ) with(this) {
399 info_thread( L ) i = { active_thread() };
400 queue_info_thread_timeout(this, i, time);
401 }
402
403 void wait( condition_variable(L) & this, uintptr_t info, Time time ) with(this) {
404 info_thread( L ) i = { active_thread(), info };
405 queue_info_thread_timeout(this, i, time);
406 }
407
408 void wait( condition_variable(L) & this, L & l ) with(this) {
409 info_thread(L) i = { active_thread() };
410 i.lock = &l;
411 queue_info_thread( this, i );
412 }
413
414 void wait( condition_variable(L) & this, L & l, uintptr_t info ) with(this) {
415 info_thread(L) i = { active_thread(), info };
416 i.lock = &l;
417 queue_info_thread( this, i );
418 }
419
420 void wait( condition_variable(L) & this, L & l, Duration duration ) with(this) {
421 info_thread(L) i = { active_thread() };
422 i.lock = &l;
423 queue_info_thread_timeout(this, i, __kernel_get_time() + duration );
424 }
425
426 void wait( condition_variable(L) & this, L & l, uintptr_t info, Duration duration ) with(this) {
427 info_thread(L) i = { active_thread(), info };
428 i.lock = &l;
429 queue_info_thread_timeout(this, i, __kernel_get_time() + duration );
430 }
431
432 void wait( condition_variable(L) & this, L & l, Time time ) with(this) {
433 info_thread(L) i = { active_thread() };
434 i.lock = &l;
435 queue_info_thread_timeout(this, i, time );
436 }
437
438 void wait( condition_variable(L) & this, L & l, uintptr_t info, Time time ) with(this) {
439 info_thread(L) i = { active_thread(), info };
440 i.lock = &l;
441 queue_info_thread_timeout(this, i, time );
442 }
443}
Note: See TracBrowser for help on using the repository browser.