renamed smp_mutex to _smp_mutex to skip swapglobals
[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 static struct CmiStateStruct Cmi_default_state; /* State structure to return during startup */
87
88 /************************ Win32 kernel SMP threads **************/
89
90 #if CMK_SHARED_VARS_NT_THREADS
91
92 CmiNodeLock CmiMemLock_lock;
93 #ifdef CMK_NO_ASM_AVAILABLE
94 CmiNodeLock cmiMemoryLock;
95 #endif
96 static HANDLE comm_mutex;
97 #define CmiCommLockOrElse(x) /*empty*/
98 #define CmiCommLock() (WaitForSingleObject(comm_mutex, INFINITE))
99 #define CmiCommUnlock() (ReleaseMutex(comm_mutex))
100
101 static DWORD Cmi_state_key = 0xFFFFFFFF;
102 static CmiState     Cmi_state_vector = 0;
103
104 #if 0
105 #  define CmiGetState() ((CmiState)TlsGetValue(Cmi_state_key))
106 #else
107 CmiState CmiGetState()
108 {
109   CmiState result;
110   result = (CmiState)TlsGetValue(Cmi_state_key);
111   if(result == 0) {
112         return &Cmi_default_state;
113         /* PerrorExit("CmiGetState: TlsGetValue");*/
114   }
115   return result;
116 }
117 #endif
118
119 CmiNodeLock CmiCreateLock(void)
120 {
121   HANDLE hMutex = CreateMutex(NULL, FALSE, NULL);
122   return hMutex;
123 }
124
125 void CmiDestroyLock(CmiNodeLock lk)
126 {
127   CloseHandle(lk);
128 }
129
130 void CmiYield(void) 
131
132   Sleep(0);
133 }
134
135 #define CmiGetStateN(n) (Cmi_state_vector+(n))
136
137 /*
138 static DWORD WINAPI comm_thread(LPVOID dummy)
139 {  
140   if (Cmi_charmrun_fd!=-1)
141     while (1) CommunicationServerThread(5);
142   return 0;
143 }
144
145 static DWORD WINAPI call_startfn(LPVOID vindex)
146 {
147   int index = (int)vindex;
148  
149   CmiState state = Cmi_state_vector + index;
150   if(Cmi_state_key == 0xFFFFFFFF) PerrorExit("TlsAlloc");
151   if(TlsSetValue(Cmi_state_key, (LPVOID)state) == 0) PerrorExit("TlsSetValue");
152
153   ConverseRunPE(0);
154   return 0;
155 }
156 */
157
158 static DWORD WINAPI call_startfn(LPVOID vindex)
159 {
160   int index = (int)vindex;
161  
162   CmiState state = Cmi_state_vector + index;
163   if(Cmi_state_key == 0xFFFFFFFF) PerrorExit("TlsAlloc");
164   if(TlsSetValue(Cmi_state_key, (LPVOID)state) == 0) PerrorExit("TlsSetValue");
165
166   ConverseRunPE(0);
167 #if 0
168   if (index<_Cmi_mynodesize)
169           ConverseRunPE(0); /*Regular worker thread*/
170   else { /*Communication thread*/
171           CommunicationServerInit();
172           if (Cmi_charmrun_fd!=-1)
173                   while (1) CommunicationServerThread(5);
174   } 
175 #endif
176   return 0;
177 }
178
179
180 /*Classic sense-reversing barrier algorithm.
181 FIXME: This should be the barrier implementation for 
182 all thread types.
183 */
184 static volatile HANDLE barrier_mutex;
185 static volatile int    barrier_wait[2] = {0,0};
186 static volatile int    barrier_which = 0;
187
188 void CmiNodeBarrierCount(int nThreads) {
189   int doWait = 1;
190   int which;
191
192   while (WaitForSingleObject(barrier_mutex, INFINITE)!=WAIT_OBJECT_0);
193   which=barrier_which;
194   barrier_wait[which]++;
195   if (barrier_wait[which] == nThreads) {
196     barrier_which = !which;
197     barrier_wait[barrier_which] = 0;/*Reset new counter*/
198     doWait = 0;
199   }
200   while (!ReleaseMutex(barrier_mutex));
201
202   if (doWait)
203       while(barrier_wait[which] != nThreads)
204                   sleep(0);/*<- could also just spin here*/
205 }
206
207 static void CmiStartThreads(char **argv)
208 {
209   int     i,tocreate;
210   DWORD   threadID;
211   HANDLE  thr;
212
213   CmiMemLock_lock=CmiCreateLock();
214   comm_mutex = CmiCreateLock();
215   barrier_mutex = CmiCreateLock();
216 #ifdef CMK_NO_ASM_AVAILABLE
217   cmiMemoryLock = CmiCreateLock();
218   if (CmiMyNode()==0) CmiPrintf("CmiMemory: fences and atomic operations not available in native assembly\n");
219 #endif
220
221   Cmi_state_key = TlsAlloc();
222   if(Cmi_state_key == 0xFFFFFFFF) PerrorExit("TlsAlloc main");
223   
224   Cmi_state_vector =
225     (CmiState)calloc(_Cmi_mynodesize+1, sizeof(struct CmiStateStruct));
226   
227   for (i=0; i<_Cmi_mynodesize; i++)
228     CmiStateInit(i+Cmi_nodestart, i, CmiGetStateN(i));
229   /*Create a fake state structure for the comm. thread*/
230 /*  CmiStateInit(-1,_Cmi_mynodesize,CmiGetStateN(_Cmi_mynodesize)); */
231   CmiStateInit(_Cmi_mynode+CmiNumPes(),_Cmi_mynodesize,CmiGetStateN(_Cmi_mynodesize));
232   
233 #if CMK_MULTICORE || CMK_SMP_NO_COMMTHD
234   if (!Cmi_commthread)
235     tocreate = _Cmi_mynodesize-1;
236   else
237 #endif
238   tocreate = _Cmi_mynodesize;
239   for (i=1; i<=tocreate; i++) {
240     if((thr = CreateThread(NULL, 0, call_startfn, (LPVOID)i, 0, &threadID)) 
241        == NULL) PerrorExit("CreateThread");
242     CloseHandle(thr);
243   }
244   
245   if(TlsSetValue(Cmi_state_key, (LPVOID)Cmi_state_vector) == 0) 
246     PerrorExit("TlsSetValue");
247 }
248
249 static void CmiDestoryLocks()
250 {
251   CloseHandle(comm_mutex);
252   CloseHandle(CmiMemLock_lock);
253   CmiMemLock_lock = 0;
254   CloseHandle(barrier_mutex);
255 #ifdef CMK_NO_ASM_AVAILABLE
256   CloseHandle(cmiMemoryLock);
257 #endif
258 }
259
260 /***************** Pthreads kernel SMP threads ******************/
261 #elif CMK_SHARED_VARS_POSIX_THREADS_SMP
262
263 CmiNodeLock CmiMemLock_lock;
264 #ifdef CMK_NO_ASM_AVAILABLE
265 CmiNodeLock cmiMemoryLock;
266 #endif
267 int _Cmi_noprocforcommthread=0;/*this variable marks if there is an extra processor for comm thread
268 in smp*/
269
270 #if CMK_TLS_THREAD && !CMK_NOT_USE_TLS_THREAD
271 static __thread struct CmiStateStruct     Cmi_mystate;
272 static CmiState     *Cmi_state_vector;
273
274 CmiState CmiGetState() {
275         return &Cmi_mystate;
276 }
277 #define CmiGetStateN(n) Cmi_state_vector[n]
278
279 #else
280
281 static pthread_key_t Cmi_state_key=(pthread_key_t)(-1);
282 static CmiState     Cmi_state_vector;
283
284 #if 0
285 #define CmiGetState() ((CmiState)pthread_getspecific(Cmi_state_key))
286 #else
287 CmiState CmiGetState() {
288         CmiState ret=(CmiState)pthread_getspecific(Cmi_state_key);
289         if (ret==NULL || Cmi_state_key == (pthread_key_t)(-1)) {
290                 return &Cmi_default_state;
291         }
292         return ret;
293 }
294
295 #endif
296 #define CmiGetStateN(n) (Cmi_state_vector+(n))
297 #endif
298
299
300 CmiNodeLock CmiCreateLock(void)
301 {
302   CmiNodeLock lk = (CmiNodeLock)malloc(sizeof(pthread_mutex_t));
303   _MEMCHECK(lk);
304   pthread_mutex_init(lk,(pthread_mutexattr_t *)0);
305   return lk;
306 }
307
308 void CmiDestroyLock(CmiNodeLock lk)
309 {
310   pthread_mutex_destroy(lk);
311   free(lk);
312 }
313
314 void CmiYield(void) { sched_yield(); }
315
316 int barrier = 0;
317 pthread_cond_t barrier_cond = PTHREAD_COND_INITIALIZER;
318 pthread_mutex_t barrier_mutex = PTHREAD_MUTEX_INITIALIZER;
319
320 void CmiNodeBarrierCount(int nThreads)
321 {
322   static unsigned int volatile level = 0;
323   unsigned int cur;
324   pthread_mutex_lock(&barrier_mutex);
325   cur = level;
326   /* CmiPrintf("[%d] CmiNodeBarrierCount: %d of %d level:%d\n", CmiMyPe(), barrier, nThreads, level); */
327   barrier++;
328   if(barrier != nThreads) {
329       /* occasionally it wakes up without having reach the count */
330     while (cur == level)
331       pthread_cond_wait(&barrier_cond, &barrier_mutex);
332   }
333   else{
334     barrier = 0;
335     level++;  /* !level;  */
336     pthread_cond_broadcast(&barrier_cond);
337   }
338   pthread_mutex_unlock(&barrier_mutex);
339 }
340
341 static CmiNodeLock comm_mutex;
342
343 #define CmiCommLockOrElse(x) /*empty*/
344
345 #if 1
346 /*Regular comm. lock*/
347 #  define CmiCommLock() CmiLock(comm_mutex)
348 #  define CmiCommUnlock() CmiUnlock(comm_mutex)
349 #else
350 /*Verbose debugging comm. lock*/
351 static int comm_mutex_isLocked=0;
352 void CmiCommLock(void) {
353         if (comm_mutex_isLocked) 
354                 CmiAbort("CommLock: already locked!\n");
355         CmiLock(comm_mutex);
356         comm_mutex_isLocked=1;
357 }
358 void CmiCommUnlock(void) {
359         if (!comm_mutex_isLocked)
360                 CmiAbort("CommUnlock: double unlock!\n");
361         comm_mutex_isLocked=0;
362         CmiUnlock(comm_mutex);
363 }
364 #endif
365
366 /*
367 static void comm_thread(void)
368 {
369   while (1) CommunicationServer(5);
370 }
371
372 static void *call_startfn(void *vindex)
373 {
374   int index = (int)vindex;
375   CmiState state = Cmi_state_vector + index;
376   pthread_setspecific(Cmi_state_key, state);
377   ConverseRunPE(0);
378   return 0;
379 }
380 */
381
382 static void *call_startfn(void *vindex)
383 {
384   size_t index = (size_t)vindex;
385 #if CMK_TLS_THREAD && !CMK_NOT_USE_TLS_THREAD
386   if (index<_Cmi_mynodesize) 
387     CmiStateInit(index+Cmi_nodestart, index, &Cmi_mystate);
388   else
389     CmiStateInit(_Cmi_mynode+CmiNumPes(),_Cmi_mynodesize,&Cmi_mystate);
390   Cmi_state_vector[index] = &Cmi_mystate;
391 #else
392   CmiState state = Cmi_state_vector + index;
393   pthread_setspecific(Cmi_state_key, state);
394 #endif
395
396   ConverseRunPE(0);
397 #if 0
398   if (index<_Cmi_mynodesize) 
399           ConverseRunPE(0); /*Regular worker thread*/
400   else 
401   { /*Communication thread*/
402           CommunicationServerInit();
403           if (Cmi_charmrun_fd!=-1)
404                   while (1) CommunicationServer(5,COM_SERVER_FROM_SMP);
405   }
406 #endif  
407   return 0;
408 }
409
410 static void CmiStartThreads(char **argv)
411 {
412   pthread_t pid;
413   size_t i;
414   int ok, tocreate;
415   pthread_attr_t attr;
416
417   CmiMemLock_lock=CmiCreateLock();
418   comm_mutex=CmiCreateLock();
419   _smp_mutex = CmiCreateLock();
420 #ifdef CMK_NO_ASM_AVAILABLE
421   cmiMemoryLock = CmiCreateLock();
422   if (CmiMyNode()==0) CmiPrintf("CmiMemory: fences and atomic operations not available in native assembly\n");
423 #endif
424
425 #if ! (CMK_TLS_THREAD && !CMK_NOT_USE_TLS_THREAD)
426   pthread_key_create(&Cmi_state_key, 0);
427   Cmi_state_vector =
428     (CmiState)calloc(_Cmi_mynodesize+1, sizeof(struct CmiStateStruct));
429   for (i=0; i<_Cmi_mynodesize; i++)
430     CmiStateInit(i+Cmi_nodestart, i, CmiGetStateN(i));
431   /*Create a fake state structure for the comm. thread*/
432 /*  CmiStateInit(-1,_Cmi_mynodesize,CmiGetStateN(_Cmi_mynodesize)); */
433   CmiStateInit(_Cmi_mynode+CmiNumPes(),_Cmi_mynodesize,CmiGetStateN(_Cmi_mynodesize));
434 #else
435     /* for main thread */
436   Cmi_state_vector = (CmiState *)calloc(_Cmi_mynodesize+1, sizeof(CmiState));
437 #if CMK_CONVERSE_MPI
438       /* main thread is communication thread */
439   CmiStateInit(_Cmi_mynode+CmiNumPes(), _Cmi_mynodesize, &Cmi_mystate);
440   Cmi_state_vector[_Cmi_mynodesize] = &Cmi_mystate;
441 #else
442       /* main thread is of rank 0 */
443   CmiStateInit(Cmi_nodestart, 0, &Cmi_mystate);
444   Cmi_state_vector[0] = &Cmi_mystate;
445 #endif
446 #endif
447
448 #if CMK_MULTICORE || CMK_SMP_NO_COMMTHD
449   if (!Cmi_commthread)
450     tocreate = _Cmi_mynodesize-1;
451   else
452 #endif
453   tocreate = _Cmi_mynodesize;
454 #if CMK_CONVERSE_MPI
455   for (i=0; i<=tocreate-1; i++) {          /* skip comm thread */
456 #else
457   for (i=1; i<=tocreate; i++) {            /* skip rank 0 main thread */
458 #endif
459     pthread_attr_init(&attr);
460     pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
461     ok = pthread_create(&pid, &attr, call_startfn, (void *)i);
462     if (ok<0) PerrorExit("pthread_create"); 
463     pthread_attr_destroy(&attr);
464   }
465 #if ! (CMK_TLS_THREAD && !CMK_NOT_USE_TLS_THREAD)
466   pthread_setspecific(Cmi_state_key, Cmi_state_vector);
467 #endif
468
469 }
470
471 static void CmiDestoryLocks()
472 {
473   pthread_mutex_destroy(comm_mutex);
474   pthread_mutex_destroy(CmiMemLock_lock);
475   CmiMemLock_lock = 0;
476   pthread_mutex_destroy(&barrier_mutex);
477 #ifdef CMK_NO_ASM_AVAILABLE
478   pthread_mutex_destroy(cmiMemoryLock);
479 #endif
480 }
481
482 #endif
483
484 #if !CMK_SHARED_VARS_UNAVAILABLE
485
486 /* Wait for all worker threads */
487 void  CmiNodeBarrier(void) {
488   CmiNodeBarrierCount(CmiMyNodeSize());
489 }
490
491 /* Wait for all worker threads as well as comm. thread */
492 /* unfortunately this could also be called in a seemingly non smp version
493    net-win32, which actually is implemented as smp with comm. thread */
494 void CmiNodeAllBarrier(void) {
495 #if CMK_MULTICORE || CMK_SMP_NO_COMMTHD
496   if (!Cmi_commthread)
497   CmiNodeBarrierCount(CmiMyNodeSize());
498   else
499 #endif
500   CmiNodeBarrierCount(CmiMyNodeSize()+1);
501 }
502
503 #endif
504
505 /***********************************************************
506  * SMP Idle Locking
507  *   In an SMP system, idle processors need to sleep on a
508  * lock so that if a message for them arrives, they can be
509  * woken up.
510  **********************************************************/
511
512 #if CMK_SHARED_VARS_NT_THREADS
513
514 static void CmiIdleLock_init(CmiIdleLock *l) {
515   l->hasMessages=0;
516   l->isSleeping=0;
517   l->sem=CreateSemaphore(NULL,0,1, NULL);
518 }
519
520 static void CmiIdleLock_sleep(CmiIdleLock *l,int msTimeout) {
521   if (l->hasMessages) return;
522   l->isSleeping=1;
523   MACHSTATE(4,"Processor going to sleep {")
524   WaitForSingleObject(l->sem,msTimeout);
525   MACHSTATE(4,"} Processor awake again")
526   l->isSleeping=0;
527 }
528
529 static void CmiIdleLock_addMessage(CmiIdleLock *l) {
530   l->hasMessages=1;
531   if (l->isSleeping) { /*The PE is sleeping on this lock-- wake him*/  
532     MACHSTATE(4,"Waking sleeping processor")
533     ReleaseSemaphore(l->sem,1,NULL);
534   }
535 }
536 static void CmiIdleLock_checkMessage(CmiIdleLock *l) {
537   l->hasMessages=0;
538 }
539
540 #elif CMK_SHARED_VARS_POSIX_THREADS_SMP
541
542 static void CmiIdleLock_init(CmiIdleLock *l) {
543   l->hasMessages=0;
544   l->isSleeping=0;
545   pthread_mutex_init(&l->mutex,NULL);
546   pthread_cond_init(&l->cond,NULL);
547 }
548
549 static void getTimespec(int msFromNow,struct timespec *dest) {
550   struct timeval cur;
551   int secFromNow;
552   /*Get the current time*/
553   gettimeofday(&cur,NULL);
554   dest->tv_sec=cur.tv_sec;
555   dest->tv_nsec=cur.tv_usec*1000;
556   /*Add in the wait time*/
557   secFromNow=msFromNow/1000;
558   msFromNow-=secFromNow*1000;
559   dest->tv_sec+=secFromNow;
560   dest->tv_nsec+=1000*1000*msFromNow;
561   /*Wrap around if we overflowed the nsec field*/
562   while (dest->tv_nsec>=1000000000ul) {
563     dest->tv_nsec-=1000000000ul;
564     dest->tv_sec++;
565   }
566 }
567
568 static void CmiIdleLock_sleep(CmiIdleLock *l,int msTimeout) {
569   struct timespec wakeup;
570
571   if (l->hasMessages) return;
572   l->isSleeping=1;
573   MACHSTATE(4,"Processor going to sleep {")
574   pthread_mutex_lock(&l->mutex);
575   getTimespec(msTimeout,&wakeup);
576   while (!l->hasMessages)
577     if (ETIMEDOUT==pthread_cond_timedwait(&l->cond,&l->mutex,&wakeup))
578       break;
579   pthread_mutex_unlock(&l->mutex);
580   MACHSTATE(4,"} Processor awake again")
581   l->isSleeping=0;
582 }
583
584 static void CmiIdleLock_wakeup(CmiIdleLock *l) {
585   l->hasMessages=1; 
586   MACHSTATE(4,"Waking sleeping processor")
587   /*The PE is sleeping on this condition variable-- wake him*/
588   pthread_mutex_lock(&l->mutex);
589   pthread_cond_signal(&l->cond);
590   pthread_mutex_unlock(&l->mutex);
591 }
592
593 static void CmiIdleLock_addMessage(CmiIdleLock *l) {
594   if (l->isSleeping) CmiIdleLock_wakeup(l);
595   l->hasMessages=1;
596 }
597 static void CmiIdleLock_checkMessage(CmiIdleLock *l) {
598   l->hasMessages=0;
599 }
600 #else
601 #define CmiIdleLock_sleep(x, y) /*empty*/
602
603 static void CmiIdleLock_init(CmiIdleLock *l) {
604   l->hasMessages=0;
605 }
606 static void CmiIdleLock_addMessage(CmiIdleLock *l) {
607   l->hasMessages=1;
608 }
609 static void CmiIdleLock_checkMessage(CmiIdleLock *l) {
610   l->hasMessages=0;
611 }
612 #endif
613
614 void CmiStateInit(int pe, int rank, CmiState state)
615 {
616 #if CMK_SMP_MULTIQ
617   int i;
618 #endif
619
620   MACHSTATE(4,"StateInit")
621   state->pe = pe;
622   state->rank = rank;
623   if (rank==CmiMyNodeSize()) return; /* Communications thread */
624 #if !CMK_SMP_MULTIQ
625   state->recv = PCQueueCreate();
626 #else
627   for(i=0; i<MULTIQ_GRPSIZE; i++) state->recv[i]=PCQueueCreate();
628   state->myGrpIdx = rank % MULTIQ_GRPSIZE;
629   state->curPolledIdx = 0;
630 #endif
631   state->localqueue = CdsFifo_Create();
632   CmiIdleLock_init(&state->idle);
633 }
634
635 void CmiNodeStateInit(CmiNodeState *nodeState)
636 {
637 #if CMK_IMMEDIATE_MSG
638   MACHSTATE(4,"NodeStateInit")
639   nodeState->immSendLock = CmiCreateLock();
640   nodeState->immRecvLock = CmiCreateLock();
641   nodeState->immQ = PCQueueCreate();
642   nodeState->delayedImmQ = PCQueueCreate();
643 #endif
644 #if CMK_NODE_QUEUE_AVAILABLE
645   nodeState->CmiNodeRecvLock = CmiCreateLock();
646   nodeState->NodeRecv = PCQueueCreate();
647 #endif
648 }
649
650 /*@}*/