source: libcfa/src/concurrency/locks.cfa @ 9db2c92

ADTarm-ehast-experimentalenumforall-pointer-decayjacob/cs343-translationnew-ast-unique-exprpthread-emulationqualifiedEnum
Last change on this file since 9db2c92 was 454f478, checked in by Thierry Delisle <tdelisle@…>, 3 years ago

Re-arranged and commented low-level headers.
Main goal was for better support of weakso locks that are comming.

  • Property mode set to 100644
File size: 13.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(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(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(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}
358
359//-----------------------------------------------------------------------------
360// Semaphore
361void  ?{}( semaphore & this, int count = 1 ) {
362        (this.lock){};
363        this.count = count;
364        (this.waiting){};
365}
366void ^?{}(semaphore & this) {}
367
368bool P(semaphore & this) with( this ){
369        lock( lock __cfaabi_dbg_ctx2 );
370        count -= 1;
371        if ( count < 0 ) {
372                // queue current task
373                append( waiting, active_thread() );
374
375                // atomically release spin lock and block
376                unlock( lock );
377                park();
378                return true;
379        }
380        else {
381            unlock( lock );
382            return false;
383        }
384}
385
386bool V(semaphore & this) with( this ) {
387        $thread * thrd = 0p;
388        lock( lock __cfaabi_dbg_ctx2 );
389        count += 1;
390        if ( count <= 0 ) {
391                // remove task at head of waiting list
392                thrd = pop_head( waiting );
393        }
394
395        unlock( lock );
396
397        // make new owner
398        unpark( thrd );
399
400        return thrd != 0p;
401}
402
403bool V(semaphore & this, unsigned diff) with( this ) {
404        $thread * thrd = 0p;
405        lock( lock __cfaabi_dbg_ctx2 );
406        int release = max(-count, (int)diff);
407        count += diff;
408        for(release) {
409                unpark( pop_head( waiting ) );
410        }
411
412        unlock( lock );
413
414        return thrd != 0p;
415}
Note: See TracBrowser for help on using the repository browser.