//-< SYNC.H >--------------------------------------------------------*--------* // FastDB Version 1.0 (c) 1999 GARRET * ? * // (Main Memory Database Management System) * /\| * // * / \ * // Created: 20-Nov-98 K.A. Knizhnik * / [] \ * // Last update: 20-Dec-98 K.A. Knizhnik * GARRET * //-------------------------------------------------------------------*--------* // Intertask synchonization primitives //-------------------------------------------------------------------*--------* #ifndef __SYNC_H__ #define __SYNC_H__ class FASTDB_DLL_ENTRY dbSystem { public: static unsigned getCurrentTimeMsec(); }; #ifdef _WIN32 #ifdef SET_NULL_DACL class FASTDB_DLL_ENTRY dbNullSecurityDesciptor { public: SECURITY_DESCRIPTOR sd; SECURITY_ATTRIBUTES sa; dbNullSecurityDesciptor() { InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION); SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE); sa.nLength = sizeof(sa); sa.bInheritHandle = TRUE; sa.lpSecurityDescriptor = &sd; } static dbNullSecurityDesciptor instance; }; #define FASTDB_SECURITY_ATTRIBUTES &dbNullSecurityDesciptor::instance.sa #else /* ! SET_NULL_DACL */ #define FASTDB_SECURITY_ATTRIBUTES NULL #endif /* ! SET_NULL_DACL */ class FASTDB_DLL_ENTRY dbMutex { CRITICAL_SECTION cs; public: dbMutex() { InitializeCriticalSection(&cs); } ~dbMutex() { DeleteCriticalSection(&cs); } void lock() { EnterCriticalSection(&cs); } void unlock() { LeaveCriticalSection(&cs); } }; #define thread_proc WINAPI class FASTDB_DLL_ENTRY dbThread { HANDLE h; public: typedef void (thread_proc* thread_proc_t)(void*); void create(thread_proc_t f, void* arg) { DWORD threadid; h = CreateThread(FASTDB_SECURITY_ATTRIBUTES, 0, LPTHREAD_START_ROUTINE(f), arg, 0, &threadid); } enum ThreadPriority { THR_PRI_LOW, THR_PRI_HIGH }; void setPriority(ThreadPriority pri) { SetThreadPriority(h, pri == THR_PRI_LOW ? THREAD_PRIORITY_IDLE : THREAD_PRIORITY_HIGHEST); } void join() { WaitForSingleObject(h, INFINITE); CloseHandle(h); h = NULL; } void detach() { if (h != NULL) { CloseHandle(h); h = NULL; } } dbThread() { h = NULL; } ~dbThread() { if (h != NULL) { CloseHandle(h); } } static int numberOfProcessors() { #ifdef PHAR_LAP return 1; #else /* ! PHAR_LAP */ SYSTEM_INFO sysinfo; GetSystemInfo(&sysinfo); return sysinfo.dwNumberOfProcessors; #endif /* ! PHAR_LAP */ } }; class FASTDB_DLL_ENTRY dbProcessId { DWORD tid; public: bool operator != (dbProcessId const& other) const { return tid != other.tid; } void clear() { tid = 0; } static dbProcessId getCurrent() { dbProcessId curr; curr.tid = GetCurrentThreadId(); return curr; } // rschaaf: Temporary addition to aid debugging DWORD getTid() { return tid; } DWORD getPid() { return tid; } }; class FASTDB_DLL_ENTRY dbInitializationMutex { HANDLE m; public: enum initializationStatus { InitializationError, AlreadyInitialized, NotYetInitialized }; initializationStatus initialize(char const* name) { initializationStatus status; m = CreateMutex(FASTDB_SECURITY_ATTRIBUTES, true, name); if (GetLastError() == ERROR_ALREADY_EXISTS) { status = WaitForSingleObject(m, INFINITE) == WAIT_OBJECT_0 ? AlreadyInitialized : InitializationError; ReleaseMutex(m); } else if (m != NULL) { status = NotYetInitialized; } else { status = InitializationError; } return status; } void done() { ReleaseMutex(m); } bool close() { CloseHandle(m); return false; } void erase() { close(); } dbInitializationMutex() { m = NULL; } }; const int dbMaxSemValue = 1000000; class FASTDB_DLL_ENTRY dbSemaphore { protected: HANDLE s; public: bool wait(unsigned msec = INFINITE) { int rc = WaitForSingleObject(s, msec); assert(rc == WAIT_OBJECT_0 || rc == WAIT_TIMEOUT); return rc == WAIT_OBJECT_0; } void signal(unsigned inc = 1) { if (inc != 0) { ReleaseSemaphore(s, inc, NULL); } } void reset() { while (WaitForSingleObject(s, 0) == WAIT_OBJECT_0); } bool open(char const* name, unsigned initValue = 0) { s = CreateSemaphore(FASTDB_SECURITY_ATTRIBUTES, initValue, dbMaxSemValue, name); return s != NULL; } void close() { CloseHandle(s); } void erase() { close(); } dbSemaphore() { s = NULL; } }; class FASTDB_DLL_ENTRY dbEvent { protected: HANDLE e; public: bool wait(unsigned msec = INFINITE) { int rc = WaitForSingleObject(e, msec); assert(rc == WAIT_OBJECT_0 || rc == WAIT_TIMEOUT); return rc == WAIT_OBJECT_0; } void signal() { SetEvent(e); } void reset() { ResetEvent(e); } bool open(char const* name, bool signaled = false) { e = CreateEvent(FASTDB_SECURITY_ATTRIBUTES, true, signaled, name); return e != NULL; } void close() { CloseHandle(e); } void erase() { close(); } dbEvent() { e = NULL; } }; class FASTDB_DLL_ENTRY dbLocalSemaphore : public dbSemaphore { public: bool wait(dbMutex& mutex, time_t timeoutMsec) { mutex.unlock(); int rc = WaitForSingleObject(s, timeoutMsec); assert(rc == WAIT_OBJECT_0 || rc == WAIT_TIMEOUT); mutex.lock(); return rc == WAIT_OBJECT_0; } void wait(dbMutex& mutex) { mutex.unlock(); int rc = WaitForSingleObject(s, INFINITE); assert(rc == WAIT_OBJECT_0); mutex.lock(); } bool open(unsigned initValue = 0) { return dbSemaphore::open(NULL, initValue); } }; class FASTDB_DLL_ENTRY dbLocalEvent : public dbEvent { public: bool wait(dbMutex& mutex, time_t timeoutMsec) { mutex.unlock(); int rc = WaitForSingleObject(e, timeoutMsec); assert(rc == WAIT_OBJECT_0 || rc == WAIT_TIMEOUT); mutex.lock(); return rc == WAIT_OBJECT_0; } void wait(dbMutex& mutex) { mutex.unlock(); int rc = WaitForSingleObject(e, INFINITE); assert(rc == WAIT_OBJECT_0); mutex.lock(); } bool open(bool signaled = false) { return dbEvent::open(NULL, signaled); } }; template class dbThreadContext { int index; public: T* get() { return (T*)TlsGetValue(index); } void set(T* value) { TlsSetValue(index, value); } dbThreadContext() { index = TlsAlloc(); assert(index != TLS_OUT_OF_INDEXES); } ~dbThreadContext() { TlsFree(index); } }; template class dbSharedObject { T* ptr; HANDLE h; public: bool open(char* name) { #ifdef NO_MMAP ptr = new T(); #else /* ! NO_MMAP */ h = CreateFileMapping(INVALID_HANDLE_VALUE, FASTDB_SECURITY_ATTRIBUTES, PAGE_READWRITE, 0, sizeof(T), name); if (h == NULL) { return false; } ptr = (T*)MapViewOfFile(h, FILE_MAP_ALL_ACCESS, 0, 0, 0); if (ptr == NULL) { CloseHandle(h); return false; } #endif /* ! NO_MMAP */ return true; } T* get() { return ptr; } void close() { #ifdef NO_MMAP delete[] ptr; #else /* ! NO_MMAP */ UnmapViewOfFile(ptr); CloseHandle(h); #endif /* ! NO_MMAP */ } void erase() { close(); } dbSharedObject() { ptr = NULL; h = NULL; } }; typedef long sharedsem_t; class FASTDB_DLL_ENTRY dbGlobalCriticalSection { HANDLE event; sharedsem_t* count; public: void enter() { if (InterlockedDecrement(count) != 0) { // another process is in critical section int rc = WaitForSingleObject(event, INFINITE); assert (rc == WAIT_OBJECT_0); } } void leave() { if (InterlockedIncrement(count) <= 0) { // some other processes try to enter critical section SetEvent(event); } } bool open(char const* name, long* count) { this->count = count; event = OpenEvent(EVENT_ALL_ACCESS, FALSE, name); return event != NULL; } bool create(char const* name, long* count) { this->count = count; *count = 1; event = CreateEvent(FASTDB_SECURITY_ATTRIBUTES, false, false, name); return event != NULL; } void close() { CloseHandle(event); } void erase() { close(); } dbGlobalCriticalSection() { event = NULL; } }; #else /* ! _WIN32 - Unix? */ #include #include #include #include #include #include #ifdef USE_POSIX_API #include #include #else /* ! USE_POSIX_API */ #include #include #include #include #endif /* ! USE_POSIX_API */ #define thread_proc #ifndef NO_PTHREADS #include class dbMutex { friend class dbLocalEvent; friend class dbLocalSemaphore; pthread_mutex_t cs; public: dbMutex() { pthread_mutex_init(&cs, NULL); } ~dbMutex() { pthread_mutex_destroy(&cs); } void lock() { pthread_mutex_lock(&cs); } void unlock() { pthread_mutex_unlock(&cs); } }; const size_t dbThreadStackSize = 1024*1024; class dbThread { pthread_t thread; public: typedef void (thread_proc* thread_proc_t)(void*); void create(thread_proc_t f, void* arg) { pthread_attr_t attr; pthread_attr_init(&attr); #if !defined(__linux__) pthread_attr_setstacksize(&attr, dbThreadStackSize); #endif /* !defined(__linux__) */ #if defined(_AIX41) // At AIX 4.1, 4.2 threads are by default created detached pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_UNDETACHED); #endif /* defined(_AIX41) */ pthread_create(&thread, &attr, (void*(*)(void*))f, arg); pthread_attr_destroy(&attr); } void join() { void* result; pthread_join(thread, &result); } void detach() { pthread_detach(thread); } enum ThreadPriority { THR_PRI_LOW, THR_PRI_HIGH }; void setPriority(ThreadPriority pri) { #if defined(PRI_OTHER_MIN) && defined(PRI_OTHER_MAX) struct sched_param sp; sp.sched_priority = pri == THR_PRI_LOW ? PRI_OTHER_MIN : PRI_OTHER_MAX; pthread_setschedparam(thread, SCHED_OTHER, &sp); #endif /* defined(PRI_OTHER_MIN) && defined(PRI_OTHER_MAX) */ } static int numberOfProcessors(); }; class dbLocalEvent { pthread_cond_t cond; int signaled; public: void wait(dbMutex& mutex) { while (!signaled) { pthread_cond_wait(&cond, &mutex.cs); } } bool wait(dbMutex& mutex, time_t timeout) { while (!signaled) { struct timespec abs_ts; #ifdef PTHREAD_GET_EXPIRATION_NP struct timespec rel_ts; rel_ts.tv_sec = timeout/1000; rel_ts.tv_nsec = timeout%1000*1000000; pthread_get_expiration_np(&rel_ts, &abs_ts); #else /* ! PTHREAD_GET_EXPIRATION_NP */ struct timeval cur_tv; gettimeofday(&cur_tv, NULL); abs_ts.tv_sec = cur_tv.tv_sec + timeout/1000; abs_ts.tv_nsec = cur_tv.tv_usec*1000 + timeout%1000*1000000; if (abs_ts.tv_nsec > 1000000000) { abs_ts.tv_nsec -= 1000000000; abs_ts.tv_sec += 1; } #endif /* ! PTHREAD_GET_EXPIRATION_NP */ int rc = pthread_cond_timedwait(&cond, &mutex.cs, &abs_ts); if (rc == ETIMEDOUT) { return false; } } return true; } void signal() { signaled = true; pthread_cond_broadcast(&cond); } void reset() { signaled = false; } void open(bool initValue = false) { signaled = initValue; pthread_cond_init(&cond, NULL); } void close() { pthread_cond_destroy(&cond); } }; class dbLocalSemaphore { pthread_cond_t cond; int count; public: void wait(dbMutex& mutex) { while (count == 0) { pthread_cond_wait(&cond, &mutex.cs); } count -= 1; } void wait(dbMutex& mutex, time_t timeout) { while (count == 0) { struct timespec abs_ts; #ifdef PTHREAD_GET_EXPIRATION_NP struct timespec rel_ts; rel_ts.tv_sec = timeout/1000; rel_ts.tv_nsec = timeout%1000*1000000; pthread_get_expiration_np(&rel_ts, &abs_ts); #else /* ! PTHREAD_GET_EXPIRATION_NP */ struct timeval cur_tv; gettimeofday(&cur_tv, NULL); abs_ts.tv_sec = cur_tv.tv_sec + timeout/1000; abs_ts.tv_nsec = cur_tv.tv_usec*1000 + timeout%1000*1000000; if (abs_ts.tv_nsec > 1000000000) { abs_ts.tv_nsec -= 1000000000; abs_ts.tv_sec += 1; } #endif /* ! PTHREAD_GET_EXPIRATION_NP */ pthread_cond_timedwait(&cond, &mutex.cs, &abs_ts); } count -= 1; } void signal(unsigned inc = 1) { count += inc; if (inc > 1) { pthread_cond_broadcast(&cond); } else if (inc == 1) { pthread_cond_signal(&cond); } } void open(unsigned initValue = 0) { pthread_cond_init(&cond, NULL); count = initValue; } void close() { pthread_cond_destroy(&cond); } }; template class dbThreadContext { pthread_key_t key; public: T* get() { return (T*)pthread_getspecific(key); } void set(T* value) { pthread_setspecific(key, value); } dbThreadContext() { pthread_key_create(&key, NULL); } ~dbThreadContext() { pthread_key_delete(key); } }; class dbProcessId { int pid; pthread_t tid; public: bool operator != (dbProcessId const& other) const { return pid != other.pid || tid != other.tid; } void clear() { pid = 0; tid = 0; } static dbProcessId getCurrent() { dbProcessId curr; curr.pid = getpid(); curr.tid = pthread_self(); return curr; } // for Linux: Temporary addition to aid debugging int getTid() { return (int)tid; } int getPid() { return pid; } }; #else /* ! NO_PTHREADS */ class dbMutex { public: void lock() {} void unlock() {} }; class dbThread { public: typedef void (thread_proc* thread_proc_t)(void*); void create(thread_proc_t f, void* arg) { f(arg); } void join() {} void detach() {} enum ThreadPriority { THR_PRI_LOW, THR_PRI_HIGH }; void setPriority(ThreadPriority pri) { } static int numberOfProcessors() { return 1; } }; class dbLocalSemaphore { int count; public: void wait(dbMutex&) { assert (count > 0); count -= 1; } void signal(unsigned inc = 1) { count += inc; } void open(unsigned initValue = 0) { count = initValue; } void close() {} }; class dbLocalEvent { bool signaled; public: void wait(dbMutex&) { assert(signaled); } bool wait(dbMutex& mutex, time_t timeout) { return true; } void signal() { signaled = true; } void reset() { signaled = false; } void open(bool initValue = false) { signaled = initValue; } void close() {} }; template class dbThreadContext { T* value; public: T* get() { return value; } void set(T* value) { this->value = value; } dbThreadContext() { value = NULL; } }; class dbProcessId { int pid; public: bool operator != (dbProcessId const& other) const { return pid != other.pid; } void clear() { pid = 0; } static dbProcessId getCurrent() { dbProcessId curr; curr.pid = getpid(); return curr; } }; #endif /* ! NO_PTHREADS */ #define INFINITE (~0U) #ifdef USE_POSIX_API class dbInitializationMutex { sem_t* sem; public: enum initializationStatus { InitializationError, AlreadyInitialized, NotYetInitialized }; initializationStatus initialize(char const* name) { initializationStatus status; char* tmp = NULL; if (*name != '/') { tmp = new char[strlen(name)+2]; strcpy(tmp+1, name); *tmp = '/'; name = tmp; } while (true) { sem = sem_open(name, 0); if (sem == NULL) { if (errno == ENOENT) { sem = sem_open(name, O_CREAT|O_EXCL, 0777, 0); if (sem != NULL) { status = NotYetInitialized; break; } else if (errno != EEXIST) { status = InitializationError; break; } } else { status = InitializationError; break; } } else { status = (sem_wait(sem) == 0 && sem_post(sem) == 0) ? AlreadyInitialized : InitializationError; break; } } delete[] tmp; return status; } void done() { sem_post(sem); } bool close() { sem_close(sem); return false; } void erase() { close(); } }; class dbSemaphore { protected: sem_t* sem; public: void wait() { int rc = sem_wait(sem); assert(rc == 0); } bool wait(unsigned msec) { #ifdef POSIX_1003_1d struct timespec abs_ts; struct timeval cur_tv; clock_gettime(CLOCK_REALTIME, &cur_tv); abs_ts.tv_sec = cur_tv.tv_sec + (msec + tv.tv_usec / 1000) / 1000000; abs_ts.tv_nsec = (msec + tv.tv_usec / 1000) % 1000000 * 1000; int rc = sem_timedwait(sem, &abs_ts); if (rc < 0) { assert(errno == ETIMEDOUT); return false; } return true; #else /* ! POSIX_1003_1d */ int rc = sem_wait(sem); assert(rc == 0); return true; #endif /* ! POSIX_1003_1d */ } void signal(unsigned inc = 1) { while (--inc > 0) { sem_post(sem); } } void reset() { while (sem_trywait(sem) == 0); } bool open(char const* name, unsigned initValue = 0) { char* tmp = NULL; if (*name != '/') { tmp = new char[strlen(name)+2]; strcpy(tmp+1, name); *tmp = '/'; name = tmp; } sem = sem_open(name, O_CREAT, 0777, initValue); delete[] tmp; return sem != NULL; } void close() { sem_close(sem); } void erase() { close(); } }; class dbEvent : public dbSemaphore { public: void wait() { dbSemaphore::wait(); sem_post(sem); } bool wait(unsigned msec) { if (dbSemaphore::wait(msec)) { sem_post(sem); return true; } return false; } void signal() { while (sem_trywait(sem) == 0); sem_post(sem); } void reset() { while (sem_trywait(sem) == 0); } bool open(char const* name, bool signaled = false) { return dbSemaphore::open(name, (int)signaled); } }; template class dbSharedObject { char* name; T* ptr; int fd; public: dbSharedObject() { name = NULL; ptr = NULL; fd = -1; } bool open(char* fileName) { delete[] name; name = new char[strlen(fileName) + 1]; strcpy(name, fileName); fd = ::open(fileName, O_RDWR|O_CREAT, 0777); if (fd < 0) { return false; } ptr = (T*)mmap(NULL, DOALIGN(sizeof(T), 4096), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (ptr == MAP_FAILED) { ptr = NULL; ::close(fd); return false; } return true; } T* get() { return ptr; } void close() { if (ptr != NULL) { munmap(ptr, DOALIGN(sizeof(T), 4096)); } if (fd > 0) { ::close(fd); } } void erase() { close(); unlink(name); } ~dbSharedObject() { delete[] name; } }; #else /* ! USE_POSIX_API */ extern char const* keyFileDir; // default value: "/tmp/" class dbInitializationMutex { int semid; public: enum initializationStatus { InitializationError, AlreadyInitialized, NotYetInitialized }; initializationStatus initialize(char const* name); void done(); bool close(); void erase(); }; class dbSemaphore { int s; public: bool wait(unsigned msec = INFINITE); void signal(unsigned inc = 1); bool open(char const* name, unsigned initValue = 0); void reset(); void close(); void erase(); }; class dbEvent { int e; public: bool wait(unsigned msec = INFINITE); void signal(); void reset(); bool open(char const* name, bool signaled = false); void close(); void erase(); }; class dbSharedMemory { protected: char* ptr; int shm; public: bool open(char const* name, size_t size); void close(); void erase(); char* get_base() { return ptr; } }; template class dbSharedObject : public dbSharedMemory { public: bool open(char* name) { return dbSharedMemory::open(name, sizeof(T)); } T* get() { return (T*)ptr; } }; #endif /* ! USE_POSIX_API */ #if defined(__QNX__) #define LOG_SEM(sem) (int)((sem).__m_count) typedef pthread_mutext_t sharedsem_t; class dbGlobalCriticalSection { pthread_mutexattr_t attr; sharedsem_t* sem; public: void enter() { int rc = pthread_mutex_lock(sem); assert(rc == 0); } void leave() { int rc = pthread_mutex_unlock(sem); assert(rc == 0); } bool open(char const*, sharedsem_t* shr) { sem = shr; return true; } bool create(char const*, sharedsem_t* shr) { sem = shr; pthread_mutexattr_init(&attr); pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); pthread_mutexattr_setrecursive(&attr, PTHREAD_RECURSIVE_ENABLE); pthread_mutex_init(sem, &attr); return true; } void close() {} void erase() { pthread_mutex_destroy(sem); } }; #elif defined(__osf__) #define LOG_SEM(sem) (int)((sem).msem_state) #include typedef msemaphore sharedsem_t; class dbGlobalCriticalSection { sharedsem_t* sem; public: void enter() { int rc; while ((rc = msem_lock(sem, 0)) < 0 && errno == EINTR); assert(rc == 0); } void leave() { int rc = msem_unlock(sem, 0); assert(rc == 0); } bool open(char const*, sharedsem_t* shr) { sem = shr; return true; } bool create(char const*, sharedsem_t* shr) { sem = shr; msem_init(shr, MSEM_UNLOCKED); return true; } void close() {} void erase() { msem_remove(sem); } }; #elif defined(__sun) #define LOG_SEM(sem) (int)((sem).count) #include #include typedef sema_t sharedsem_t; class dbGlobalCriticalSection { sharedsem_t* sem; public: void enter() { int rc; while ((rc = sema_wait(sem)) < 0 && errno == EINTR); assert(rc == 0); } void leave() { int rc = sema_post(sem); assert(rc == 0); } bool open(char const*, sharedsem_t* shr) { sem = shr; return true; } bool create(char const*, sharedsem_t* shr) { sem = shr; return sema_init(shr, 1, USYNC_PROCESS, NULL) == 0; } void close() {} void erase() { sema_destroy(sem); } }; #elif defined(USE_POSIX_API) #define LOG_SEM(sem) (*(int *)&(sem)) typedef sem_t sharedsem_t; class dbGlobalCriticalSection { sharedsem_t* sem; public: void enter() { int rc = sem_wait(sem); assert(rc == 0); } void leave() { int rc = sem_post(sem); assert(rc == 0); } bool open(char const* name, sharedsem_t* shr) { sem = shr; return true; } bool create(char const* name, sharedsem_t* shr) { sem = shr; return sem_init(sem, 1, 1) == 0; } void close() {} void erase() { sem_destroy(sem); } }; #else #define LOG_SEM(sem) (int)(sem) typedef long sharedsem_t; class dbGlobalCriticalSection { int semid; sharedsem_t* count; public: void enter(); void leave(); bool open(char const* name, sharedsem_t* shr); bool create(char const* name, sharedsem_t* shr); void close() {} void erase(); }; #endif #endif /* ! def _WIN32 */ class FASTDB_DLL_ENTRY dbCriticalSection { private: dbMutex& mutex; public: dbCriticalSection(dbMutex& guard) : mutex(guard) { mutex.lock(); } ~dbCriticalSection() { mutex.unlock(); } }; #define LOG_SEM(sem) (int)(sem) #define SMALL_BUF_SIZE 512 class FASTDB_DLL_ENTRY dbSmallBuffer { protected: char* buf; char smallBuf[SMALL_BUF_SIZE]; size_t used; public: dbSmallBuffer(size_t size) { if (size > SMALL_BUF_SIZE) { buf = new char[size]; } else { buf = smallBuf; } used = size; } dbSmallBuffer() { used = 0; buf = smallBuf; } void put(size_t size) { if (size > SMALL_BUF_SIZE && size > used) { if (buf != smallBuf) { delete[] buf; } buf = new char[size]; used = size; } } operator char*() { return buf; } char* base() { return buf; } ~dbSmallBuffer() { if (buf != smallBuf) { delete[] buf; } } }; class dbThreadPool; class FASTDB_DLL_ENTRY dbPooledThread { private: friend class dbThreadPool; dbThread thread; dbThreadPool* pool; dbPooledThread* next; dbThread::thread_proc_t f; void* arg; bool running; dbLocalSemaphore startSem; dbLocalSemaphore readySem; static void thread_proc pooledThreadFunc(void* arg); void run(); void stop(); dbPooledThread(dbThreadPool* threadPool); ~dbPooledThread(); }; class FASTDB_DLL_ENTRY dbThreadPool { friend class dbPooledThread; dbPooledThread* freeThreads; dbMutex mutex; public: dbPooledThread* create(dbThread::thread_proc_t f, void* arg); void join(dbPooledThread* thr); dbThreadPool(); ~dbThreadPool(); }; #endif /*__SYNC_H__ */