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

ADTarm-ehast-experimentalenumforall-pointer-decayjacob/cs343-translationnew-ast-unique-exprpthread-emulationqualifiedEnum
Last change on this file since c20533ea was c20533ea, checked in by Thierry Delisle <tdelisle@…>, 3 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
RevLine 
[848439f]1#include "locks.hfa"
2#include "kernel_private.hfa"
3
4#include <kernel.hfa>
5#include <stdlib.hfa>
6
7///////////////////////////////////////////////////////////////////
8//// info_thread
9///////////////////////////////////////////////////////////////////
[cad1df1]10
[848439f]11forall(dtype L | is_blocking_lock(L)) {
12        void ?{}( info_thread(L) & this, $thread * t ) {
[cad1df1]13                ((Seqable &) this){};
[848439f]14                this.t = t;
15                this.lock = 0p;
16        }
17
18        void ?{}( info_thread(L) & this, $thread * t, uintptr_t info ) {
[cad1df1]19                ((Seqable &) this){};
[848439f]20                this.t = t;
21                this.info = info;
22                this.lock = 0p;
23        }
24
[cad1df1]25        void ^?{}( info_thread(L) & this ){ }
[c131a02]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        }
[848439f]34}
[cad1df1]35
[848439f]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
[cad1df1]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 ) {}
[848439f]57
58void lock( blocking_lock & this ) with( this ) {
59        lock( lock __cfaabi_dbg_ctx2 );
[797a193]60        if ( owner == active_thread() && !multi_acquisition) { // single acquisition lock is held by current thread
[cad1df1]61                abort("A single acquisition lock holder attempted to reacquire the lock resulting in a deadlock.");
[797a193]62        } else if ( owner != 0p && owner != active_thread() ) { // lock is held by some other thread
[c131a02]63                addTail( blocked_threads, *active_thread() );
[848439f]64                wait_count++;
65                unlock( lock );
[eeb5023]66                park( );
[797a193]67        } else if ( owner == active_thread() && multi_acquisition ) { // multi acquisition lock is held by current thread
[848439f]68                recursion_count++;
69                unlock( lock );
[797a193]70        } else { // lock isn't held
[6a8882c]71                owner = active_thread();
[848439f]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 );
[797a193]80        if ( owner == 0p ) { // lock isn't held
[6a8882c]81                owner = active_thread();
82                recursion_count = 1;
[848439f]83                ret = true;
[797a193]84        } else if ( owner == active_thread() && multi_acquisition ) { // multi acquisition lock is held by current thread
[848439f]85                recursion_count++;
86                ret = true;
87        }
88        unlock( lock );
89        return ret;
90}
91
[cad1df1]92void unlock_error_check( blocking_lock & this ) with( this ) {
[797a193]93        if ( owner == 0p ){ // lock isn't held
[cad1df1]94                abort( "There was an attempt to release a lock that isn't held" );
[6a8882c]95        } else if ( strict_owner && owner != active_thread() ) {
[cad1df1]96                abort( "A thread other than the owner attempted to release an owner lock" );
[848439f]97        }
[cad1df1]98}
99
100void pop_and_set_new_owner( blocking_lock & this ) with( this ) {
[c131a02]101        $thread * t = &dropHead( blocked_threads );
[cad1df1]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 );
[848439f]111        recursion_count--;
[797a193]112        if ( recursion_count == 0 ) { // if recursion count is zero release lock and set new owner if one is waiting
[cad1df1]113                pop_and_set_new_owner( this );
[848439f]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
[797a193]130void on_notify( blocking_lock & this, $thread * t ) with( this ) {
[848439f]131    lock( lock __cfaabi_dbg_ctx2 );
[797a193]132        if ( owner != 0p ) { // lock held
[c131a02]133                addTail( blocked_threads, *t );
[848439f]134                wait_count++;
135                unlock( lock );
[797a193]136        } else {        // lock not held
[848439f]137                owner = t;
[6a8882c]138                recursion_count = 1;
[eeb5023]139                unpark( t );
[848439f]140                unlock( lock );
141        }
142}
143
[797a193]144void on_wait( blocking_lock & this ) with( this ) {
[848439f]145    lock( lock __cfaabi_dbg_ctx2 );
[cad1df1]146        unlock_error_check( this );
147        pop_and_set_new_owner( this );
[848439f]148        unlock( lock );
149}
150
151///////////////////////////////////////////////////////////////////
152//// Overloaded routines for traits
153///////////////////////////////////////////////////////////////////
154
[797a193]155// These routines are temporary until an inheritance bug is fixed
[848439f]156
[cad1df1]157void lock( single_acquisition_lock & this ){ lock( (blocking_lock &)this ); }
158void unlock( single_acquisition_lock & this ){ unlock( (blocking_lock &)this ); }
[797a193]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 ); }
[cad1df1]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 ); }
[797a193]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 ); }
[cad1df1]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 ); }
[797a193]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 ); }
[cad1df1]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 ); }
[848439f]177
178
179forall(dtype L | is_blocking_lock(L)) {
[c20533ea]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 ) { }
[848439f]191
[c5bbb9b]192        void timeout_handler ( alarm_node_wrap(L) & this ) with( this ) {
[eeb5023]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 );
[797a193]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
[dff1fd1]200                i->signalled = false;
[797a193]201                        remove( cond->blocked_threads, *i );    // remove this thread O(1)
[6a8882c]202                        cond->count--;
[797a193]203                        if( i->lock ) {
204                                on_notify(*i->lock, i->t);                      // call lock's on_notify if a lock was passed
[eeb5023]205                } else {
[797a193]206                        unpark( i->t );                                         // otherwise wake thread
[eeb5023]207                }
208            }
209            unlock( cond->lock );
[848439f]210        }
211
[797a193]212        // this casts the alarm node to our wrapped type since we used type erasure
[cad1df1]213        void alarm_node_wrap_cast( alarm_node_t & a ) { timeout_handler( (alarm_node_wrap(L) &)a ); }
[848439f]214
[c20533ea]215}
216
217///////////////////////////////////////////////////////////////////
218//// condition variable
219///////////////////////////////////////////////////////////////////
220
221forall(dtype L | is_blocking_lock(L)) {
222
[848439f]223        void ?{}( condition_variable(L) & this ){
[eeb5023]224                this.lock{};
225                this.blocked_threads{};
226                this.count = 0;
[848439f]227        }
228
[cad1df1]229        void ^?{}( condition_variable(L) & this ){ }
[848439f]230
[cad1df1]231        void process_popped( condition_variable(L) & this, info_thread(L) & popped ) with( this ) {
232                if(&popped != 0p) {
[dff1fd1]233                        popped.signalled = true;
[eeb5023]234                        count--;
[cad1df1]235                        if (popped.lock) {
[797a193]236                                on_notify(*popped.lock, popped.t); // if lock passed call on_notify
[848439f]237                        } else {
[797a193]238                                unpark(popped.t); // otherwise wake thread
[848439f]239                        }
240                }
[cad1df1]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 ));
[848439f]247                unlock( lock );
248                return ret;
249        }
250
[eeb5023]251        bool notify_all( condition_variable(L) & this ) with(this) {
[848439f]252                lock( lock __cfaabi_dbg_ctx2 );
[cad1df1]253                bool ret = !empty(blocked_threads);
254                while( !empty(blocked_threads) ) {
255                        process_popped(this, dropHead( blocked_threads ));
[848439f]256                }
257                unlock( lock );
258                return ret;
259        }
260
[eeb5023]261        uintptr_t front( condition_variable(L) & this ) with(this) {
[cad1df1]262                return empty(blocked_threads) ? NULL : head(blocked_threads).info;
[848439f]263        }
264
[cad1df1]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; }
[848439f]268
[cad1df1]269        size_t queue_and_get_recursion( condition_variable(L) & this, info_thread(L) * i ) with(this) {
[797a193]270                addTail( blocked_threads, *i ); // add info_thread to waiting queue
[cad1df1]271                count++;
272                size_t recursion_count = 0;
[797a193]273                if (i->lock) { // if lock was passed get recursion count to reset to after waking thread
[cad1df1]274                        recursion_count = get_recursion_count(*i->lock);
[797a193]275                        on_wait( *i->lock );
[cad1df1]276                }
277                return recursion_count;
[848439f]278        }
279
[cad1df1]280        // helper for wait()'s' with no timeout
[eeb5023]281        void queue_info_thread( condition_variable(L) & this, info_thread(L) & i ) with(this) {
[848439f]282                lock( lock __cfaabi_dbg_ctx2 );
[cad1df1]283                size_t recursion_count = queue_and_get_recursion(this, &i);
[848439f]284                unlock( lock );
[eeb5023]285                park( ); // blocks here
286                if (i.lock) set_recursion_count(*i.lock, recursion_count); // resets recursion count here after waking
[848439f]287        }
288
[eeb5023]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 );
[cad1df1]292                size_t recursion_count = queue_and_get_recursion(this, &info);
[4aeaee5]293                alarm_node_wrap(L) node_wrap = { t, 0`s, alarm_node_wrap_cast };
[eeb5023]294                node_wrap.cond = &this;
[cad1df1]295                node_wrap.i = &info;
[eeb5023]296                register_self( &node_wrap.alarm_node );
[848439f]297                unlock( lock );
[eeb5023]298                park();
[797a193]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
[848439f]301        }
302
[eeb5023]303        void wait( condition_variable(L) & this ) with(this) {
[3959595]304                info_thread( L ) i = { active_thread() };
[848439f]305                queue_info_thread( this, i );
306        }
307
[eeb5023]308        void wait( condition_variable(L) & this, uintptr_t info ) with(this) {
[3959595]309                info_thread( L ) i = { active_thread(), info };
[848439f]310                queue_info_thread( this, i );
311        }
[6a8882c]312       
[dff1fd1]313        bool wait( condition_variable(L) & this, Duration duration ) with(this) {
[3959595]314                info_thread( L ) i = { active_thread() };
[eeb5023]315                queue_info_thread_timeout(this, i, __kernel_get_time() + duration );
[dff1fd1]316                return i.signalled;
[848439f]317        }
318
[dff1fd1]319        bool wait( condition_variable(L) & this, uintptr_t info, Duration duration ) with(this) {
[3959595]320                info_thread( L ) i = { active_thread(), info };
[eeb5023]321                queue_info_thread_timeout(this, i, __kernel_get_time() + duration );
[dff1fd1]322                return i.signalled;
[848439f]323        }
324
[dff1fd1]325        bool wait( condition_variable(L) & this, Time time ) with(this) {
[3959595]326                info_thread( L ) i = { active_thread() };
[eeb5023]327                queue_info_thread_timeout(this, i, time);
[dff1fd1]328                return i.signalled;
[848439f]329        }
330
[dff1fd1]331        bool wait( condition_variable(L) & this, uintptr_t info, Time time ) with(this) {
[3959595]332                info_thread( L ) i = { active_thread(), info };
[eeb5023]333                queue_info_thread_timeout(this, i, time);
[dff1fd1]334                return i.signalled;
[848439f]335        }
336
[eeb5023]337        void wait( condition_variable(L) & this, L & l ) with(this) {
[3959595]338                info_thread(L) i = { active_thread() };
[eeb5023]339                i.lock = &l;
340                queue_info_thread( this, i );
[848439f]341        }
342
[eeb5023]343        void wait( condition_variable(L) & this, L & l, uintptr_t info ) with(this) {
[3959595]344                info_thread(L) i = { active_thread(), info };
[eeb5023]345                i.lock = &l;
346                queue_info_thread( this, i );
[848439f]347        }
[6a8882c]348       
[dff1fd1]349        bool wait( condition_variable(L) & this, L & l, Duration duration ) with(this) {
[3959595]350                info_thread(L) i = { active_thread() };
[eeb5023]351                i.lock = &l;
352                queue_info_thread_timeout(this, i, __kernel_get_time() + duration );
[dff1fd1]353                return i.signalled;
[848439f]354        }
[6a8882c]355       
[dff1fd1]356        bool wait( condition_variable(L) & this, L & l, uintptr_t info, Duration duration ) with(this) {
[3959595]357                info_thread(L) i = { active_thread(), info };
[eeb5023]358                i.lock = &l;
359                queue_info_thread_timeout(this, i, __kernel_get_time() + duration );
[dff1fd1]360                return i.signalled;
[848439f]361        }
[6a8882c]362       
[dff1fd1]363        bool wait( condition_variable(L) & this, L & l, Time time ) with(this) {
[3959595]364                info_thread(L) i = { active_thread() };
[eeb5023]365                i.lock = &l;
366                queue_info_thread_timeout(this, i, time );
[dff1fd1]367                return i.signalled;
[848439f]368        }
[6a8882c]369       
[dff1fd1]370        bool wait( condition_variable(L) & this, L & l, uintptr_t info, Time time ) with(this) {
[3959595]371                info_thread(L) i = { active_thread(), info };
[eeb5023]372                i.lock = &l;
373                queue_info_thread_timeout(this, i, time );
[dff1fd1]374                return i.signalled;
[848439f]375        }
376}
Note: See TracBrowser for help on using the repository browser.