source: trunk/libs/libpthread/pthread.c @ 445

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

Restructure the mini_libc.

File size: 14.6 KB
Line 
1/*
2 * pthread.c - User leve <pthread> library 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 <hal_user.h>
25#include <hal_types.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <pthread.h>
29#include <almosmkh.h>
30#include <syscalls_numbers.h>
31
32#define PTHREAD_MUTEX_DEBUG     0
33#define PTHREAD_BARRIER_DEBUG   0
34
35////////////////////////////////////////////////////////////////////////////////////////////
36//                  Threads
37////////////////////////////////////////////////////////////////////////////////////////////
38
39/////////////////////////////////////////////////
40int pthread_create( pthread_t            * trdid,
41                    const pthread_attr_t * attr,
42                    void                 * start_func,
43                    void                 * start_args )
44{
45    return hal_user_syscall( SYS_THREAD_CREATE,
46                             (reg_t)trdid,
47                             (reg_t)attr,
48                             (reg_t)start_func,
49                             (reg_t)start_args );
50}
51
52/////////////////////////////////////
53int pthread_join( pthread_t    trdid,
54                  void      ** exit_value )
55{
56    return hal_user_syscall( SYS_THREAD_JOIN,
57                             (reg_t)exit_value, 0, 0, 0 );
58}
59
60///////////////////////////////////////
61int pthread_detach( pthread_t   trdid )
62{
63    return hal_user_syscall( SYS_THREAD_DETACH,
64                             (reg_t)trdid, 0, 0, 0 );
65}
66
67/////////////////////////////////////
68int pthread_exit( void * exit_value )
69{
70    return hal_user_syscall( SYS_THREAD_EXIT,
71                             (reg_t)exit_value, 0, 0, 0 );
72}
73
74///////////////////
75int pthread_yield()
76{
77    return hal_user_syscall( SYS_THREAD_YIELD, 0, 0, 0, 0 );
78}
79
80////////////////////////////////////////////////////////////////////////////////////////////
81//                            Barriers
82////////////////////////////////////////////////////////////////////////////////////////////
83
84////////////////////////////////////////////////////////////////////////////////////////////
85// This recursive function initializes the SQT nodes
86// traversing the SQT from root to bottom
87////////////////////////////////////////////////////////////////////////////////////////////
88static void sqt_barrier_build( pthread_barrier_t  * barrier, 
89                               unsigned int         x,
90                               unsigned int         y,
91                               unsigned int         level,
92                               sqt_node_t         * parent,
93                               unsigned int         x_size,
94                               unsigned int         y_size,
95                               unsigned int         nthreads ) 
96{
97    // get target node address
98    sqt_node_t * node = barrier->node[x][y][level];
99   
100    if (level == 0 )        // terminal case
101    {
102        // initializes target node
103        node->arity    = nthreads;   
104        node->count    = nthreads;   
105        node->sense    = 0;   
106        node->level    = 0;   
107        node->parent   = parent;
108        node->child[0] = NULL;
109        node->child[1] = NULL;
110        node->child[2] = NULL;
111        node->child[3] = NULL;
112
113#if PTHREAD_BARRIER_DEBUG
114printf("\n[BARRIER] %s : sqt_node[%d][%d][%d] / arity %d / desc %x\n"
115"parent %x / child0 %x / child1 %x / child2 %x / child3 %x\n", 
116__FUNCTION__, x, y, level, node->arity, node, node->parent, 
117node->child[0], node->child[1], node->child[2], node->child[3] );
118#endif
119
120    }
121    else                   // non terminal case
122    {
123        unsigned int cx[4];   // x coordinate for children
124        unsigned int cy[4];   // y coordinate for children
125        unsigned int arity = 0;
126        unsigned int i;
127
128        // the child0 coordinates are equal to the parent coordinates
129        // other children coordinates are incremented depending on the level value
130        cx[0] = x;
131        cy[0] = y;
132
133        cx[1] = x;
134        cy[1] = y + (1 << (level-1));
135
136        cx[2] = x + (1 << (level-1));
137        cy[2] = y;
138
139        cx[3] = x + (1 << (level-1));
140        cy[3] = y + (1 << (level-1));
141
142        // initializes parent node taken into account the actual number of childs
143        // child pointer is NULL if coordinates outside the mesh
144        for ( i = 0 ; i < 4 ; i++ )
145        {
146            if ( (cx[i] < x_size) && (cy[i] < y_size) ) 
147            {
148                node->child[i] = barrier->node[cx[i]][cy[i]][level-1];
149                arity++;
150            }
151            else  node->child[i] = NULL;
152        }
153        node->arity    = arity; 
154        node->count    = arity;
155        node->sense    = 0;
156        node->level    = level;
157        node->parent   = parent;
158
159#if PTHREAD_BARRIER_DEBUG
160printf("\n[BARRIER] %s : sqt_node[%d][%d][%d] / arity %d / desc %x\n"
161"parent %x / child0 %x / child1 %x / child2 %x / child3 %x\n", 
162__FUNCTION__, x, y, level, node->arity, node, node->parent,
163node->child[0], node->child[1], node->child[2], node->child[3] );
164#endif
165
166        // recursive calls for children nodes
167        for ( i = 0 ; i < 4 ; i++ )
168        {
169            if ( (cx[i] < x_size) && (cy[i] < y_size) ) 
170            sqt_barrier_build( barrier, 
171                               cx[i], 
172                               cy[i], 
173                               level-1, 
174                               node, 
175                               x_size,
176                               y_size,
177                               nthreads );
178        }
179    }
180}  // end sqt_barrier_build()
181
182////////////////////////////////////////////////////////////////
183int pthread_barrier_init( pthread_barrier_t           * barrier,
184                          const pthread_barrierattr_t * attr,
185                          unsigned int                  count )
186{
187    unsigned int x_size;
188    unsigned int y_size;
189    unsigned int nthreads;
190
191    if( attr != NULL )
192    {
193        x_size   = attr->x_size;
194        y_size   = attr->y_size;
195        nthreads = attr->nthreads;
196    }
197    else
198    {
199        x_size   = 1;
200        y_size   = 1;
201        nthreads = count;
202    }
203
204    // check attributes
205    if( (x_size * y_size * nthreads) != count )
206    {
207        printf("\[ERROR] in %s : count != x_size * y_size * nthreads/n", __FUNCTION__);
208        exit( EXIT_FAILURE );
209    }
210   
211    // compute SQT levels
212    unsigned int levels; 
213    unsigned int z = (x_size > y_size) ? x_size : y_size;
214    levels = (z < 2) ? 1 : (z < 3) ? 2 : (z < 5) ? 3 : (z < 9) ? 4 : 5;
215
216#if PTHREAD_BARRIER_DEBUG
217unsigned int side = (z < 2) ? 1 : (z < 3) ? 2 : (z < 5) ? 4 : (z < 9) ? 8 : 16;
218printf("\n[BARRIER] %s : x_size = %d / y_size = %d / levels = %d / side = %d\n",
219__FUNCTION__ , x_size , y_size , levels , side );
220#endif
221
222    // allocates memory for the SQT nodes and initializes SQT nodes pointers array
223    // the actual number of SQT nodes in a cluster(x,y) depends on (x,y):
224    // At least 1 node / at most 5 nodes
225    unsigned int x;          // x coordinate for one SQT node
226    unsigned int y;          // y coordinate for one SQT node
227    unsigned int l;          // level for one SQT node
228    for ( x = 0 ; x < x_size ; x++ )
229    {
230        for ( y = 0 ; y < y_size ; y++ )
231        {
232            unsigned int cxy = (x<<QDT_YWIDTH) + y;
233               
234            for ( l = 0 ; l < levels ; l++ )         
235            {
236                if ( ( (l == 0) && ((x&0x00) == 0) && ((y&0x00) == 0) ) ||
237                     ( (l == 1) && ((x&0x01) == 0) && ((y&0x01) == 0) ) ||
238                     ( (l == 2) && ((x&0x03) == 0) && ((y&0x03) == 0) ) ||
239                     ( (l == 3) && ((x&0x07) == 0) && ((y&0x07) == 0) ) ||
240                     ( (l == 4) && ((x&0x0F) == 0) && ((y&0x0F) == 0) ) )
241                 {
242                     sqt_node_t * node = remote_malloc( sizeof(sqt_node_t) , cxy ); 
243
244                     if( node == NULL )
245                     {
246                         printf("\n[ERROR] in %s : cannot allocate sqt_node in cluster %x\n",
247                         __FUNCTION__ , cxy );
248                         return -1;
249                     }
250
251                     barrier->node[x][y][l] = node;
252
253                 }
254            }
255        }
256    }
257           
258    // recursively initialize all SQT nodes from root to bottom
259    sqt_barrier_build( barrier,
260                       0,       
261                       0,
262                       levels-1,
263                       NULL,
264                       x_size,
265                       y_size,
266                       nthreads );
267
268    hal_user_fence();
269
270    return 0;
271
272}  // end pthread_barrier_init
273
274//////////////////////////////////////////////////////////////////////////////////////////
275// This recursive function decrements the distributed "count" variables,
276// traversing the SQT from bottom to root.
277// The last arrived thread reset the local node before returning.
278//////////////////////////////////////////////////////////////////////////////////////////
279static void sqt_barrier_decrement( sqt_node_t * node )
280{
281
282#if PTHREAD_BARRIER_DEBUG
283unsigned int    cxy;
284unsigned int    lid;
285get_core( &cxy , &lid );
286printf("\n[BARRIER] %s : core[%x,%d] decrement SQT barrier node %x :\n"
287" level = %d / parent = %x / arity = %d / sense = %d / count = %d\n",
288__FUNCTION__ , cxy , lid , (unsigned int)node , 
289node->level , node->parent, node->arity , node->sense , node->count );
290#endif
291
292    unsigned int expected;
293   
294    // compute expected sense value
295    if ( node->sense == 0) expected = 1;
296    else                   expected = 0;
297
298    // atomically decrement count
299    int count = hal_user_atomic_add( (int *)&node->count , -1 );
300
301    // last arrived thread makes the recursive call
302    if ( count == 1 )                                     // last thread 
303    {
304        // decrement the parent node if the current node is not the root
305        if ( node->parent != NULL )  sqt_barrier_decrement( node->parent );
306
307        // reset the current node
308        node->sense = expected;
309        node->count = node->arity;
310
311#if PTHREAD_BARRIER_DEBUG
312printf("\n[BARRIER] %s : core[%x,%d] reset SQT barrier node %x :\n"
313" level = %d / arity = %d / sense = %d / count = %d\n",
314__FUNCTION__ , cxy , lid , (unsigned int)node , 
315node->level , node->arity , node->sense , node->count );
316#endif
317        return;
318    }
319    else                                               // not the last thread
320    {
321        while( 1 )
322        {
323            // poll sense
324            if( node->sense == expected ) break;
325
326            // deschedule
327            pthread_yield();
328        }
329
330        return;
331    }
332} // end sqt_barrier_decrement()
333   
334///////////////////////////////////////////////////////
335int pthread_barrier_wait( pthread_barrier_t * barrier )
336{
337    // get calling core cluster
338    unsigned int    cxy;
339    unsigned int    lid;
340    get_core( &cxy , &lid );
341
342    // get calling core coordinate
343    unsigned int    x = cxy >> QDT_YWIDTH;
344    unsigned int    y = cxy &  QDT_YMASK;
345
346#if PTHREAD_BARRIER_DEBUG
347printf("\n[BARRIER] %s : enter for core[%x,%d] / barrier = %x / node = %x\n", 
348__FUNCTION__ , cxy , lid , barrier, barrier->node[x][y][0] );
349#endif
350
351    // recursively decrement count from bottom to root
352    sqt_barrier_decrement( barrier->node[x][y][0] );
353
354    hal_user_fence();
355
356    return 0;
357
358}  // end pthread_barrier_wait()
359
360////////////////////////////////////////////////////////////////////////////////////////////
361//                               Mutexes
362////////////////////////////////////////////////////////////////////////////////////////////
363
364//////////////////////////////////////////////////////////
365int pthread_mutex_init( pthread_mutex_t           * mutex,
366                        const pthread_mutexattr_t * attr )
367{
368    if( attr != NULL )
369    {
370        printf("\n[ERROR] in %s : <attr> argument not supported\n", __FUNCTION__);
371        return -1;
372    }
373
374    mutex->current = 0;
375    mutex->free    = 0;
376
377#if PTHEAD_MUTEX_DEBUG
378unsigned int cxy;
379unsigned int lid;
380get_core( &cxy , &lid );
381printf("\n[MUTEX DEBUG] %s : core[%x,%d] initializes mutex %x\n",
382__FUNCTION__, cxy, lid, mutex );
383#endif
384
385    return 0;
386}
387
388/////////////////////////////////////////////////
389int pthread_mutex_lock( pthread_mutex_t * mutex ) 
390{
391    unsigned int          ticket;
392
393    // get next free ticket
394    ticket = (unsigned int)hal_user_atomic_add( (int *)&mutex->free, 1 );
395
396#if PTHREAD_MUTEX_DEBUG
397unsigned int cxy;
398unsigned int lid;
399get_core( &cxy , &lid );
400printf("\n[MUTEX DEBUG] %s : core[%x,%d] get ticket %d\n",
401" / mutex = %x / current = %d / free = %d\n",
402__FUNCTION__, cxy, lid, ticket, mutex, mutex->current, mutex->free );
403#endif
404
405    // poll the current index
406    while( 1 )
407    {
408        if( mutex->current == ticket) break;
409    }
410               
411#if PTHREAD_MUTEX_DEBUG
412printf("\n[MUTEX DEBUG] %s : core[%x,%d] get mutex %x / current = %d / free = %d\n",
413__FUNCTION__, cxy, lid, mutex, mutex->current, mutex->free );
414#endif
415
416    return 0;
417}
418
419////////////////////////////////////////////////////
420int pthread_mutex_trylock( pthread_mutex_t * mutex )
421{
422    unsigned int          ticket;
423
424    // get next free ticket
425    ticket = (unsigned int)hal_user_atomic_add( (int *)&mutex->free, 1 );
426
427#if PTHREAD_MUTEX_DEBUG
428unsigned int    cxy;
429unsigned int    lid;
430get_core( &cxy, &lid );
431printf("\n[MUTEX DEBUG] %s : core[%x,%d] get ticket = %d"
432" / mutex = %x / current = %d / free = %d\n",
433__FUNCTION__, cxy, lid, ticket, mutex, mutex->current, mutex->free );
434#endif
435
436    // test ticket
437    if( ticket == mutex->current ) return 0;       // success
438    else                           return -1;      // failure
439}
440   
441///////////////////////////////////////////////////
442int pthread_mutex_unlock( pthread_mutex_t * mutex )
443{
444    hal_user_fence();
445
446    mutex->current = mutex->current + 1;
447
448#if PTHREAD_MUTEX_DEBUG
449unsigned int    cxy;
450unsigned int    lid;
451get_core( &cxy , &lid );
452printf("\n[MUTEX_DEBUG] %s : core[%x,%d] releases mutex %x"
453" / current = %d / free = %d\n",
454__FUNCTION__, cxy, lid, mutex, mutex->current, mutex->free );
455#endif
456
457    return 0;
458}
459
460
461// Local Variables:
462// tab-width: 4
463// c-basic-offset: 4
464// c-file-offsets:((innamespace . 0)(inline-open . 0))
465// indent-tabs-mode: nil
466// End:
467// vim: filetype=c:expandtab:shiftwidth=4:tabstop=4:softtabstop=4
468
Note: See TracBrowser for help on using the repository browser.