d56d1291625c9a61c8c993f411033b5326200aff
[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           printf("calling tau_pthread_create");
432     ok = tau_pthread_create(&pid, &attr, call_startfn, (void *)i);
433     if (ok<0) PerrorExit("pthread_create"); 
434     pthread_attr_destroy(&attr);
435   }
436 #if ! (CMK_TLS_THREAD && CMK_USE_TLS_THREAD)
437   pthread_setspecific(Cmi_state_key, Cmi_state_vector);
438 #endif
439 }
440
441 static void CmiDestoryLocks()
442 {
443   pthread_mutex_destroy(comm_mutex);
444   pthread_mutex_destroy(CmiMemLock_lock);
445   CmiMemLock_lock = 0;
446   pthread_mutex_destroy(&barrier_mutex);
447 }
448
449 #endif
450
451 #if !CMK_SHARED_VARS_UNAVAILABLE
452
453 /* Wait for all worker threads */
454 void  CmiNodeBarrier(void) {
455   CmiNodeBarrierCount(CmiMyNodeSize());
456 }
457
458 /* Wait for all worker threads as well as comm. thread */
459 /* unfortunately this could also be called in a seemingly non smp version
460    net-win32, which actually is implemented as smp with comm. thread */
461 void CmiNodeAllBarrier(void) {
462 #if CMK_MULTICORE
463   if (!Cmi_commthread)
464   CmiNodeBarrierCount(CmiMyNodeSize());
465   else
466 #endif
467   CmiNodeBarrierCount(CmiMyNodeSize()+1);
468 }
469
470 #endif
471
472 /***********************************************************
473  * SMP Idle Locking
474  *   In an SMP system, idle processors need to sleep on a
475  * lock so that if a message for them arrives, they can be
476  * woken up.
477  **********************************************************/
478
479 #if CMK_SHARED_VARS_NT_THREADS
480
481 static void CmiIdleLock_init(CmiIdleLock *l) {
482   l->hasMessages=0;
483   l->isSleeping=0;
484   l->sem=CreateSemaphore(NULL,0,1, NULL);
485 }
486
487 static void CmiIdleLock_sleep(CmiIdleLock *l,int msTimeout) {
488   if (l->hasMessages) return;
489   l->isSleeping=1;
490   MACHSTATE(4,"Processor going to sleep {")
491   WaitForSingleObject(l->sem,msTimeout);
492   MACHSTATE(4,"} Processor awake again")
493   l->isSleeping=0;
494 }
495
496 static void CmiIdleLock_addMessage(CmiIdleLock *l) {
497   l->hasMessages=1;
498   if (l->isSleeping) { /*The PE is sleeping on this lock-- wake him*/  
499     MACHSTATE(4,"Waking sleeping processor")
500     ReleaseSemaphore(l->sem,1,NULL);
501   }
502 }
503 static void CmiIdleLock_checkMessage(CmiIdleLock *l) {
504   l->hasMessages=0;
505 }
506
507 #elif CMK_SHARED_VARS_POSIX_THREADS_SMP
508
509 static void CmiIdleLock_init(CmiIdleLock *l) {
510   l->hasMessages=0;
511   l->isSleeping=0;
512   pthread_mutex_init(&l->mutex,NULL);
513   pthread_cond_init(&l->cond,NULL);
514 }
515
516 static void getTimespec(int msFromNow,struct timespec *dest) {
517   struct timeval cur;
518   int secFromNow;
519   /*Get the current time*/
520   gettimeofday(&cur,NULL);
521   dest->tv_sec=cur.tv_sec;
522   dest->tv_nsec=cur.tv_usec*1000;
523   /*Add in the wait time*/
524   secFromNow=msFromNow/1000;
525   msFromNow-=secFromNow*1000;
526   dest->tv_sec+=secFromNow;
527   dest->tv_nsec+=1000*1000*msFromNow;
528   /*Wrap around if we overflowed the nsec field*/
529   while (dest->tv_nsec>=1000000000ul) {
530     dest->tv_nsec-=1000000000ul;
531     dest->tv_sec++;
532   }
533 }
534
535 static void CmiIdleLock_sleep(CmiIdleLock *l,int msTimeout) {
536   struct timespec wakeup;
537
538   if (l->hasMessages) return;
539   l->isSleeping=1;
540   MACHSTATE(4,"Processor going to sleep {")
541   pthread_mutex_lock(&l->mutex);
542   getTimespec(msTimeout,&wakeup);
543   while (!l->hasMessages)
544     if (ETIMEDOUT==pthread_cond_timedwait(&l->cond,&l->mutex,&wakeup))
545       break;
546   pthread_mutex_unlock(&l->mutex);
547   MACHSTATE(4,"} Processor awake again")
548   l->isSleeping=0;
549 }
550
551 static void CmiIdleLock_wakeup(CmiIdleLock *l) {
552   l->hasMessages=1; 
553   MACHSTATE(4,"Waking sleeping processor")
554   /*The PE is sleeping on this condition variable-- wake him*/
555   pthread_mutex_lock(&l->mutex);
556   pthread_cond_signal(&l->cond);
557   pthread_mutex_unlock(&l->mutex);
558 }
559
560 static void CmiIdleLock_addMessage(CmiIdleLock *l) {
561   if (l->isSleeping) CmiIdleLock_wakeup(l);
562   l->hasMessages=1;
563 }
564 static void CmiIdleLock_checkMessage(CmiIdleLock *l) {
565   l->hasMessages=0;
566 }
567 #else
568 #define CmiIdleLock_sleep(x, y) /*empty*/
569
570 static void CmiIdleLock_init(CmiIdleLock *l) {
571   l->hasMessages=0;
572 }
573 static void CmiIdleLock_addMessage(CmiIdleLock *l) {
574   l->hasMessages=1;
575 }
576 static void CmiIdleLock_checkMessage(CmiIdleLock *l) {
577   l->hasMessages=0;
578 }
579 #endif
580
581 void CmiStateInit(int pe, int rank, CmiState state)
582 {
583   MACHSTATE(4,"StateInit")
584   state->pe = pe;
585   state->rank = rank;
586   if (rank==CmiMyNodeSize()) return; /* Communications thread */
587   state->recv = PCQueueCreate();
588   state->localqueue = CdsFifo_Create();
589   CmiIdleLock_init(&state->idle);
590 }
591
592 void CmiNodeStateInit(CmiNodeState *nodeState)
593 {
594 #if CMK_IMMEDIATE_MSG
595   MACHSTATE(4,"NodeStateInit")
596   nodeState->immSendLock = CmiCreateLock();
597   nodeState->immRecvLock = CmiCreateLock();
598   nodeState->immQ = PCQueueCreate();
599   nodeState->delayedImmQ = PCQueueCreate();
600 #endif
601 #if CMK_NODE_QUEUE_AVAILABLE
602   nodeState->CmiNodeRecvLock = CmiCreateLock();
603   nodeState->NodeRecv = PCQueueCreate();
604 #endif
605 }
606
607 /*@}*/