blocxx
|
00001 /******************************************************************************* 00002 * Copyright (C) 2005, Vintela, Inc. All rights reserved. 00003 * Copyright (C) 2006, Novell, Inc. All rights reserved. 00004 * 00005 * Redistribution and use in source and binary forms, with or without 00006 * modification, are permitted provided that the following conditions are met: 00007 * 00008 * * Redistributions of source code must retain the above copyright notice, 00009 * this list of conditions and the following disclaimer. 00010 * * Redistributions in binary form must reproduce the above copyright 00011 * notice, this list of conditions and the following disclaimer in the 00012 * documentation and/or other materials provided with the distribution. 00013 * * Neither the name of 00014 * Vintela, Inc., 00015 * nor Novell, Inc., 00016 * nor the names of its contributors or employees may be used to 00017 * endorse or promote products derived from this software without 00018 * specific prior written permission. 00019 * 00020 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 00021 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 00022 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 00023 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 00024 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 00025 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 00026 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 00027 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 00028 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 00029 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 00030 * POSSIBILITY OF SUCH DAMAGE. 00031 *******************************************************************************/ 00032 00033 00038 #include "blocxx/BLOCXX_config.h" 00039 #include "blocxx/ThreadImpl.hpp" 00040 #include "blocxx/Mutex.hpp" 00041 #include "blocxx/Assertion.hpp" 00042 #include "blocxx/Thread.hpp" 00043 #include "blocxx/NonRecursiveMutexLock.hpp" 00044 #include "blocxx/NonRecursiveMutex.hpp" 00045 #include "blocxx/Condition.hpp" 00046 #include "blocxx/Timeout.hpp" 00047 #include "blocxx/Format.hpp" 00048 #include "blocxx/TimeoutTimer.hpp" 00049 #if defined(BLOCXX_WIN32) 00050 #include "blocxx/Map.hpp" 00051 #include "blocxx/MutexLock.hpp" 00052 #endif 00053 #include <cassert> 00054 #include <cstring> 00055 #include <cstddef> 00056 00057 extern "C" 00058 { 00059 #ifdef BLOCXX_HAVE_SYS_TIME_H 00060 #include <sys/time.h> 00061 #endif 00062 00063 #include <sys/types.h> 00064 00065 #ifdef BLOCXX_HAVE_UNISTD_H 00066 #include <unistd.h> 00067 #endif 00068 00069 #include <errno.h> 00070 #include <signal.h> 00071 00072 #ifdef BLOCXX_USE_PTHREAD 00073 #include <pthread.h> 00074 #endif 00075 00076 #ifdef BLOCXX_WIN32 00077 #include <process.h> 00078 #endif 00079 } 00080 00081 namespace BLOCXX_NAMESPACE 00082 { 00083 00084 namespace ThreadImpl { 00085 00087 // STATIC 00088 void 00089 sleep(UInt32 milliSeconds) 00090 { 00091 sleep(Timeout::relative(milliSeconds / 1000.0)); 00092 } 00093 00094 void 00095 sleep(const Timeout& timeout) 00096 { 00097 NonRecursiveMutex mtx; 00098 NonRecursiveMutexLock lock(mtx); 00099 Condition cond; 00100 TimeoutTimer timer(timeout); 00101 while (!timer.expired()) 00102 { 00103 // if it timed out, no reason to loop again 00104 if (!cond.timedWait(lock, timer.asAbsoluteTimeout())) 00105 { 00106 return; 00107 } 00108 timer.loop(); 00109 } 00110 } 00112 // STATIC 00113 void 00114 yield() 00115 { 00116 #if defined(BLOCXX_HAVE_SCHED_YIELD) 00117 sched_yield(); 00118 #elif defined(BLOCXX_WIN32) 00119 ThreadImpl::testCancel(); 00120 ::SwitchToThread(); 00121 #else 00122 ThreadImpl::sleep(1); 00123 #endif 00124 } 00125 00126 #if defined(BLOCXX_USE_PTHREAD) 00127 namespace { 00128 struct LocalThreadParm 00129 { 00130 ThreadFunction m_func; 00131 void* m_funcParm; 00132 }; 00133 extern "C" { 00134 static void* 00135 threadStarter(void* arg) 00136 { 00137 // set our cancellation state 00138 #ifdef BLOCXX_NCR 00139 pthread_setcancel(CANCEL_ON); 00140 pthread_setasynccancel(CANCEL_OFF); 00141 #else 00142 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); 00143 pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); 00144 #endif 00145 00146 // block all signals except SIGUSR1, which is used to signal termination 00147 sigset_t signalSet; 00148 int rv = sigfillset(&signalSet); 00149 BLOCXX_ASSERT(rv == 0); 00150 rv = sigdelset(&signalSet, SIGUSR1); 00151 BLOCXX_ASSERT(rv == 0); 00152 rv = pthread_sigmask(SIG_SETMASK, &signalSet, 0); 00153 BLOCXX_ASSERT(rv == 0); 00154 00155 LocalThreadParm* parg = static_cast<LocalThreadParm*>(arg); 00156 ThreadFunction func = parg->m_func; 00157 void* funcParm = parg->m_funcParm; 00158 delete parg; 00159 Int32 rval = (*func)(funcParm); 00160 void* prval = reinterpret_cast<void*>(static_cast<ptrdiff_t>(rval)); 00161 pthread_exit(prval); 00162 return prval; 00163 } 00164 } 00165 // The purpose of this class is to retrieve the default stack size only once 00166 // at library load time and re-use it thereafter. 00167 struct default_stack_size 00168 { 00169 #if !defined(BLOCXX_NCR) 00170 default_stack_size() 00171 { 00172 // if anything in this function fails, we'll just leave val == 0. 00173 val = 0; 00174 needsSetting = false; 00175 00176 // make sure we have a big enough stack. BloCxx can use quite a bit, so we'll try to make sure we get at least 1 MB. 00177 // 1 MB is just an arbitrary number. The default on Linux is 2 MB which has never been a problem. However, on UnixWare 00178 // the default is really low (16K IIRC) and that isn't enough. It would be good to do some sort of measurement... 00179 #ifdef _POSIX_THREAD_ATTR_STACKSIZE 00180 pthread_attr_t stack_size_attr; 00181 if (pthread_attr_init(&stack_size_attr) != 0) 00182 { 00183 return; 00184 } 00185 if (pthread_attr_getstacksize(&stack_size_attr, &val) != 0) 00186 { 00187 return; 00188 } 00189 00190 if (val < 1048576) 00191 { 00192 val = 1048576; // 1 MB 00193 needsSetting = true; 00194 } 00195 #ifdef PTHREAD_STACK_MIN 00196 if (PTHREAD_STACK_MIN > val) 00197 { 00198 val = PTHREAD_STACK_MIN; 00199 needsSetting = true; 00200 } 00201 #endif 00202 00203 #endif //#ifdef _POSIX_THREAD_ATTR_STACKSIZE 00204 } 00205 00206 #else //#if !defined(BLOCXX_NCR) 00207 default_stack_size() 00208 { 00209 // if anything in this function fails, we'll just leave val == 0. 00210 val = 0; 00211 needsSetting = false; 00212 00213 // make sure we have a big enough stack. BloCxx can use quite a bit, so we'll try to make sure we get at least 1 MB. 00214 // 1 MB is just an arbitrary number. The default on Linux is 2 MB which has never been a problem. However, on UnixWare 00215 // the default is really low (16K IIRC) and that isn't enough. It would be good to do some sort of measurement... 00216 #ifdef _POSIX_THREAD_ATTR_STACKSIZE 00217 pthread_attr_t stack_size_attr; 00218 if (pthread_attr_create(&stack_size_attr) != 0) 00219 { 00220 return; 00221 } 00222 00223 val = pthread_attr_getstacksize(stack_size_attr); 00224 if (static_cast<signed>(val) == -1) 00225 { 00226 return; 00227 } 00228 00229 //we do not set the minimal stack size in 1 Mb because NCR returns 32K 00230 //and if we set 1M or even 256K we get 'Out of Memory' 00231 00232 #if defined(PTHREAD_STACK_MIN) && defined(_SC_THREAD_STACK_MIN) 00233 if (PTHREAD_STACK_MIN > val) 00234 { 00235 val = PTHREAD_STACK_MIN; 00236 needsSetting = true; 00237 } 00238 #endif 00239 00240 #endif //#ifdef _POSIX_THREAD_ATTR_STACKSIZE 00241 } 00242 #endif //#if !defined(BLOCXX_NCR) 00243 00244 static size_t val; 00245 static bool needsSetting; 00246 }; 00247 00248 size_t default_stack_size::val = 0; 00249 bool default_stack_size::needsSetting(false); 00250 default_stack_size g_theDefaultStackSize; 00252 pthread_once_t once_control = PTHREAD_ONCE_INIT; 00253 pthread_key_t theKey; 00254 extern "C" { 00255 00256 #ifdef BLOCXX_NCR 00257 static void 00258 SIGUSR1Handler() 00259 { 00260 // do nothing 00261 } 00262 #else 00263 static void 00264 SIGUSR1Handler(int sig) 00265 { 00266 // do nothing 00267 } 00268 #endif 00269 00271 static void doOneTimeThreadInitialization() 00272 { 00273 #ifdef BLOCXX_NCR 00274 pthread_keycreate(&theKey, NULL); 00275 #else 00276 pthread_key_create(&theKey, NULL); 00277 #endif 00278 // Handle SIGUSR1 so we can safely send it to threads when we want to cancel them. 00279 struct sigaction temp; 00280 memset(&temp, '\0', sizeof(temp)); 00281 sigaction(SIGUSR1, 0, &temp); 00282 temp.sa_handler = SIGUSR1Handler; 00283 sigemptyset(&temp.sa_mask); 00284 temp.sa_flags = 0; 00285 sigaction(SIGUSR1, &temp, NULL); 00286 } 00287 00288 } // end extern "C" 00289 } // end unnamed namespace 00291 00292 #if !defined(BLOCXX_NCR) 00293 // STATIC 00294 int 00295 createThread(Thread_t& handle, ThreadFunction func, 00296 void* funcParm, UInt32 threadFlags) 00297 { 00298 int cc = 0; 00299 pthread_attr_t attr; 00300 pthread_attr_init(&attr); 00301 if (!(threadFlags & BLOCXX_THREAD_FLG_JOINABLE)) 00302 { 00303 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 00304 } 00305 00306 #if !defined(BLOCXX_VALGRIND_SUPPORT) // valgrind doesn't like us to set the stack size 00307 // Won't be set to true unless _POSIX_THREAD_ATTR_STACKSIZE is defined 00308 if (default_stack_size::needsSetting) 00309 { 00310 pthread_attr_setstacksize(&attr, default_stack_size::val); 00311 } 00312 #endif 00313 00314 LocalThreadParm* parg = new LocalThreadParm; 00315 parg->m_func = func; 00316 parg->m_funcParm = funcParm; 00317 cc = pthread_create(&handle, &attr, threadStarter, parg); 00318 pthread_attr_destroy(&attr); 00319 return cc; 00320 } 00321 00322 #else //#if !defined(BLOCXX_NCR) 00323 // STATIC 00324 int 00325 createThread(Thread_t& handle, ThreadFunction func, 00326 void* funcParm, UInt32 threadFlags) 00327 { 00328 int cc = 0; 00329 pthread_attr_t attr; 00330 pthread_attr_create(&attr); 00331 00332 #if !defined(BLOCXX_VALGRIND_SUPPORT) // valgrind doesn't like us to set the stack size 00333 // Won't be set to true unless _POSIX_THREAD_ATTR_STACKSIZE is defined 00334 if (default_stack_size::needsSetting) 00335 { 00336 pthread_attr_setstacksize(&attr, default_stack_size::val); 00337 } 00338 #endif 00339 00340 LocalThreadParm* parg = new LocalThreadParm; 00341 parg->m_func = func; 00342 parg->m_funcParm = funcParm; 00343 if (pthread_create(&handle, attr, threadStarter, parg) != 0) 00344 { 00345 cc = -1; 00346 } 00347 00348 if (cc != -1 && !(threadFlags & BLOCXX_THREAD_FLG_JOINABLE)) 00349 { 00350 pthread_detach(&handle); 00351 } 00352 00353 pthread_attr_delete(&attr); 00354 return cc; 00355 } 00356 #endif //#if !defined(BLOCXX_NCR) 00357 00358 // STATIC 00359 void 00360 exitThread(Thread_t&, Int32 rval) 00361 { 00362 void* prval = reinterpret_cast<void*>(static_cast<ptrdiff_t>(rval)); 00363 pthread_exit(prval); 00364 } 00365 00366 00367 #if defined(BLOCXX_SIZEOF_PTHREAD_T) 00368 #if BLOCXX_SIZEOF_PTHREAD_T == 2 00369 #define BLOCXX_THREAD_CONVERTER UInt16 00370 #elif BLOCXX_SIZEOF_PTHREAD_T == 4 00371 #define BLOCXX_THREAD_CONVERTER UInt32 00372 #elif BLOCXX_SIZEOF_PTHREAD_T == 8 00373 #define BLOCXX_THREAD_CONVERTER UInt64 00374 #else 00375 #ifdef BLOCXX_NCR //BLOCXX_SIZEOF_PTHREAD_T=0 for this OS 00376 #define BLOCXX_THREAD_CONVERTER UInt16 00377 #else /* BLOCXX_SIZEOF_PTHREAD_T */ 00378 #error Unexpected size for pthread_t 00379 #endif /* BLOCXX_NCR */ 00380 #endif /* BLOCXX_SIZEOF_PTHREAD_T */ 00381 #else 00382 #error No pthread_t size was found! 00383 #endif /* defined(BLOCXX_SIZEOF_PTHREAD_T) */ 00384 00385 UInt64 thread_t_ToUInt64(Thread_t thr) 00386 { 00387 #ifdef BLOCXX_NCR 00388 return UInt64(BLOCXX_THREAD_CONVERTER(cma_thread_get_unique(&thr))); 00389 #else 00390 return UInt64(BLOCXX_THREAD_CONVERTER(thr)); 00391 #endif 00392 } 00393 #undef BLOCXX_THREAD_CONVERTER 00394 00396 // STATIC 00397 void 00398 destroyThread(Thread_t& ) 00399 { 00400 } 00402 // STATIC 00403 int 00404 setThreadDetached(Thread_t& handle) 00405 { 00406 #ifdef BLOCXX_NCR 00407 int cc = pthread_detach(&handle); 00408 #else 00409 int cc = pthread_detach(handle); 00410 #endif 00411 if (cc != 0) 00412 { 00413 if (cc != EINVAL) 00414 { 00415 cc = -1; 00416 } 00417 } 00418 return cc; 00419 } 00421 // STATIC 00422 int 00423 joinThread(Thread_t& handle, Int32& rval) 00424 { 00425 void* prval(0); 00426 if ((errno = pthread_join(handle, &prval)) == 0) 00427 { 00428 rval = static_cast<Int32>(reinterpret_cast<ptrdiff_t>(prval)); 00429 return 0; 00430 } 00431 else 00432 { 00433 return 1; 00434 } 00435 } 00437 void 00438 testCancel() 00439 { 00440 // set up our TLS which will be used to store the Thread* in. 00441 pthread_once(&once_control, &doOneTimeThreadInitialization); 00442 Thread* theThread = NULL; 00443 #ifdef BLOCXX_NCR 00444 pthread_addr_t addr_ptr = NULL; 00445 int ret = pthread_getspecific(theKey, &addr_ptr); 00446 if (ret == 0) 00447 { 00448 theThread = reinterpret_cast<Thread*>(addr_ptr); 00449 } 00450 #else 00451 theThread = reinterpret_cast<Thread*>(pthread_getspecific(theKey)); 00452 #endif 00453 if (theThread == 0) 00454 { 00455 return; 00456 } 00457 if (AtomicGet(theThread->m_cancelRequested) == 1) 00458 { 00459 // We don't use BLOCXX_THROW here because 00460 // ThreadCancelledException is special. It's not derived 00461 // from Exception on purpose so it can be propagated up 00462 // the stack easier. This exception shouldn't be caught and not 00463 // re-thrown anywhere except in Thread::threadRunner() 00464 throw ThreadCancelledException(); 00465 } 00466 } 00468 void saveThreadInTLS(void* pTheThread) 00469 { 00470 // set up our TLS which will be used to store the Thread* in. 00471 pthread_once(&once_control, &doOneTimeThreadInitialization); 00472 int rc; 00473 if ((rc = pthread_setspecific(theKey, pTheThread)) != 0) 00474 { 00475 BLOCXX_THROW(ThreadException, Format("pthread_setspecific failed. error = %1(%2)", rc, strerror(rc)).c_str()); 00476 } 00477 } 00479 void sendSignalToThread(Thread_t threadID, int signo) 00480 { 00481 int rc; 00482 if ((rc = pthread_kill(threadID, signo)) != 0) 00483 { 00484 BLOCXX_THROW(ThreadException, Format("pthread_kill failed. error = %1(%2)", rc, strerror(rc)).c_str()); 00485 } 00486 } 00488 void cancel(Thread_t threadID) 00489 { 00490 int rc; 00491 if ((rc = pthread_cancel(threadID)) != 0) 00492 { 00493 BLOCXX_THROW(ThreadException, Format("pthread_cancel failed. error = %1(%2)", rc, strerror(rc)).c_str()); 00494 } 00495 } 00496 #endif // #ifdef BLOCXX_USE_PTHREAD 00497 00498 #if defined(BLOCXX_WIN32) 00499 00500 namespace { 00501 00502 struct WThreadInfo 00503 { 00504 HANDLE handle; 00505 BLOCXX_NAMESPACE::Thread* pTheThread; 00506 }; 00507 00508 typedef Map<DWORD, WThreadInfo> Win32ThreadMap; 00509 Win32ThreadMap g_threads; 00510 Mutex g_threadsGuard; 00511 00512 struct LocalThreadParm 00513 { 00514 ThreadFunction m_func; 00515 void* m_funcParm; 00516 }; 00517 00519 extern "C" { 00520 unsigned __stdcall threadStarter(void* arg) 00521 { 00522 LocalThreadParm* parg = reinterpret_cast<LocalThreadParm*>(arg); 00523 ThreadFunction func = parg->m_func; 00524 void* funcParm = parg->m_funcParm; 00525 delete parg; 00526 Int32 rval = (*func)(funcParm); 00527 ::_endthreadex(static_cast<unsigned>(rval)); 00528 return rval; 00529 } 00530 } // End extern "C" 00531 00533 void 00534 addThreadToMap(DWORD threadId, HANDLE threadHandle) 00535 { 00536 MutexLock ml(g_threadsGuard); 00537 WThreadInfo wi; 00538 wi.handle = threadHandle; 00539 wi.pTheThread = 0; 00540 g_threads[threadId] = wi; 00541 } 00542 00544 HANDLE 00545 getThreadHandle(DWORD threadId) 00546 { 00547 MutexLock ml(g_threadsGuard); 00548 HANDLE chdl = 0; 00549 Win32ThreadMap::iterator it = g_threads.find(threadId); 00550 if (it != g_threads.end()) 00551 { 00552 chdl = it->second.handle; 00553 } 00554 return chdl; 00555 } 00556 00558 void 00559 setThreadPointer(DWORD threadId, Thread* pTheThread) 00560 { 00561 MutexLock ml(g_threadsGuard); 00562 Win32ThreadMap::iterator it = g_threads.find(threadId); 00563 if (it != g_threads.end()) 00564 { 00565 it->second.pTheThread = pTheThread; 00566 } 00567 } 00568 00570 HANDLE 00571 removeThreadFromMap(DWORD threadId) 00572 { 00573 MutexLock ml(g_threadsGuard); 00574 HANDLE chdl = 0; 00575 Win32ThreadMap::iterator it = g_threads.find(threadId); 00576 if (it != g_threads.end()) 00577 { 00578 chdl = it->second.handle; 00579 g_threads.erase(it); 00580 } 00581 return chdl; 00582 } 00583 00585 Thread* 00586 getThreadObject(DWORD threadId) 00587 { 00588 Thread* pTheThread = 0; 00589 MutexLock ml(g_threadsGuard); 00590 Win32ThreadMap::iterator it = g_threads.find(threadId); 00591 if (it != g_threads.end()) 00592 { 00593 pTheThread = it->second.pTheThread; 00594 } 00595 return pTheThread; 00596 } 00597 00598 } // End unnamed namespace 00599 00601 // STATIC 00602 int 00603 createThread(Thread_t& handle, ThreadFunction func, 00604 void* funcParm, UInt32 threadFlags) 00605 { 00606 int cc = -1; 00607 HANDLE hThread; 00608 unsigned threadId; 00609 00610 LocalThreadParm* parg = new LocalThreadParm; 00611 parg->m_func = func; 00612 parg->m_funcParm = funcParm; 00613 hThread = reinterpret_cast<HANDLE>(::_beginthreadex(NULL, 0, threadStarter, 00614 parg, 0, &threadId)); 00615 if (hThread != 0) 00616 { 00617 addThreadToMap(threadId, hThread); 00618 handle = threadId; 00619 cc = 0; 00620 } 00621 else 00622 { 00623 cc = errno; 00624 } 00625 00626 return cc; 00627 } 00629 // STATIC 00630 void 00631 exitThread(Thread_t&, Int32 rval) 00632 { 00633 ::_endthreadex(static_cast<unsigned>(rval)); 00634 } 00635 00637 // STATIC 00638 UInt64 thread_t_ToUInt64(Thread_t thr) 00639 { 00640 // This should really be a compile time assert. 00641 BLOCXX_ASSERTMSG(sizeof(unsigned long) >= sizeof(Thread_t)," Thread_t truncated!"); 00642 return static_cast<UInt64>(thr); 00643 } 00644 00646 // STATIC 00647 void 00648 destroyThread(Thread_t& threadId) 00649 { 00650 HANDLE thdl = removeThreadFromMap(threadId); 00651 if (thdl != 0) 00652 { 00653 ::CloseHandle(thdl); 00654 } 00655 } 00657 // STATIC 00658 int 00659 setThreadDetached(Thread_t& handle) 00660 { 00661 // No need for this on Win32 00662 return 0; 00663 } 00665 // STATIC 00666 int 00667 joinThread(Thread_t& threadId, Int32& rvalArg) 00668 { 00669 int cc = -1; 00670 DWORD rval; 00671 HANDLE thdl = getThreadHandle(threadId); 00672 if (thdl != 0) 00673 { 00674 if (::WaitForSingleObject(thdl, INFINITE) != WAIT_FAILED) 00675 { 00676 if (::GetExitCodeThread(thdl, &rval) != 0) 00677 { 00678 rvalArg = static_cast<Int32>(rval); 00679 cc = 0; 00680 } 00681 } 00682 } 00683 return cc; 00684 } 00685 00687 void 00688 testCancel() 00689 { 00690 DWORD threadId = ThreadImpl::currentThread(); 00691 Thread* pTheThread = getThreadObject(threadId); 00692 if (pTheThread) 00693 { 00694 if (AtomicGet(pTheThread->m_cancelRequested) == 1) 00695 { 00696 // We don't use BLOCXX_THROW here because 00697 // ThreadCancelledException is special. It's not derived 00698 // from Exception on purpose so it can be propagated up 00699 // the stack easier. This exception shouldn't be caught and not 00700 // re-thrown anywhere except in Thread::threadRunner() 00701 throw ThreadCancelledException(); 00702 } 00703 } 00704 } 00706 void saveThreadInTLS(void* pThreadArg) 00707 { 00708 Thread* pThread = static_cast<Thread*>(pThreadArg); 00709 DWORD threadId = pThread->getId(); 00710 setThreadPointer(threadId, pThread); 00711 } 00713 void sendSignalToThread(Thread_t threadID, int signo) 00714 { 00715 } 00717 void cancel(Thread_t threadId) 00718 { 00719 HANDLE thdl = getThreadHandle(threadId); 00720 if (thdl != 0) 00721 { 00722 ::TerminateThread(thdl, -1); 00723 } 00724 } 00725 00726 #endif // #ifdef BLOCXX_WIN32 00727 } // end namespace BLOCXX_ThreadImpl 00728 00729 } // end namespace BLOCXX_NAMESPACE 00730