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