/src/racket/src/mzrt.c

http://github.com/gmarceau/PLT · C · 783 lines · 602 code · 152 blank · 29 comment · 67 complexity · 469ea76288957b7bf9a7027aa985719f MD5 · raw file

  1. #include "schpriv.h"
  2. #ifdef MZ_USE_MZRT
  3. /************************************************************************/
  4. /************************************************************************/
  5. /************************************************************************/
  6. #define MZRT_INTERNAL
  7. #include "mzrt.h"
  8. #include "schgc.h"
  9. THREAD_LOCAL_DECL(mz_proc_thread *proc_thread_self);
  10. #ifdef MZ_XFORM
  11. START_XFORM_SUSPEND;
  12. #endif
  13. /* std C headers */
  14. #include <stdlib.h>
  15. #include <stdio.h>
  16. #include <errno.h>
  17. #include <../sconfig.h>
  18. /* platform headers */
  19. #ifdef WIN32
  20. # include <windows.h>
  21. # include <process.h>
  22. #else
  23. # include <pthread.h>
  24. # include <signal.h>
  25. # include <unistd.h>
  26. # include <time.h>
  27. # if defined(UNIX_LIMIT_STACK) || defined(UNIX_LIMIT_FDSET_SIZE)
  28. # include <signal.h>
  29. # include <sys/time.h>
  30. # include <sys/resource.h>
  31. # endif
  32. #endif
  33. /* Define this is we need CGC support for threads. This was needed
  34. when we tried to make places work with the Boehm GC, but since that has
  35. other problems (notably disappearing links), we have given up on
  36. having threads cooperate with CGC. */
  37. /* #define NEED_GC_THREAD_OPS */
  38. #ifdef NEED_GC_THREAD_OPS
  39. int GC_pthread_join(pthread_t thread, void **retval);
  40. int GC_pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void*), void * arg);
  41. int GC_pthread_detach(pthread_t thread);
  42. #endif
  43. void mzrt_set_user_break_handler(void (*user_break_handler)(int))
  44. {
  45. #ifdef WIN32
  46. #else
  47. signal(SIGINT, user_break_handler);
  48. #endif
  49. }
  50. static void rungdb() {
  51. #ifdef WIN32
  52. #else
  53. pid_t pid = getpid();
  54. char outbuffer[100];
  55. char inbuffer[10];
  56. fprintf(stderr, "pid # %i resume(r)/gdb(d)/exit(e)?\n", pid);
  57. fflush(stderr);
  58. while(1) {
  59. while(read(fileno(stdin), inbuffer, 10) <= 0){
  60. if(errno != EINTR){
  61. fprintf(stderr, "Error detected %i\n", errno);
  62. }
  63. }
  64. switch(inbuffer[0]) {
  65. case 'r':
  66. return;
  67. break;
  68. case 'd':
  69. snprintf(outbuffer, 100, "xterm -e gdb ./racket3m %d &", pid);
  70. fprintf(stderr, "%s\n", outbuffer);
  71. if(system(outbuffer))
  72. fprintf(stderr, "system failed\n");
  73. break;
  74. case 'e':
  75. default:
  76. exit(1);
  77. break;
  78. }
  79. }
  80. #endif
  81. }
  82. #ifndef WIN32
  83. static void segfault_handler(int signal_num) {
  84. pid_t pid = getpid();
  85. fprintf(stderr, "sig# %i pid# %i\n", signal_num, pid);
  86. rungdb();
  87. }
  88. #endif
  89. void mzrt_set_segfault_debug_handler()
  90. {
  91. #ifdef WIN32
  92. #else
  93. signal(SIGSEGV, segfault_handler);
  94. #endif
  95. }
  96. void mzrt_sleep(int seconds)
  97. {
  98. #ifdef WIN32
  99. Sleep(seconds * 1000);
  100. #else
  101. struct timespec set;
  102. struct timespec rem;
  103. set.tv_sec = seconds;
  104. set.tv_nsec = 0;
  105. rem.tv_sec = 0;
  106. rem.tv_nsec = 0;
  107. while ((-1 == nanosleep(&set, &rem))) {
  108. //fprintf(stderr, "%i %i INITIAL\n", set.tv_sec, set.tv_nsec);
  109. //fprintf(stderr, "%i %i LEFT\n", rem.tv_sec, rem.tv_nsec);
  110. set = rem;
  111. //fprintf(stderr, "%i %i NOW\n", set.tv_sec, set.tv_nsec);
  112. }
  113. #endif
  114. }
  115. /***********************************************************************/
  116. /* Threads */
  117. /***********************************************************************/
  118. typedef struct mzrt_thread_stub_data {
  119. mz_proc_thread_start start_proc;
  120. void *data;
  121. mz_proc_thread *thread;
  122. } mzrt_thread_stub_data;
  123. void *mzrt_thread_stub(void *data){
  124. mzrt_thread_stub_data *stub_data = (mzrt_thread_stub_data*) data;
  125. mz_proc_thread_start start_proc = stub_data->start_proc;
  126. void *start_proc_data = stub_data->data;
  127. void* res;
  128. scheme_init_os_thread();
  129. proc_thread_self = stub_data->thread;
  130. free(data);
  131. res = start_proc(start_proc_data);
  132. if (!--proc_thread_self->refcount)
  133. free(proc_thread_self);
  134. scheme_done_os_thread();
  135. return res;
  136. }
  137. #ifdef WIN32
  138. DWORD WINAPI mzrt_win_thread_stub(void *data)
  139. {
  140. return (DWORD)mzrt_thread_stub(data);
  141. }
  142. #endif
  143. mzrt_thread_id mz_proc_thread_self() {
  144. #ifdef WIN32
  145. return GetCurrentThread();
  146. #else
  147. return pthread_self();
  148. #endif
  149. }
  150. mzrt_thread_id mz_proc_thread_id(mz_proc_thread* thread) {
  151. return thread->threadid;
  152. }
  153. mz_proc_thread* mzrt_proc_first_thread_init() {
  154. /* initialize mz_proc_thread struct for first thread that wasn't created with mz_proc_thread_create */
  155. mz_proc_thread *thread = (mz_proc_thread*)malloc(sizeof(mz_proc_thread));
  156. thread->threadid = mz_proc_thread_self();
  157. proc_thread_self = thread;
  158. thread->refcount = 1;
  159. return thread;
  160. }
  161. mz_proc_thread* mz_proc_thread_create_w_stacksize(mz_proc_thread_start start_proc, void* data, intptr_t stacksize)
  162. {
  163. mz_proc_thread *thread = (mz_proc_thread*)malloc(sizeof(mz_proc_thread));
  164. mzrt_thread_stub_data *stub_data;
  165. int ok;
  166. # ifndef WIN32
  167. pthread_attr_t *attr;
  168. pthread_attr_t attr_storage;
  169. if (stacksize) {
  170. attr = &attr_storage;
  171. pthread_attr_init(attr);
  172. pthread_attr_setstacksize(attr, stacksize); /*8MB*/
  173. } else
  174. attr = NULL;
  175. # endif
  176. thread->refcount = 2;
  177. stub_data = (mzrt_thread_stub_data*)malloc(sizeof(mzrt_thread_stub_data));
  178. stub_data->start_proc = start_proc;
  179. stub_data->data = data;
  180. stub_data->thread = thread;
  181. # ifdef WIN32
  182. thread->threadid = (HANDLE)_beginthreadex(NULL, stacksize, mzrt_win_thread_stub, stub_data, 0, NULL);
  183. ok = (thread->threadid != -1L);
  184. # else
  185. # ifdef NEED_GC_THREAD_OPS
  186. ok = !GC_pthread_create(&thread->threadid, attr, mzrt_thread_stub, stub_data);
  187. # else
  188. ok = !pthread_create(&thread->threadid, attr, mzrt_thread_stub, stub_data);
  189. # endif
  190. # endif
  191. if (!ok) {
  192. free(thread);
  193. free(stub_data);
  194. return NULL;
  195. }
  196. return thread;
  197. }
  198. mz_proc_thread* mz_proc_thread_create(mz_proc_thread_start start_proc, void* data) {
  199. intptr_t stacksize;
  200. #if defined(OS_X) || defined(linux)
  201. stacksize = 8*1024*1024;
  202. #else
  203. stacksize = 0;
  204. #endif
  205. return mz_proc_thread_create_w_stacksize(start_proc, data, stacksize);
  206. }
  207. void * mz_proc_thread_wait(mz_proc_thread *thread) {
  208. void *rc;
  209. #ifdef WIN32
  210. DWORD rcw;
  211. WaitForSingleObject(thread->threadid,INFINITE);
  212. GetExitCodeThread(thread->threadid, &rcw);
  213. rc = (void *)rcw;
  214. CloseHandle(thread->threadid);
  215. #else
  216. # ifdef NEED_GC_THREAD_OPS
  217. GC_pthread_join(thread->threadid, &rc);
  218. # else
  219. pthread_join(thread->threadid, &rc);
  220. # endif
  221. #endif
  222. if (!--thread->refcount)
  223. free(thread);
  224. return rc;
  225. }
  226. int mz_proc_thread_detach(mz_proc_thread *thread) {
  227. int rc;
  228. #ifdef WIN32
  229. rc = CloseHandle(thread->threadid);
  230. #else
  231. # ifdef NEED_GC_THREAD_OPS
  232. rc = GC_pthread_detach(thread->threadid);
  233. # else
  234. rc = pthread_detach(thread->threadid);
  235. # endif
  236. #endif
  237. if (!--thread->refcount)
  238. free(thread);
  239. return rc;
  240. }
  241. void mz_proc_thread_exit(void *rc) {
  242. #ifdef WIN32
  243. _endthreadex((unsigned)rc);
  244. #else
  245. # ifndef MZ_PRECISE_GC
  246. pthread_exit(rc);
  247. # else
  248. pthread_exit(rc);
  249. # endif
  250. #endif
  251. }
  252. /***********************************************************************/
  253. /* RW Lock */
  254. /***********************************************************************/
  255. /* Unix **************************************************************/
  256. #ifndef WIN32
  257. # ifdef HAVE_PTHREAD_RWLOCK
  258. struct mzrt_rwlock {
  259. pthread_rwlock_t lock;
  260. };
  261. int mzrt_rwlock_create(mzrt_rwlock **lock) {
  262. *lock = malloc(sizeof(mzrt_rwlock));
  263. return pthread_rwlock_init(&(*lock)->lock, NULL);
  264. }
  265. int mzrt_rwlock_rdlock(mzrt_rwlock *lock) {
  266. return pthread_rwlock_rdlock(&lock->lock);
  267. }
  268. int mzrt_rwlock_wrlock(mzrt_rwlock *lock) {
  269. return pthread_rwlock_wrlock(&lock->lock);
  270. }
  271. int mzrt_rwlock_tryrdlock(mzrt_rwlock *lock) {
  272. return pthread_rwlock_tryrdlock(&lock->lock);
  273. }
  274. int mzrt_rwlock_trywrlock(mzrt_rwlock *lock) {
  275. return pthread_rwlock_trywrlock(&lock->lock);
  276. }
  277. int mzrt_rwlock_unlock(mzrt_rwlock *lock) {
  278. return pthread_rwlock_unlock(&lock->lock);
  279. }
  280. int mzrt_rwlock_destroy(mzrt_rwlock *lock) {
  281. return pthread_rwlock_destroy(&lock->lock);
  282. }
  283. # else
  284. struct mzrt_rwlock {
  285. pthread_mutex_t m;
  286. pthread_cond_t cr, cw;
  287. int readers, writers, write_waiting;
  288. };
  289. int mzrt_rwlock_create(mzrt_rwlock **lock) {
  290. int err;
  291. *lock = malloc(sizeof(mzrt_rwlock));
  292. err = pthread_mutex_init(&(*lock)->m, NULL);
  293. if (err) { free(*lock); return err; }
  294. err = pthread_cond_init(&(*lock)->cr, NULL);
  295. if (err) { free(*lock); return err; }
  296. err = pthread_cond_init(&(*lock)->cw, NULL);
  297. if (err) { free(*lock); return err; }
  298. return err;
  299. }
  300. static int rwlock_rdlock(mzrt_rwlock *lock, int just_try) {
  301. int err;
  302. err = pthread_mutex_lock(&lock->m);
  303. if (err) return err;
  304. while (lock->writers || lock->write_waiting) {
  305. if (just_try) {
  306. err = pthread_mutex_unlock(&lock->m);
  307. if (err) return err;
  308. return EBUSY;
  309. } else {
  310. err = pthread_cond_wait(&lock->cr, &lock->m);
  311. if (err)
  312. return err;
  313. }
  314. }
  315. lock->readers++;
  316. return pthread_mutex_unlock(&lock->m);
  317. }
  318. int mzrt_rwlock_rdlock(mzrt_rwlock *lock) {
  319. return rwlock_rdlock(lock, 0);
  320. }
  321. static int rwlock_wrlock(mzrt_rwlock *lock, int just_try) {
  322. int err;
  323. err = pthread_mutex_lock(&lock->m);
  324. if (err) return err;
  325. while (lock->writers || lock->readers) {
  326. if (just_try) {
  327. err = pthread_mutex_unlock(&lock->m);
  328. if (err) return err;
  329. return EBUSY;
  330. } else {
  331. lock->write_waiting++;
  332. err = pthread_cond_wait(&lock->cw, &lock->m);
  333. --lock->write_waiting;
  334. if (err)
  335. return err;
  336. }
  337. }
  338. lock->writers++;
  339. return pthread_mutex_unlock(&lock->m);
  340. }
  341. int mzrt_rwlock_wrlock(mzrt_rwlock *lock) {
  342. return rwlock_wrlock(lock, 0);
  343. }
  344. int mzrt_rwlock_tryrdlock(mzrt_rwlock *lock) {
  345. return rwlock_rdlock(lock, 1);
  346. }
  347. int mzrt_rwlock_trywrlock(mzrt_rwlock *lock) {
  348. return rwlock_wrlock(lock, 1);
  349. }
  350. int mzrt_rwlock_unlock(mzrt_rwlock *lock) {
  351. int err;
  352. err = pthread_mutex_lock(&lock->m);
  353. if (err) return err;
  354. if (lock->readers)
  355. --lock->readers; /* must have been a read lock */
  356. else
  357. --lock->writers;
  358. if (lock->write_waiting)
  359. err = pthread_cond_signal(&lock->cw);
  360. else
  361. err = pthread_cond_broadcast(&lock->cr);
  362. if (err) return err;
  363. return pthread_mutex_unlock(&lock->m);
  364. }
  365. int mzrt_rwlock_destroy(mzrt_rwlock *lock) {
  366. pthread_mutex_destroy(&lock->m);
  367. pthread_cond_destroy(&lock->cr);
  368. pthread_cond_destroy(&lock->cw);
  369. free(lock);
  370. return 0;
  371. }
  372. # endif
  373. struct mzrt_mutex {
  374. pthread_mutex_t mutex;
  375. };
  376. int mzrt_mutex_create(mzrt_mutex **mutex) {
  377. *mutex = malloc(sizeof(struct mzrt_mutex));
  378. return pthread_mutex_init(&(*mutex)->mutex, NULL);
  379. }
  380. int mzrt_mutex_lock(mzrt_mutex *mutex) {
  381. return pthread_mutex_lock(&mutex->mutex);
  382. }
  383. int mzrt_mutex_trylock(mzrt_mutex *mutex) {
  384. return pthread_mutex_trylock(&mutex->mutex);
  385. }
  386. int mzrt_mutex_unlock(mzrt_mutex *mutex) {
  387. return pthread_mutex_unlock(&mutex->mutex);
  388. }
  389. int mzrt_mutex_destroy(mzrt_mutex *mutex) {
  390. return pthread_mutex_destroy(&mutex->mutex);
  391. }
  392. struct mzrt_cond {
  393. pthread_cond_t cond;
  394. };
  395. int mzrt_cond_create(mzrt_cond **cond) {
  396. *cond = malloc(sizeof(struct mzrt_cond));
  397. return pthread_cond_init(&(*cond)->cond, NULL);
  398. }
  399. int mzrt_cond_wait(mzrt_cond *cond, mzrt_mutex *mutex) {
  400. return pthread_cond_wait(&cond->cond, &mutex->mutex);
  401. }
  402. int mzrt_cond_timedwait(mzrt_cond *cond, mzrt_mutex *mutex, intptr_t seconds, intptr_t nanoseconds) {
  403. struct timespec timeout;
  404. timeout.tv_sec = seconds;
  405. timeout.tv_nsec = nanoseconds;
  406. return pthread_cond_timedwait(&cond->cond, &mutex->mutex, &timeout);
  407. }
  408. int mzrt_cond_signal(mzrt_cond *cond) {
  409. return pthread_cond_signal(&cond->cond);
  410. }
  411. int mzrt_cond_broadcast(mzrt_cond *cond) {
  412. return pthread_cond_broadcast(&cond->cond);
  413. }
  414. int mzrt_cond_destroy(mzrt_cond *cond) {
  415. return pthread_cond_destroy(&cond->cond);
  416. }
  417. struct mzrt_sema {
  418. int ready;
  419. pthread_mutex_t m;
  420. pthread_cond_t c;
  421. };
  422. int mzrt_sema_create(mzrt_sema **_s, int v)
  423. {
  424. mzrt_sema *s;
  425. int err;
  426. s = (mzrt_sema *)malloc(sizeof(mzrt_sema));
  427. err = pthread_mutex_init(&s->m, NULL);
  428. if (err) {
  429. free(s);
  430. return err;
  431. }
  432. err = pthread_cond_init(&s->c, NULL);
  433. if (err) {
  434. pthread_mutex_destroy(&s->m);
  435. free(s);
  436. return err;
  437. }
  438. s->ready = v;
  439. *_s = s;
  440. return 0;
  441. }
  442. int mzrt_sema_wait(mzrt_sema *s)
  443. {
  444. pthread_mutex_lock(&s->m);
  445. while (!s->ready) {
  446. pthread_cond_wait(&s->c, &s->m);
  447. }
  448. --s->ready;
  449. pthread_mutex_unlock(&s->m);
  450. return 0;
  451. }
  452. int mzrt_sema_post(mzrt_sema *s)
  453. {
  454. pthread_mutex_lock(&s->m);
  455. s->ready++;
  456. pthread_cond_signal(&s->c);
  457. pthread_mutex_unlock(&s->m);
  458. return 0;
  459. }
  460. int mzrt_sema_destroy(mzrt_sema *s)
  461. {
  462. pthread_mutex_destroy(&s->m);
  463. pthread_cond_destroy(&s->c);
  464. free(s);
  465. return 0;
  466. }
  467. #endif
  468. /* Windows **************************************************************/
  469. #ifdef WIN32
  470. typedef struct mzrt_rwlock {
  471. HANDLE readEvent;
  472. HANDLE writeMutex;
  473. LONG readers;
  474. } mzrt_rwlock;
  475. int mzrt_rwlock_create(mzrt_rwlock **lock) {
  476. *lock = malloc(sizeof(mzrt_rwlock));
  477. (*lock)->readers = 0;
  478. /* CreateEvent(LPSECURITY_ATTRIBUTES, manualReset, initiallySignaled, LPCSTR name) */
  479. if (! ((*lock)->readEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
  480. return 0;
  481. if (! ((*lock)->writeMutex = CreateMutex(NULL, FALSE, NULL)))
  482. return 0;
  483. return 1;
  484. }
  485. static int get_win32_os_error() {
  486. return 0;
  487. }
  488. static int mzrt_rwlock_rdlock_worker(mzrt_rwlock *lock, DWORD millis) {
  489. DWORD rc = WaitForSingleObject(lock->writeMutex, millis);
  490. if (rc == WAIT_FAILED || rc == WAIT_TIMEOUT );
  491. return 0;
  492. InterlockedIncrement(&lock->readers);
  493. if (! ResetEvent(lock->readEvent))
  494. return 0;
  495. if (!ReleaseMutex(lock->writeMutex))
  496. return 0;
  497. return 1;
  498. }
  499. static int mzrt_rwlock_wrlock_worker(mzrt_rwlock *lock, DWORD millis) {
  500. DWORD rc = WaitForSingleObject(lock->writeMutex, millis);
  501. if (rc == WAIT_FAILED || rc == WAIT_TIMEOUT );
  502. return 0;
  503. if (lock->readers) {
  504. if (millis) {
  505. rc = WaitForSingleObject(lock->readEvent, millis);
  506. }
  507. else {
  508. rc = WAIT_TIMEOUT;
  509. }
  510. if (rc == WAIT_FAILED || rc == WAIT_TIMEOUT );
  511. return 0;
  512. }
  513. return 1;
  514. }
  515. int mzrt_rwlock_rdlock(mzrt_rwlock *lock) {
  516. return mzrt_rwlock_rdlock_worker(lock, INFINITE);
  517. }
  518. int mzrt_rwlock_wrlock(mzrt_rwlock *lock) {
  519. return mzrt_rwlock_wrlock_worker(lock, INFINITE);
  520. }
  521. int mzrt_rwlock_tryrdlock(mzrt_rwlock *lock) {
  522. return mzrt_rwlock_rdlock_worker(lock, 0);
  523. }
  524. int mzrt_rwlock_trywrlock(mzrt_rwlock *lock) {
  525. return mzrt_rwlock_wrlock_worker(lock, 0);
  526. }
  527. int mzrt_rwlock_unlock(mzrt_rwlock *lock) {
  528. DWORD rc = 0;
  529. if (!ReleaseMutex(lock->writeMutex)) {
  530. rc = get_win32_os_error();
  531. }
  532. if (rc == ERROR_NOT_OWNER) {
  533. if (lock->readers && !InterlockedDecrement(&lock->readers) && !SetEvent(lock->readEvent)) {
  534. rc = get_win32_os_error();
  535. }
  536. else {
  537. rc = 0;
  538. }
  539. }
  540. return !rc;
  541. }
  542. int mzrt_rwlock_destroy(mzrt_rwlock *lock) {
  543. int rc = 1;
  544. rc &= CloseHandle(lock->readEvent);
  545. rc &= CloseHandle(lock->writeMutex);
  546. return rc;
  547. }
  548. struct mzrt_mutex {
  549. CRITICAL_SECTION critical_section;
  550. };
  551. int mzrt_mutex_create(mzrt_mutex **mutex) {
  552. *mutex = malloc(sizeof(mzrt_mutex));
  553. InitializeCriticalSection(&(*mutex)->critical_section);
  554. return 0;
  555. }
  556. int mzrt_mutex_lock(mzrt_mutex *mutex) {
  557. EnterCriticalSection(&mutex->critical_section);
  558. return 0;
  559. }
  560. int mzrt_mutex_trylock(mzrt_mutex *mutex) {
  561. /* FIXME: TryEnterCriticalSection() requires NT:
  562. if (!TryEnterCriticalSection(&mutex->critical_section))
  563. return 1; */
  564. return 0;
  565. }
  566. int mzrt_mutex_unlock(mzrt_mutex *mutex) {
  567. LeaveCriticalSection(&mutex->critical_section);
  568. return 0;
  569. }
  570. int mzrt_mutex_destroy(mzrt_mutex *mutex) {
  571. DeleteCriticalSection(&mutex->critical_section);
  572. return 0;
  573. }
  574. struct mzrt_cond {
  575. int nothing;
  576. };
  577. int mzrt_cond_create(mzrt_cond **cond) {
  578. return 0;
  579. }
  580. int mzrt_cond_wait(mzrt_cond *cond, mzrt_mutex *mutex) {
  581. return 0;
  582. }
  583. int mzrt_cond_timedwait(mzrt_cond *cond, mzrt_mutex *mutex, intptr_t secs, intptr_t nsecs) {
  584. return 0;
  585. }
  586. int mzrt_cond_signal(mzrt_cond *cond) {
  587. return 0;
  588. }
  589. int mzrt_cond_broadcast(mzrt_cond *cond) {
  590. return 0;
  591. }
  592. int mzrt_cond_destroy(mzrt_cond *cond) {
  593. return 0;
  594. }
  595. struct mzrt_sema {
  596. HANDLE ws;
  597. };
  598. int mzrt_sema_create(mzrt_sema **_s, int v)
  599. {
  600. mzrt_sema *s;
  601. HANDLE ws;
  602. s = (mzrt_sema *)malloc(sizeof(mzrt_sema));
  603. ws = CreateSemaphore(NULL, v, 32000, NULL);
  604. s->ws = ws;
  605. *_s = s;
  606. return 0;
  607. }
  608. int mzrt_sema_wait(mzrt_sema *s)
  609. {
  610. WaitForSingleObject(s->ws, INFINITE);
  611. return 0;
  612. }
  613. int mzrt_sema_post(mzrt_sema *s)
  614. {
  615. ReleaseSemaphore(s->ws, 1, NULL);
  616. return 0;
  617. }
  618. int mzrt_sema_destroy(mzrt_sema *s)
  619. {
  620. CloseHandle(s->ws);
  621. free(s);
  622. return 0;
  623. }
  624. #endif
  625. /************************************************************************/
  626. /************************************************************************/
  627. /************************************************************************/
  628. #ifdef MZ_XFORM
  629. END_XFORM_SUSPEND;
  630. #endif
  631. #endif