source: trunk/kernel/libk/remote_mutex.c @ 599

Last change on this file since 599 was 581, checked in by alain, 6 years ago

1) Improve the busylock debug infrastructure.
2) introduce a non-distributed, but portable implementation for the pthread_barrier.

File size: 13.3 KB
Line 
1/*
2 * remote_mutex.c - POSIX mutex implementation.
3 *
4 * Authors   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_remote.h>
27#include <thread.h>
28#include <xlist.h>
29#include <scheduler.h>
30#include <remote_busylock.h>
31#include <remote_mutex.h>
32
33
34/////////////////////////////////////////////////
35xptr_t remote_mutex_from_ident( intptr_t  ident )
36{
37    // get pointer on local process_descriptor
38    process_t * process = CURRENT_THREAD->process;
39
40    // get extended pointer on reference process
41    xptr_t      ref_xp = process->ref_xp;
42
43    // get cluster and local pointer on reference process
44    cxy_t          ref_cxy = GET_CXY( ref_xp );
45    process_t    * ref_ptr = (process_t *)GET_PTR( ref_xp );
46
47    // get extended pointers on mutexes list 
48    xptr_t root_xp = XPTR( ref_cxy , &ref_ptr->mutex_root );
49    xptr_t lock_xp = XPTR( ref_cxy , &ref_ptr->sync_lock );
50
51    // get lock protecting synchro lists
52    remote_queuelock_acquire( lock_xp );
53 
54    // scan reference process mutex list
55    xptr_t           iter_xp;
56    xptr_t           mutex_xp;
57    cxy_t            mutex_cxy;
58    remote_mutex_t * mutex_ptr;
59    intptr_t         current;
60    bool_t           found = false;
61           
62    XLIST_FOREACH( root_xp , iter_xp )
63    {
64        mutex_xp  = XLIST_ELEMENT( iter_xp , remote_mutex_t , list );
65        mutex_cxy = GET_CXY( mutex_xp );
66        mutex_ptr = (remote_mutex_t *)GET_PTR( mutex_xp );
67        current     = (intptr_t)hal_remote_lpt( XPTR( mutex_cxy , &mutex_ptr->ident ) );   
68        if( ident == current )
69        {
70            found = true;
71            break;
72        }
73    }
74
75    // relese lock protecting synchros lists
76    remote_queuelock_release( lock_xp );
77 
78    if( found == false )  return XPTR_NULL;
79    else                  return mutex_xp;
80
81}  // end remote_mutex_from_ident()
82
83/////////////////////////////////////////////
84error_t remote_mutex_create( intptr_t ident )
85{ 
86    xptr_t           mutex_xp;
87    remote_mutex_t * mutex_ptr;
88
89    // get pointer on local process descriptor
90    process_t * process = CURRENT_THREAD->process;
91
92    // get extended pointer on reference process
93    xptr_t      ref_xp = process->ref_xp;
94
95    // get reference process cluster and local pointer
96    cxy_t       ref_cxy = GET_CXY( ref_xp );
97    process_t * ref_ptr = (process_t *)GET_PTR( ref_xp );
98
99    // allocate memory for mutex descriptor
100    if( ref_cxy == local_cxy )                  // local cluster is the reference
101    {
102        kmem_req_t req;   
103        req.type    = KMEM_MUTEX;
104        req.flags   = AF_ZERO;
105        mutex_ptr   = kmem_alloc( &req );
106        mutex_xp    = XPTR( local_cxy , mutex_ptr );
107    }
108    else                                       // reference is remote
109    {
110        rpc_kcm_alloc_client( ref_cxy , KMEM_MUTEX , &mutex_xp );
111        mutex_ptr = GET_PTR( mutex_xp );
112    }
113
114    if( mutex_ptr == NULL ) return 0xFFFFFFFF;
115
116    // initialise mutex
117    hal_remote_s32 ( XPTR( ref_cxy , &mutex_ptr->taken )   , 0 );
118    hal_remote_spt( XPTR( ref_cxy , &mutex_ptr->ident )   , (void *)ident );
119    xlist_entry_init( XPTR( ref_cxy , &mutex_ptr->list ) );
120    xlist_root_init( XPTR( ref_cxy , &mutex_ptr->root ) );
121    hal_remote_s64( XPTR( ref_cxy , &mutex_ptr->owner ) , XPTR_NULL );
122    remote_busylock_init( XPTR( ref_cxy , &mutex_ptr->lock ), LOCK_MUTEX_STATE );
123
124    // get root of mutexes list in process, and list_entry in mutex
125    xptr_t root_xp = XPTR( ref_cxy , &ref_ptr->mutex_root );
126    xptr_t xp_list = XPTR( ref_cxy , &mutex_ptr->list );
127
128    // get lock protecting user synchros lists
129    remote_queuelock_acquire( XPTR( ref_cxy , &ref_ptr->sync_lock ) );
130
131    // register mutex in process descriptor
132    xlist_add_first( root_xp , xp_list );
133
134    // release lock protecting user synchros lists
135    remote_queuelock_release( XPTR( ref_cxy , &ref_ptr->sync_lock ) );
136
137#if DEBUG_MUTEX
138thread_t * this = CURRENT_THREAD;
139if( (uint32_t)hal_get_cycles() > DEBUG_QUEUELOCK )
140printk("\n[DBG] %s : thread %x in %x process / mutex(%x,%x)\n",
141__FUNCTION__, this->trdid, this->process->pid, local_cxy, mutex_ptr );
142#endif
143
144
145    return 0;
146
147}  // end remote_mutex_create()
148
149////////////////////////////////////////////
150void remote_mutex_destroy( xptr_t mutex_xp )
151{
152    // get pointer on local process descriptor
153    process_t * process = CURRENT_THREAD->process;
154
155    // get extended pointer on reference process
156    xptr_t      ref_xp = process->ref_xp;
157
158    // get reference process cluster and local pointer
159    cxy_t       ref_cxy = GET_CXY( ref_xp );
160    process_t * ref_ptr = (process_t *)GET_PTR( ref_xp );
161
162    // get mutex cluster and local pointer
163    cxy_t            mutex_cxy = GET_CXY( mutex_xp );
164    remote_mutex_t * mutex_ptr = (remote_mutex_t *)GET_PTR( mutex_xp );
165
166    // get lock protecting user synchros lists
167    remote_queuelock_acquire( XPTR( ref_cxy , &ref_ptr->sync_lock ) );
168
169    // remove mutex from reference process xlist
170    xlist_unlink( XPTR( mutex_cxy , &mutex_ptr->list ) );
171
172    // release lock protecting user synchros lists
173    remote_queuelock_release( XPTR( ref_cxy , &ref_ptr->sync_lock ) );
174
175    // release memory allocated for mutexaphore descriptor
176    if( mutex_cxy == local_cxy )                            // reference is local
177    {
178        kmem_req_t  req;
179        req.type = KMEM_MUTEX;
180        req.ptr  = mutex_ptr;
181        kmem_free( &req );
182    }
183    else                                                  // reference is remote
184    {
185        rpc_kcm_free_client( mutex_cxy , mutex_ptr , KMEM_BARRIER );
186    }
187
188}  // end remote_mutex_destroy()
189
190/////////////////////////////////////////
191void remote_mutex_lock( xptr_t mutex_xp )
192{ 
193    // get cluster and pointers on calling thread
194    cxy_t            caller_cxy = local_cxy;
195    thread_t       * caller_ptr = CURRENT_THREAD;
196    xptr_t           caller_xp  = XPTR( caller_cxy , caller_ptr );
197
198    // check calling thread can yield
199    thread_assert_can_yield( caller_ptr , __FUNCTION__ );
200
201    // get cluster and local pointer on mutex
202    remote_mutex_t * mutex_ptr = GET_PTR( mutex_xp );
203    cxy_t            mutex_cxy = GET_CXY( mutex_xp );
204
205    // get extended pointers on mutex fields
206    xptr_t           taken_xp = XPTR( mutex_cxy , &mutex_ptr->taken );
207    xptr_t           owner_xp = XPTR( mutex_cxy , &mutex_ptr->owner );
208    xptr_t           root_xp  = XPTR( mutex_cxy , &mutex_ptr->root );
209    xptr_t           lock_xp  = XPTR( mutex_cxy , &mutex_ptr->lock );
210
211    while( 1 )
212    {
213        // get busylock protecting mutex state
214        remote_busylock_acquire( lock_xp );
215
216        // test mutex state
217        if( hal_remote_l32( taken_xp ) == 0 )                 // success
218        {
219            // register calling thread as mutex owner
220            hal_remote_s64( owner_xp , caller_xp );
221
222            // update mutex state
223            hal_remote_s32( taken_xp , 1 );
224
225#if DEBUG_MUTEX
226thread_t * this = CURRENT_THREAD;
227if( (uint32_t)hal_get_cycles() > DEBUG_MUTEX )
228printk("\n[DBG] %s : thread %x in process %x SUCCESS on mutex(%x,%x)\n",
229__FUNCTION__, this->trdid, this->process->pid, mutex_cxy, mutex_ptr );
230#endif
231
232            // release busylock protecting mutex state
233            remote_busylock_release( lock_xp ); 
234
235             return;
236        }
237        else                                                 //  already taken
238        {
239            // block the calling thread   
240            thread_block( caller_xp , THREAD_BLOCKED_USERSYNC );
241
242            // register calling thread in mutex waiting queue
243            xptr_t entry_xp = XPTR( caller_cxy , &caller_ptr->wait_xlist );
244            xlist_add_last( root_xp , entry_xp );
245
246#if DEBUG_MUTEX
247thread_t * this = CURRENT_THREAD;
248if( (uint32_t)hal_get_cycles() > DEBUG_MUTEX )
249printk("\n[DBG] %s : thread %x in process %x BLOCKED on mutex(%x,%x)\n",
250__FUNCTION__, this->trdid, this->process->pid, mutex_cxy, mutex_ptr );
251#endif
252
253            // release busylock protecting mutex state
254            remote_busylock_release( lock_xp ); 
255
256            // deschedule calling thread
257            sched_yield("blocked on mutex");
258        }
259    } 
260}  // end remote_mutex_lock()
261
262//////////////////////////////////////////////
263error_t remote_mutex_unlock( xptr_t mutex_xp )
264{
265    // memory barrier before mutex release
266    hal_fence();
267
268    // get cluster and local pointer on mutex
269    remote_mutex_t * mutex_ptr = GET_PTR( mutex_xp );
270    cxy_t            mutex_cxy = GET_CXY( mutex_xp );
271
272    // get cluster and pointers on calling thread
273    cxy_t            caller_cxy = local_cxy;
274    thread_t       * caller_ptr = CURRENT_THREAD;
275    xptr_t           caller_xp  = XPTR( caller_cxy , caller_ptr );
276
277    // get extended pointers on mutex fields
278    xptr_t           taken_xp = XPTR( mutex_cxy , &mutex_ptr->taken );
279    xptr_t           owner_xp = XPTR( mutex_cxy , &mutex_ptr->owner );
280    xptr_t           root_xp  = XPTR( mutex_cxy , &mutex_ptr->root );
281    xptr_t           lock_xp  = XPTR( mutex_cxy , &mutex_ptr->lock );
282
283    // get busylock protecting mutex state
284    remote_busylock_acquire( lock_xp );
285   
286    // check calling thread is mutex owner
287    if( hal_remote_l64( owner_xp ) != caller_xp )
288    {
289        // release busylock protecting mutex state
290        remote_busylock_release( lock_xp );
291
292        return 0xFFFFFFFF;
293    }
294
295#if DEBUG_MUTEX
296thread_t * this = CURRENT_THREAD;
297if( (uint32_t)hal_get_cycles() > DEBUG_MUTEX )
298printk("\n[DBG] %s : thread %x in %x process EXIT / mutex(%x,%x)\n",
299__FUNCTION__, this->trdid, this->process->pid, mutex_cxy, mutex_ptr );
300#endif
301
302    // update owner field,
303    hal_remote_s64( owner_xp , XPTR_NULL );
304
305    // update taken field
306    hal_remote_s32( taken_xp , 0 );
307
308    // unblock first waiting thread if waiting list non empty
309    if( xlist_is_empty( root_xp ) == false )
310    {
311        // get extended pointer on first waiting thread
312        xptr_t     thread_xp  = XLIST_FIRST( root_xp , thread_t , wait_xlist );
313        thread_t * thread_ptr = GET_PTR( thread_xp );
314        cxy_t      thread_cxy = GET_CXY( thread_xp );
315
316#if DEBUG_MUTEX
317if( (uint32_t)hal_get_cycles() > DEBUG_MUTEX )
318{
319trdid_t     trdid   = hal_remote_l32( XPTR( thread_cxy , &thread_ptr->trdid ) );
320process_t * process = hal_remote_lpt( XPTR( thread_cxy , &thread_ptr->process ) );
321pid_t       pid     = hal_remote_l32( XPTR( thread_cxy , &process->pid ) );
322printk("\n[DBG] %s : thread %x in process %x UNBLOCK thread %x in process %d / mutex(%x,%x)\n",
323__FUNCTION__, this->trdid, this->process->pid, trdid, pid, mutex_cxy, mutex_ptr );
324}
325#endif
326
327        // remove this thread from waiting queue
328        xlist_unlink( XPTR( thread_cxy , &thread_ptr->wait_xlist ) );
329
330        // unblock first waiting thread
331        thread_unblock( thread_xp , THREAD_BLOCKED_USERSYNC ); 
332    }
333   
334    // release busylock protecting mutex state
335    remote_busylock_release( lock_xp );
336
337    return 0;
338
339}  // end remote_mutex_unlock()
340
341///////////////////////////////////////////////
342error_t remote_mutex_trylock( xptr_t mutex_xp )
343{
344    // get cluster and local pointer on mutex
345    remote_mutex_t * mutex_ptr = GET_PTR( mutex_xp );
346    cxy_t            mutex_cxy = GET_CXY( mutex_xp );
347
348    // get cluster and pointers on calling thread
349    cxy_t            caller_cxy = local_cxy;
350    thread_t       * caller_ptr = CURRENT_THREAD;
351    xptr_t           caller_xp  = XPTR( caller_cxy , caller_ptr );
352
353    // get extended pointers on mutex fields
354    xptr_t           taken_xp = XPTR( mutex_cxy , &mutex_ptr->taken );
355    xptr_t           owner_xp = XPTR( mutex_cxy , &mutex_ptr->owner );
356    xptr_t           lock_xp  = XPTR( mutex_cxy , &mutex_ptr->lock );
357
358    // get busylock protecting mutex state
359    remote_busylock_acquire( lock_xp );
360
361    // test mutex state
362    if( hal_remote_l32( taken_xp ) == 0 )                 // success
363    {
364        // register calling thread as mutex owner
365        hal_remote_s64( owner_xp , caller_xp );
366
367        // update mutex state
368        hal_remote_s32( taken_xp , 1 );
369
370#if DEBUG_MUTEX
371thread_t * this = CURRENT_THREAD;
372if( (uint32_t)hal_get_cycles() > DEBUG_QUEUELOCK )
373printk("\n[DBG] %s : SUCCESS for thread %x in process %x / mutex(%x,%x)\n",
374__FUNCTION__, this->trdid, this->process->pid, mutex_cxy, mutex_ptr );
375#endif
376        // release busylock protecting mutex state
377        remote_busylock_release( lock_xp ); 
378
379        return 0;
380    }
381    else                                                 //  already taken
382    {
383
384#if DEBUG_MUTEX
385thread_t * this = CURRENT_THREAD;
386if( (uint32_t)hal_get_cycles() > DEBUG_QUEUELOCK )
387printk("\n[DBG] %s : FAILURE for thread %x in process %x / mutex(%x,%x)\n",
388__FUNCTION__, this->trdid, this->process->pid, mutex_cxy, mutex_ptr );
389#endif
390        // release busylock protecting mutex state
391        remote_busylock_release( lock_xp ); 
392
393        return 0xFFFFFFFF;
394    }
395}  // end remote_mutex_trylock()
Note: See TracBrowser for help on using the repository browser.