Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members   Related Pages  

VMDThreads.C

Go to the documentation of this file.
00001 /***************************************************************************
00002  *cr
00003  *cr            (C) Copyright 1995-2009 The Board of Trustees of the
00004  *cr                        University of Illinois
00005  *cr                         All Rights Reserved
00006  *cr
00007  ***************************************************************************/
00008 
00009 /***************************************************************************
00010  * RCS INFORMATION:
00011  *
00012  *      $RCSfile: VMDThreads.C,v $
00013  *      $Author: johns $        $Locker:  $             $State: Exp $
00014  *      $Revision: 1.85 $       $Date: 2009/08/18 04:46:55 $
00015  *
00016  ***************************************************************************
00017  * DESCRIPTION:
00018  *
00019  * VMDThreads.C - code for spawning threads on various platforms.
00020  *                Code donated by John Stone, john.stone@gmail.com 
00021  *                This code was originally written for the
00022  *                Tachyon Parallel/Multiprocessor Ray Tracer. 
00023  *                Improvements have been donated by Mr. Stone on an 
00024  *                ongoing basis. 
00025  *
00026  ***************************************************************************/
00027 
00028 #if 0
00029 #include <stdlib.h>
00030 #include <string.h>
00031 #include <stdio.h>
00032 
00033 /* If compiling on Linux, enable the GNU CPU affinity functions in both */
00034 /* libc and the libpthreads                                             */
00035 #if defined(__linux)
00036 #define _GNU_SOURCE 1
00037 #endif
00038 
00039 #include "VMDThreads.h"
00040 
00041 #ifdef _MSC_VER
00042 #include <windows.h>
00043 #include <winbase.h>
00044 #endif
00045 
00046 /* needed for call to sysconf() */
00047 #if defined(__sun) || defined(__linux) || defined(__irix) || defined(_CRAY) || defined(__osf__) || defined(_AIX)
00048 #include<unistd.h>
00049 #endif
00050 
00051 #if defined(__APPLE__) && defined(VMDTHREADS)
00052 #include <Carbon/Carbon.h> /* Carbon APIs for Multiprocessing */
00053 #endif
00054 
00055 #if defined(__hpux)
00056 #include <sys/mpctl.h>
00057 #endif
00058 
00059 
00060 #ifdef __cplusplus
00061 extern "C" {
00062 #endif
00063 
00064 
00065 int vmd_thread_numphysprocessors(void) {
00066   int a=1;
00067 
00068 #ifdef VMDTHREADS
00069 #if defined(__APPLE__) 
00070   a = MPProcessorsScheduled(); /* Number of active/running CPUs */
00071 #endif
00072 
00073 #ifdef _MSC_VER
00074   struct _SYSTEM_INFO sysinfo;
00075   GetSystemInfo(&sysinfo);
00076   a = sysinfo.dwNumberOfProcessors; /* total number of CPUs */
00077 #endif /* _MSC_VER */
00078 
00079 #if defined(_CRAY)
00080   a = sysconf(_SC_CRAY_NCPU);
00081 #endif
00082 
00083 #if defined(__sun) || defined(__linux) || defined(__osf__) || defined(_AIX)
00084   a = sysconf(_SC_NPROCESSORS_ONLN); /* number of active/running CPUs */
00085 #endif /* SunOS */
00086 
00087 #if defined(__irix)
00088   a = sysconf(_SC_NPROC_ONLN); /* number of active/running CPUs */
00089 #endif /* IRIX */
00090 
00091 #if defined(__hpux)
00092   a = mpctl(MPC_GETNUMSPUS, 0, 0); /* total number of CPUs */
00093 #endif /* HPUX */
00094 #endif /* VMDTHREADS */
00095 
00096   return a;
00097 }
00098 
00099 int vmd_thread_numprocessors(void) {
00100   int a=1;
00101 
00102 #ifdef VMDTHREADS
00103   /* Allow the user to override the number of CPUs for use */
00104   /* in scalability testing, debugging, etc.               */
00105   char *forcecount = getenv("VMDFORCECPUCOUNT");
00106   if (forcecount != NULL) {
00107     if (sscanf(forcecount, "%d", &a) == 1) {
00108       return a; /* if we got a valid count, return it */
00109     } else {
00110       a=1;      /* otherwise use the real available hardware CPU count */
00111     }
00112   }
00113 
00114   /* otherwise return the number of physical processors currently available */
00115   a = vmd_thread_numphysprocessors();
00116 
00117   /* XXX we should add checking for the current CPU affinity masks here, */
00118   /* and return the min of the physical processor count and CPU affinity */
00119   /* mask enabled CPU count.                                             */
00120 #endif /* VMDTHREADS */
00121 
00122   return a;
00123 }
00124 
00125 
00126 int * vmd_cpu_affinitylist(int *cpuaffinitycount) {
00127   int *affinitylist = NULL;
00128   *cpuaffinitycount = -1; /* return count -1 if unimplemented or err occurs */
00129 
00130 /* Win32 process affinity mask query */
00131 /* XXX untested, but based on the linux code, may work with a few tweaks */
00132 #if 0 && defined(_MSC_VER)
00133   HANDLE myproc = GetCurrentProcess(); /* returns a psuedo-handle */
00134   DWORD affinitymask, sysaffinitymask;
00135 
00136   if (!GetProcessAffinityMask(myproc, &affinitymask, &sysaffinitymask)) {
00137     /* count length of affinity list */
00138     int affinitycount=0;
00139     int i;
00140     for (i=0; i<31; i++) {
00141       affinitycount += (affinitymask >> i) & 0x1;
00142     }
00143   
00144     /* build affinity list */
00145     if (affinitycount > 0) {
00146       affinitylist = (int *) malloc(affinitycount * sizeof(int));
00147       if (affinitylist == NULL)
00148         return NULL;
00149 
00150       int curcount = 0;
00151       for (i=0; i<CPU_SETSIZE; i++) {
00152         if (CPU_ISSET(i, &affinitymask)) {
00153           affinitylist[curcount] = i;
00154           curcount++;
00155         }
00156       }
00157     }
00158 
00159     *cpuaffinitycount = affinitycount; /* return final affinity list */
00160   }
00161 #endif
00162 
00163 /* Linux process affinity mask query */
00164 #if defined(__linux)
00165 
00166 /* protect ourselves from some older Linux distros */
00167 #if defined(CPU_SETSIZE)
00168   int i;
00169   cpu_set_t affinitymask;
00170   int affinitycount=0;
00171 
00172   /* PID 0 refers to the current process */
00173   if (sched_getaffinity(0, sizeof(affinitymask), &affinitymask) < 0) {
00174     perror("vmd_cpu_affinitylist: sched_getaffinity");
00175     return NULL;
00176   }
00177 
00178   /* count length of affinity list */
00179   for (i=0; i<CPU_SETSIZE; i++) {
00180     affinitycount += CPU_ISSET(i, &affinitymask);
00181   }
00182 
00183   /* build affinity list */
00184   if (affinitycount > 0) {
00185     affinitylist = (int *) malloc(affinitycount * sizeof(int));
00186     if (affinitylist == NULL)
00187       return NULL;
00188 
00189     int curcount = 0;
00190     for (i=0; i<CPU_SETSIZE; i++) {
00191       if (CPU_ISSET(i, &affinitymask)) {
00192         affinitylist[curcount] = i;
00193         curcount++;
00194       }
00195     }
00196   }
00197 
00198   *cpuaffinitycount = affinitycount; /* return final affinity list */
00199 #endif
00200 #endif
00201 
00202   /* MacOS X 10.5.x has a CPU affinity query/set capability finally      */
00203   /* http://developer.apple.com/releasenotes/Performance/RN-AffinityAPI/ */
00204 
00205   /* Solaris and HP-UX use pset_bind() and related functions, and they   */
00206   /* don't use the single-level mask-based scheduling mechanism that     */
00207   /* the others, use.  Instead, they use a hierarchical tree of          */
00208   /* processor sets and processes float within those, or are tied to one */
00209   /* processor that's a member of a particular set.                      */
00210 
00211   return affinitylist;
00212 }
00213 
00214 
00215 int vmd_thread_set_self_cpuaffinity(int cpu) {
00216   int status=-1; /* unsupported by default */
00217 
00218 #ifdef VMDTHREADS
00219 
00220 #if defined(__linux)
00221 #if 0
00222   /* XXX this code is too new even for RHEL4, though it runs on Fedora 7 */
00223   /* and other newer revs.                                               */
00224   /* NPTL systems can assign per-thread affinities this way              */
00225   cpu_set_t affinitymask;
00226   CPU_ZERO(&affinitymask); 
00227   CPU_SET(cpu, &affinitymask);
00228   status = pthread_setaffinity_np(pthread_self(), sizeof(affinitymask), &affinitymask);
00229 #else
00230   /* non-NPTL systems based on the clone() API must use this method      */
00231   cpu_set_t affinitymask;
00232   CPU_ZERO(&affinitymask); 
00233   CPU_SET(cpu, &affinitymask);
00234 
00235   /* PID 0 refers to the current process */
00236   if ((status=sched_setaffinity(0, sizeof(affinitymask), &affinitymask)) < 0) {
00237     perror("vmd_thread_set_self_cpuaffinitylist: sched_setaffinity");
00238     return status;
00239   }
00240 #endif
00241 
00242   /* call sched_yield() so new affinity mask takes effect immediately */
00243   sched_yield();
00244 #endif /* linux */
00245 
00246   /* MacOS X 10.5.x has a CPU affinity query/set capability finally      */
00247   /* http://developer.apple.com/releasenotes/Performance/RN-AffinityAPI/ */
00248 
00249   /* Solaris and HP-UX use pset_bind() and related functions, and they   */
00250   /* don't use the single-level mask-based scheduling mechanism that     */
00251   /* the others, use.  Instead, they use a hierarchical tree of          */
00252   /* processor sets and processes float within those, or are tied to one */
00253   /* processor that's a member of a particular set.                      */
00254 #endif
00255 
00256   return status;
00257 }
00258 
00259 
00260 int vmd_thread_setconcurrency(int nthr) {
00261   int status=0;
00262 
00263 #ifdef VMDTHREADS
00264 #if defined(__sun) 
00265 #ifdef USEPOSIXTHREADS 
00266   status = pthread_setconcurrency(nthr);
00267 #else
00268   status = thr_setconcurrency(nthr);
00269 #endif
00270 #endif /* SunOS */
00271 
00272 #if defined(__irix) || defined(_AIX)
00273   status = pthread_setconcurrency(nthr);
00274 #endif
00275 #endif /* VMDTHREADS */
00276 
00277   return status;
00278 }
00279 
00280 
00281 
00282 /* Typedef to eliminate compiler warning caused by C/C++ linkage conflict. */
00283 #ifdef __cplusplus
00284 extern "C" {
00285 #endif
00286   typedef void * (*VMDTHREAD_START_ROUTINE)(void *);
00287 #ifdef __cplusplus
00288 }
00289 #endif
00290 
00291 int vmd_thread_create(vmd_thread_t * thr, void * fctn(void *), void * arg) {
00292   int status=0;
00293 
00294 #ifdef VMDTHREADS 
00295 #ifdef _MSC_VER
00296   DWORD tid; /* thread id, msvc only */
00297   *thr = CreateThread(NULL, 8192, (LPTHREAD_START_ROUTINE) fctn, arg, 0, &tid);
00298   if (*thr == NULL) {
00299     status = -1;
00300   }
00301 #endif /* _MSC_VER */
00302 
00303 #ifdef USEPOSIXTHREADS 
00304 #if defined(_AIX)
00305   {
00306     pthread_attr_t attr;
00307     pthread_attr_init(&attr);
00308     pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
00309     status = pthread_create(thr, &attr, fctn, arg);
00310     pthread_attr_destroy(&attr);
00311   }
00312 #else   
00313   status = pthread_create(thr, NULL, (VMDTHREAD_START_ROUTINE)fctn, arg);
00314 #endif 
00315 #endif /* USEPOSIXTHREADS */
00316 #endif /* VMDTHREADS */
00317  
00318   return status;
00319 }
00320 
00321 
00322 int vmd_thread_join(vmd_thread_t thr, void ** stat) {
00323   int status=0;  
00324 
00325 #ifdef VMDTHREADS
00326 #ifdef _MSC_VER
00327   DWORD wstatus = 0;
00328  
00329   wstatus = WAIT_TIMEOUT;
00330  
00331   while (wstatus != WAIT_OBJECT_0) {
00332     wstatus = WaitForSingleObject(thr, INFINITE);
00333   }
00334 #endif /* _MSC_VER */
00335 
00336 #ifdef USEPOSIXTHREADS
00337   status = pthread_join(thr, stat);
00338 #endif /* USEPOSIXTHREADS */
00339 #endif /* VMDTHREADS */
00340 
00341   return status;
00342 }  
00343 
00344 
00345 int vmd_mutex_init(vmd_mutex_t * mp) {
00346   int status=0;
00347 
00348 #ifdef VMDTHREADS
00349 #ifdef _MSC_VER
00350   InitializeCriticalSection(mp);
00351 #endif /* _MSC_VER */
00352 
00353 #ifdef USEPOSIXTHREADS
00354   status = pthread_mutex_init(mp, 0);
00355 #endif /* USEPOSIXTHREADS */
00356 #endif /* VMDTHREADS */
00357 
00358   return status;
00359 }
00360 
00361 
00362 int vmd_mutex_lock(vmd_mutex_t * mp) {
00363   int status=0;
00364 
00365 #ifdef VMDTHREADS
00366 #ifdef _MSC_VER
00367   EnterCriticalSection(mp);
00368 #endif /* _MSC_VER */
00369 
00370 #ifdef USEPOSIXTHREADS
00371   status = pthread_mutex_lock(mp);
00372 #endif /* USEPOSIXTHREADS */
00373 #endif /* VMDTHREADS */
00374 
00375   return status;
00376 }
00377 
00378 
00379 int vmd_mutex_unlock(vmd_mutex_t * mp) {
00380   int status=0;
00381 
00382 #ifdef VMDTHREADS  
00383 #ifdef _MSC_VER
00384   LeaveCriticalSection(mp);
00385 #endif /* _MSC_VER */
00386 
00387 #ifdef USEPOSIXTHREADS
00388   status = pthread_mutex_unlock(mp);
00389 #endif /* USEPOSIXTHREADS */
00390 #endif /* VMDTHREADS */
00391 
00392   return status;
00393 }
00394 
00395 
00396 int vmd_mutex_destroy(vmd_mutex_t * mp) {
00397   int status=0;
00398 
00399 #ifdef VMDTHREADS
00400 #ifdef _MSC_VER
00401   DeleteCriticalSection(mp);
00402 #endif /* _MSC_VER */
00403 
00404 #ifdef USEPOSIXTHREADS
00405   status = pthread_mutex_destroy(mp);
00406 #endif /* USEPOSIXTHREADS */
00407 #endif /* VMDTHREADS */
00408 
00409   return status;
00410 }
00411 
00412 
00413 
00414 int vmd_cond_init(vmd_cond_t * cvp) {
00415   int status=0;
00416 
00417 #ifdef VMDTHREADS
00418 #ifdef _MSC_VER
00419 #if defined(VMDUSEWIN2008CONDVARS)
00420   InitializeConditionVariable(cvp);
00421 #else
00422   /* XXX not implemented */
00423   cvp->waiters = 0;
00424 
00425   /* Create an auto-reset event. */
00426   cvp->events[VMD_COND_SIGNAL] = CreateEvent(NULL,  /* no security */
00427                                              FALSE, /* auto-reset event */
00428                                              FALSE, /* non-signaled initially */
00429                                              NULL); /* unnamed */
00430 
00431   // Create a manual-reset event.
00432   cvp->events[VMD_COND_BROADCAST] = CreateEvent(NULL,  /* no security */
00433                                                 TRUE,  /* manual-reset */
00434                                                 FALSE, /* non-signaled initially*/
00435                                                 NULL); /* unnamed */
00436 #endif
00437 #endif /* _MSC_VER */
00438 
00439 #ifdef USEPOSIXTHREADS
00440   status = pthread_cond_init(cvp, NULL);
00441 #endif /* USEPOSIXTHREADS */
00442 #endif /* VMDTHREADS */
00443 
00444   return status;
00445 }
00446 
00447 int vmd_cond_destroy(vmd_cond_t * cvp) {
00448   int status=0;
00449 
00450 #ifdef VMDTHREADS
00451 #ifdef _MSC_VER
00452 #if defined(VMDUSEWIN2008CONDVARS)
00453   /* XXX not implemented */
00454 #else
00455   CloseHandle(cvp->events[VMD_COND_SIGNAL]);
00456   CloseHandle(cvp->events[VMD_COND_BROADCAST]);
00457 #endif
00458 #endif /* _MSC_VER */
00459 
00460 #ifdef USEPOSIXTHREADS
00461   status = pthread_cond_destroy(cvp);
00462 #endif /* USEPOSIXTHREADS */
00463 #endif /* VMDTHREADS */
00464 
00465   return status;
00466 }
00467 
00468 int vmd_cond_wait(vmd_cond_t * cvp, vmd_mutex_t * mp) {
00469   int status=0;
00470 
00471 #ifdef VMDTHREADS
00472 #ifdef _MSC_VER
00473 #if defined(VMDUSEWIN2008CONDVARS)
00474   SleepConditionVariableCS(cvp, mp, INFINITE)
00475 #else
00476 #if !defined(VMDUSEINTERLOCKEDATOMICOPS)
00477   EnterCriticalSection(&cvp->waiters_lock);
00478   cvp->waiters++;
00479   LeaveCriticalSection(&cvp->waiters_lock);
00480 #else
00481   InterlockedIncrement(&cvp->waiters);
00482 #endif
00483 
00484   LeaveCriticalSection(mp); /* SetEvent() maintains state, avoids lost wakeup */
00485 
00486   /* Wait either a single or broadcast even to become signalled */
00487   int result = WaitForMultipleObjects(2, cvp->events, FALSE, INFINITE);
00488 
00489 #if !defined(VMDUSEINTERLOCKEDATOMICOPS)
00490   EnterCriticalSection (&cvp->waiters_lock);
00491   cvp->waiters--;
00492   LONG last_waiter = 
00493     ((result == (WAIT_OBJECT_0 + VMD_COND_BROADCAST)) && cvp->waiters == 0);
00494   LeaveCriticalSection (&cvp->waiters_lock);
00495 #else
00496   LONG my_waiter = InterlockedDecrement(&cvp->waiters);
00497   LONG last_waiter = 
00498     ((result == (WAIT_OBJECT_0 + VMD_COND_BROADCAST)) && my_waiter == 0);
00499 #endif
00500 
00501   /* Some thread called cond_broadcast() */
00502   if (last_waiter)
00503     /* We're the last waiter to be notified or to stop waiting, so */
00504     /* reset the manual event.                                     */
00505     ResetEvent(cvp->events[VMD_COND_BROADCAST]); 
00506 
00507   EnterCriticalSection(mp);
00508 #endif
00509 #endif /* _MSC_VER */
00510 
00511 #ifdef USEPOSIXTHREADS
00512   status = pthread_cond_wait(cvp, mp);
00513 #endif /* USEPOSIXTHREADS */
00514 #endif /* VMDTHREADS */
00515 
00516   return status;
00517 }
00518 
00519 int vmd_cond_signal(vmd_cond_t * cvp) {
00520   int status=0;
00521 
00522 #ifdef VMDTHREADS
00523 #ifdef _MSC_VER
00524 #if defined(VMDUSEWIN2008CONDVARS)
00525   WakeConditionVariable(cvp);
00526 #else
00527 #if !defined(VMDUSEINTERLOCKEDATOMICOPS)
00528   EnterCriticalSection(&cvp->waiters_lock);
00529   int have_waiters = (cvp->waiters > 0);
00530   LeaveCriticalSection(&cvp->waiters_lock);
00531   if (have_waiters)
00532     SetEvent (cvp->events[VMD_COND_SIGNAL]);
00533 #else
00534   if (InterlockedExchangeAdd(&cvp->waiters, 0) > 0)
00535     SetEvent(cvp->events[VMD_COND_SIGNAL]);
00536 #endif
00537 #endif
00538 #endif /* _MSC_VER */
00539 
00540 #ifdef USEPOSIXTHREADS
00541   status = pthread_cond_signal(cvp);
00542 #endif /* USEPOSIXTHREADS */
00543 #endif /* VMDTHREADS */
00544 
00545   return status;
00546 }
00547 
00548 int vmd_cond_broadcast(vmd_cond_t * cvp) {
00549   int status=0;
00550 
00551 #ifdef VMDTHREADS
00552 #ifdef _MSC_VER
00553 #if defined(VMDUSEWIN2008CONDVARS)
00554   WakeAllConditionVariable(cvp);
00555 #else
00556 #if !defined(VMDUSEINTERLOCKEDATOMICOPS)
00557   EnterCriticalSection(&cvp->waiters_lock);
00558   int have_waiters = (cvp->waiters > 0);
00559   LeaveCriticalSection(&cvp->waiters_lock);
00560   if (have_waiters)
00561     SetEvent(cvp->events[VMD_COND_BROADCAST]);
00562 #else
00563   if (InterlockedExchangeAdd(&cvp->waiters, 0) > 0)
00564     SetEvent(cvp->events[VMD_COND_BROADCAST]);
00565 #endif
00566 
00567 #endif
00568 #endif /* _MSC_VER */
00569 
00570 #ifdef USEPOSIXTHREADS
00571   status = pthread_cond_broadcast(cvp);
00572 #endif /* USEPOSIXTHREADS */
00573 #endif /* VMDTHREADS */
00574 
00575   return status;
00576 }
00577   
00578 
00579 
00580 #if !defined(VMDTHREADS)
00581 
00582 int vmd_thread_barrier_init_proc_shared(vmd_barrier_t *barrier, int n_clients) {
00583   return 0;
00584 }
00585 
00586 void vmd_thread_barrier_destroy(vmd_barrier_t *barrier) {
00587 }
00588 
00589 int vmd_thread_barrier(vmd_barrier_t *barrier, int increment) {
00590   return 0;
00591 }
00592 
00593 #else 
00594 
00595 #ifdef USEPOSIXTHREADS
00596 
00597 /* When rendering in the CAVE we use a special synchronization    */
00598 /* mode so that shared memory mutexes and condition variables     */
00599 /* will work correctly when accessed from multiple processes.     */
00600 /* Inter-process synchronization involves the kernel to a greater */
00601 /* degree, so these barriers are substantially more costly to use */
00602 /* than the ones designed for use within a single-process.        */
00603 int vmd_thread_barrier_init_proc_shared(vmd_barrier_t *barrier, int n_clients) {
00604   if (barrier != NULL) {
00605     barrier->n_clients = n_clients;
00606     barrier->n_waiting = 0;
00607     barrier->phase = 0;
00608     barrier->sum = 0;
00609 
00610     pthread_mutexattr_t mattr;
00611     pthread_condattr_t  cattr;
00612 
00613     printf("Setting barriers to have system scope...\n");
00614 
00615     pthread_mutexattr_init(&mattr);
00616     if (pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED) != 0) {
00617       printf("WARNING: could not set mutex to process shared scope\n");
00618     }
00619 
00620     pthread_condattr_init(&cattr);
00621     if (pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED) != 0) {
00622       printf("WARNING: could not set mutex to process shared scope\n");
00623     }
00624 
00625     pthread_mutex_init(&barrier->lock, &mattr);
00626     pthread_cond_init(&barrier->wait_cv, &cattr);
00627 
00628     pthread_condattr_destroy(&cattr);
00629     pthread_mutexattr_destroy(&mattr);
00630   }
00631 
00632   return 0;
00633 }
00634 
00635 void vmd_thread_barrier_destroy(vmd_barrier_t *barrier) {
00636   pthread_mutex_destroy(&barrier->lock);
00637   pthread_cond_destroy(&barrier->wait_cv);
00638 }
00639 
00640 int vmd_thread_barrier(vmd_barrier_t *barrier, int increment) {
00641   int my_phase;
00642   int my_result;
00643 
00644   pthread_mutex_lock(&barrier->lock);
00645   my_phase = barrier->phase;
00646   barrier->sum += increment;
00647   barrier->n_waiting++;
00648 
00649   if (barrier->n_waiting == barrier->n_clients) {
00650     barrier->result = barrier->sum;
00651     barrier->sum = 0;
00652     barrier->n_waiting = 0;
00653     barrier->phase = 1 - my_phase;
00654     pthread_cond_broadcast(&barrier->wait_cv);
00655   }
00656 
00657   while (barrier->phase == my_phase) {
00658     pthread_cond_wait(&barrier->wait_cv, &barrier->lock);
00659   }
00660 
00661   my_result = barrier->result;
00662 
00663   pthread_mutex_unlock(&barrier->lock);
00664 
00665   return my_result;
00666 }
00667 
00668 #endif
00669 
00670 #endif /* VMDTHREADS */
00671 
00672 #ifdef __cplusplus
00673 }
00674 #endif
00675 
00676 #endif

Generated on Mon Nov 23 01:33:01 2009 for VMD (current) by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002