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

Last change on this file since 600 was 600, checked in by alain, 3 years ago

Cosmetic: improve debug.

File size: 8.8 KB
Line 
1/*
2 * rwlock.c - kernel local read/write lock implementation.
3 *
4 * Author  Alain Greiner     (2016,2017,2018)
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
24#include <kernel_config.h>
25#include <hal_kernel_types.h>
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
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 )
43{ 
44        lock->taken   = 0;
45    lock->count   = 0;
46
47    list_root_init( &lock->rd_root );
48    list_root_init( &lock->wr_root );
49
50    busylock_init( &lock->lock , type );
51}
52
53/////////////////////////////////////////
54void rwlock_rd_acquire( rwlock_t * lock )
55{
56    thread_t * this = CURRENT_THREAD;
57
58    // check calling thread can yield
59    thread_assert_can_yield( this , __FUNCTION__ );
60
61    // get busylock
62    busylock_acquire( &lock->lock );
63
64    // block and deschedule if lock already taken
65    while( lock->taken )
66    {
67
68#if DEBUG_RWLOCK
69if( DEBUG_RWLOCK < (uint32_t)hal_get_cycles() )
70printk("\n[%s] thread[%x,%x] READ BLOCK on rwlock %s [%x,%x]\n",
71__FUNCTION__, this->process->pid, this->trdid, 
72lock_type_str[lock->lock.type], local_cxy, lock );
73#endif
74        // register reader thread in waiting queue
75        list_add_last( &lock->rd_root , &this->wait_list );
76
77        // block reader thread
78        thread_block( XPTR( local_cxy , this ) , THREAD_BLOCKED_LOCK );
79       
80        // release busylock
81        busylock_release( &lock->lock );
82
83        // deschedule
84        sched_yield("reader wait rwlock");
85       
86        // get busylock
87        busylock_acquire( &lock->lock );
88    }
89
90#if DEBUG_RWLOCK
91if( DEBUG_RWLOCK < (uint32_t)hal_get_cycles() )
92printk("\n[%s] thread[%x,%x] READ ACQUIRE rwlock %s [%x,%x]\n",
93__FUNCTION__, this->process->pid, this->trdid, 
94lock_type_str[lock->lock.type], local_cxy, lock );
95#endif
96
97    // increment number of readers
98    lock->count++;
99
100    // release busylock
101    busylock_release( &lock->lock );
102
103}  // end rwlock_rd_acquire()
104
105/////////////////////////////////////////
106void rwlock_wr_acquire( rwlock_t * lock )
107{
108    thread_t * this = CURRENT_THREAD;
109
110    // check calling thread can yield
111    thread_assert_can_yield( this , __FUNCTION__ );
112
113    // get busylock
114    busylock_acquire( &lock->lock );
115
116    // block and deschedule if lock already taken or existing read access
117    while( lock->taken || lock->count )
118    {
119
120#if DEBUG_RWLOCK
121if( DEBUG_RWLOCK < (uint32_t)hal_get_cycles() )
122printk("\n[%s] thread[%x,%x] WRITE BLOCK on rwlock %s [%x,%x]\n",
123__FUNCTION__, this->process->pid, this->trdid, 
124lock_type_str[lock->lock.type], local_cxy, lock );
125#endif
126        // register writer in waiting queue
127        list_add_last( &lock->wr_root , &this->wait_list );
128
129        // block reader thread
130        thread_block( XPTR( local_cxy , this ) , THREAD_BLOCKED_LOCK );
131       
132        // release busylock
133        busylock_release( &lock->lock );
134
135        // deschedule
136        sched_yield("writer wait rwlock");
137       
138        // get busylock
139        busylock_acquire( &lock->lock );
140    }
141
142#if DEBUG_RWLOCK
143if( DEBUG_RWLOCK < (uint32_t)hal_get_cycles() )
144printk("\n[%s] thread[%x,%x] WRITE ACQUIRE rwlock %s [%x,%x]\n",
145__FUNCTION__, this->process->pid, this->trdid, 
146lock_type_str[lock->lock.type], local_cxy, lock );
147#endif
148
149    // take the rwlock
150    lock->taken = 1;
151
152    // release busylock
153    busylock_release( &lock->lock );
154
155}  // end rwlock_wr_acquire()
156
157/////////////////////////////////////////
158void rwlock_rd_release( rwlock_t * lock )
159{
160    // synchronize memory before lock release
161    hal_fence();
162
163    // get busylock
164    busylock_acquire( &lock->lock );
165
166#if DEBUG_RWLOCK
167thread_t * this = CURRENT_THREAD;
168if( DEBUG_RWLOCK < (uint32_t)hal_get_cycles() )
169printk("\n[%s] thread[%x,%x] READ RELEASE rwlock %s [%x,%x]\n",
170__FUNCTION__, this->process->pid, this->trdid, 
171lock_type_str[lock->lock.type], local_cxy, lock );
172#endif
173
174    // decrement number of readers
175    lock->count--;
176
177    // release first writer in waiting queue if no current readers
178    // and writers waiting queue non empty
179    if( (lock->count == 0) && (list_is_empty( &lock->wr_root ) == false) )
180    {
181        // get first writer thread
182        thread_t * thread = LIST_FIRST( &lock->wr_root , thread_t , wait_list );
183
184#if DEBUG_RWLOCK
185if( DEBUG_RWLOCK < (uint32_t)hal_get_cycles() )
186printk("\n[%s] thread[%x,%x] UNBLOCK thread[%x,%x] / rwlock %s [%x,%x]\n",
187__FUNCTION__, this->process->pid, this->trdid, thread->process->pid, thread->trdid,
188lock_type_str[lock->lock.type], local_cxy, lock );
189#endif
190
191        // remove this waiting thread from waiting list
192        list_unlink( &thread->wait_list );
193
194        // unblock this waiting thread
195        thread_unblock( XPTR( local_cxy , thread ) , THREAD_BLOCKED_LOCK );
196    }
197    // release all readers in waiting queue if writers waiting queue empty
198    // and readers waiting queue non empty
199    else if( list_is_empty( &lock->wr_root ) && (list_is_empty( &lock->rd_root ) == false) )
200    {
201        while( list_is_empty( &lock->rd_root ) == false )
202        {
203            // get first reader thread
204            thread_t * thread = LIST_FIRST( &lock->wr_root , thread_t , wait_list );
205
206#if DEBUG_RWLOCK
207if( DEBUG_RWLOCK < (uint32_t)hal_get_cycles() )
208printk("\n[%s] thread[%x,%x] UNBLOCK thread[%x,%x] / rwlock %s [%x,%x]\n",
209__FUNCTION__, this->process->pid, this->trdid, thread->process->pid, thread->trdid,
210lock_type_str[lock->lock.type], local_cxy, lock );
211#endif
212   
213            // remove this waiting thread from waiting list
214            list_unlink( &thread->wait_list );
215
216            // unblock this waiting thread
217            thread_unblock( XPTR( local_cxy , thread ) , THREAD_BLOCKED_LOCK );
218        }
219    }
220
221    // release busylock
222    busylock_release( &lock->lock );
223
224}  // end rwlock_rd_release()
225
226/////////////////////////////////////////
227void rwlock_wr_release( rwlock_t * lock )
228{
229    // synchronize memory before lock release
230    hal_fence();
231
232    // get busylock
233    busylock_acquire( &lock->lock );
234
235#if DEBUG_RWLOCK
236thread_t * this = CURRENT_THREAD;
237if( DEBUG_RWLOCK < (uint32_t)hal_get_cycles() )
238printk("\n[%s] thread[%x,%x] WRITE RELEASE rwlock %s [%x,%x]\n",
239__FUNCTION__, this->process->pid, this->trdid, 
240lock_type_str[lock->lock.type], local_cxy, lock );
241#endif
242
243    // release the rwlock
244    lock->taken = 0;
245
246    // release first waiting writer thread if writers waiting queue non empty
247    if( list_is_empty( &lock->wr_root ) == false )
248    {
249        // get first writer thread
250        thread_t * thread = LIST_FIRST( &lock->wr_root , thread_t , wait_list );
251
252#if DEBUG_RWLOCK
253if( DEBUG_RWLOCK < (uint32_t)hal_get_cycles() )
254printk("\n[%s] thread[%x,%x] UNBLOCK thread[%x,%x] / rwlock %s [%x,%x]\n",
255__FUNCTION__, this->process->pid, this->trdid, thread->process->pid, thread->trdid,
256lock_type_str[lock->lock.type], local_cxy, lock );
257#endif
258        // remove this waiting thread from waiting list
259        list_unlink( &thread->wait_list );
260
261        // unblock this waiting thread
262        thread_unblock( XPTR( local_cxy , thread ) , THREAD_BLOCKED_LOCK );
263    }
264
265    // check readers waiting queue and release all if writers waiting queue empty
266    else 
267    {
268        while( list_is_empty( &lock->rd_root ) == false )
269        {
270            // get first reader thread
271            thread_t * thread = LIST_FIRST( &lock->rd_root , thread_t , wait_list );
272
273#if DEBUG_RWLOCK
274if( DEBUG_RWLOCK < (uint32_t)hal_get_cycles() )
275printk("\n[%s] thread[%x,%x] UNBLOCK thread[%x,%x] / rwlock %s [%x,%x]\n",
276__FUNCTION__, this->process->pid, this->trdid, thread->process->pid, thread->trdid,
277lock_type_str[lock->lock.type], local_cxy, lock );
278#endif
279            // remove this waiting thread from waiting list
280            list_unlink( &thread->wait_list );
281
282            // unblock this waiting thread
283            thread_unblock( XPTR( local_cxy , thread ) , THREAD_BLOCKED_LOCK );
284        }
285    }
286
287    // release busylock
288    busylock_release( &lock->lock );
289
290}  // end rwlock_wr_release()
291
292
Note: See TracBrowser for help on using the repository browser.