Merge release-4-6 into master
[alexxy/gromacs.git] / src / gromacs / gmxlib / thread_mpi / tmpi_init.c
1 /*
2 This source code file is part of thread_mpi.  
3 Written by Sander Pronk, Erik Lindahl, and possibly others. 
4
5 Copyright (c) 2009, Sander Pronk, Erik Lindahl.
6 All rights reserved.
7
8 Redistribution and use in source and binary forms, with or without
9 modification, are permitted provided that the following conditions are met:
10 1) Redistributions of source code must retain the above copyright
11    notice, this list of conditions and the following disclaimer.
12 2) Redistributions in binary form must reproduce the above copyright
13    notice, this list of conditions and the following disclaimer in the
14    documentation and/or other materials provided with the distribution.
15 3) Neither the name of the copyright holders nor the
16    names of its contributors may be used to endorse or promote products
17    derived from this software without specific prior written permission.
18
19 THIS SOFTWARE IS PROVIDED BY US ''AS IS'' AND ANY
20 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 DISCLAIMED. IN NO EVENT SHALL WE BE LIABLE FOR ANY
23 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 If you want to redistribute modifications, please consider that
31 scientific software is very special. Version control is crucial -
32 bugs must be traceable. We will be happy to consider code for
33 inclusion in the official distribution, but derived work should not
34 be called official thread_mpi. Details are found in the README & COPYING
35 files.
36 */
37
38
39 #ifdef HAVE_TMPI_CONFIG_H
40 #include "tmpi_config.h"
41 #endif
42
43 #ifdef HAVE_CONFIG_H
44 #include "config.h"
45 #endif
46
47 #ifdef HAVE_UNISTD_H
48 #include <unistd.h>
49 #endif
50
51 #include <errno.h>
52 #include <stdlib.h>
53 #include <stdio.h>
54 #include <string.h>
55 #if ! (defined( _WIN32 ) || defined( _WIN64 ) )
56 #include <sys/time.h>
57
58 #endif
59
60
61 #include "impl.h"
62
63 #ifdef TMPI_TRACE
64 #include <stdarg.h>
65 #endif
66
67
68
69
70
71
72 /* there are a few global variables that maintain information about the
73    running threads. Some are defined by the MPI standard: */
74 /* TMPI_COMM_WORLD is in tmpi_malloc.c due to technical reasons */
75 tMPI_Group TMPI_GROUP_EMPTY=NULL;
76
77
78 /* the threads themselves (tmpi_comm only contains lists of pointers to this
79       structure */
80 struct tmpi_thread *threads=NULL;
81 int Nthreads=0;
82
83 /* thread info */
84 tMPI_Thread_key_t id_key; /* the key to get the thread id */
85
86
87
88 /* whether MPI has finalized (we need this to distinguish pre-inited from
89        post-finalized states */
90 static tmpi_bool tmpi_finalized=FALSE;
91
92 /* misc. global information about MPI */
93 struct tmpi_global *tmpi_global=NULL;
94
95
96
97
98
99
100 /* start N threads with argc, argv (used by tMPI_Init)*/
101 void tMPI_Start_threads(tmpi_bool main_returns, int N, 
102                         tMPI_Affinity_strategy aff_strategy,
103                         int *argc, char ***argv, 
104                         void (*start_fn)(void*), void *start_arg,
105                         int (*start_fn_main)(int, char**));
106
107 /* starter function for threads; takes a void pointer to a
108       struct tmpi_starter_, which calls main() if tmpi_start_.fn == NULL */
109 static void* tMPI_Thread_starter(void *arg);
110
111 /* allocate and initialize the data associated with a thread structure */
112 static void tMPI_Thread_init(struct tmpi_thread *th);
113 /* deallocate the data associated with a thread structure */
114 static void tMPI_Thread_destroy(struct tmpi_thread *th);
115
116
117
118
119 #ifdef TMPI_TRACE
120 void tMPI_Trace_print(const char *fmt, ...)
121 {
122     va_list argp;
123     struct tmpi_thread* th=NULL;
124     static tMPI_Thread_mutex_t mtx=TMPI_THREAD_MUTEX_INITIALIZER;
125
126     tMPI_Thread_mutex_lock(&mtx);
127     if (threads)
128     {
129         th=tMPI_Get_current();
130         printf("THREAD %02d: ", (int)(th-threads));
131     }
132     else
133     {
134         printf("THREAD main: ");
135     }
136     va_start(argp, fmt);
137     vprintf(fmt, argp);
138     printf("\n");
139     fflush(stdout);
140     va_end(argp);
141     tMPI_Thread_mutex_unlock(&mtx);
142 }
143 #endif
144
145
146 #if 0
147 struct tmpi_thread *tMPI_Get_current(void)
148 {
149     if (!threads)
150         return NULL;
151
152     return (struct tmpi_thread*)tMPI_thread_getspecific(id_key);
153
154
155
156 unsigned int tMPI_Threadnr(struct tmpi_thread *thr)
157 {
158     return thr-threads;
159 }
160 #endif
161 #if 0
162 unsigned int tMPI_This_threadnr(void)
163 {
164     return tMPI_Get_current()-threads;
165 }
166
167 struct tmpi_thread *tMPI_Get_thread(tMPI_Comm comm, int rank)
168 {
169     /* check destination */
170     if ( (rank < 0) || (rank > comm->grp.N) )
171     {
172         tMPI_Error(comm, TMPI_ERR_GROUP_RANK);
173         return NULL;
174     }
175     return comm->grp.peers[rank];
176 }
177 #endif
178
179 tmpi_bool tMPI_Is_master(void)
180 {
181     /* if there are no other threads, we're the main thread */
182     if ( (!TMPI_COMM_WORLD) || TMPI_COMM_WORLD->grp.N==0)
183         return TRUE;
184
185     /* otherwise we know this through thread specific data: */
186     /* whether the thread pointer points to the head of the threads array */
187     return (tmpi_bool)(tMPI_Get_current() == threads); 
188 }
189
190 tMPI_Comm tMPI_Get_comm_self(void)
191 {
192     struct tmpi_thread* th=tMPI_Get_current();
193     return th->self_comm;
194 }
195
196
197 int tMPI_Get_N(int *argc, char ***argv, const char *optname, int *nthreads)
198 {
199     int i;
200     int ret=TMPI_SUCCESS;
201
202     *nthreads=0;
203     if (!optname)
204     {
205         i=0;
206     }
207     else
208     {
209         for(i=1;i<*argc;i++)
210         {
211             if (strcmp(optname, (*argv)[i]) == 0)
212             {
213                 break;
214             }
215         }
216     }
217     if (i+1 < (*argc))
218     {
219         /* the number of processes is an argument */
220         char *end;
221         *nthreads=strtol((*argv)[i+1], &end, 10);
222         if ( !end || (*end != 0) )
223         {
224             *nthreads=0;
225             ret=TMPI_FAILURE;
226         }
227     }
228     if (*nthreads<1)
229     {
230         int nth=tMPI_Thread_get_hw_number();
231
232         if (nth<1) nth=1; /* make sure it's at least 1 */
233         *nthreads=nth;
234     }
235
236     return ret;
237 }
238
239 static void tMPI_Thread_init(struct tmpi_thread *th)
240 {
241     int N_envelopes=(Nthreads+1)*N_EV_ALLOC;  
242     int N_send_envelopes=N_EV_ALLOC;  
243     int N_reqs=(Nthreads+1)*N_EV_ALLOC;  
244     int i;
245
246     /* we set our thread id, as a thread-specific piece of global data. */
247     tMPI_Thread_setspecific(id_key, th);
248
249     /* allocate comm.self */
250     th->self_comm=tMPI_Comm_alloc(TMPI_COMM_WORLD, 1);
251     th->self_comm->grp.peers[0]=th;
252
253     /* allocate envelopes */
254     tMPI_Free_env_list_init( &(th->envelopes), N_envelopes );
255     /* recv list */
256     tMPI_Recv_env_list_init( &(th->evr));
257     /* send lists */
258     th->evs=(struct send_envelope_list*)tMPI_Malloc(
259                         sizeof(struct send_envelope_list)*Nthreads);
260     for(i=0;i<Nthreads;i++)
261     {
262         tMPI_Send_env_list_init( &(th->evs[i]), N_send_envelopes);
263     }
264
265     tMPI_Atomic_set( &(th->ev_outgoing_received), 0);
266
267     tMPI_Event_init( &(th->p2p_event) );
268
269     /* allocate requests */
270     tMPI_Req_list_init(&(th->rql), N_reqs);
271
272 #ifdef USE_COLLECTIVE_COPY_BUFFER
273     /* allcate copy_buffer list */
274     tMPI_Copy_buffer_list_init(&(th->cbl_multi), (Nthreads+1)*(N_COLL_ENV+1),
275                                Nthreads*COPY_BUFFER_SIZE);
276 #endif
277
278 #ifdef TMPI_PROFILE
279     tMPI_Profile_init(&(th->profile));
280 #endif
281     /* now wait for all other threads to come on line, before we
282        start the MPI program */
283     tMPI_Thread_barrier_wait( &(tmpi_global->barrier) );
284 }
285
286
287 static void tMPI_Thread_destroy(struct tmpi_thread *th)
288 {
289     int i;
290
291     tMPI_Recv_env_list_destroy( &(th->evr));
292     for(i=0;i<Nthreads;i++)
293     {
294         tMPI_Send_env_list_destroy( &(th->evs[i]));
295     }
296     free(th->evs);
297     tMPI_Free_env_list_destroy( &(th->envelopes) );
298     tMPI_Event_destroy( &(th->p2p_event) );
299     tMPI_Req_list_destroy( &(th->rql) );
300
301 #ifdef USE_COLLECTIVE_COPY_BUFFER
302     tMPI_Copy_buffer_list_destroy(&(th->cbl_multi));
303 #endif
304
305     for(i=0;i<th->argc;i++)
306     {
307         free(th->argv[i]);
308     }
309 }
310
311 static void tMPI_Global_init(struct tmpi_global *g, int Nthreads)
312 {
313     g->usertypes=NULL;
314     g->N_usertypes=0;
315     g->Nalloc_usertypes=0;
316     tMPI_Thread_mutex_init(&(g->timer_mutex));
317     tMPI_Spinlock_init(&(g->datatype_lock));
318
319     tMPI_Thread_barrier_init( &(g->barrier), Nthreads);
320
321     tMPI_Thread_mutex_init(&(g->comm_link_lock));
322
323 #if ! (defined( _WIN32 ) || defined( _WIN64 ) )
324     /* the time at initialization. */
325     gettimeofday( &(g->timer_init), NULL);
326 #else
327     /* the time at initialization. */
328     g->timer_init=GetTickCount();
329 #endif
330
331 }
332
333 static void tMPI_Global_destroy(struct tmpi_global *g)
334 {
335     tMPI_Thread_barrier_destroy(&(g->barrier));
336     tMPI_Thread_mutex_destroy(&(g->timer_mutex));
337     tMPI_Thread_mutex_destroy(&(g->comm_link_lock));
338 }
339
340
341
342
343 static void* tMPI_Thread_starter(void *arg)
344 {
345     struct tmpi_thread *th=(struct tmpi_thread*)arg;
346
347 #ifdef TMPI_TRACE
348     tMPI_Trace_print("Created thread nr. %d", (int)(th-threads));
349 #endif
350
351     tMPI_Thread_init(th);
352
353     /* start_fn, start_arg, argc and argv were set by the calling function */ 
354     if (! th->start_fn )
355     {
356         th->start_fn_main(th->argc, th->argv);
357     }
358     else
359     {
360         th->start_fn(th->start_arg);
361         if (!tmpi_finalized)
362             tMPI_Finalize();
363     }
364
365     return 0;
366 }
367
368
369 void tMPI_Start_threads(tmpi_bool main_returns, int N, 
370                         tMPI_Affinity_strategy aff_strategy,
371                         int *argc, char ***argv, 
372                         void (*start_fn)(void*), void *start_arg,
373                         int (*start_fn_main)(int, char**))
374 {
375 #ifdef TMPI_TRACE
376     tMPI_Trace_print("tMPI_Start_threads(%d, %d, %d, %d, %d, %p, %p, %p, %p)", 
377                       main_returns, N, aff_strategy, argc, argv, start_fn, 
378                       start_arg);
379 #endif
380     if (N>0) 
381     {
382         int i;
383         int set_affinity=FALSE;
384
385         tmpi_finalized=FALSE;
386         Nthreads=N;
387
388         /* allocate global data */
389         tmpi_global=(struct tmpi_global*)
390                         tMPI_Malloc(sizeof(struct tmpi_global));
391         tMPI_Global_init(tmpi_global, N);
392
393         /* allocate world and thread data */
394         threads=(struct tmpi_thread*)tMPI_Malloc(sizeof(struct tmpi_thread)*N);
395         TMPI_COMM_WORLD=tMPI_Comm_alloc(NULL, N);
396         TMPI_GROUP_EMPTY=tMPI_Group_alloc();
397
398         if (tMPI_Thread_key_create(&id_key, NULL))
399         {
400             tMPI_Error(TMPI_COMM_WORLD, TMPI_ERR_INIT);
401         }
402         for(i=0;i<N;i++)
403         {
404             TMPI_COMM_WORLD->grp.peers[i]=&(threads[i]);
405
406             /* copy argc, argv */
407             if (argc && argv)
408             {
409                 int j;
410                 threads[i].argc=*argc;
411                 threads[i].argv=(char**)tMPI_Malloc(threads[i].argc*
412                                                    sizeof(char*));
413                 for(j=0;j<threads[i].argc;j++)
414                 {
415 #if ! (defined( _WIN32 ) || defined( _WIN64 ) )
416                     threads[i].argv[j]=strdup( (*argv)[j] );
417 #else
418                     threads[i].argv[j]=_strdup( (*argv)[j] );
419 #endif
420                 }
421             }
422             else
423             {
424                 threads[i].argc=0;
425                 threads[i].argv=NULL;
426             }
427             threads[i].start_fn=start_fn;
428             threads[i].start_fn_main=start_fn_main;
429             threads[i].start_arg=start_arg;
430         }
431
432         /* now check whether to set affinity */
433         if (aff_strategy == TMPI_AFFINITY_ALL_CORES) 
434         {
435             int nhw=tMPI_Thread_get_hw_number();
436             if ((nhw > 1) && (nhw == N))
437             {
438                 set_affinity=TRUE;
439             }
440         }
441
442         /* set thread 0's properties */
443         threads[0].thread_id=tMPI_Thread_self();
444         if (set_affinity)
445         {
446             /* set the main thread's affinity */
447             tMPI_Thread_setaffinity_single(threads[0].thread_id, 0);
448         }
449
450         for(i=1;i<N;i++) /* zero is the main thread */
451         {
452             int ret;
453             ret=tMPI_Thread_create(&(threads[i].thread_id), 
454                                    tMPI_Thread_starter,
455                                    (void*)&(threads[i]) ) ;
456
457             if (set_affinity)
458             {
459                 tMPI_Thread_setaffinity_single(threads[i].thread_id, i);
460             }
461             if(ret)
462             {
463                 tMPI_Error(TMPI_COMM_WORLD, TMPI_ERR_INIT);
464             }
465         }
466         /* the main thread also runs start_fn if we don't want
467            it to return */
468         if (!main_returns)
469             tMPI_Thread_starter((void*)&(threads[0]));
470         else
471             tMPI_Thread_init(&(threads[0]));
472     }
473 }
474
475
476 int tMPI_Init(int *argc, char ***argv, 
477               int (*start_function)(int, char**))
478 {
479 #ifdef TMPI_TRACE
480     tMPI_Trace_print("tMPI_Init(%p, %p, %p)", argc, argv, start_function);
481 #endif
482
483     if (TMPI_COMM_WORLD==0) /* we're the main process */
484     {
485         int N=0;
486         tMPI_Get_N(argc, argv, "-nt", &N);
487         tMPI_Start_threads(TRUE, N, TMPI_AFFINITY_ALL_CORES, argc, argv, 
488                            NULL, NULL, start_function);
489     }
490     else
491     {
492         /* if we're a sub-thread we need don't need to do anyhing, because 
493            everything has already been set up by either the main thread, 
494            or the thread runner function.*/
495     }
496     return TMPI_SUCCESS;
497 }
498
499
500
501
502 int tMPI_Init_fn(int main_thread_returns, int N, 
503                  tMPI_Affinity_strategy aff_strategy,
504                  void (*start_function)(void*), void *arg)
505 {
506 #ifdef TMPI_TRACE
507     tMPI_Trace_print("tMPI_Init_fn(%d, %p, %p)", N, start_function, arg);
508 #endif
509
510     if (N<1)
511     {
512         N=tMPI_Thread_get_hw_number();
513         if (N<1) N=1; /*because that's what the fn returns if it doesn't know*/
514     }
515
516     if (TMPI_COMM_WORLD==0 && N>=1) /* we're the main process */
517     {
518         tMPI_Start_threads(main_thread_returns, N, aff_strategy, 
519                            0, 0, start_function, arg, NULL);
520     }
521     return TMPI_SUCCESS;
522 }
523
524 int tMPI_Initialized(int *flag)
525 {
526 #ifdef TMPI_TRACE
527     tMPI_Trace_print("tMPI_Initialized(%p)", flag);
528 #endif
529
530     *flag=(TMPI_COMM_WORLD && !tmpi_finalized);
531
532     return TMPI_SUCCESS;
533 }
534
535 int tMPI_Finalize(void)
536 {
537     int i;
538 #ifdef TMPI_TRACE
539     tMPI_Trace_print("tMPI_Finalize()");
540 #endif
541 #ifdef TMPI_DEBUG
542     printf("%5d: tMPI_Finalize called\n", tMPI_This_threadnr());
543     fflush(stdout);
544 #endif
545
546 #ifdef TMPI_PROFILE
547     {
548         struct tmpi_thread *cur=tMPI_Get_current();
549
550         tMPI_Profile_stop( &(cur->profile) );
551         tMPI_Thread_barrier_wait( &(tmpi_global->barrier) );
552
553         if (tMPI_Is_master())
554         {
555             tMPI_Profiles_summarize(Nthreads, threads);
556         }
557     }
558 #endif
559     tMPI_Thread_barrier_wait( &(tmpi_global->barrier) );
560
561     if (tMPI_Is_master())
562     {
563
564         /* we just wait for all threads to finish; the order isn't very 
565            relevant, as all threads should arrive at their endpoints soon. */
566         for(i=1;i<Nthreads;i++)
567         {
568             if (tMPI_Thread_join(threads[i].thread_id, NULL))
569             {
570                 tMPI_Error(TMPI_COMM_WORLD, TMPI_ERR_FINALIZE);
571             }
572             tMPI_Thread_destroy(&(threads[i]));
573         }
574         /* at this point, we are the only thread left, so we can 
575            destroy the global structures with impunity. */
576         tMPI_Thread_destroy(&(threads[0]));
577         free(threads);
578
579         tMPI_Thread_key_delete(id_key);
580         /* de-allocate all the comm stuctures. */
581         {
582             tMPI_Comm cur;
583
584             tMPI_Thread_mutex_lock(&(tmpi_global->comm_link_lock));
585             cur=TMPI_COMM_WORLD->next;
586             while(cur && (cur!=TMPI_COMM_WORLD) )
587             {
588                 tMPI_Comm next=cur->next;
589                 tMPI_Comm_destroy(cur, FALSE);
590                 cur=next;
591             }
592             tMPI_Comm_destroy(TMPI_COMM_WORLD, FALSE);
593             tMPI_Thread_mutex_unlock(&(tmpi_global->comm_link_lock));
594         }
595
596         tMPI_Group_free(&TMPI_GROUP_EMPTY);
597         threads=0;
598         TMPI_COMM_WORLD=NULL;
599         TMPI_GROUP_EMPTY=NULL;
600         Nthreads=0;
601
602         /* deallocate the 'global' structure */
603         tMPI_Global_destroy(tmpi_global);
604         free(tmpi_global);
605
606         tmpi_finalized=TRUE;
607     }
608     else
609     {
610         tMPI_Thread_exit(0);
611     }
612     return TMPI_SUCCESS;
613 }
614
615
616 int tMPI_Finalized(int *flag)
617 {
618 #ifdef TMPI_TRACE
619     tMPI_Trace_print("tMPI_Finalized(%p)", flag);
620 #endif
621     *flag=tmpi_finalized;
622
623     return TMPI_SUCCESS;
624 }
625
626
627
628 int tMPI_Abort(tMPI_Comm comm, int errorcode)
629 {
630 #ifdef TMPI_TRACE
631     tMPI_Trace_print("tMPI_Abort(%p, %d)", comm, errorcode);
632 #endif
633 #if 0
634     /* we abort(). This way we can run a debugger on it */
635     fprintf(stderr, "tMPI_Abort called with error code %d",errorcode);
636     if (comm==TMPI_COMM_WORLD)
637         fprintf(stderr, " on TMPI_COMM_WORLD");
638     fprintf(stderr,"\n");
639     fflush(stdout);
640
641     abort();
642 #else
643     /* we just kill all threads, but not the main process */
644     
645     if (tMPI_Is_master())
646     {
647         if (comm==TMPI_COMM_WORLD)
648             fprintf(stderr, 
649                "tMPI_Abort called on TMPI_COMM_WORLD main with errorcode=%d\n",
650                errorcode);
651         else
652         fprintf(stderr, "tMPI_Abort called on main thread with errorcode=%d\n",
653                 errorcode);
654         fflush(stderr);
655         exit(errorcode);
656     }
657     else
658     {
659         int *ret;
660         /* kill myself */
661         fprintf(stderr, "tMPI_Abort called with error code %d on thread %d\n", 
662                         errorcode, tMPI_This_threadnr());
663         fflush(stderr);
664         ret=(int*)malloc(sizeof(int));
665         tMPI_Thread_exit(ret);
666     }
667 #endif
668     return TMPI_SUCCESS;
669 }
670
671
672 int tMPI_Get_processor_name(char *name, int *resultlen)
673 {
674     int nr=tMPI_Threadnr(tMPI_Get_current());
675     unsigned int digits=0;
676     const unsigned int base=10;
677
678 #ifdef TMPI_TRACE
679     tMPI_Trace_print("tMPI_Get_processor_name(%p, %p)", name, resultlen);
680 #endif
681     /* we don't want to call sprintf here (it turns out to be not entirely
682        thread-safe on Mac OS X, for example), so we do it our own way: */
683
684     /* first determine number of digits */
685     {
686         int rest=nr;
687         while(rest > 0)
688         {
689             rest /= base;
690             digits++;
691         }
692         if (digits==0)
693             digits=1;
694     }
695 #if ! (defined( _WIN32 ) || defined( _WIN64 ) )
696     strcpy(name, "thread #");
697 #else
698     strncpy_s(name, TMPI_MAX_PROCESSOR_NAME, "thread #", TMPI_MAX_PROCESSOR_NAME);
699 #endif
700     /* now construct the number */
701     {
702         size_t len=strlen(name);
703         unsigned int i;
704         int rest=nr;
705
706         for(i=0;i<digits;i++)
707         {
708             size_t pos=len + (digits-i-1);
709             if (pos < (TMPI_MAX_PROCESSOR_NAME -1) )
710                 name[ pos ]=(char)('0' + rest%base);
711             rest /= base;
712         }
713         if ( (digits+len) < TMPI_MAX_PROCESSOR_NAME)
714             name[digits + len]='\0';
715         else
716             name[TMPI_MAX_PROCESSOR_NAME]='\0';
717
718     }
719     if (resultlen)
720         *resultlen=(int)strlen(name); /* For some reason the MPI standard
721                                          uses ints instead of size_ts for
722                                          sizes. */
723     return TMPI_SUCCESS;
724 }
725
726
727
728
729
730 /* TODO: there must be better ways to do this */
731 double tMPI_Wtime(void)
732 {
733     double ret=0;
734
735 #ifdef TMPI_TRACE
736     tMPI_Trace_print("tMPI_Wtime()");
737 #endif
738
739 #if ! (defined( _WIN32 ) || defined( _WIN64 ) )
740     {
741         struct timeval tv;
742         long int secdiff;
743         int usecdiff;
744
745         gettimeofday(&tv, NULL);
746         secdiff = tv.tv_sec - tmpi_global->timer_init.tv_sec;
747         usecdiff = tv.tv_usec - tmpi_global->timer_init.tv_usec;
748
749         ret=(double)secdiff + 1e-6*usecdiff;
750     }
751 #else
752     {
753         DWORD tv=GetTickCount();
754    
755         /* the windows absolute time GetTickCount() wraps around in ~49 days,
756            so it's safer to always use differences, and assume that our
757            program doesn't run that long.. */
758         ret=1e-3*((unsigned int)(tv - tmpi_global->timer_init));
759     }
760 #endif
761     return ret;
762 }
763
764 double tMPI_Wtick(void)
765 {
766 #if ! (defined( _WIN32 ) || defined( _WIN64 ) )
767     /* In Unix, we don't really know. Any modern OS should be at least
768        this precise, though */
769     return 1./100.;
770 #else
771     /* According to the Windows documentation, this is about right: */
772     return 1./100.;
773 #endif
774 }
775
776
777
778
779
780
781
782 int tMPI_Get_count(tMPI_Status *status, tMPI_Datatype datatype, int *count)
783 {
784 #ifdef TMPI_TRACE
785     tMPI_Trace_print("tMPI_Get_count(%p, %p, %p)", status, datatype, count);
786 #endif
787     if (!status)
788     {
789         return tMPI_Error(TMPI_COMM_WORLD, TMPI_ERR_STATUS);
790     }
791     *count = (int)(status->transferred/datatype->size);
792     return TMPI_SUCCESS;
793 }
794
795
796