PageRenderTime 52ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/xmltooling-1.4.2/xmltooling/util/Win32Threads.cpp

#
C++ | 430 lines | 308 code | 60 blank | 62 comment | 38 complexity | 5c57d118aa93a0431ab28c24d494dade MD5 | raw file
Possible License(s): Apache-2.0, LGPL-2.1
  1. /**
  2. * Licensed to the University Corporation for Advanced Internet
  3. * Development, Inc. (UCAID) under one or more contributor license
  4. * agreements. See the NOTICE file distributed with this work for
  5. * additional information regarding copyright ownership.
  6. *
  7. * UCAID licenses this file to you under the Apache License,
  8. * Version 2.0 (the "License"); you may not use this file except
  9. * in compliance with the License. You may obtain a copy of the
  10. * License at
  11. *
  12. * http://www.apache.org/licenses/LICENSE-2.0
  13. *
  14. * Unless required by applicable law or agreed to in writing,
  15. * software distributed under the License is distributed on an
  16. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
  17. * either express or implied. See the License for the specific
  18. * language governing permissions and limitations under the License.
  19. */
  20. /**
  21. * Win32Threads.cpp
  22. *
  23. * Thread and locking wrappers for Win32 platforms.
  24. */
  25. #include "internal.h"
  26. #include "logging.h"
  27. #include "util/Threads.h"
  28. #include <algorithm>
  29. #ifndef WIN32
  30. # error "This implementation is for WIN32 platforms."
  31. #endif
  32. using namespace xmltooling::logging;
  33. using namespace xmltooling;
  34. using namespace std;
  35. // base error code for a routine to return on failure
  36. #define THREAD_ERROR_TIMEOUT (1)
  37. #define THREAD_ERROR_WAKE_OTHER (2)
  38. #define THREAD_ERROR (3)
  39. // windows returns non zero for success pthreads returns zero
  40. static int XMLTOOL_DLLLOCAL map_windows_error_status_to_pthreads(int rc=0) {
  41. if(rc!=0) // success?
  42. return 0;
  43. Category::getInstance(XMLTOOLING_LOGCAT".Threads").error("error from thread operation (%d)", GetLastError());
  44. return THREAD_ERROR;
  45. }
  46. namespace xmltooling {
  47. // two levels of classes are needed here
  48. // in case InitializeCriticalSection
  49. // throws an exception we can keep from
  50. // calling the critical_section destructor
  51. // on unitilized data, or it could be done with a flag
  52. struct XMLTOOL_DLLLOCAL critical_section_data {
  53. CRITICAL_SECTION cs;
  54. critical_section_data(){
  55. InitializeCriticalSection(&cs);
  56. }
  57. };
  58. class XMLTOOL_DLLLOCAL critical_section {
  59. private:
  60. critical_section_data cse;
  61. public:
  62. critical_section(){}
  63. ~critical_section(){
  64. DeleteCriticalSection (&cse.cs);
  65. }
  66. void enter(void) {
  67. EnterCriticalSection(&cse.cs);
  68. }
  69. void leave(void) {
  70. LeaveCriticalSection(&cse.cs);
  71. }
  72. };
  73. // hold a critical section over the lifetime of this object
  74. // used to make a stack variable that unlocks automaticly
  75. // on return/throw
  76. class XMLTOOL_DLLLOCAL with_crit_section {
  77. private:
  78. critical_section& cs;
  79. public:
  80. with_crit_section(critical_section& acs):cs(acs){
  81. cs.enter();
  82. }
  83. ~with_crit_section(){
  84. cs.leave();
  85. }
  86. };
  87. class XMLTOOL_DLLLOCAL ThreadImpl : public Thread {
  88. private:
  89. HANDLE thread_id;
  90. public:
  91. ThreadImpl(void* (*start_routine)(void*), void* arg, size_t stacksize) : thread_id(0) {
  92. thread_id=CreateThread(
  93. 0, // security attributes
  94. stacksize, // 0 just means the default size anyway
  95. (LPTHREAD_START_ROUTINE ) start_routine,
  96. arg,
  97. 0, // flags, default is don't create suspended which is what we want
  98. 0);
  99. if (thread_id==0) {
  100. map_windows_error_status_to_pthreads();
  101. throw ThreadingException("Thread creation failed.");
  102. }
  103. }
  104. ~ThreadImpl() {
  105. (void)detach();
  106. }
  107. int detach() {
  108. if (thread_id==0)
  109. return THREAD_ERROR;
  110. int rc=map_windows_error_status_to_pthreads(CloseHandle(thread_id));
  111. thread_id=0;
  112. return rc;
  113. }
  114. int join(void** thread_return) {
  115. if (thread_id==0)
  116. return THREAD_ERROR;
  117. if (thread_return!=0)
  118. *thread_return=0;
  119. int rc=WaitForSingleObject(thread_id,INFINITE);
  120. switch(rc) {
  121. case WAIT_OBJECT_0:
  122. if (thread_return)
  123. map_windows_error_status_to_pthreads(GetExitCodeThread(thread_id,(unsigned long*)thread_return));
  124. default:
  125. return THREAD_ERROR;
  126. }
  127. return 0;
  128. }
  129. int kill(int signo) {
  130. if (thread_id==0)
  131. return THREAD_ERROR;
  132. return map_windows_error_status_to_pthreads(TerminateThread(thread_id,signo));
  133. }
  134. };
  135. class XMLTOOL_DLLLOCAL MutexImpl : public Mutex {
  136. private:
  137. CRITICAL_SECTION mhandle;
  138. public:
  139. MutexImpl() {
  140. InitializeCriticalSection(&mhandle);
  141. }
  142. ~MutexImpl() {
  143. DeleteCriticalSection(&mhandle);
  144. }
  145. int lock() {
  146. EnterCriticalSection(&mhandle);
  147. return 0;
  148. }
  149. int unlock() {
  150. LeaveCriticalSection(&mhandle);
  151. return 0;
  152. }
  153. };
  154. class XMLTOOL_DLLLOCAL CondWaitImpl : public CondWait {
  155. private:
  156. HANDLE cond;
  157. public:
  158. CondWaitImpl() : cond(CreateEvent(0,false,false,0)) {
  159. if(cond==0) {
  160. map_windows_error_status_to_pthreads();
  161. throw ThreadingException("Event creation failed.");
  162. }
  163. };
  164. ~CondWaitImpl() {
  165. if((cond!=0) && (!CloseHandle(cond)))
  166. map_windows_error_status_to_pthreads();
  167. }
  168. int wait(Mutex* mutex) {
  169. return timedwait(mutex,INFINITE);
  170. }
  171. int signal() {
  172. if(!SetEvent(cond))
  173. return map_windows_error_status_to_pthreads();
  174. return 0;
  175. }
  176. int broadcast() {
  177. throw ThreadingException("Broadcast not implemented on Win32 platforms.");
  178. }
  179. // wait for myself to signal and this mutex or the timeout
  180. int timedwait(Mutex* mutex, int delay_seconds) {
  181. int rc=mutex->unlock();
  182. if(rc!=0)
  183. return rc;
  184. int delay_ms=delay_seconds;
  185. if(delay_seconds!=INFINITE)
  186. delay_ms*=1000;
  187. rc=WaitForSingleObject(cond,delay_ms);
  188. int rc2=mutex->lock();
  189. if(rc2!=0)
  190. return rc2;
  191. switch(rc) {
  192. case WAIT_ABANDONED:
  193. case WAIT_OBJECT_0:
  194. case WAIT_TIMEOUT:
  195. return 0;
  196. default:
  197. return map_windows_error_status_to_pthreads();
  198. }
  199. return 0;
  200. }
  201. };
  202. class XMLTOOL_DLLLOCAL RWLockImpl : public RWLock {
  203. private:
  204. // used to protect read or write to the data below
  205. critical_section cs;
  206. // event handle threads wait on when the lock they want is busy
  207. // normally set to signaled all the time, if some thread can't get what
  208. // they want they reset it and sleep. on releasing a lock set it to
  209. // signaled if someone may have wanted what you just released
  210. HANDLE wake_waiters;
  211. // number of threads holding a read lock
  212. int num_readers;
  213. // true iff there a writer has our lock
  214. bool have_writer;
  215. public:
  216. RWLockImpl() : wake_waiters(0), num_readers(0), have_writer(true) {
  217. with_crit_section acs(cs);
  218. wake_waiters=CreateEvent(0,true,true,0);
  219. have_writer=false;
  220. if (wake_waiters==0) {
  221. map_windows_error_status_to_pthreads();
  222. throw ThreadingException("Event creation for shared lock failed.");
  223. }
  224. }
  225. ~RWLockImpl() {
  226. with_crit_section acs(cs);
  227. if ((wake_waiters!=0) && (!CloseHandle(wake_waiters)))
  228. map_windows_error_status_to_pthreads();
  229. }
  230. int rdlock() {
  231. while(1) {
  232. // wait for the lock maybe being availible
  233. // we will find out for sure inside the critical section
  234. if (WaitForSingleObject(wake_waiters,INFINITE)!=WAIT_OBJECT_0)
  235. return map_windows_error_status_to_pthreads();
  236. with_crit_section alock(cs);
  237. // invariant not locked for reading and writing
  238. if ((num_readers!=0) && (have_writer))
  239. return THREAD_ERROR;
  240. // if no writer we can join any existing readers
  241. if (!have_writer) {
  242. num_readers++;
  243. return 0;
  244. }
  245. // have a writer, mark the synchronization object
  246. // so everyone waits, when the writer unlocks it will wake us
  247. if (!ResetEvent(wake_waiters))
  248. return map_windows_error_status_to_pthreads();
  249. }
  250. return THREAD_ERROR;
  251. }
  252. int wrlock() {
  253. while(1) {
  254. // wait for the lock maybe being availible
  255. // we will find out for sure inside the critical section
  256. if (WaitForSingleObject(wake_waiters,INFINITE)!=WAIT_OBJECT_0)
  257. return map_windows_error_status_to_pthreads();
  258. with_crit_section bla(cs);
  259. // invariant not locked for reading and writing
  260. if ((num_readers!=0) && (have_writer))
  261. return THREAD_ERROR;
  262. // if no writer and no readers we can become the writer
  263. if ((num_readers==0) && (!have_writer)) {
  264. have_writer=true;
  265. return 0;
  266. }
  267. // lock is busy, the unlocker will wake us
  268. if (!ResetEvent(wake_waiters))
  269. return map_windows_error_status_to_pthreads();
  270. }
  271. return THREAD_ERROR;
  272. }
  273. int unlock() {
  274. with_crit_section mumble(cs);
  275. // invariant not locked for reading and writing
  276. if ((num_readers!=0) && (have_writer))
  277. return THREAD_ERROR;
  278. // error if nothing locked
  279. if ((num_readers==0) && (!have_writer))
  280. return THREAD_ERROR;
  281. // if there was a writer it has to be us so unlock write lock
  282. have_writer=false;
  283. // if there where any reades there is one less now
  284. if(num_readers>0)
  285. num_readers--;
  286. // if no readers left wake up any readers/writers waiting
  287. // to have a go at it
  288. if (num_readers==0)
  289. if (!SetEvent(wake_waiters))
  290. return map_windows_error_status_to_pthreads();
  291. return 0;
  292. }
  293. };
  294. typedef void (*destroy_hook_type)(void*);
  295. class XMLTOOL_DLLLOCAL ThreadKeyImpl : public ThreadKey {
  296. private:
  297. destroy_hook_type destroy_hook;
  298. DWORD key;
  299. static critical_section cs;
  300. static set<ThreadKeyImpl*> m_keys;
  301. friend class ThreadKey;
  302. public:
  303. ThreadKeyImpl(void (*destroy_fcn)(void*)) : destroy_hook(destroy_fcn) {
  304. key=TlsAlloc();
  305. if (destroy_fcn) {
  306. with_crit_section wcs(cs);
  307. m_keys.insert(this);
  308. }
  309. };
  310. virtual ~ThreadKeyImpl() {
  311. if (destroy_hook) {
  312. destroy_hook(TlsGetValue(key));
  313. with_crit_section wcs(cs);
  314. m_keys.erase(this);
  315. }
  316. TlsFree(key);
  317. }
  318. int setData(void* data) {
  319. TlsSetValue(key, data);
  320. return 0;
  321. }
  322. void* getData() const {
  323. return TlsGetValue(key);
  324. }
  325. void onDetach() const {
  326. if (destroy_hook) {
  327. destroy_hook(TlsGetValue(key));
  328. TlsSetValue(key, nullptr);
  329. }
  330. }
  331. };
  332. };
  333. //
  334. // public "static" creation functions
  335. //
  336. Thread* Thread::create(void* (*start_routine)(void*), void* arg, size_t stacksize)
  337. {
  338. return new ThreadImpl(start_routine, arg, stacksize);
  339. }
  340. void Thread::exit(void* return_val)
  341. {
  342. ExitThread((DWORD)return_val);
  343. }
  344. void Thread::sleep(int seconds)
  345. {
  346. Sleep(seconds * 1000);
  347. }
  348. Mutex * Mutex::create()
  349. {
  350. return new MutexImpl();
  351. }
  352. CondWait * CondWait::create()
  353. {
  354. return new CondWaitImpl();
  355. }
  356. RWLock * RWLock::create()
  357. {
  358. return new RWLockImpl();
  359. }
  360. critical_section ThreadKeyImpl::cs;
  361. set<ThreadKeyImpl*> ThreadKeyImpl::m_keys;
  362. ThreadKey* ThreadKey::create (void (*destroy_fcn)(void*))
  363. {
  364. return new ThreadKeyImpl(destroy_fcn);
  365. }
  366. void ThreadKey::onDetach()
  367. {
  368. with_crit_section wcs(ThreadKeyImpl::cs);
  369. for_each(ThreadKeyImpl::m_keys.begin(), ThreadKeyImpl::m_keys.end(), mem_fun<void,ThreadKeyImpl>(&ThreadKeyImpl::onDetach));
  370. }