fixed tau compilation
[charm.git] / src / arch / util / machine-smp.c
1 /** @file
2  * @brief Common function reimplementation for SMP machines
3  * @ingroup Machine
4  *
5  * OS Threads
6  *
7  * This version of converse is for multiple-processor workstations,
8  * and we assume that the OS provides threads to gain access to those
9  * multiple processors.  This section contains an interface layer for
10  * the OS specific threads package.  It contains routines to start
11  * the threads, routines to access their thread-specific state, and
12  * routines to control mutual exclusion between them.
13  *
14  * In addition, we wish to support nonthreaded operation.  To do this,
15  * we provide a version of these functions that uses the main/only thread
16  * as a single PE, and simulates a communication thread using interrupts.
17  *
18  *
19  * CmiStartThreads()
20  *
21  *    Allocates one CmiState structure per PE.  Initializes all of
22  *    the CmiState structures using the function CmiStateInit.
23  *    Starts processor threads 1..N (not 0, that's the one
24  *    that calls CmiStartThreads), as well as the communication
25  *    thread.  Each processor thread (other than 0) must call ConverseInitPE
26  *    followed by Cmi_startfn.  The communication thread must be an infinite
27  *    loop that calls the function CommunicationServer over and over.
28  *
29  * CmiGetState()
30  *
31  *    When called by a PE-thread, returns the processor-specific state
32  *    structure for that PE.
33  *
34  * CmiGetStateN(int n)
35  *
36  *    returns processor-specific state structure for the PE of rank n.
37  *
38  * CmiMemLock() and CmiMemUnlock()
39  *
40  *    The memory module calls these functions to obtain mutual exclusion
41  *    in the memory routines, and to keep interrupts from reentering malloc.
42  *
43  * CmiCommLock() and CmiCommUnlock()
44  *
45  *    These functions lock a mutex that insures mutual exclusion in the
46  *    communication routines.
47  *
48  * CmiMyPe() and CmiMyRank()
49  *
50  *    The usual.  Implemented here, since a highly-optimized version
51  *    is possible in the nonthreaded case.
52  *
53
54   
55   FIXME: There is horrible duplication of code (e.g. locking code)
56    both here and in converse.h.  It could be much shorter.  OSL 9/9/2000
57
58  *****************************************************************************/
59
60 /**
61  * \addtogroup Machine
62  * @{
63  */
64
65 /*
66 for SMP versions:
67
68 CmiStateInit
69 CmiNodeStateInit
70 CmiGetState
71 CmiGetStateN
72 CmiYield
73 CmiStartThreads
74
75 CmiIdleLock_init
76 CmiIdleLock_sleep
77 CmiIdleLock_addMessage
78 CmiIdleLock_checkMessage
79 */
80
81 #include "machine-smp.h"
82
83 void CmiStateInit(int pe, int rank, CmiState state);
84 void CommunicationServerInit();
85
86 /************************ Win32 kernel SMP threads **************/
87 static struct CmiStateStruct Cmi_default_state; /* State structure to return during startup */
88
89 #if CMK_SHARED_VARS_NT_THREADS
90
91 CmiNodeLock CmiMemLock_lock;
92 static HANDLE comm_mutex;
93 #define CmiCommLockOrElse(x) /*empty*/
94 #define CmiCommLock() (WaitForSingleObject(comm_mutex, INFINITE))
95 #define CmiCommUnlock() (ReleaseMutex(comm_mutex))
96
97 static DWORD Cmi_state_key = 0xFFFFFFFF;
98 static CmiState     Cmi_state_vector = 0;
99
100 #if 0
101 #  define CmiGetState() ((CmiState)TlsGetValue(Cmi_state_key))
102 #else
103 CmiState CmiGetState()
104 {
105   CmiState result;
106   result = (CmiState)TlsGetValue(Cmi_state_key);
107   if(result == 0) {
108         return &Cmi_default_state;
109         /* PerrorExit("CmiGetState: TlsGetValue");*/
110   }
111   return result;
112 }
113 #endif
114
115 CmiNodeLock CmiCreateLock(void)
116 {
117   HANDLE hMutex = CreateMutex(NULL, FALSE, NULL);
118   return hMutex;
119 }
120
121 void CmiDestroyLock(CmiNodeLock lk)
122 {
123   CloseHandle(lk);
124 }
125
126 void CmiYield(void) 
127
128   Sleep(0);
129 }
130
131 #define CmiGetStateN(n) (Cmi_state_vector+(n))
132
133 /*
134 static DWORD WINAPI comm_thread(LPVOID dummy)
135 {  
136   if (Cmi_charmrun_fd!=-1)
137     while (1) CommunicationServerThread(5);
138   return 0;
139 }
140
141 static DWORD WINAPI call_startfn(LPVOID vindex)
142 {
143   int index = (int)vindex;
144  
145   CmiState state = Cmi_state_vector + index;
146   if(Cmi_state_key == 0xFFFFFFFF) PerrorExit("TlsAlloc");
147   if(TlsSetValue(Cmi_state_key, (LPVOID)state) == 0) PerrorExit("TlsSetValue");
148
149   ConverseRunPE(0);
150   return 0;
151 }
152 */
153
154 static DWORD WINAPI call_startfn(LPVOID vindex)
155 {
156   int index = (int)vindex;
157  
158   CmiState state = Cmi_state_vector + index;
159   if(Cmi_state_key == 0xFFFFFFFF) PerrorExit("TlsAlloc");
160   if(TlsSetValue(Cmi_state_key, (LPVOID)state) == 0) PerrorExit("TlsSetValue");
161
162   ConverseRunPE(0);
163 #if 0
164   if (index<_Cmi_mynodesize)
165           ConverseRunPE(0); /*Regular worker thread*/
166   else { /*Communication thread*/
167           CommunicationServerInit();
168           if (Cmi_charmrun_fd!=-1)
169                   while (1) CommunicationServerThread(5);
170   } 
171 #endif
172   return 0;
173 }
174
175
176 /*Classic sense-reversing barrier algorithm.
177 FIXME: This should be the barrier implementation for 
178 all thread types.
179 */
180 static volatile HANDLE barrier_mutex;
181 static volatile int    barrier_wait[2] = {0,0};
182 static volatile int    barrier_which = 0;
183
184 void CmiNodeBarrierCount(int nThreads) {
185   int doWait = 1;
186   int which;
187
188   while (WaitForSingleObject(barrier_mutex, INFINITE)!=WAIT_OBJECT_0);
189   which=barrier_which;
190   barrier_wait[which]++;
191   if (barrier_wait[which] == nThreads) {
192     barrier_which = !which;
193     barrier_wait[barrier_which] = 0;/*Reset new counter*/
194     doWait = 0;
195   }
196   while (!ReleaseMutex(barrier_mutex));
197
198   if (doWait)
199       while(barrier_wait[which] != nThreads)
200                   sleep(0);/*<- could also just spin here*/
201 }
202
203 static void CmiStartThreads(char **argv)
204 {
205   int     i,tocreate;
206   DWORD   threadID;
207   HANDLE  thr;
208
209   CmiMemLock_lock=CmiCreateLock();
210   comm_mutex = CmiCreateLock();
211   barrier_mutex = CmiCreateLock();
212
213   Cmi_state_key = TlsAlloc();
214   if(Cmi_state_key == 0xFFFFFFFF) PerrorExit("TlsAlloc main");
215   
216   Cmi_state_vector =
217     (CmiState)calloc(_Cmi_mynodesize+1, sizeof(struct CmiStateStruct));
218   
219   for (i=0; i<_Cmi_mynodesize; i++)
220     CmiStateInit(i+Cmi_nodestart, i, CmiGetStateN(i));
221   /*Create a fake state structure for the comm. thread*/
222 /*  CmiStateInit(-1,_Cmi_mynodesize,CmiGetStateN(_Cmi_mynodesize)); */
223   CmiStateInit(_Cmi_mynode+CmiNumPes(),_Cmi_mynodesize,CmiGetStateN(_Cmi_mynodesize));
224   
225 #if CMK_MULTICORE
226   if (!Cmi_commthread)
227     tocreate = _Cmi_mynodesize-1;
228   else
229 #endif
230   tocreate = _Cmi_mynodesize;
231   for (i=1; i<=tocreate; i++) {
232     if((thr = CreateThread(NULL, 0, call_startfn, (LPVOID)i, 0, &threadID)) 
233        == NULL) PerrorExit("CreateThread");
234     CloseHandle(thr);
235   }
236   
237   if(TlsSetValue(Cmi_state_key, (LPVOID)Cmi_state_vector) == 0) 
238     PerrorExit("TlsSetValue");
239 }
240
241 static void CmiDestoryLocks()
242 {
243   CloseHandle(comm_mutex);
244   CloseHandle(CmiMemLock_lock);
245   CmiMemLock_lock = 0;
246   CloseHandle(barrier_mutex);
247 }
248
249 /***************** Pthreads kernel SMP threads ******************/
250 #elif CMK_SHARED_VARS_POSIX_THREADS_SMP
251
252 CmiNodeLock CmiMemLock_lock;
253 int _Cmi_noprocforcommthread=0;/*this variable marks if there is an extra processor for comm thread
254 in smp*/
255
256 #if CMK_TLS_THREAD && CMK_USE_TLS_THREAD
257 static __thread struct CmiStateStruct     Cmi_mystate;
258 static CmiState     *Cmi_state_vector;
259
260 CmiState CmiGetState() {
261         return &Cmi_mystate;
262 }
263 #define CmiGetStateN(n) Cmi_state_vector[n]
264
265 #else
266
267 static pthread_key_t Cmi_state_key=(pthread_key_t)(-1);
268 static CmiState     Cmi_state_vector;
269
270 #if 0
271 #define CmiGetState() ((CmiState)pthread_getspecific(Cmi_state_key))
272 #else
273 CmiState CmiGetState() {
274         CmiState ret=(CmiState)pthread_getspecific(Cmi_state_key);
275         if (ret==NULL) {
276                 return &Cmi_default_state;
277         }
278         return ret;
279 }
280
281 #endif
282 #define CmiGetStateN(n) (Cmi_state_vector+(n))
283 #endif
284
285
286 CmiNodeLock CmiCreateLock(void)
287 {
288   CmiNodeLock lk = (CmiNodeLock)malloc(sizeof(pthread_mutex_t));
289   _MEMCHECK(lk);
290   pthread_mutex_init(lk,(pthread_mutexattr_t *)0);
291   return lk;
292 }
293
294 void CmiDestroyLock(CmiNodeLock lk)
295 {
296   pthread_mutex_destroy(lk);
297   free(lk);
298 }
299
300 void CmiYield(void) { sched_yield(); }
301
302 int barrier = 0;
303 pthread_cond_t barrier_cond = PTHREAD_COND_INITIALIZER;
304 pthread_mutex_t barrier_mutex = PTHREAD_MUTEX_INITIALIZER;
305
306 void CmiNodeBarrierCount(int nThreads)
307 {
308   static unsigned int volatile level = 0;
309   unsigned int cur;
310   pthread_mutex_lock(&barrier_mutex);
311   cur = level;
312   /* CmiPrintf("[%d] CmiNodeBarrierCount: %d of %d level:%d\n", CmiMyPe(), barrier, nThreads, level); */
313   barrier++;
314   if(barrier != nThreads) {
315       /* occasionally it wakes up without having reach the count */
316     while (cur == level)
317       pthread_cond_wait(&barrier_cond, &barrier_mutex);
318   }
319   else{
320     barrier = 0;
321     level++;  /* !level;  */
322     pthread_cond_broadcast(&barrier_cond);
323   }
324   pthread_mutex_unlock(&barrier_mutex);
325 }
326
327 static CmiNodeLock comm_mutex;
328
329 #define CmiCommLockOrElse(x) /*empty*/
330
331 #if 1
332 /*Regular comm. lock*/
333 #  define CmiCommLock() CmiLock(comm_mutex)
334 #  define CmiCommUnlock() CmiUnlock(comm_mutex)
335 #else
336 /*Verbose debugging comm. lock*/
337 static int comm_mutex_isLocked=0;
338 void CmiCommLock(void) {
339         if (comm_mutex_isLocked) 
340                 CmiAbort("CommLock: already locked!\n");
341         CmiLock(comm_mutex);
342         comm_mutex_isLocked=1;
343 }
344 void CmiCommUnlock(void) {
345         if (!comm_mutex_isLocked)
346                 CmiAbort("CommUnlock: double unlock!\n");
347         comm_mutex_isLocked=0;
348         CmiUnlock(comm_mutex);
349 }
350 #endif
351
352 /*
353 static void comm_thread(void)
354 {
355   while (1) CommunicationServer(5);
356 }
357
358 static void *call_startfn(void *vindex)
359 {
360   int index = (int)vindex;
361   CmiState state = Cmi_state_vector + index;
362   pthread_setspecific(Cmi_state_key, state);
363   ConverseRunPE(0);
364   return 0;
365 }
366 */
367
368 static void *call_startfn(void *vindex)
369 {
370   size_t index = (size_t)vindex;
371 #if CMK_TLS_THREAD && CMK_USE_TLS_THREAD
372   if (index<_Cmi_mynodesize) 
373     CmiStateInit(index+Cmi_nodestart, index, &Cmi_mystate);
374   else
375     CmiStateInit(_Cmi_mynode+CmiNumPes(),_Cmi_mynodesize,&Cmi_mystate);
376   Cmi_state_vector[index] = &Cmi_mystate;
377 #else
378   CmiState state = Cmi_state_vector + index;
379   pthread_setspecific(Cmi_state_key, state);
380 #endif
381
382   ConverseRunPE(0);
383 #if 0
384   if (index<_Cmi_mynodesize) 
385           ConverseRunPE(0); /*Regular worker thread*/
386   else 
387   { /*Communication thread*/
388           CommunicationServerInit();
389           if (Cmi_charmrun_fd!=-1)
390                   while (1) CommunicationServer(5,COM_SERVER_FROM_SMP);
391   }
392 #endif  
393   return 0;
394 }
395
396 static void CmiStartThreads(char **argv)
397 {
398   pthread_t pid;
399   size_t i;
400   int ok, tocreate;
401   pthread_attr_t attr;
402
403   CmiMemLock_lock=CmiCreateLock();
404   comm_mutex=CmiCreateLock();
405   smp_mutex = CmiCreateLock();
406
407 #if ! (CMK_TLS_THREAD && CMK_USE_TLS_THREAD)
408   pthread_key_create(&Cmi_state_key, 0);
409   Cmi_state_vector =
410     (CmiState)calloc(_Cmi_mynodesize+1, sizeof(struct CmiStateStruct));
411   for (i=0; i<_Cmi_mynodesize; i++)
412     CmiStateInit(i+Cmi_nodestart, i, CmiGetStateN(i));
413   /*Create a fake state structure for the comm. thread*/
414 /*  CmiStateInit(-1,_Cmi_mynodesize,CmiGetStateN(_Cmi_mynodesize)); */
415   CmiStateInit(_Cmi_mynode+CmiNumPes(),_Cmi_mynodesize,CmiGetStateN(_Cmi_mynodesize));
416 #else
417   Cmi_state_vector = (CmiState *)calloc(_Cmi_mynodesize+1, sizeof(CmiState));
418   CmiStateInit(Cmi_nodestart, 0, &Cmi_mystate);
419   Cmi_state_vector[0] = &Cmi_mystate;
420 #endif
421
422 #if CMK_MULTICORE
423   if (!Cmi_commthread)
424     tocreate = _Cmi_mynodesize-1;
425   else
426 #endif
427   tocreate = _Cmi_mynodesize;
428   for (i=1; i<=tocreate; i++) {
429     pthread_attr_init(&attr);
430     pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
431 #if CMK_WITH_TAU
432     ok = tau_pthread_create(&pid, &attr, call_startfn, (void *)i);
433 #else
434     ok = pthread_create(&pid, &attr, call_startfn, (void *)i);
435 #endif
436     if (ok<0) PerrorExit("pthread_create"); 
437     pthread_attr_destroy(&attr);
438   }
439 #if ! (CMK_TLS_THREAD && CMK_USE_TLS_THREAD)
440   pthread_setspecific(Cmi_state_key, Cmi_state_vector);
441 #endif
442 }
443
444 static void CmiDestoryLocks()
445 {
446   pthread_mutex_destroy(comm_mutex);
447   pthread_mutex_destroy(CmiMemLock_lock);
448   CmiMemLock_lock = 0;
449   pthread_mutex_destroy(&barrier_mutex);
450 }
451
452 #endif
453
454 #if !CMK_SHARED_VARS_UNAVAILABLE
455
456 /* Wait for all worker threads */
457 void  CmiNodeBarrier(void) {
458   CmiNodeBarrierCount(CmiMyNodeSize());
459 }
460
461 /* Wait for all worker threads as well as comm. thread */
462 /* unfortunately this could also be called in a seemingly non smp version
463    net-win32, which actually is implemented as smp with comm. thread */
464 void CmiNodeAllBarrier(void) {
465 #if CMK_MULTICORE
466   if (!Cmi_commthread)
467   CmiNodeBarrierCount(CmiMyNodeSize());
468   else
469 #endif
470   CmiNodeBarrierCount(CmiMyNodeSize()+1);
471 }
472
473 #endif
474
475 /***********************************************************
476  * SMP Idle Locking
477  *   In an SMP system, idle processors need to sleep on a
478  * lock so that if a message for them arrives, they can be
479  * woken up.
480  **********************************************************/
481
482 #if CMK_SHARED_VARS_NT_THREADS
483
484 static void CmiIdleLock_init(CmiIdleLock *l) {
485   l->hasMessages=0;
486   l->isSleeping=0;
487   l->sem=CreateSemaphore(NULL,0,1, NULL);
488 }
489
490 static void CmiIdleLock_sleep(CmiIdleLock *l,int msTimeout) {
491   if (l->hasMessages) return;
492   l->isSleeping=1;
493   MACHSTATE(4,"Processor going to sleep {")
494   WaitForSingleObject(l->sem,msTimeout);
495   MACHSTATE(4,"} Processor awake again")
496   l->isSleeping=0;
497 }
498
499 static void CmiIdleLock_addMessage(CmiIdleLock *l) {
500   l->hasMessages=1;
501   if (l->isSleeping) { /*The PE is sleeping on this lock-- wake him*/  
502     MACHSTATE(4,"Waking sleeping processor")
503     ReleaseSemaphore(l->sem,1,NULL);
504   }
505 }
506 static void CmiIdleLock_checkMessage(CmiIdleLock *l) {
507   l->hasMessages=0;
508 }
509
510 #elif CMK_SHARED_VARS_POSIX_THREADS_SMP
511
512 static void CmiIdleLock_init(CmiIdleLock *l) {
513   l->hasMessages=0;
514   l->isSleeping=0;
515   pthread_mutex_init(&l->mutex,NULL);
516   pthread_cond_init(&l->cond,NULL);
517 }
518
519 static void getTimespec(int msFromNow,struct timespec *dest) {
520   struct timeval cur;
521   int secFromNow;
522   /*Get the current time*/
523   gettimeofday(&cur,NULL);
524   dest->tv_sec=cur.tv_sec;
525   dest->tv_nsec=cur.tv_usec*1000;
526   /*Add in the wait time*/
527   secFromNow=msFromNow/1000;
528   msFromNow-=secFromNow*1000;
529   dest->tv_sec+=secFromNow;
530   dest->tv_nsec+=1000*1000*msFromNow;
531   /*Wrap around if we overflowed the nsec field*/
532   while (dest->tv_nsec>=1000000000ul) {
533     dest->tv_nsec-=1000000000ul;
534     dest->tv_sec++;
535   }
536 }
537
538 static void CmiIdleLock_sleep(CmiIdleLock *l,int msTimeout) {
539   struct timespec wakeup;
540
541   if (l->hasMessages) return;
542   l->isSleeping=1;
543   MACHSTATE(4,"Processor going to sleep {")
544   pthread_mutex_lock(&l->mutex);
545   getTimespec(msTimeout,&wakeup);
546   while (!l->hasMessages)
547     if (ETIMEDOUT==pthread_cond_timedwait(&l->cond,&l->mutex,&wakeup))
548       break;
549   pthread_mutex_unlock(&l->mutex);
550   MACHSTATE(4,"} Processor awake again")
551   l->isSleeping=0;
552 }
553
554 static void CmiIdleLock_wakeup(CmiIdleLock *l) {
555   l->hasMessages=1; 
556   MACHSTATE(4,"Waking sleeping processor")
557   /*The PE is sleeping on this condition variable-- wake him*/
558   pthread_mutex_lock(&l->mutex);
559   pthread_cond_signal(&l->cond);
560   pthread_mutex_unlock(&l->mutex);
561 }
562
563 static void CmiIdleLock_addMessage(CmiIdleLock *l) {
564   if (l->isSleeping) CmiIdleLock_wakeup(l);
565   l->hasMessages=1;
566 }
567 static void CmiIdleLock_checkMessage(CmiIdleLock *l) {
568   l->hasMessages=0;
569 }
570 #else
571 #define CmiIdleLock_sleep(x, y) /*empty*/
572
573 static void CmiIdleLock_init(CmiIdleLock *l) {
574   l->hasMessages=0;
575 }
576 static void CmiIdleLock_addMessage(CmiIdleLock *l) {
577   l->hasMessages=1;
578 }
579 static void CmiIdleLock_checkMessage(CmiIdleLock *l) {
580   l->hasMessages=0;
581 }
582 #endif
583
584 void CmiStateInit(int pe, int rank, CmiState state)
585 {
586   MACHSTATE(4,"StateInit")
587   state->pe = pe;
588   state->rank = rank;
589   if (rank==CmiMyNodeSize()) return; /* Communications thread */
590   state->recv = PCQueueCreate();
591   state->localqueue = CdsFifo_Create();
592   CmiIdleLock_init(&state->idle);
593 }
594
595 void CmiNodeStateInit(CmiNodeState *nodeState)
596 {
597 #if CMK_IMMEDIATE_MSG
598   MACHSTATE(4,"NodeStateInit")
599   nodeState->immSendLock = CmiCreateLock();
600   nodeState->immRecvLock = CmiCreateLock();
601   nodeState->immQ = PCQueueCreate();
602   nodeState->delayedImmQ = PCQueueCreate();
603 #endif
604 #if CMK_NODE_QUEUE_AVAILABLE
605   nodeState->CmiNodeRecvLock = CmiCreateLock();
606   nodeState->NodeRecv = PCQueueCreate();
607 #endif
608 }
609
610 /*@}*/