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

Last change on this file since 645 was 637, checked in by alain, 5 years ago

Introduce the non-standard pthread_parallel_create() system call
and re-write the <fft> and <sort> applications to improve the
intrinsic paralelism in applications.

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