/*- * Copyright 2005-2007 Guram Dukashvili * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ //--------------------------------------------------------------------------- #include //--------------------------------------------------------------------------- namespace ksys { //--------------------------------------------------------------------------- #if HAVE_SCHED_H //--------------------------------------------------------------------------- int schedGetPriorityMin() { #ifdef __FreeBSD__ return 0; #else int pri = sched_get_priority_min(sched_getscheduler(0)); if( pri == -1 ){ int32_t err = errno; newObjectV1C2(err,__PRETTY_FUNCTION__); } return pri; #endif } //--------------------------------------------------------------------------- int schedGetPriorityMax() { #ifdef __FreeBSD__ return 31; #else int pri = sched_get_priority_max(sched_getscheduler(0)); if( pri == -1 ){ int32_t err = errno; newObjectV1C2(err,__PRETTY_FUNCTION__); } return pri; #endif } //--------------------------------------------------------------------------- #endif //--------------------------------------------------------------------------- uint8_t currentThreadPlaceHolder[sizeof(ThreadLocalVariable)]; //------------------------------------------------------------------------------ ///////////////////////////////////////////////////////////////////////////// //--------------------------------------------------------------------------- void Thread::afterConstruction() { if( !object_.heap_ ) newObjectV1C2( EINVAL, __PRETTY_FUNCTION__ + utf8::String(" ") + "Thread must be allocated dynamicaly only via newObject function(s)." )->throwSP(); } //--------------------------------------------------------------------------- #if defined(__WIN32__) || defined(__WIN64__) DWORD WINAPI Thread::threadFunc(LPVOID thread) #else void * Thread::threadFunc(void * thread) #endif { #if HAVE_PTHREAD_H if( (errno = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL)) != 0 ){ perror(NULL); abort(); } if( (errno = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL)) != 0 ){ perror(NULL); abort(); } /*if( (errno = pthread_mutex_lock(&reinterpret_cast(thread)->mutex_)) != 0 ){ perror(NULL); abort(); } if( (errno = pthread_mutex_unlock(&reinterpret_cast(thread)->mutex_)) != 0 ){ perror(NULL); abort(); } if( (errno = pthread_mutex_destroy(&reinterpret_cast(thread)->mutex_)) != 0 ){ perror(NULL); abort(); } reinterpret_cast(thread)->mutex_ = NULL;*/ #endif intptr_t i; try { for( i = beforeExecuteActions().count() - 1; i >= 0; i-- ) ((void (*)(void *)) beforeExecuteActions()[i].handler())(beforeExecuteActions()[i].data()); } catch( ... ){ } #if defined(__WIN32__) || defined(__WIN64__) reinterpret_cast(thread)->exitCode_ = STILL_ACTIVE; #endif reinterpret_cast(thread)->finished_ = false; reinterpret_cast(thread)->started_ = true; try { currentThread() = reinterpret_cast(thread); reinterpret_cast(thread)->threadExecute(); #if defined(__WIN32__) || defined(__WIN64__) if( reinterpret_cast(thread)->exitCode_ == (int32_t) STILL_ACTIVE ) reinterpret_cast(thread)->exitCode_ = 0; #endif } catch( ExceptionSP & e ){ e->writeStdError(); reinterpret_cast(thread)->exitCode_ = e->code(); } for( i = afterExecuteActions().count() - 1; i >= 0; i-- ) ((void (*)(void *)) afterExecuteActions()[i].handler())(afterExecuteActions()[i].data()); reinterpret_cast(thread)->finished_ = true; currentThread() = NULL; #if defined(__WIN32__) || defined(__WIN64__) // while( PostThreadMessage(mainThreadId,threadFinishMessage,0,0) == 0 ) Sleep(1); return (DWORD) reinterpret_cast(thread)->exitCode_; #elif HAVE_PTHREAD_H return (void *) reinterpret_cast(thread)->exitCode_; // pthread_exit((void *) reinterpret_cast(thread)->exitCode_); #endif } //--------------------------------------------------------------------------- Thread::Thread() : //#if __x86_64__ // getnameinfo take more then 8 Kb stack space #if defined(__WIN32__) || defined(__WIN64__) stackSize_(64u * 1024u), #else stackSize_(64u * 1024u/*PTHREAD_STACK_MIN*/), #endif //#else // stackSize_(PTHREAD_STACK_MIN), //#endif #if defined(__WIN32__) || defined(__WIN64__) handle_(NULL), id_(~DWORD(0)), #elif HAVE_PTHREAD_H handle_((pthread_t) NULL),// mutex_(NULL), #endif exitCode_(0), started_(false), terminated_(false), finished_(false) { // stackSize((uintptr_t) getpagesize()); } //--------------------------------------------------------------------------- Thread & Thread::resume() { int32_t err; #if defined(__WIN32__) || defined(__WIN64__) if( handle_ == NULL ){ started_ = terminated_ = false; handle_ = CreateThread(NULL,stackSize_,threadFunc,this,CREATE_SUSPENDED,&id_); if( handle_ == NULL ){ err = GetLastError() + errorOffset; newObjectV1C2(err,__PRETTY_FUNCTION__)->throwSP(); } if( ResumeThread(handle_) == (DWORD) - 1 ){ err = GetLastError() + errorOffset; newObjectV1C2(err,__PRETTY_FUNCTION__)->throwSP(); } } else if( finished_ ){ wait(); resume(); } #elif HAVE_PTHREAD_H pthread_attr_t attr; memset(&attr,0,sizeof(attr)); if( handle_ == (pthread_t) NULL ){ started_ = terminated_ = false; // if( (errno = pthread_mutex_init(&mutex_,NULL)) != 0 ) goto l1; if( (errno = pthread_attr_init(&attr)) != 0 ) goto l1; if( (errno = pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_JOINABLE)) != 0 ) goto l1; if( (errno = pthread_attr_setstacksize(&attr,stackSize_)) != 0 ) goto l1; #if HAVE_PTHREAD_ATTR_SETGUARDSIZE if( (errno = pthread_attr_setguardsize(&attr,0)) != 0 ) goto l1; #endif //if( (errno = pthread_mutex_lock(&mutex_)) != 0 ) goto l1; if( (errno = pthread_create(&handle_,&attr,threadFunc,this)) != 0 ){ // pthread_mutex_unlock(&mutex_); goto l1; } } else if( finished_ ){ wait(); resume(); } /*if( (errno = pthread_mutex_unlock(&mutex_)) != 0 ){ perror(NULL); abort(); }*/ return *this; l1: err = errno; pthread_attr_destroy(&attr); //pthread_mutex_destroy(&mutex_); //mutex_ = NULL; newObjectV1C2(err,__PRETTY_FUNCTION__)->throwSP(); #endif return *this; } //--------------------------------------------------------------------------- Thread & Thread::suspend() { #if defined(__WIN32__) || defined(__WIN64__) if( SuspendThread(handle_) == (DWORD) -1 ){ int32_t err = GetLastError() + errorOffset; newObjectV1C2(err,__PRETTY_FUNCTION__)->throwSP(); } #else newObjectV1C2(ENOSYS,__PRETTY_FUNCTION__)->throwSP(); #endif return *this; } //--------------------------------------------------------------------------- Thread & Thread::wait() { #if defined(__WIN32__) || defined(__WIN64__) if( handle_ != NULL ){ threadBeforeWait(); DWORD exitCode; BOOL r = GetExitCodeThread(handle_,&exitCode); if( r == 0 || exitCode == STILL_ACTIVE ){ if( WaitForSingleObject(handle_,INFINITE) == WAIT_FAILED ){ int32_t err = GetLastError() + errorOffset; newObjectV1C2(err,__PRETTY_FUNCTION__)->throwSP(); } GetExitCodeThread(handle_,&exitCode); } CloseHandle(handle_); handle_ = NULL; #elif HAVE_PTHREAD_H if( handle_ != (pthread_t) NULL ){ threadBeforeWait(); void * result; if( (errno = pthread_join(handle_,&result)) != 0 ){ perror(NULL); abort(); } /*if( mutex_ != NULL ){ if( (errno = pthread_mutex_destroy(&mutex_)) != 0 ){ perror(NULL); abort(); } mutex_ = NULL; }*/ handle_ = (pthread_t) NULL; #endif threadAfterWait(); } return *this; } //--------------------------------------------------------------------------- Thread & Thread::priority(uintptr_t pri,bool noThrow) { if( started_ && !finished_ ){ #if HAVE_SYSCONF // if( sysconf(_POSIX_THREAD_PRIORITY_SCHEDULING) <= 0 ) return *this; #endif #if HAVE_PTHREAD_GETSCHEDPARAM && HAVE_PTHREAD_SETSCHEDPARAM int policy; struct sched_param param; if( (errno = pthread_getschedparam(handle_,&policy,¶m)) != 0 ){ l1: int32_t err = errno; AutoPtr e(newObjectV1C2(err,utf8::int2Str(pri) + " " + __PRETTY_FUNCTION__)); if( !noThrow ) e.ptr(NULL)->throwSP(); e->writeStdError(); return *this; } param.sched_priority = pri; if( (errno = pthread_setschedparam(handle_,policy,¶m)) != 0 ) goto l1; #elif HAVE_PTHREAD_SETPRIO if( (errno = pthread_setprio(handle_,int(pri))) != 0 ){ int32_t err = errno; AutoPtr e(newObjectV1C2(err,utf8::int2Str(pri) + " " + __PRETTY_FUNCTION__)); if( !noThrow ) e.ptr(NULL)->throwSP(); e->writeStdError(); } #elif defined(__WIN32__) || defined(__WIN64__) if( SetThreadPriority(handle_,(int) pri) == 0 ){ int32_t err = GetLastError() + errorOffset; AutoPtr e(newObjectV1C2(err,utf8::int2Str(pri) + " " + __PRETTY_FUNCTION__)); if( !noThrow ) e.ptr(NULL)->throwSP(); e->writeStdError(); } #endif } return *this; } //--------------------------------------------------------------------------- uintptr_t Thread::priority() const { uintptr_t pri = 0; if( started_ && !finished_ ){ #if HAVE_SYSCONF // if( sysconf(_POSIX_THREAD_PRIORITY_SCHEDULING) <= 0 ) return pri; #endif #if HAVE_PTHREAD_GETSCHEDPARAM && HAVE_PTHREAD_SETSCHEDPARAM int policy; struct sched_param param; if( (errno = pthread_getschedparam(handle_,&policy,¶m)) != 0 ){ int32_t err = errno; newObjectV1C2(err,__PRETTY_FUNCTION__)->throwSP(); } pri = param.sched_priority; #elif HAVE_PTHREAD_GETPRIO int a = pthread_getprio(handle_); if( a == -1 ){ int32_t err = errno; newObjectV1C2(err,__PRETTY_FUNCTION__)->throwSP(); } pri = a; #elif defined(__WIN32__) || defined(__WIN64__) pri = GetThreadPriority(handle_); if( pri == THREAD_PRIORITY_ERROR_RETURN ){ int32_t err = GetLastError() + errorOffset; newObjectV1C2(err,__PRETTY_FUNCTION__)->throwSP(); } #endif } return pri; } //--------------------------------------------------------------------------- #if defined(__WIN32__) || defined(__WIN64__) //--------------------------------------------------------------------------- //#pragma comment (linker, "/defaultlib:ntdll.lib") //--------------------------------------------------------------------------- /*typedef enum _SYSTEM_INFORMATION_CLASS { SystemBasicInformation, // 0 Y N SystemProcessorInformation, // 1 Y N SystemPerformanceInformation, // 2 Y N SystemTimeOfDayInformation, // 3 Y N SystemNotImplemented1, // 4 Y N SystemProcessesAndThreadsInformation, // 5 Y N SystemCallCounts, // 6 Y N SystemConfigurationInformation, // 7 Y N SystemProcessorTimes, // 8 Y N SystemGlobalFlag, // 9 Y Y SystemNotImplemented2, // 10 Y N SystemModuleInformation, // 11 Y N SystemLockInformation, // 12 Y N SystemNotImplemented3, // 13 Y N SystemNotImplemented4, // 14 Y N SystemNotImplemented5, // 15 Y N SystemHandleInformation, // 16 Y N SystemObjectInformation, // 17 Y N SystemPagefileInformation, // 18 Y N SystemInstructionEmulationCounts, // 19 Y N SystemInvalidInfoClass1, // 20 SystemCacheInformation, // 21 Y Y SystemPoolTagInformation, // 22 Y N SystemProcessorStatistics, // 23 Y N SystemDpcInformation, // 24 Y Y SystemNotImplemented6, // 25 Y N SystemLoadImage, // 26 N Y SystemUnloadImage, // 27 N Y SystemTimeAdjustment, // 28 Y Y SystemNotImplemented7, // 29 Y N SystemNotImplemented8, // 30 Y N SystemNotImplemented9, // 31 Y N SystemCrashDumpInformation, // 32 Y N SystemExceptionInformation, // 33 Y N SystemCrashDumpStateInformation, // 34 Y Y/N SystemKernelDebuggerInformation, // 35 Y N SystemContextSwitchInformation, // 36 Y N SystemRegistryQuotaInformation, // 37 Y Y SystemLoadAndCallImage, // 38 N Y SystemPrioritySeparation, // 39 N Y SystemNotImplemented10, // 40 Y N SystemNotImplemented11, // 41 Y N SystemInvalidInfoClass2, // 42 SystemInvalidInfoClass3, // 43 SystemTimeZoneInformation, // 44 Y N SystemLookasideInformation, // 45 Y N SystemSetTimeSlipEvent, // 46 N Y SystemCreateSession, // 47 N Y SystemDeleteSession, // 48 N Y SystemInvalidInfoClass4, // 49 SystemRangeStartInformation, // 50 Y N SystemVerifierInformation, // 51 Y Y SystemAddVerifier, // 52 N Y SystemSessionProcessesInformation // 53 Y N } SYSTEM_INFORMATION_CLASS; */ typedef enum { StateInitialized, StateReady, StateRunning, StateStandby, StateTerminated, StateWait, StateTransition, StateUnknown } THREAD_STATE; typedef enum _KWAIT_REASON { Executive, FreePage, PageIn, PoolAllocation, DelayExecution, Suspended, UserRequest, WrExecutive, WrFreePage, WrPageIn, WrPoolAllocation, WrDelayExecution, WrSuspended, WrUserRequest, WrEventPair, WrQueue, WrLpcReceive, WrLpcReply, WrVirtualMemory, WrPageOut, WrRendezvous, Spare2, Spare3, Spare4, Spare5, Spare6, WrKernel } KWAIT_REASON; typedef struct _UNICODE_STRING { USHORT Length; USHORT MaximumLength; #ifdef MIDL_PASS [size_is(MaximumLength / 2), length_is((Length) / 2) ] USHORT * Buffer; #else // MIDL_PASS PWSTR Buffer; #endif // MIDL_PASS } UNICODE_STRING; typedef LONG KPRIORITY; typedef struct _VM_COUNTERS { SIZE_T PeakVirtualSize; SIZE_T VirtualSize; ULONG PageFaultCount; SIZE_T PeakWorkingSetSize; SIZE_T WorkingSetSize; SIZE_T QuotaPeakPagedPoolUsage; SIZE_T QuotaPagedPoolUsage; SIZE_T QuotaPeakNonPagedPoolUsage; SIZE_T QuotaNonPagedPoolUsage; SIZE_T PagefileUsage; SIZE_T PeakPagefileUsage; } VM_COUNTERS; typedef VM_COUNTERS *PVM_COUNTERS; typedef struct _CLIENT_ID { HANDLE UniqueProcess; HANDLE UniqueThread; } CLIENT_ID; typedef CLIENT_ID *PCLIENT_ID; typedef struct _SYSTEM_THREAD { FILETIME ftKernelTime; // 100 nsec units FILETIME ftUserTime; // 100 nsec units FILETIME ftCreateTime; // relative to 01-01-1601 DWORD dWaitTime; PVOID pStartAddress; CLIENT_ID Cid; // process/thread ids DWORD dPriority; DWORD dBasePriority; DWORD dContextSwitches; DWORD dThreadState; // 2=running, 5=waiting KWAIT_REASON WaitReason; DWORD dReserved01; } SYSTEM_THREAD, * PSYSTEM_THREAD, **PPSYSTEM_THREAD; typedef struct _SYSTEM_PROCESS { // common members DWORD dNext; // relative offset DWORD dThreadCount; DWORD dReserved01; DWORD dReserved02; DWORD dReserved03; DWORD dReserved04; DWORD dReserved05; DWORD dReserved06; FILETIME ftCreateTime; // relative to 01-01-1601 FILETIME ftUserTime; // 100 nsec units FILETIME ftKernelTime; // 100 nsec units UNICODE_STRING usName; KPRIORITY BasePriority; DWORD dUniqueProcessId; DWORD dInheritedFromUniqueProcessId; DWORD dHandleCount; DWORD dReserved07; DWORD dReserved08; VM_COUNTERS VmCounters; // see ntddk.h DWORD dCommitCharge; // bytes } SYSTEM_PROCESS, * PSYSTEM_PROCESS, **PPSYSTEM_PROCESS; typedef struct _SYSTEM_PROCESS_NT4 { // Windows NT 4.0 SYSTEM_PROCESS Process; // common members SYSTEM_THREAD aThreads [1]; // thread array } SYSTEM_PROCESS_NT4, * PSYSTEM_PROCESS_NT4, **PPSYSTEM_PROCESS_NT4; typedef struct _SYSTEM_PROCESS_NT5 { // Windows 2000 SYSTEM_PROCESS Process; // common members IO_COUNTERS IoCounters; // see ntddk.h SYSTEM_THREAD aThreads [1]; // thread array } SYSTEM_PROCESS_NT5, * PSYSTEM_PROCESS_NT5, **PPSYSTEM_PROCESS_NT5; #ifndef STATUS_INFO_LENGTH_MISMATCH #define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L) #endif #ifndef STATUS_SUCCESS #define STATUS_SUCCESS ((NTSTATUS)0x00000000L) // ntsubauth #endif typedef NTSTATUS (NTAPI * PFNZwQuerySystemInformation)( IN SYSTEM_INFORMATION_CLASS SystemInformationClass, IN OUT PVOID SystemInformation, IN ULONG SystemInformationLength, OUT PULONG ReturnLength OPTIONAL ); typedef NTSTATUS (NTAPI * PFNNtQuerySystemInformation)( IN SYSTEM_INFORMATION_CLASS SystemInformationClass, OUT PVOID SystemInformation, IN ULONG SystemInformationLength, OUT PULONG ReturnLength OPTIONAL ); //--------------------------------------------------------------------------- bool Thread::isSuspended(uintptr_t tid) { bool r = false; HINSTANCE hInstLib = LoadLibraryEx("ntdll.dll",NULL,DONT_RESOLVE_DLL_REFERENCES); if( hInstLib == NULL ){ int32_t err = GetLastError() + errorOffset; newObjectV1C2(err,__PRETTY_FUNCTION__)->throwSP(); } PFNNtQuerySystemInformation pNtQuerySystemInformation = (PFNNtQuerySystemInformation) GetProcAddress(hInstLib,"NtQuerySystemInformation"); union { PSYSTEM_PROCESS_INFORMATION pProcess; PSYSTEM_PROCESS_NT4 pProcessNT4; PSYSTEM_PROCESS_NT5 pProcessNT5; }; PSYSTEM_PROCESS_INFORMATION pInfo = NULL; ULONG returnLength, length = 0; NTSTATUS status = pNtQuerySystemInformation( SystemProcessInformation, pInfo, length, &returnLength ); while( status == STATUS_INFO_LENGTH_MISMATCH ){ length = returnLength; if( pInfo == NULL ){ pInfo = (PSYSTEM_PROCESS_INFORMATION) LocalAlloc(LMEM_FIXED,returnLength); } else { pInfo = (PSYSTEM_PROCESS_INFORMATION) LocalReAlloc(pInfo,returnLength,0); } status = pNtQuerySystemInformation( SystemProcessInformation, pInfo, length, &returnLength ); } if( status == STATUS_SUCCESS ){ pProcess = pInfo; uintptr_t pid = GetCurrentProcessId(); for(;;){ /*fprintf(stdout,"%S %u\n", pProcessNT5->Process.usName.Buffer != NULL ? pProcessNT5->Process.usName.Buffer : L"Idle", pProcess->UniqueProcessId );*/ if( (uintptr_t) pProcess->UniqueProcessId == pid ){ PSYSTEM_THREAD pThread, pThread1; if( isWinNT4() ){ pThread = pProcessNT4->aThreads; } else { pThread = pProcessNT5->aThreads; } pThread1 = pThread + pProcessNT5->Process.dThreadCount; while( pThread < pThread1 ){ /*fprintf(stdout,"%S %u %u\n", pProcessNT5->Process.usName.Buffer != NULL ? pProcessNT5->Process.usName.Buffer : L"Idle", pThread->Cid.UniqueProcess, pThread->Cid.UniqueThread );*/ if( (uintptr_t) pThread->Cid.UniqueThread == tid ){ r = pThread->dThreadState == StateStandby || pThread->dThreadState == StateWait; pProcess->NextEntryOffset = 0; break; } pThread++; } } if( pProcess->NextEntryOffset == 0 ) break; pProcess = (PSYSTEM_PROCESS_INFORMATION) ((PBYTE) pProcess + pProcess->NextEntryOffset); } } else { LocalFree(pInfo); FreeLibrary(hInstLib); int32_t err = status + errorOffset; newObjectV1C2(err,__PRETTY_FUNCTION__)->throwSP(); } LocalFree(pInfo); FreeLibrary(hInstLib); return r; } //--------------------------------------------------------------------------- #endif //--------------------------------------------------------------------------- #if defined(__WIN32__) || defined(__WIN64__) //--------------------------------------------------------------------------- uintptr_t Thread::waitForSignal(uintptr_t mId) { MSG msg; for(;;){ if( PeekMessage(&msg,NULL,0,0,PM_REMOVE) != 0 ){ TranslateMessage(&msg); DispatchMessage(&msg); } if( msg.message == mId ) break; if( WaitMessage() == 0 ){ int32_t err = GetLastError() + errorOffset; newObjectV1C2(err,__PRETTY_FUNCTION__)->throwSP(); } } return msg.message; } //--------------------------------------------------------------------------- #elif HAVE_SIGNAL_H //--------------------------------------------------------------------------- uintptr_t Thread::waitForSignal(uintptr_t sId) { for(;;){ waitForSignalsSemaphore(); if( sId == 0 ){ if( signalsCounters[SIGINT - 1] > 0 ) return SIGINT; if( signalsCounters[SIGQUIT - 1] > 0 ) return SIGQUIT; if( signalsCounters[SIGTERM - 1] > 0 ) return SIGTERM; } else if( signalsCounters[sId - 1] > 0 ) return sId; } } //--------------------------------------------------------------------------- #endif //--------------------------------------------------------------------------- uint8_t Thread::beforeExecuteActions_[sizeof(Array)]; uint8_t Thread::afterExecuteActions_[sizeof(Array)]; //--------------------------------------------------------------------------- void Thread::initialize() { new (beforeExecuteActions_) Array; new (afterExecuteActions_) Array; new (currentThreadPlaceHolder) ThreadLocalVariable; } //--------------------------------------------------------------------------- void Thread::cleanup() { currentThread().~ThreadLocalVariable(); afterExecuteActions().~Array(); beforeExecuteActions().~Array(); } //--------------------------------------------------------------------------- } // namespace ksys //---------------------------------------------------------------------------