source: libcfa/src/concurrency/locks.cfa @ 92bfda0

ADTarm-ehast-experimentalenumforall-pointer-decayjacob/cs343-translationnew-ast-unique-exprpthread-emulationqualifiedEnum
Last change on this file since 92bfda0 was ac5816d, checked in by Thierry Delisle <tdelisle@…>, 4 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.