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

arm-ehenumforall-pointer-decayjacob/cs343-translationnew-ast-unique-exprpthread-emulationqualifiedEnum
Last change on this file since eeb5023 was eeb5023, checked in by Colby Alexander Parsons <caparsons@…>, 2 years ago

added full timeout functionality to unified condition variables

  • Property mode set to 100644
File size: 12.4 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        lock( lock __cfaabi_dbg_ctx2 );
79        if ( owner == kernelTLS.this_thread && !multi_acquisition) {
80                fprintf(stderr, "A single acquisition lock holder attempted to reacquire the lock resulting in a deadlock."); // Possibly throw instead
81        exit(EXIT_FAILURE);
82        } else if ( owner != 0p && owner != kernelTLS.this_thread ) {
83                append( blocked_threads, kernelTLS.this_thread );
84                wait_count++;
85                unlock( lock );
86                park( );
87        } else if ( owner == kernelTLS.this_thread && multi_acquisition ) {
88                recursion_count++;
89                unlock( lock );
90        } else {
91                owner = kernelTLS.this_thread;
92                recursion_count = 1;
93                unlock( lock );
94        }
95}
96
97bool try_lock( blocking_lock & this ) with( this ) {
98        bool ret = false;
99        lock( lock __cfaabi_dbg_ctx2 );
100        if ( owner == 0p ) {
101                owner = kernelTLS.this_thread;
102                if ( multi_acquisition ) recursion_count = 1;
103                ret = true;
104        } else if ( owner == kernelTLS.this_thread && multi_acquisition ) {
105                recursion_count++;
106                ret = true;
107        }
108        unlock( lock );
109        return ret;
110}
111
112void unlock( blocking_lock & this ) with( this ) {
113        lock( lock __cfaabi_dbg_ctx2 );
114        if ( owner == 0p ){ // no owner implies lock isn't held
115                fprintf( stderr, "There was an attempt to release a lock that isn't held" );
116                return;
117        } else if ( strict_owner && owner != kernelTLS.this_thread ) {
118                fprintf( stderr, "A thread other than the owner attempted to release an owner lock" );
119                return;
120        }
121        recursion_count--;
122        if ( recursion_count == 0 ) {
123                $thread * thrd = pop_head( blocked_threads );
124                owner = thrd;
125                recursion_count = ( thrd && multi_acquisition ? 1 : 0 );
126                wait_count--;
127                unpark( thrd );
128        }
129        unlock( lock );
130}
131
132size_t wait_count( blocking_lock & this ) with( this ) {
133        return wait_count;
134}
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 add_( blocking_lock & this, $thread * t ) with( this ) {
146    lock( lock __cfaabi_dbg_ctx2 );
147        if ( owner != 0p ) {
148                append( blocked_threads, t );
149                wait_count++;
150                unlock( lock );
151        } else {
152                owner = t;
153                if ( multi_acquisition ) recursion_count = 1;
154                #if !defined( __CFA_NO_STATISTICS__ )
155                        kernelTLS.this_stats = t->curr_cluster->stats;
156                #endif
157                unpark( t );
158                unlock( lock );
159        }
160}
161
162void remove_( blocking_lock & this ) with( this ) {
163    lock( lock __cfaabi_dbg_ctx2 );
164        if ( owner == 0p ){ // no owner implies lock isn't held
165                fprintf( stderr, "A lock that is not held was passed to a synchronization lock" );
166        } else if ( strict_owner && owner != kernelTLS.this_thread ) {
167                fprintf( stderr, "A thread other than the owner of a lock passed it to a synchronization lock" );
168        } else {
169                $thread * thrd = pop_head( blocked_threads );
170                owner = thrd;
171                recursion_count = ( thrd && multi_acquisition ? 1 : 0 );
172                wait_count--;
173                unpark( thrd );
174        }
175        unlock( lock );
176}
177
178///////////////////////////////////////////////////////////////////
179//// Overloaded routines for traits
180///////////////////////////////////////////////////////////////////
181
182// This is temporary until an inheritance bug is fixed
183
184void lock( mutex_lock & this ){
185        lock( (blocking_lock &)this );
186}
187
188void unlock( mutex_lock & this ){
189        unlock( (blocking_lock &)this );
190}
191
192void add_( mutex_lock & this, struct $thread * t ){
193        add_( (blocking_lock &)this, t );
194}
195
196void remove_( mutex_lock & this ){
197        remove_( (blocking_lock &)this );
198}
199
200void set_recursion_count( mutex_lock & this, size_t recursion ){
201        set_recursion_count( (blocking_lock &)this, recursion );
202}
203
204size_t get_recursion_count( mutex_lock & this ){
205        get_recursion_count( (blocking_lock &)this );
206}
207
208void lock( recursive_mutex_lock & this ){
209        lock( (blocking_lock &)this );
210}
211
212void unlock( recursive_mutex_lock & this ){
213        unlock( (blocking_lock &)this );
214}
215
216void add_( recursive_mutex_lock & this, struct $thread * t ){
217        add_( (blocking_lock &)this, t );
218}
219
220void remove_( recursive_mutex_lock & this ){
221        remove_( (blocking_lock &)this );
222}
223
224void set_recursion_count( recursive_mutex_lock & this, size_t recursion ){
225        set_recursion_count( (blocking_lock &)this, recursion );
226}
227
228size_t get_recursion_count( recursive_mutex_lock & this ){
229        get_recursion_count( (blocking_lock &)this );
230}
231
232///////////////////////////////////////////////////////////////////
233//// condition variable
234///////////////////////////////////////////////////////////////////
235
236forall(dtype L | is_blocking_lock(L)) {
237
238        void timeout_handler ( condition_variable(L) * cond , info_thread(L) ** i ) {
239        // This condition_variable member is called from the kernel, and therefore, cannot block, but it can spin.
240            lock( cond->lock __cfaabi_dbg_ctx2 );
241            if ( (*i)->listed ) {                       // is thread on queue
242                info_thread(L) * copy = *i;
243                        remove( cond->blocked_threads, i );              //remove this thread O(1)
244                        if( !copy->lock ) {
245                                unlock( cond->lock );
246                                printf("here");
247                                #if !defined( __CFA_NO_STATISTICS__ )
248                                        kernelTLS.this_stats = copy->t->curr_cluster->stats;
249                                #endif
250                                unpark( copy->t );
251                                printf("here2");
252                } else {
253                        add_(*copy->lock, copy->t);                     // call lock's add_
254                }
255            }
256            unlock( cond->lock );
257        }
258
259        void alarm_node_callback( alarm_node_wrap(L) & this ) with( this ) {
260                timeout_handler(cond, i);
261        }
262
263        void alarm_node_wrap_cast( alarm_node_t & a ) {
264                alarm_node_callback( (alarm_node_wrap(L) &)a );
265        }
266
267        void ?{}( condition_variable(L) & this ){
268                this.lock{};
269                this.blocked_threads{};
270                this.count = 0;
271        }
272
273        void ^?{}( condition_variable(L) & this ){
274                // default
275        }
276
277        void ?{}( alarm_node_wrap(L) & this, $thread * thrd, Time alarm, Duration period, Alarm_Callback callback ) {
278                this.alarm_node{ thrd, alarm, period, callback };
279        }
280
281        void ^?{}( alarm_node_wrap(L) & this ) {
282                // default
283        }
284
285        bool notify_one( condition_variable(L) & this ) with( this ) {
286                lock( lock __cfaabi_dbg_ctx2 );
287                bool ret = !!blocked_threads;
288                info_thread(L) * popped = pop_head( blocked_threads );
289                popped->listed = false;
290                if(popped != 0p) {
291                        add_(*popped->lock, popped->t);
292                        count--;
293                }
294                unlock( lock );
295                return ret;
296        }
297
298        bool notify_all( condition_variable(L) & this ) with(this) {
299                lock( lock __cfaabi_dbg_ctx2 );
300                bool ret = blocked_threads ? true : false;
301                while( blocked_threads ) {
302                        info_thread(L) * popped = pop_head( blocked_threads );
303                        popped->listed = false;
304                        if(popped != 0p){
305                                add_(*popped->lock, popped->t);
306                                count--;
307                        }
308                }
309                unlock( lock );
310                return ret;
311        }
312
313        uintptr_t front( condition_variable(L) & this ) with(this) {
314                if(!blocked_threads) return NULL;
315                return peek(blocked_threads)->info;
316        }
317
318        bool empty( condition_variable(L) & this ) with(this) {
319                return blocked_threads ? false : true;
320        }
321
322        int counter( condition_variable(L) & this ) with(this) {
323                return count;
324        }
325
326        // helper for wait()'s' without a timeout
327        void queue_info_thread( condition_variable(L) & this, info_thread(L) & i ) with(this) {
328                lock( lock __cfaabi_dbg_ctx2 );
329                append( this.blocked_threads, &i );
330                count++;
331                i.listed = true;
332                size_t recursion_count;
333                if (i.lock) {
334                        recursion_count = get_recursion_count(*i.lock);
335                        remove_( *i.lock );
336                }
337               
338                unlock( lock );
339                park( ); // blocks here
340
341                if (i.lock) set_recursion_count(*i.lock, recursion_count); // resets recursion count here after waking
342        }
343
344        // helper for wait()'s' with a timeout
345        void queue_info_thread_timeout( condition_variable(L) & this, info_thread(L) & info, Time t ) with(this) {
346                lock( lock __cfaabi_dbg_ctx2 );
347
348                info_thread(L) * queue_ptr = &info;
349
350                alarm_node_wrap(L) node_wrap = { info.t, t, 0`s, alarm_node_wrap_cast };
351                node_wrap.cond = &this;
352                node_wrap.i = &queue_ptr;
353
354                register_self( &node_wrap.alarm_node );
355
356                append( blocked_threads, queue_ptr );
357                info.listed = true;
358                count++;
359
360                size_t recursion_count;
361                if (info.lock) {
362                        recursion_count = get_recursion_count(*info.lock);
363                        remove_( *info.lock );
364                }
365
366                unlock( lock );
367                park();
368
369                if (info.lock) set_recursion_count(*info.lock, recursion_count);
370        }
371
372        void wait( condition_variable(L) & this ) with(this) {
373                info_thread( L ) i = { kernelTLS.this_thread };
374                queue_info_thread( this, i );
375        }
376
377        void wait( condition_variable(L) & this, uintptr_t info ) with(this) {
378                info_thread( L ) i = { kernelTLS.this_thread, info };
379                queue_info_thread( this, i );
380        }
381       
382        void wait( condition_variable(L) & this, Duration duration ) with(this) {
383                info_thread( L ) i = { kernelTLS.this_thread };
384                queue_info_thread_timeout(this, i, __kernel_get_time() + duration );
385        }
386
387        void wait( condition_variable(L) & this, uintptr_t info, Duration duration ) with(this) {
388                info_thread( L ) i = { kernelTLS.this_thread, info };
389                queue_info_thread_timeout(this, i, __kernel_get_time() + duration );
390        }
391
392        void wait( condition_variable(L) & this, Time time ) with(this) {
393                info_thread( L ) i = { kernelTLS.this_thread };
394                queue_info_thread_timeout(this, i, time);
395        }
396
397        void wait( condition_variable(L) & this, uintptr_t info, Time time ) with(this) {
398                info_thread( L ) i = { kernelTLS.this_thread, info };
399                queue_info_thread_timeout(this, i, time);
400        }
401
402        void wait( condition_variable(L) & this, L & l ) with(this) {
403                info_thread(L) i = { kernelTLS.this_thread };
404                i.lock = &l;
405                queue_info_thread( this, i );
406        }
407
408        void wait( condition_variable(L) & this, L & l, uintptr_t info ) with(this) {
409                info_thread(L) i = { kernelTLS.this_thread, info };
410                i.lock = &l;
411                queue_info_thread( this, i );
412        }
413       
414        void wait( condition_variable(L) & this, L & l, Duration duration ) with(this) {
415                info_thread(L) i = { kernelTLS.this_thread };
416                i.lock = &l;
417                queue_info_thread_timeout(this, i, __kernel_get_time() + duration );
418        }
419       
420        void wait( condition_variable(L) & this, L & l, uintptr_t info, Duration duration ) with(this) {
421                info_thread(L) i = { kernelTLS.this_thread, info };
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, Time time ) with(this) {
427                info_thread(L) i = { kernelTLS.this_thread };
428                i.lock = &l;
429                queue_info_thread_timeout(this, i, time );
430        }
431       
432        void wait( condition_variable(L) & this, L & l, uintptr_t info, Time time ) with(this) {
433                info_thread(L) i = { kernelTLS.this_thread, info };
434                i.lock = &l;
435                queue_info_thread_timeout(this, i, time );
436        }
437}
438
439thread T1 {};
440thread T2 {};
441
442recursive_mutex_lock m;
443condition_variable( recursive_mutex_lock ) c;
444
445void main( T1 & this ) {
446        printf("T1 start\n");
447        lock(m);
448        printf("%d\n", counter(c));
449        if(empty(c)) {
450                printf("T1 wait\n");
451                wait(c,m,12);
452        }else{
453                printf("%d\n", front(c));
454                notify_one(c);
455        }
456        unlock(m);
457        printf("curr thd in main %p \n", kernelTLS.this_thread);
458        printf("T1 waits for 2s\n");
459        lock(m);
460        wait( c, m, 2`s );
461        unlock(m);
462        printf("T1 wakes\n");
463        printf("T1 done\n");
464}
465
466void main( T2 & this ) {
467        printf("T2 start\n");
468        lock(m);
469        printf("%d\n", counter(c));
470        if(empty(c)) {
471                printf("T2 wait\n");
472                wait(c,m,12);
473        }else{
474                printf("%d\n", front(c));
475                notify_one(c);
476        }
477        unlock(m);
478        printf("T2 done\n");
479}
480
481int main() {
482        printf("start\n");
483        processor p[2];
484        {
485                T1 t1;
486                T2 t2;
487        }
488        printf("done\n");
489}
Note: See TracBrowser for help on using the repository browser.