Main Page | Class Hierarchy | Class List | File List | Class Members

sync_unix.h

00001 //-< SYNC_UNIX.H >---------------------------------------------------*--------*
00002 // FastDB                    Version 1.0         (c) 1999  GARRET    *     ?  *
00003 // (Main Memory Database Management System)                          *   /\|  *
00004 //                                                                   *  /  \  *
00005 //                          Created:     20-Nov-98    K.A. Knizhnik  * / [] \ *
00006 //                          Last update: 20-Dec-98    K.A. Knizhnik  * GARRET *
00007 //-------------------------------------------------------------------*--------*
00008 // Intertask synchonization primitives for Unix platforms
00009 //-------------------------------------------------------------------*--------*
00010 
00011 #ifndef __SYNC_UNIX_H__
00012 #define __SYNC_UNIX_H__
00013 
00014 // Standard includes for all Unix platforms
00015 #include <unistd.h>
00016 #include <string.h>
00017 #include <fcntl.h>
00018 #include <sys/time.h>
00019 #include <sys/types.h>
00020 #include <errno.h>
00021 
00022 #if !defined(USE_POSIX_SEMAPHORES) || !defined(USE_POSIX_MMAP) || !USE_POSIX_MMAP
00023 #include <sys/ipc.h> 
00024 extern char const* keyFileDir; // default value: "/tmp/" 
00025 #endif
00026 
00027 #if defined(USE_POSIX_SEMAPHORES)
00028 #include <semaphore.h>  // For POSIX style semaphores
00029 #else
00030 #include <sys/sem.h>    // For SysV style semaphores
00031 #endif
00032 
00033 #if defined(USE_POSIX_MMAP) && USE_POSIX_MMAP
00034 #include <sys/mman.h>   // For mmap()
00035 #else
00036 #include <sys/shm.h>    
00037 #include <sys/mman.h>
00038 #endif
00039 
00040 
00041 #define thread_proc
00042 
00044 // If this system uses pthread based threads, then define
00045 //   dbMutex(), dbThread(), dbLocalEvent(), etc as pthread-based implemenations
00046 
00047 #ifndef NO_PTHREADS
00048 
00049 // Use pthread based implementation
00050 #include <pthread.h>
00051 
00052 class dbMutex { 
00053     friend class dbLocalEvent;
00054     friend class dbLocalSemaphore;
00055     pthread_mutex_t cs;
00056     bool            initialized;
00057   public:
00058     dbMutex() {
00059         pthread_mutex_init(&cs, NULL);
00060         initialized = true;
00061     }
00062     ~dbMutex() {
00063         pthread_mutex_destroy(&cs);
00064         initialized = false;
00065     }
00066     bool isInitialized() { 
00067         return initialized;
00068     }
00069     void lock() {
00070         if (initialized) { 
00071             pthread_mutex_lock(&cs);
00072         }
00073     }
00074     void unlock() {
00075         if (initialized) { 
00076             pthread_mutex_unlock(&cs);
00077         }
00078     }
00079 };
00080 
00081 const size_t dbThreadStackSize = 1024*1024;
00082 
00083 class dbThread { 
00084     pthread_t thread;
00085   public:
00086     typedef void (thread_proc* thread_proc_t)(void*);
00087     void create(thread_proc_t f, void* arg) {
00088         pthread_attr_t attr;
00089         pthread_attr_init(&attr);
00090 #if !defined(__linux__)
00091         pthread_attr_setstacksize(&attr, dbThreadStackSize);
00092 #endif
00093 #if defined(_AIX41)
00094         // At AIX 4.1, 4.2 threads are by default created detached
00095         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_UNDETACHED);
00096 #endif
00097         pthread_create(&thread, &attr, (void*(*)(void*))f, arg);
00098         pthread_attr_destroy(&attr);
00099     }
00100 
00101     void join() { 
00102         void* result;
00103         pthread_join(thread, &result);
00104     }
00105     void detach() { 
00106         pthread_detach(thread);
00107     }
00108 
00109     enum ThreadPriority { 
00110         THR_PRI_LOW, 
00111         THR_PRI_HIGH
00112     };
00113     void setPriority(ThreadPriority pri) { 
00114 #if defined(PRI_OTHER_MIN) && defined(PRI_OTHER_MAX)
00115         struct sched_param sp;
00116         sp.sched_priority = pri == THR_PRI_LOW ? PRI_OTHER_MIN : PRI_OTHER_MAX;
00117         pthread_setschedparam(thread, SCHED_OTHER, &sp); 
00118 #endif
00119     }
00120 
00121     static int numberOfProcessors();
00122 };
00123 
00124 
00125 class dbLocalEvent { 
00126     pthread_cond_t   cond;
00127     int              signaled;
00128   public:
00129     void wait(dbMutex& mutex) { 
00130         while (!signaled) { 
00131             pthread_cond_wait(&cond, &mutex.cs);
00132         }
00133     }
00134     bool wait(dbMutex& mutex, time_t timeout) {
00135         while (!signaled) {
00136             struct timespec abs_ts; 
00137 #ifdef PTHREAD_GET_EXPIRATION_NP
00138             struct timespec rel_ts; 
00139             rel_ts.tv_sec = timeout/1000; 
00140             rel_ts.tv_nsec = timeout%1000*1000000;
00141             pthread_get_expiration_np(&rel_ts, &abs_ts);
00142 #else
00143             struct timeval cur_tv;
00144             gettimeofday(&cur_tv, NULL);
00145             abs_ts.tv_sec = cur_tv.tv_sec + timeout/1000; 
00146             abs_ts.tv_nsec = cur_tv.tv_usec*1000 + timeout%1000*1000000;
00147             if (abs_ts.tv_nsec > 1000000000) { 
00148                 abs_ts.tv_nsec -= 1000000000;
00149                 abs_ts.tv_sec += 1;
00150             }
00151 #endif
00152             int rc = pthread_cond_timedwait(&cond, &mutex.cs, &abs_ts);
00153             if (rc == ETIMEDOUT) { 
00154                 return false;
00155             }
00156         }
00157         return true;
00158     }
00159     void signal() {
00160         signaled = true;
00161         pthread_cond_broadcast(&cond);
00162     }
00163     void reset() {
00164         signaled = false;
00165     }
00166     void open(bool initValue = false) { 
00167         signaled = initValue;
00168         pthread_cond_init(&cond, NULL);
00169     }
00170     void close() {
00171         pthread_cond_destroy(&cond);
00172     }
00173 };
00174 
00175 class dbLocalSemaphore { 
00176     pthread_cond_t   cond;
00177     int              count;
00178   public:
00179     void wait(dbMutex& mutex) { 
00180         while (count == 0) { 
00181             pthread_cond_wait(&cond, &mutex.cs);
00182         }
00183         count -= 1;
00184     }
00185     void wait(dbMutex& mutex, time_t timeout) {
00186         while (count == 0) {
00187             struct timespec abs_ts; 
00188 #ifdef PTHREAD_GET_EXPIRATION_NP
00189             struct timespec rel_ts; 
00190             rel_ts.tv_sec = timeout/1000; 
00191             rel_ts.tv_nsec = timeout%1000*1000000;
00192             pthread_get_expiration_np(&rel_ts, &abs_ts);
00193 #else
00194             struct timeval cur_tv;
00195             gettimeofday(&cur_tv, NULL);
00196             abs_ts.tv_sec = cur_tv.tv_sec + timeout/1000; 
00197             abs_ts.tv_nsec = cur_tv.tv_usec*1000 + timeout%1000*1000000;
00198             if (abs_ts.tv_nsec > 1000000000) { 
00199                 abs_ts.tv_nsec -= 1000000000;
00200                 abs_ts.tv_sec += 1;
00201             }
00202 #endif
00203             pthread_cond_timedwait(&cond, &mutex.cs, &abs_ts);
00204         }
00205         count -= 1;
00206     }
00207      void signal(unsigned inc = 1) {
00208         count += inc;
00209         if (inc > 1) { 
00210             pthread_cond_broadcast(&cond);
00211         } else if (inc == 1) { 
00212             pthread_cond_signal(&cond);
00213         }
00214     }
00215     void open(unsigned initValue = 0) { 
00216         pthread_cond_init(&cond, NULL);
00217         count = initValue;
00218     }
00219     void close() {
00220         pthread_cond_destroy(&cond);
00221     }
00222 };
00223 
00224 template<class T> 
00225 class dbThreadContext { 
00226     pthread_key_t key;
00227   public:
00228     T* get() { 
00229         return (T*)pthread_getspecific(key);
00230     }
00231     void set(T* value) { 
00232         pthread_setspecific(key, value);
00233     }
00234     dbThreadContext() { 
00235         pthread_key_create(&key, NULL);
00236     }
00237     ~dbThreadContext() { 
00238         pthread_key_delete(key);
00239     }
00240 };
00241 
00242 class dbProcessId { 
00243     int       pid;
00244     pthread_t tid;
00245   public:
00246     bool operator != (dbProcessId const& other) const { 
00247         return pid != other.pid || tid != other.tid;
00248     }
00249 
00250     void clear() { 
00251         pid = 0;
00252         tid = 0;
00253     }
00254 
00255     static dbProcessId getCurrent() {
00256         dbProcessId curr;
00257         curr.pid = getpid();
00258         curr.tid = pthread_self();
00259         return curr;
00260     }
00261 };
00262 
00263 #else // NO_PTHREAD
00264 
00265 // Non pthread based threads, mutexes, etc.
00266 // Maps to skeleton  functions, this implementation isn't using threads.
00267 
00268 class dbMutex {
00269     bool initialized;
00270 
00271    public:
00272     dbMutex() {
00273         initialized = true;
00274     }
00275 
00276     ~dbMutex() { 
00277         initialized = false;
00278     }
00279 
00280     bool isInitialized() { 
00281         return initialized;
00282     }
00283 
00284     void lock() {}
00285     void unlock() {}
00286 };
00287 
00288 class dbThread { 
00289   public:
00290     typedef void (thread_proc* thread_proc_t)(void*);
00291     void create(thread_proc_t f, void* arg) { f(arg); }
00292     void join() {}
00293     void detach() {}
00294     enum ThreadPriority { 
00295         THR_PRI_LOW, 
00296         THR_PRI_HIGH
00297     };
00298     void setPriority(ThreadPriority pri) { }
00299     static int numberOfProcessors() { return 1; }
00300 };
00301 
00302 class dbLocalSemaphore { 
00303     int count;
00304   public:
00305     void wait(dbMutex&) { 
00306         assert (count > 0);
00307         count -= 1;
00308     }
00309     void signal(unsigned inc = 1) {
00310         count += inc;
00311     }
00312     void open(unsigned initValue = 0) {
00313         count = initValue;
00314     }
00315     void close() {}
00316 };
00317 
00318 class dbLocalEvent { 
00319     bool signaled;
00320   public:
00321     void wait(dbMutex&) { 
00322         assert(signaled);
00323     }
00324     bool wait(dbMutex& mutex, time_t timeout) {
00325         return true;
00326     }
00327     void signal() {
00328         signaled = true;
00329     }
00330     void reset() {
00331         signaled = false;
00332     }
00333     void open(bool initValue = false) {
00334         signaled = initValue;
00335     }
00336     void close() {}
00337 };
00338 
00339 template<class T>
00340 class dbThreadContext { 
00341     T* value;
00342   public:
00343     T* get() { 
00344         return value;
00345     }
00346     void set(T* value) { 
00347         this->value = value;
00348     }
00349     dbThreadContext() { value = NULL; }
00350 };
00351 
00352 
00353 class dbProcessId { 
00354     int       pid;
00355   public:
00356     bool operator != (dbProcessId const& other) const { 
00357         return pid != other.pid;
00358     }
00359     
00360     void clear() { 
00361         pid = 0;
00362     }
00363 
00364     static dbProcessId getCurrent() {
00365         dbProcessId curr;
00366         curr.pid = getpid();
00367         return curr;
00368     }
00369 };
00370 
00371 #endif // NO_PTHREAD
00372 
00373 
00374 #define INFINITE (~0U)
00375 
00376 
00377 #ifdef USE_POSIX_SEMAPHORES
00378 // Initialization Mutex using Posix based semaphores
00379 class dbInitializationMutex { 
00380     sem_t* sem;
00381   public: 
00382     enum initializationStatus { 
00383         InitializationError, 
00384         AlreadyInitialized,
00385         NotYetInitialized
00386     };
00387     initializationStatus initialize(char const* name) { 
00388         initializationStatus status;
00389         char* tmp = NULL;
00390         if (*name != '/') { 
00391             tmp = new char[strlen(name)+2];
00392             strcpy(tmp+1, name);
00393             *tmp = '/';
00394             name = tmp;
00395         }
00396         while (true) {
00397             sem = sem_open(name, 0);
00398             if (sem == SEM_FAILED) { 
00399                 if (errno == ENOENT) {
00400                     sem = sem_open(name, O_CREAT|O_EXCL, 0777, 0);
00401                     if (sem != SEM_FAILED) { 
00402                         status = NotYetInitialized;
00403                         break;
00404                     } else if (errno != EEXIST) { 
00405                         status = InitializationError;
00406                         break;
00407                     }
00408                 } else { 
00409                     status = InitializationError;
00410                     break;
00411                 }
00412             } else { 
00413                 status = (sem_wait(sem) == 0 && sem_post(sem) == 0) 
00414                     ? AlreadyInitialized : InitializationError;
00415                 break;
00416             }
00417         }
00418         delete[] tmp;
00419         return status;
00420     }
00421 
00422     void done() { 
00423         sem_post(sem);
00424     }
00425     bool close() {
00426         sem_close(sem);
00427         return false;
00428     }
00429     void erase() { 
00430         close();
00431     }
00432 };
00433 
00434 class dbSemaphore { 
00435   protected:
00436     sem_t* sem;
00437   public:
00438     void wait() { 
00439         int rc = sem_wait(sem);
00440         assert(rc == 0);
00441     }
00442 
00443     bool wait(unsigned msec) { 
00444 #ifdef POSIX_1003_1d
00445         struct timespec abs_ts;
00446         struct timeval  cur_tv;
00447         clock_gettime(CLOCK_REALTIME, &cur_tv);
00448         abs_ts.tv_sec = cur_tv.tv_sec + (msec + cur_tv.tv_usec / 1000) / 1000000; 
00449         abs_ts.tv_nsec = (msec + cur_tv.tv_usec / 1000) % 1000000 * 1000;
00450         int rc = sem_timedwait(sem, &abs_ts);
00451         if (rc < 0) { 
00452             assert(errno == ETIMEDOUT);
00453             return false;
00454         }
00455         return true;
00456 #else 
00457         int rc = sem_wait(sem);
00458         assert(rc == 0);
00459         return true;
00460 #endif  
00461     }
00462 
00463     void signal(unsigned inc = 1) {
00464         while (--inc > 0) { 
00465             sem_post(sem);
00466         }
00467     }
00468     void reset() { 
00469         while (sem_trywait(sem) == 0);
00470     }    
00471     bool open(char const* name, unsigned initValue = 0) {
00472         char* tmp = NULL;
00473         if (*name != '/') { 
00474             tmp = new char[strlen(name)+2];
00475             strcpy(tmp+1, name);
00476             *tmp = '/';
00477             name = tmp;
00478         }
00479         sem = sem_open(name, O_CREAT, 0777, initValue);
00480         delete[] tmp;
00481         return sem != NULL; 
00482     }
00483     void close() {
00484         sem_close(sem);
00485     }
00486     void erase() { 
00487         close();
00488     }
00489 };
00490 
00491 class dbEvent : public dbSemaphore { 
00492   public:
00493     void wait() { 
00494         dbSemaphore::wait();
00495         sem_post(sem);
00496     }
00497     bool wait(unsigned msec) { 
00498         if (dbSemaphore::wait(msec)) { 
00499             sem_post(sem);
00500             return true;
00501         }
00502         return false;
00503     }
00504     void signal() {
00505         while (sem_trywait(sem) == 0);
00506         sem_post(sem);
00507     }
00508     void reset() {
00509         while (sem_trywait(sem) == 0);
00510     }
00511     bool open(char const* name, bool signaled = false) {
00512         return dbSemaphore::open(name, (int)signaled);
00513     }
00514 };
00515 #else // USE_POSIX_SEMAPHORES
00516 
00517 // Define local implemenation of InitializationMutex in sync.cpp
00518 class dbInitializationMutex { 
00519     int semid;
00520   public: 
00521     enum initializationStatus { 
00522         InitializationError, 
00523         AlreadyInitialized,
00524         NotYetInitialized
00525     };
00526     initializationStatus initialize(char const* name);
00527     void done(); 
00528     bool close();
00529     void erase();
00530 };
00531 
00532 
00533 class dbSemaphore { 
00534     int s;
00535   public:
00536     bool wait(unsigned msec = INFINITE);
00537     void signal(unsigned inc = 1);
00538     bool open(char const* name, unsigned initValue = 0);
00539     void reset();
00540     void close();
00541     void erase();
00542 };
00543 
00544 class dbEvent { 
00545     int e;
00546   public:
00547     bool wait(unsigned msec = INFINITE);
00548     void signal();
00549     void reset();
00550     bool open(char const* name, bool signaled = false);
00551     void close();
00552     void erase();
00553 };
00554 #endif // USE_POSIX_SEMAPHORES
00555 
00556 
00557 // Define dbSharedObject and dbSharedMemory
00558 #if defined(USE_POSIX_MMAP) && USE_POSIX_MMAP
00559 
00560 // For POSIX dbSharedObject, we use mmap()
00561 template<class T>
00562 class dbSharedObject { 
00563     char* name;
00564     T*  ptr;
00565     int fd;
00566   public:
00567 
00568     dbSharedObject() { 
00569         name = NULL;
00570         ptr = NULL;
00571         fd = -1;
00572     }
00573 
00574     bool open(char* fileName) { 
00575         delete[] name;
00576         name = new char[strlen(fileName) + 1];
00577         strcpy(name, fileName);
00578         fd = ::open(fileName, O_RDWR|O_CREAT, 0777);
00579         if (fd < 0) { 
00580             return false;
00581         }
00582         if (ftruncate(fd, sizeof(T)) < 0) {
00583             ::close(fd);
00584             return false;
00585         }
00586         ptr = (T*)mmap(NULL,
00587                        DOALIGN(sizeof(T), 4096),
00588                        PROT_READ|PROT_WRITE,
00589                        MAP_SHARED,
00590                        fd,
00591                        0);
00592         if (ptr == MAP_FAILED) { 
00593             ptr = NULL;
00594             ::close(fd);
00595             return false;
00596         }
00597         return true;
00598     }
00599 
00600     T* get() { return ptr; }
00601 
00602     void close() { 
00603         if (ptr != NULL) { 
00604             munmap(ptr, DOALIGN(sizeof(T), 4096));
00605         }
00606         if (fd > 0) { 
00607             ::close(fd);
00608         }
00609     }
00610     void erase() {
00611         close();
00612         unlink(name);   
00613     }  
00614 
00615     ~dbSharedObject() { 
00616         delete[] name;
00617     }
00618 };
00619 
00620 #else // USE_POSIX_MMAP
00621 
00622 // Non POSIX, internal implementations of SharedMemory and SharedObject
00623 extern char const* keyFileDir; // default value: "/tmp/" 
00624 class dbSharedMemory { 
00625   protected:
00626     char*  ptr;
00627     int    shm;
00628 
00629   public:
00630     bool  open(char const* name, size_t size); 
00631     void  close();
00632     void  erase(); 
00633     char* get_base() { 
00634         return ptr;
00635     }
00636 };
00637 
00638 template<class T>
00639 class dbSharedObject : public dbSharedMemory { 
00640   public:
00641     bool open(char* name) { 
00642         return dbSharedMemory::open(name, sizeof(T));
00643     }
00644     T* get() { return (T*)ptr; }
00645 };
00646 
00647 #endif
00648 
00650 // Define dBGlobalCriticalSection for various platforms
00651 
00652 // QNX uses a pthread based mutex for its implementation
00653 //     Use only if pthread support is also enabled, else we'll use the default case
00654 #if defined(__QNX__) && !defined(NO_PTHREADS)
00655 typedef pthread_mutex_t sharedsem_t;
00656 
00657 class dbGlobalCriticalSection { 
00658     pthread_mutexattr_t attr;
00659     sharedsem_t* sem;
00660   public:
00661     void enter() {
00662         int rc = pthread_mutex_lock(sem);
00663         assert(rc == 0);
00664     }
00665     void leave() { 
00666         int rc = pthread_mutex_unlock(sem);
00667         assert(rc == 0);
00668     }
00669     bool open(char const*, sharedsem_t* shr) { 
00670         sem = shr;
00671         return true;
00672     }
00673     bool create(char const*, sharedsem_t* shr) { 
00674         sem = shr;
00675         pthread_mutexattr_init(&attr);
00676         pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
00677         pthread_mutexattr_setrecursive(&attr, PTHREAD_RECURSIVE_ENABLE);
00678         pthread_mutex_init(sem, &attr);
00679         return true;
00680     }
00681     void close() {}
00682     void erase() {
00683         pthread_mutex_destroy(sem);
00684     }
00685 };
00686 
00687 
00688 #elif defined(__osf__)
00689 // OSF uses "shared memory semaphores", located within a region mapped with mmap().
00690 // XXX: Perhaps Konstantin can give some insight why this method was chosen
00691 #include <errno.h>
00692 typedef msemaphore sharedsem_t;
00693 
00694 class dbGlobalCriticalSection { 
00695     sharedsem_t* sem;
00696   public:
00697     void enter() { 
00698         int rc;
00699         while ((rc = msem_lock(sem, 0)) < 0 && errno == EINTR);
00700         assert(rc == 0);
00701     }
00702     void leave() { 
00703         int rc = msem_unlock(sem, 0);
00704         assert(rc == 0);        
00705     }
00706     bool open(char const*, sharedsem_t* shr) { 
00707         sem = shr;
00708         return true;
00709     }
00710     bool create(char const*, sharedsem_t* shr) { 
00711         sem = shr;
00712         msem_init(shr, MSEM_UNLOCKED);
00713         return true;
00714     }
00715     void close() {}
00716     void erase() {
00717         msem_remove(sem);
00718     }
00719 };
00720         
00721 
00722 #elif defined(__sun)
00723 // Sun uses the Solaris style semaphore implemenation (sema_init(), sema_post())
00724 #include <synch.h>
00725 #include <errno.h>
00726 typedef sema_t sharedsem_t;
00727 
00728 class dbGlobalCriticalSection { 
00729     sharedsem_t* sem;
00730   public:
00731     void enter() { 
00732         int rc;
00733         while ((rc = sema_wait(sem)) < 0 && errno == EINTR);
00734         assert(rc == 0);
00735     }
00736     void leave() { 
00737         int rc = sema_post(sem);
00738         assert(rc == 0);
00739     }
00740     bool open(char const*, sharedsem_t* shr) { 
00741         sem = shr;
00742         return true;
00743     }
00744     bool create(char const*, sharedsem_t* shr) { 
00745         sem = shr;
00746         return sema_init(shr, 1, USYNC_PROCESS, NULL) == 0;
00747     }
00748     void close() {}
00749     void erase() {
00750         sema_destroy(sem);
00751     }
00752 };
00753 
00754 #elif defined(USE_POSIX_SEMAPHORES)
00755 // Everyone else uses the POSIX style semaphores (sem_wait(), sem_post(), etc) if defined
00756 typedef sem_t sharedsem_t;
00757 
00758 class dbGlobalCriticalSection { 
00759     sharedsem_t* sem;
00760 
00761   public:
00762     void enter() { 
00763         int rc = sem_wait(sem);
00764         assert(rc == 0);
00765     }
00766     void leave() { 
00767         int rc = sem_post(sem);
00768         assert(rc == 0);
00769     }
00770     bool open(char const* name, sharedsem_t* shr) { 
00771         sem = shr;
00772         return true;
00773     }
00774 
00775         // XXX: BUG Check the sem_init() return
00776     bool create(char const* name, sharedsem_t* shr) {   
00777         sem = shr;
00778         return sem_init(sem, 1, 1) == 0;
00779     }
00780     void close() {}
00781     void erase() { 
00782         sem_destroy(sem);
00783     }
00784 };
00785 
00786 #else
00787 
00788 #define USE_LOCAL_CS_IMPL
00789 
00790 // Lastly, use the local implementation
00791 typedef long sharedsem_t;
00792 
00793 class dbGlobalCriticalSection { 
00794     int          semid;
00795     sharedsem_t* count;
00796 
00797   public:
00798     void enter(); 
00799     void leave();
00800     bool open(char const* name, sharedsem_t* shr);
00801     bool create(char const* name, sharedsem_t* shr);
00802     void close() {}
00803     void erase();
00804 };
00805 #endif //dbGLobalCriticalSection switch
00806 
00807 #endif //__SYNC_UNIX_H__

Generated on Thu Feb 12 13:04:48 2004 for FastDB by doxygen 1.3.5