source: trunk/kernel/libk/rwlock.c @ 675

Last change on this file since 675 was 666, checked in by alain, 4 years ago

Cosmetic.

File size: 10.6 KB
RevLine 
[1]1/*
[563]2 * rwlock.c - kernel local read/write lock implementation.
[1]3 *
[629]4 * Author  Alain Greiner     (2016,2017,2018,2019)
[1]5 *
6 * Copyright (c) UPMC Sorbonne Universites
7 *
8 * This file is part of ALMOS-MKH.
9 *
10 * ALMOS-MKH is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; version 2.0 of the License.
13 *
14 * ALMOS-MKH is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with ALMOS-MKH; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23
[14]24#include <kernel_config.h>
[457]25#include <hal_kernel_types.h>
[1]26#include <hal_atomic.h>
27#include <hal_special.h>
28#include <hal_irqmask.h>
29#include <thread.h>
30#include <printk.h>
31#include <rwlock.h>
32
[563]33//////////////////////////////////////////////////////////////////////////////
34//                Extern global variables
35//////////////////////////////////////////////////////////////////////////////
36
37extern char * lock_type_str[];          // allocated in kernel_init.c
38
39
40//////////////////////////////////
41void rwlock_init( rwlock_t * lock,
42                  uint32_t   type )
[1]43{ 
[563]44        lock->taken   = 0;
[1]45    lock->count   = 0;
[409]46
[563]47    list_root_init( &lock->rd_root );
48    list_root_init( &lock->wr_root );
[409]49
[563]50    busylock_init( &lock->lock , type );
[603]51
[610]52#if DEBUG_RWLOCK_TYPE
[603]53thread_t * this = CURRENT_THREAD;
[666]54bool_t     cond = (lock_type == DEBUG_RWLOCK_TYPE) &&
55                  (((local_cxy == (cxt_t)DEBUG_RWLOCK_CXY) &&
56                    (lock == (rwlock_t*)DEBUG_RWLOCK_PTR)) ||
57                   ((DEBUG_RWLOCK_CXY == 0) &&
58                    (DEBUG_RWLOCK_PTR == 0)));
59if( cond ) printk("\n[%s] thread[%x,%x] initialise lock %s [%x,%x]\n",
[603]60__FUNCTION__, this->process->pid, this->trdid,
61lock_type_str[type], local_cxy, lock );
62#endif
63
[1]64}
65
[563]66/////////////////////////////////////////
67void rwlock_rd_acquire( rwlock_t * lock )
[1]68{
[563]69    thread_t * this = CURRENT_THREAD;
[1]70
[563]71    // check calling thread can yield
72    thread_assert_can_yield( this , __FUNCTION__ );
[1]73
[563]74    // get busylock
75    busylock_acquire( &lock->lock );
76
[623]77#if DEBUG_RWLOCK_TYPE
[666]78uint32_t   lock_type = lock->lock.type;
79bool_t     cond = (lock_type == DEBUG_RWLOCK_TYPE) &&
80                  (((local_cxy == (cxt_t)DEBUG_RWLOCK_CXY) &&
81                    (lock == (rwlock_t*)DEBUG_RWLOCK_PTR)) ||
82                   ((DEBUG_RWLOCK_CXY == 0) &&
83                    (DEBUG_RWLOCK_PTR == 0)));
[623]84#endif
85
[563]86    // block and deschedule if lock already taken
87    while( lock->taken )
[1]88    {
[563]89
[610]90#if DEBUG_RWLOCK_TYPE
[666]91if( cond ) 
[603]92printk("\n[%s] thread[%x,%x] READ BLOCK on rwlock %s [%x,%x] / taken %d / count %d\n",
[600]93__FUNCTION__, this->process->pid, this->trdid, 
[610]94lock_type_str[lock_type], local_cxy, lock, lock->taken, lock->count );
[563]95#endif
96        // register reader thread in waiting queue
97        list_add_last( &lock->rd_root , &this->wait_list );
98
99        // block reader thread
100        thread_block( XPTR( local_cxy , this ) , THREAD_BLOCKED_LOCK );
101       
102        // release busylock
103        busylock_release( &lock->lock );
104
105        // deschedule
106        sched_yield("reader wait rwlock");
107       
108        // get busylock
109        busylock_acquire( &lock->lock );
[1]110    }
111
[603]112    // increment number of readers
113    lock->count++;
114
[610]115#if DEBUG_RWLOCK_TYPE
[666]116if( cond )
[603]117printk("\n[%s] thread[%x,%x] READ ACQUIRE rwlock %s [%x,%x] / taken %d / count %d\n",
[600]118__FUNCTION__, this->process->pid, this->trdid, 
[610]119lock_type_str[lock_type], local_cxy, lock, lock->taken, lock->count );
[563]120#endif
[1]121
[563]122    // release busylock
123    busylock_release( &lock->lock );
[409]124
[563]125}  // end rwlock_rd_acquire()
[1]126
[563]127/////////////////////////////////////////
128void rwlock_wr_acquire( rwlock_t * lock )
129{
130    thread_t * this = CURRENT_THREAD;
[1]131
[563]132    // check calling thread can yield
133    thread_assert_can_yield( this , __FUNCTION__ );
[1]134
[563]135    // get busylock
136    busylock_acquire( &lock->lock );
[1]137
[623]138#if DEBUG_RWLOCK_TYPE
[666]139uint32_t   lock_type = lock->lock.type;
140bool_t     cond = (lock_type == DEBUG_RWLOCK_TYPE) &&
141                  (((local_cxy == (cxt_t)DEBUG_RWLOCK_CXY) &&
142                    (lock == (rwlock_t*)DEBUG_RWLOCK_PTR)) ||
143                   ((DEBUG_RWLOCK_CXY == 0) &&
144                    (DEBUG_RWLOCK_PTR == 0)));
[623]145#endif
146
[563]147    // block and deschedule if lock already taken or existing read access
148    while( lock->taken || lock->count )
149    {
150
[610]151#if DEBUG_RWLOCK_TYPE
[666]152if( cond )
[603]153printk("\n[%s] thread[%x,%x] WRITE BLOCK on rwlock %s [%x,%x] / taken %d / count %d\n",
[600]154__FUNCTION__, this->process->pid, this->trdid, 
[610]155lock_type_str[lock_type], local_cxy, lock, lock->taken, lock->count );
[563]156#endif
157        // register writer in waiting queue
158        list_add_last( &lock->wr_root , &this->wait_list );
[1]159
[603]160        // block writer thread
[563]161        thread_block( XPTR( local_cxy , this ) , THREAD_BLOCKED_LOCK );
162       
163        // release busylock
164        busylock_release( &lock->lock );
165
166        // deschedule
167        sched_yield("writer wait rwlock");
168       
169        // get busylock
170        busylock_acquire( &lock->lock );
171    }
172
[603]173    // take the rwlock
174    lock->taken = 1;
175
[610]176#if DEBUG_RWLOCK_TYPE
[666]177if( cond )
[603]178printk("\n[%s] thread[%x,%x] WRITE ACQUIRE rwlock %s [%x,%x] / taken %d / count %d\n",
[600]179__FUNCTION__, this->process->pid, this->trdid, 
[610]180lock_type_str[lock_type], local_cxy, lock, lock->taken, lock->count );
[409]181#endif
182
[563]183    // release busylock
184    busylock_release( &lock->lock );
185
186}  // end rwlock_wr_acquire()
187
188/////////////////////////////////////////
189void rwlock_rd_release( rwlock_t * lock )
190{
191    // synchronize memory before lock release
192    hal_fence();
193
194    // get busylock
195    busylock_acquire( &lock->lock );
196
[603]197    // decrement number of readers
198    lock->count--;
199
[610]200#if DEBUG_RWLOCK_TYPE
[600]201thread_t * this = CURRENT_THREAD;
[666]202uint32_t   lock_type = lock->lock.type;
203bool_t     cond = (lock_type == DEBUG_RWLOCK_TYPE) &&
204                  (((local_cxy == (cxt_t)DEBUG_RWLOCK_CXY) &&
205                    (lock == (rwlock_t*)DEBUG_RWLOCK_PTR)) ||
206                   ((DEBUG_RWLOCK_CXY == 0) &&
207                    (DEBUG_RWLOCK_PTR == 0)));
208#endif
209
210#if DEBUG_RWLOCK_TYPE
211if( cond )
[603]212printk("\n[%s] thread[%x,%x] READ RELEASE rwlock %s [%x,%x] / taken %d / count %d\n",
[600]213__FUNCTION__, this->process->pid, this->trdid, 
[610]214lock_type_str[lock_type], local_cxy, lock, lock->taken, lock->count );
[563]215#endif
[1]216
[563]217    // release first writer in waiting queue if no current readers
218    // and writers waiting queue non empty
219    if( (lock->count == 0) && (list_is_empty( &lock->wr_root ) == false) )
220    {
221        // get first writer thread
222        thread_t * thread = LIST_FIRST( &lock->wr_root , thread_t , wait_list );
223
[610]224#if DEBUG_RWLOCK_TYPE
[666]225if( cond )
[600]226printk("\n[%s] thread[%x,%x] UNBLOCK thread[%x,%x] / rwlock %s [%x,%x]\n",
227__FUNCTION__, this->process->pid, this->trdid, thread->process->pid, thread->trdid,
[610]228lock_type_str[lock_type], local_cxy, lock );
[563]229#endif
[1]230
[563]231        // remove this waiting thread from waiting list
232        list_unlink( &thread->wait_list );
233
234        // unblock this waiting thread
235        thread_unblock( XPTR( local_cxy , thread ) , THREAD_BLOCKED_LOCK );
236    }
237    // release all readers in waiting queue if writers waiting queue empty
238    // and readers waiting queue non empty
239    else if( list_is_empty( &lock->wr_root ) && (list_is_empty( &lock->rd_root ) == false) )
[1]240    {
[563]241        while( list_is_empty( &lock->rd_root ) == false )
242        {
243            // get first reader thread
244            thread_t * thread = LIST_FIRST( &lock->wr_root , thread_t , wait_list );
[1]245
[610]246#if DEBUG_RWLOCK_TYPE
[666]247if( cond )
[600]248printk("\n[%s] thread[%x,%x] UNBLOCK thread[%x,%x] / rwlock %s [%x,%x]\n",
249__FUNCTION__, this->process->pid, this->trdid, thread->process->pid, thread->trdid,
[610]250lock_type_str[lock_type], local_cxy, lock );
[563]251#endif
252   
253            // remove this waiting thread from waiting list
254            list_unlink( &thread->wait_list );
[1]255
[563]256            // unblock this waiting thread
257            thread_unblock( XPTR( local_cxy , thread ) , THREAD_BLOCKED_LOCK );
258        }
[1]259    }
260
[563]261    // release busylock
262    busylock_release( &lock->lock );
[1]263
[563]264}  // end rwlock_rd_release()
265
266/////////////////////////////////////////
267void rwlock_wr_release( rwlock_t * lock )
268{
269    // synchronize memory before lock release
270    hal_fence();
271
272    // get busylock
273    busylock_acquire( &lock->lock );
274
[603]275    // release the rwlock
276    lock->taken = 0;
277
[610]278#if DEBUG_RWLOCK_TYPE
[600]279thread_t * this = CURRENT_THREAD;
[666]280uint32_t   lock_type = lock->lock.type;
281bool_t     cond = (lock_type == DEBUG_RWLOCK_TYPE) &&
282                  (((local_cxy == (cxt_t)DEBUG_RWLOCK_CXY) &&
283                    (lock == (rwlock_t*)DEBUG_RWLOCK_PTR)) ||
284                   ((DEBUG_RWLOCK_CXY == 0) &&
285                    (DEBUG_RWLOCK_PTR == 0)));
286#endif
287
288#if DEBUG_RWLOCK_TYPE
289if( cond )
[603]290printk("\n[%s] thread[%x,%x] WRITE RELEASE rwlock %s [%x,%x] / taken %d / count %d\n",
[600]291__FUNCTION__, this->process->pid, this->trdid, 
[610]292lock_type_str[lock_type], local_cxy, lock, lock->taken, lock->count );
[409]293#endif
294
[563]295    // release first waiting writer thread if writers waiting queue non empty
296    if( list_is_empty( &lock->wr_root ) == false )
297    {
298        // get first writer thread
299        thread_t * thread = LIST_FIRST( &lock->wr_root , thread_t , wait_list );
[1]300
[610]301#if DEBUG_RWLOCK_TYPE
[666]302if( cond )
[600]303printk("\n[%s] thread[%x,%x] UNBLOCK thread[%x,%x] / rwlock %s [%x,%x]\n",
304__FUNCTION__, this->process->pid, this->trdid, thread->process->pid, thread->trdid,
[610]305lock_type_str[lock_type], local_cxy, lock );
[409]306#endif
[563]307        // remove this waiting thread from waiting list
308        list_unlink( &thread->wait_list );
[409]309
[563]310        // unblock this waiting thread
311        thread_unblock( XPTR( local_cxy , thread ) , THREAD_BLOCKED_LOCK );
312    }
[337]313
[563]314    // check readers waiting queue and release all if writers waiting queue empty
315    else 
316    {
317        while( list_is_empty( &lock->rd_root ) == false )
318        {
319            // get first reader thread
320            thread_t * thread = LIST_FIRST( &lock->rd_root , thread_t , wait_list );
321
[610]322#if DEBUG_RWLOCK_TYPE
[666]323if( cond )
[600]324printk("\n[%s] thread[%x,%x] UNBLOCK thread[%x,%x] / rwlock %s [%x,%x]\n",
325__FUNCTION__, this->process->pid, this->trdid, thread->process->pid, thread->trdid,
[610]326lock_type_str[lock_type], local_cxy, lock );
[563]327#endif
328            // remove this waiting thread from waiting list
329            list_unlink( &thread->wait_list );
[1]330
[563]331            // unblock this waiting thread
332            thread_unblock( XPTR( local_cxy , thread ) , THREAD_BLOCKED_LOCK );
333        }
334    }
335
336    // release busylock
337    busylock_release( &lock->lock );
338
339}  // end rwlock_wr_release()
340
341
Note: See TracBrowser for help on using the repository browser.