fixed a couple compiler warnings.
[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   tocreate = _Cmi_mynodesize-1;
227 #else
228   tocreate = _Cmi_mynodesize;
229 #endif
230   for (i=1; i<=tocreate; i++) {
231     if((thr = CreateThread(NULL, 0, call_startfn, (LPVOID)i, 0, &threadID)) 
232        == NULL) PerrorExit("CreateThread");
233     CloseHandle(thr);
234   }
235   
236   if(TlsSetValue(Cmi_state_key, (LPVOID)Cmi_state_vector) == 0) 
237     PerrorExit("TlsSetValue");
238 }
239
240 static void CmiDestoryLocks()
241 {
242   CloseHandle(comm_mutex);
243   CloseHandle(CmiMemLock_lock);
244   CmiMemLock_lock = 0;
245   CloseHandle(barrier_mutex);
246 }
247
248 /***************** Pthreads kernel SMP threads ******************/
249 #elif CMK_SHARED_VARS_POSIX_THREADS_SMP
250
251 static pthread_key_t Cmi_state_key=(pthread_key_t)(-1);
252 static CmiState     Cmi_state_vector;
253 CmiNodeLock CmiMemLock_lock;
254 int _Cmi_noprocforcommthread=0;/*this variable marks if there is an extra processor for comm thread
255 in smp*/
256
257
258 #if 0
259 #define CmiGetState() ((CmiState)pthread_getspecific(Cmi_state_key))
260 #else
261 CmiState CmiGetState() {
262         CmiState ret=(CmiState)pthread_getspecific(Cmi_state_key);
263         if (ret==NULL) {
264                 return &Cmi_default_state;
265         }
266         return ret;
267 }
268 #endif
269
270 CmiNodeLock CmiCreateLock(void)
271 {
272   CmiNodeLock lk = (CmiNodeLock)malloc(sizeof(pthread_mutex_t));
273   _MEMCHECK(lk);
274   pthread_mutex_init(lk,(pthread_mutexattr_t *)0);
275   return lk;
276 }
277
278 void CmiDestroyLock(CmiNodeLock lk)
279 {
280   pthread_mutex_destroy(lk);
281   free(lk);
282 }
283
284 void CmiYield(void) { sched_yield(); }
285
286 int barrier = 0;
287 pthread_cond_t barrier_cond = PTHREAD_COND_INITIALIZER;
288 pthread_mutex_t barrier_mutex = PTHREAD_MUTEX_INITIALIZER;
289
290 void CmiNodeBarrierCount(int nThreads)
291 {
292   static int volatile level = 0;
293   int cur;
294   pthread_mutex_lock(&barrier_mutex);
295   cur = level;
296   /*CmiPrintf("[%d] CmiNodeBarrierCount: %d of %d level:%d\n", CmiMyPe(), barrier, nThreads, level);*/
297   barrier++;
298   if(barrier != nThreads) {
299       /* occasionally it wakes up without having reach the count */
300     while (cur == level)
301       pthread_cond_wait(&barrier_cond, &barrier_mutex);
302   }
303   else{
304     barrier = 0;
305     level = !level;
306     pthread_cond_broadcast(&barrier_cond);
307   }
308   pthread_mutex_unlock(&barrier_mutex);
309 }
310
311 #define CmiGetStateN(n) (Cmi_state_vector+(n))
312
313 static CmiNodeLock comm_mutex;
314
315 #define CmiCommLockOrElse(x) /*empty*/
316
317 #if 1
318 /*Regular comm. lock*/
319 #  define CmiCommLock() CmiLock(comm_mutex)
320 #  define CmiCommUnlock() CmiUnlock(comm_mutex)
321 #else
322 /*Verbose debugging comm. lock*/
323 static int comm_mutex_isLocked=0;
324 void CmiCommLock(void) {
325         if (comm_mutex_isLocked) 
326                 CmiAbort("CommLock: already locked!\n");
327         CmiLock(comm_mutex);
328         comm_mutex_isLocked=1;
329 }
330 void CmiCommUnlock(void) {
331         if (!comm_mutex_isLocked)
332                 CmiAbort("CommUnlock: double unlock!\n");
333         comm_mutex_isLocked=0;
334         CmiUnlock(comm_mutex);
335 }
336 #endif
337
338 /*
339 static void comm_thread(void)
340 {
341   while (1) CommunicationServer(5);
342 }
343
344 static void *call_startfn(void *vindex)
345 {
346   int index = (int)vindex;
347   CmiState state = Cmi_state_vector + index;
348   pthread_setspecific(Cmi_state_key, state);
349   ConverseRunPE(0);
350   return 0;
351 }
352 */
353
354 static void *call_startfn(void *vindex)
355 {
356   size_t index = (size_t)vindex;
357   CmiState state = Cmi_state_vector + index;
358   pthread_setspecific(Cmi_state_key, state);
359
360   ConverseRunPE(0);
361 #if 0
362   if (index<_Cmi_mynodesize) 
363           ConverseRunPE(0); /*Regular worker thread*/
364   else 
365   { /*Communication thread*/
366           CommunicationServerInit();
367           if (Cmi_charmrun_fd!=-1)
368                   while (1) CommunicationServer(5,COM_SERVER_FROM_SMP);
369   }
370 #endif  
371   return 0;
372 }
373
374 static void CmiStartThreads(char **argv)
375 {
376   pthread_t pid;
377   size_t i;
378   int ok, tocreate;
379   pthread_attr_t attr;
380
381   CmiMemLock_lock=CmiCreateLock();
382   comm_mutex=CmiCreateLock();
383   smp_mutex = CmiCreateLock();
384
385   pthread_key_create(&Cmi_state_key, 0);
386   Cmi_state_vector =
387     (CmiState)calloc(_Cmi_mynodesize+1, sizeof(struct CmiStateStruct));
388   for (i=0; i<_Cmi_mynodesize; i++)
389     CmiStateInit(i+Cmi_nodestart, i, CmiGetStateN(i));
390   /*Create a fake state structure for the comm. thread*/
391 /*  CmiStateInit(-1,_Cmi_mynodesize,CmiGetStateN(_Cmi_mynodesize)); */
392   CmiStateInit(_Cmi_mynode+CmiNumPes(),_Cmi_mynodesize,CmiGetStateN(_Cmi_mynodesize));
393
394 #if CMK_MULTICORE
395   tocreate = _Cmi_mynodesize-1;
396 #else
397   tocreate = _Cmi_mynodesize;
398 #endif
399   for (i=1; i<=tocreate; i++) {
400     pthread_attr_init(&attr);
401     pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
402     ok = pthread_create(&pid, &attr, call_startfn, (void *)i);
403     if (ok<0) PerrorExit("pthread_create"); 
404     pthread_attr_destroy(&attr);
405   }
406   pthread_setspecific(Cmi_state_key, Cmi_state_vector);
407 }
408
409 static void CmiDestoryLocks()
410 {
411   pthread_mutex_destroy(comm_mutex);
412   pthread_mutex_destroy(CmiMemLock_lock);
413   CmiMemLock_lock = 0;
414   pthread_mutex_destroy(&barrier_mutex);
415 }
416
417 #endif
418
419 #if !CMK_SHARED_VARS_UNAVAILABLE
420
421 /* Wait for all worker threads */
422 void  CmiNodeBarrier(void) {
423   CmiNodeBarrierCount(CmiMyNodeSize());
424 }
425
426 /* Wait for all worker threads as well as comm. thread */
427 /* unfortunately this could also be called in a seemingly non smp version
428    net-win32, which actually is implemented as smp with comm. thread */
429 void CmiNodeAllBarrier(void) {
430 #if CMK_MULTICORE
431   CmiNodeBarrierCount(CmiMyNodeSize());
432 #else
433   CmiNodeBarrierCount(CmiMyNodeSize()+1);
434 #endif
435 }
436
437 #endif
438
439 /***********************************************************
440  * SMP Idle Locking
441  *   In an SMP system, idle processors need to sleep on a
442  * lock so that if a message for them arrives, they can be
443  * woken up.
444  **********************************************************/
445
446 #if CMK_SHARED_VARS_NT_THREADS
447
448 static void CmiIdleLock_init(CmiIdleLock *l) {
449   l->hasMessages=0;
450   l->isSleeping=0;
451   l->sem=CreateSemaphore(NULL,0,1, NULL);
452 }
453
454 static void CmiIdleLock_sleep(CmiIdleLock *l,int msTimeout) {
455   if (l->hasMessages) return;
456   l->isSleeping=1;
457   MACHSTATE(4,"Processor going to sleep {")
458   WaitForSingleObject(l->sem,msTimeout);
459   MACHSTATE(4,"} Processor awake again")
460   l->isSleeping=0;
461 }
462
463 static void CmiIdleLock_addMessage(CmiIdleLock *l) {
464   l->hasMessages=1;
465   if (l->isSleeping) { /*The PE is sleeping on this lock-- wake him*/  
466     MACHSTATE(4,"Waking sleeping processor")
467     ReleaseSemaphore(l->sem,1,NULL);
468   }
469 }
470 static void CmiIdleLock_checkMessage(CmiIdleLock *l) {
471   l->hasMessages=0;
472 }
473
474 #elif CMK_SHARED_VARS_POSIX_THREADS_SMP
475
476 static void CmiIdleLock_init(CmiIdleLock *l) {
477   l->hasMessages=0;
478   l->isSleeping=0;
479   pthread_mutex_init(&l->mutex,NULL);
480   pthread_cond_init(&l->cond,NULL);
481 }
482
483 static void getTimespec(int msFromNow,struct timespec *dest) {
484   struct timeval cur;
485   int secFromNow;
486   /*Get the current time*/
487   gettimeofday(&cur,NULL);
488   dest->tv_sec=cur.tv_sec;
489   dest->tv_nsec=cur.tv_usec*1000;
490   /*Add in the wait time*/
491   secFromNow=msFromNow/1000;
492   msFromNow-=secFromNow*1000;
493   dest->tv_sec+=secFromNow;
494   dest->tv_nsec+=1000*1000*msFromNow;
495   /*Wrap around if we overflowed the nsec field*/
496   while (dest->tv_nsec>1000000000u) {
497     dest->tv_nsec-=1000000000;
498     dest->tv_sec++;
499   }
500 }
501
502 static void CmiIdleLock_sleep(CmiIdleLock *l,int msTimeout) {
503   struct timespec wakeup;
504
505   if (l->hasMessages) return;
506   l->isSleeping=1;
507   MACHSTATE(4,"Processor going to sleep {")
508   pthread_mutex_lock(&l->mutex);
509   getTimespec(msTimeout,&wakeup);
510   while (!l->hasMessages)
511     if (ETIMEDOUT==pthread_cond_timedwait(&l->cond,&l->mutex,&wakeup))
512       break;
513   pthread_mutex_unlock(&l->mutex);
514   MACHSTATE(4,"} Processor awake again")
515   l->isSleeping=0;
516 }
517
518 static void CmiIdleLock_wakeup(CmiIdleLock *l) {
519   l->hasMessages=1; 
520   MACHSTATE(4,"Waking sleeping processor")
521   /*The PE is sleeping on this condition variable-- wake him*/
522   pthread_mutex_lock(&l->mutex);
523   pthread_cond_signal(&l->cond);
524   pthread_mutex_unlock(&l->mutex);
525 }
526
527 static void CmiIdleLock_addMessage(CmiIdleLock *l) {
528   if (l->isSleeping) CmiIdleLock_wakeup(l);
529   l->hasMessages=1;
530 }
531 static void CmiIdleLock_checkMessage(CmiIdleLock *l) {
532   l->hasMessages=0;
533 }
534 #else
535 #define CmiIdleLock_sleep(x, y) /*empty*/
536
537 static void CmiIdleLock_init(CmiIdleLock *l) {
538   l->hasMessages=0;
539 }
540 static void CmiIdleLock_addMessage(CmiIdleLock *l) {
541   l->hasMessages=1;
542 }
543 static void CmiIdleLock_checkMessage(CmiIdleLock *l) {
544   l->hasMessages=0;
545 }
546 #endif
547
548 void CmiStateInit(int pe, int rank, CmiState state)
549 {
550   MACHSTATE(4,"StateInit")
551   state->pe = pe;
552   state->rank = rank;
553   if (rank==CmiMyNodeSize()) return; /* Communications thread */
554   state->recv = PCQueueCreate();
555   state->localqueue = CdsFifo_Create();
556   CmiIdleLock_init(&state->idle);
557 }
558
559 void CmiNodeStateInit(CmiNodeState *nodeState)
560 {
561 #if CMK_IMMEDIATE_MSG
562   MACHSTATE(4,"NodeStateInit")
563   nodeState->immSendLock = CmiCreateLock();
564   nodeState->immRecvLock = CmiCreateLock();
565   nodeState->immQ = PCQueueCreate();
566   nodeState->delayedImmQ = PCQueueCreate();
567 #endif
568 #if CMK_NODE_QUEUE_AVAILABLE
569   nodeState->CmiNodeRecvLock = CmiCreateLock();
570   nodeState->NodeRecv = PCQueueCreate();
571 #endif
572 }
573
574 /*@}*/