source: trunk/libs/mini-libpthread/pthread.c@ 441

Last change on this file since 441 was 440, checked in by alain, 8 years ago

1/ Fix a bug in the Multithreaded "sort" applicationr:
The pthread_create() arguments must be declared as global variables.
2/ The exit syscall can be called by any thread of a process..

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