cthreads.pp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2002 by Peter Vreman,
  4. member of the Free Pascal development team.
  5. Linux (pthreads) threading support implementation
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. **********************************************************************}
  12. {$mode objfpc}
  13. {$ifdef linux}
  14. {$define dynpthreads} // Useless on BSD, since they are in libc
  15. {$endif}
  16. unit cthreads;
  17. interface
  18. {$S-}
  19. {$ifndef dynpthreads} // If you have problems compiling this on FreeBSD 5.x
  20. {$linklib c} // try adding -Xf
  21. {$ifndef Darwin}
  22. {$linklib pthread}
  23. {$endif darwin}
  24. {$endif}
  25. Procedure SetCThreadManager;
  26. implementation
  27. Uses
  28. BaseUnix,
  29. unix,
  30. unixtype
  31. {$ifdef dynpthreads}
  32. ,dl
  33. {$endif}
  34. ;
  35. {*****************************************************************************
  36. Generic overloaded
  37. *****************************************************************************}
  38. { Include OS specific parts. }
  39. {$i pthread.inc}
  40. Type PINTRTLEvent = ^TINTRTLEvent;
  41. TINTRTLEvent = record
  42. condvar: pthread_cond_t;
  43. mutex: pthread_mutex_t;
  44. end;
  45. {*****************************************************************************
  46. Threadvar support
  47. *****************************************************************************}
  48. const
  49. threadvarblocksize : dword = 0;
  50. var
  51. TLSKey : pthread_key_t;
  52. procedure CInitThreadvar(var offset : dword;size : dword);
  53. begin
  54. {$ifdef cpusparc}
  55. threadvarblocksize:=align(threadvarblocksize,16);
  56. {$endif cpusparc}
  57. {$ifdef cpupowerpc}
  58. threadvarblocksize:=align(threadvarblocksize,8);
  59. {$endif cpupowerc}
  60. {$ifdef cpui386}
  61. threadvarblocksize:=align(threadvarblocksize,8);
  62. {$endif cpui386}
  63. {$ifdef cpuarm}
  64. threadvarblocksize:=align(threadvarblocksize,4);
  65. {$endif cpuarm}
  66. {$ifdef cpum68k}
  67. threadvarblocksize:=align(threadvarblocksize,2);
  68. {$endif cpum68k}
  69. {$ifdef cpux86_64}
  70. threadvarblocksize:=align(threadvarblocksize,16);
  71. {$endif cpux86_64}
  72. {$ifdef cpupowerpc64}
  73. threadvarblocksize:=align(threadvarblocksize,16);
  74. {$endif cpupowerpc64}
  75. offset:=threadvarblocksize;
  76. inc(threadvarblocksize,size);
  77. end;
  78. procedure CAllocateThreadVars;
  79. var
  80. dataindex : pointer;
  81. begin
  82. { we've to allocate the memory from system }
  83. { because the FPC heap management uses }
  84. { exceptions which use threadvars but }
  85. { these aren't allocated yet ... }
  86. { allocate room on the heap for the thread vars }
  87. DataIndex:=Pointer(Fpmmap(nil,threadvarblocksize,3,MAP_PRIVATE+MAP_ANONYMOUS,-1,0));
  88. FillChar(DataIndex^,threadvarblocksize,0);
  89. pthread_setspecific(tlskey,dataindex);
  90. end;
  91. function CRelocateThreadvar(offset : dword) : pointer;
  92. var
  93. P : Pointer;
  94. begin
  95. P:=pthread_getspecific(tlskey);
  96. if (P=Nil) then
  97. begin
  98. CAllocateThreadvars;
  99. // If this also goes wrong: bye bye threadvars...
  100. P:=pthread_getspecific(tlskey);
  101. end;
  102. CRelocateThreadvar:=P+Offset;
  103. end;
  104. procedure CReleaseThreadVars;
  105. begin
  106. Fpmunmap(pointer(pthread_getspecific(tlskey)),threadvarblocksize);
  107. end;
  108. { Include OS independent Threadvar initialization }
  109. {*****************************************************************************
  110. Thread starting
  111. *****************************************************************************}
  112. type
  113. pthreadinfo = ^tthreadinfo;
  114. tthreadinfo = record
  115. f : tthreadfunc;
  116. p : pointer;
  117. stklen : cardinal;
  118. end;
  119. procedure DoneThread;
  120. begin
  121. { Release Threadvars }
  122. CReleaseThreadVars;
  123. end;
  124. function ThreadMain(param : pointer) : pointer;cdecl;
  125. var
  126. ti : tthreadinfo;
  127. {$ifdef DEBUG_MT}
  128. // in here, don't use write/writeln before having called
  129. // InitThread! I wonder if anyone ever debugged these routines,
  130. // because they will have crashed if DEBUG_MT was enabled!
  131. // this took me the good part of an hour to figure out
  132. // why it was crashing all the time!
  133. // this is kind of a workaround, we simply write(2) to fd 0
  134. s: string[100]; // not an ansistring
  135. {$endif DEBUG_MT}
  136. begin
  137. {$ifdef DEBUG_MT}
  138. s := 'New thread started, initing threadvars'#10;
  139. fpwrite(0,s[1],length(s));
  140. {$endif DEBUG_MT}
  141. { Allocate local thread vars, this must be the first thing,
  142. because the exception management and io depends on threadvars }
  143. CAllocateThreadVars;
  144. { Copy parameter to local data }
  145. {$ifdef DEBUG_MT}
  146. s := 'New thread started, initialising ...'#10;
  147. fpwrite(0,s[1],length(s));
  148. {$endif DEBUG_MT}
  149. ti:=pthreadinfo(param)^;
  150. dispose(pthreadinfo(param));
  151. { Initialize thread }
  152. InitThread(ti.stklen);
  153. { Start thread function }
  154. {$ifdef DEBUG_MT}
  155. writeln('Jumping to thread function');
  156. {$endif DEBUG_MT}
  157. ThreadMain:=pointer(ti.f(ti.p));
  158. DoneThread;
  159. pthread_exit(nil);
  160. end;
  161. function CBeginThread(sa : Pointer;stacksize : PtrUInt;
  162. ThreadFunction : tthreadfunc;p : pointer;
  163. creationFlags : dword; var ThreadId : TThreadId) : TThreadID;
  164. var
  165. ti : pthreadinfo;
  166. thread_attr : pthread_attr_t;
  167. begin
  168. {$ifdef DEBUG_MT}
  169. writeln('Creating new thread');
  170. {$endif DEBUG_MT}
  171. { Initialize multithreading if not done }
  172. if not IsMultiThread then
  173. begin
  174. if (InterLockedExchange(longint(IsMultiThread),1) = 0) then
  175. begin
  176. { We're still running in single thread mode, setup the TLS }
  177. pthread_key_create(@TLSKey,nil);
  178. InitThreadVars(@CRelocateThreadvar);
  179. end
  180. end;
  181. { the only way to pass data to the newly created thread
  182. in a MT safe way, is to use the heap }
  183. new(ti);
  184. ti^.f:=ThreadFunction;
  185. ti^.p:=p;
  186. ti^.stklen:=stacksize;
  187. { call pthread_create }
  188. {$ifdef DEBUG_MT}
  189. writeln('Starting new thread');
  190. {$endif DEBUG_MT}
  191. pthread_attr_init(@thread_attr);
  192. pthread_attr_setinheritsched(@thread_attr, PTHREAD_EXPLICIT_SCHED);
  193. // will fail under linux -- apparently unimplemented
  194. pthread_attr_setscope(@thread_attr, PTHREAD_SCOPE_PROCESS);
  195. // don't create detached, we need to be able to join (waitfor) on
  196. // the newly created thread!
  197. //pthread_attr_setdetachstate(@thread_attr, PTHREAD_CREATE_DETACHED);
  198. if pthread_create(ppthread_t(@threadid), @thread_attr, @ThreadMain,ti) <> 0 then begin
  199. threadid := TThreadID(0);
  200. end;
  201. CBeginThread:=threadid;
  202. {$ifdef DEBUG_MT}
  203. writeln('BeginThread returning ',ptrint(CBeginThread));
  204. {$endif DEBUG_MT}
  205. end;
  206. procedure CEndThread(ExitCode : DWord);
  207. begin
  208. DoneThread;
  209. pthread_detach(pthread_t(pthread_self()));
  210. pthread_exit(pointer(ptrint(ExitCode)));
  211. end;
  212. function CSuspendThread (threadHandle : TThreadID) : dword;
  213. begin
  214. result := pthread_kill(threadHandle,SIGSTOP);
  215. end;
  216. function CResumeThread (threadHandle : TThreadID) : dword;
  217. begin
  218. result := pthread_kill(threadHandle,SIGCONT);
  219. end;
  220. procedure CThreadSwitch; {give time to other threads}
  221. begin
  222. {extern int pthread_yield (void) __THROW;}
  223. {$Warning ThreadSwitch needs to be implemented}
  224. end;
  225. function CKillThread (threadHandle : TThreadID) : dword;
  226. begin
  227. pthread_detach(pthread_t(threadHandle));
  228. CKillThread := pthread_cancel(pthread_t(threadHandle));
  229. end;
  230. function CWaitForThreadTerminate (threadHandle : TThreadID; TimeoutMs : longint) : dword; {0=no timeout}
  231. var
  232. LResultP: Pointer;
  233. LResult: DWord;
  234. begin
  235. LResult := 0;
  236. LResultP := @LResult;
  237. pthread_join(pthread_t(threadHandle), @LResultP);
  238. CWaitForThreadTerminate := LResult;
  239. end;
  240. {$warning threadhandle can be larger than a dword}
  241. function CThreadSetPriority (threadHandle : TThreadID; Prio: longint): boolean; {-15..+15, 0=normal}
  242. begin
  243. {$Warning ThreadSetPriority needs to be implemented}
  244. end;
  245. {$warning threadhandle can be larger than a dword}
  246. function CThreadGetPriority (threadHandle : TThreadID): Integer;
  247. begin
  248. {$Warning ThreadGetPriority needs to be implemented}
  249. end;
  250. function CGetCurrentThreadId : TThreadID;
  251. begin
  252. CGetCurrentThreadId := TThreadID (pthread_self());
  253. end;
  254. {*****************************************************************************
  255. Delphi/Win32 compatibility
  256. *****************************************************************************}
  257. procedure CInitCriticalSection(var CS);
  258. var
  259. MAttr : pthread_mutexattr_t;
  260. res: longint;
  261. begin
  262. res:=pthread_mutexattr_init(@MAttr);
  263. if res=0 then
  264. begin
  265. res:=pthread_mutexattr_settype(@MAttr,longint(_PTHREAD_MUTEX_RECURSIVE));
  266. if res=0 then
  267. res := pthread_mutex_init(@CS,@MAttr)
  268. else
  269. { No recursive mutex support :/ }
  270. res := pthread_mutex_init(@CS,NIL);
  271. end
  272. else
  273. res:= pthread_mutex_init(@CS,NIL);
  274. pthread_mutexattr_destroy(@MAttr);
  275. if res <> 0 then
  276. runerror(6);
  277. end;
  278. procedure CEnterCriticalSection(var CS);
  279. begin
  280. if pthread_mutex_lock(@CS) <> 0 then
  281. runerror(6);
  282. end;
  283. procedure CLeaveCriticalSection(var CS);
  284. begin
  285. if pthread_mutex_unlock(@CS) <> 0 then
  286. runerror(6)
  287. end;
  288. procedure CDoneCriticalSection(var CS);
  289. begin
  290. { unlock as long as unlocking works to unlock it if it is recursive
  291. some Delphi code might call this function with a locked mutex }
  292. while pthread_mutex_unlock(@CS)=0 do
  293. ;
  294. if pthread_mutex_destroy(@CS) <> 0 then
  295. runerror(6);
  296. end;
  297. {*****************************************************************************
  298. Heap Mutex Protection
  299. *****************************************************************************}
  300. var
  301. HeapMutex : pthread_mutex_t;
  302. procedure PThreadHeapMutexInit;
  303. begin
  304. pthread_mutex_init(@heapmutex,nil);
  305. end;
  306. procedure PThreadHeapMutexDone;
  307. begin
  308. pthread_mutex_destroy(@heapmutex);
  309. end;
  310. procedure PThreadHeapMutexLock;
  311. begin
  312. pthread_mutex_lock(@heapmutex);
  313. end;
  314. procedure PThreadHeapMutexUnlock;
  315. begin
  316. pthread_mutex_unlock(@heapmutex);
  317. end;
  318. const
  319. PThreadMemoryMutexManager : TMemoryMutexManager = (
  320. MutexInit : @PThreadHeapMutexInit;
  321. MutexDone : @PThreadHeapMutexDone;
  322. MutexLock : @PThreadHeapMutexLock;
  323. MutexUnlock : @PThreadHeapMutexUnlock;
  324. );
  325. procedure InitHeapMutexes;
  326. begin
  327. SetMemoryMutexManager(PThreadMemoryMutexManager);
  328. end;
  329. type
  330. TPthreadMutex = pthread_mutex_t;
  331. Tbasiceventstate=record
  332. FSem: Pointer;
  333. FManualReset: Boolean;
  334. FEventSection: TPthreadMutex;
  335. end;
  336. plocaleventstate = ^tbasiceventstate;
  337. // peventstate=pointer;
  338. Const
  339. wrSignaled = 0;
  340. wrTimeout = 1;
  341. wrAbandoned= 2;
  342. wrError = 3;
  343. function IntBasicEventCreate(EventAttributes : Pointer; AManualReset,InitialState : Boolean;const Name : ansistring):pEventState;
  344. var
  345. MAttr : pthread_mutexattr_t;
  346. res : cint;
  347. begin
  348. new(plocaleventstate(result));
  349. plocaleventstate(result)^.FManualReset:=AManualReset;
  350. plocaleventstate(result)^.FSem:=New(PSemaphore); //sem_t.
  351. // plocaleventstate(result)^.feventsection:=nil;
  352. res:=pthread_mutexattr_init(@MAttr);
  353. if res=0 then
  354. begin
  355. res:=pthread_mutexattr_settype(@MAttr,longint(_PTHREAD_MUTEX_RECURSIVE));
  356. if Res=0 then
  357. Res:=pthread_mutex_init(@plocaleventstate(result)^.feventsection,@MAttr)
  358. else
  359. res:=pthread_mutex_init(@plocaleventstate(result)^.feventsection,nil);
  360. end
  361. else
  362. res:=pthread_mutex_init(@plocaleventstate(result)^.feventsection,nil);
  363. pthread_mutexattr_destroy(@MAttr);
  364. if res <> 0 then
  365. runerror(6);
  366. if sem_init(psem_t(plocaleventstate(result)^.FSem),ord(False),Ord(InitialState)) <> 0 then
  367. runerror(6);
  368. end;
  369. procedure Intbasiceventdestroy(state:peventstate);
  370. begin
  371. sem_destroy(psem_t( plocaleventstate(state)^.FSem));
  372. end;
  373. procedure IntbasiceventResetEvent(state:peventstate);
  374. begin
  375. While sem_trywait(psem_t( plocaleventstate(state)^.FSem))=0 do
  376. ;
  377. end;
  378. procedure IntbasiceventSetEvent(state:peventstate);
  379. Var
  380. Value : Longint;
  381. begin
  382. pthread_mutex_lock(@plocaleventstate(state)^.feventsection);
  383. Try
  384. sem_getvalue(plocaleventstate(state)^.FSem,@value);
  385. if Value=0 then
  386. sem_post(psem_t( plocaleventstate(state)^.FSem));
  387. finally
  388. pthread_mutex_unlock(@plocaleventstate(state)^.feventsection);
  389. end;
  390. end;
  391. function IntbasiceventWaitFor(Timeout : Cardinal;state:peventstate) : longint;
  392. begin
  393. If TimeOut<>Cardinal($FFFFFFFF) then
  394. result:=wrError
  395. else
  396. begin
  397. sem_wait(psem_t(plocaleventstate(state)^.FSem));
  398. result:=wrSignaled;
  399. if plocaleventstate(state)^.FManualReset then
  400. begin
  401. pthread_mutex_lock(@plocaleventstate(state)^.feventsection);
  402. Try
  403. intbasiceventresetevent(State);
  404. sem_post(psem_t( plocaleventstate(state)^.FSem));
  405. Finally
  406. pthread_mutex_unlock(@plocaleventstate(state)^.feventsection);
  407. end;
  408. end;
  409. end;
  410. end;
  411. function intRTLEventCreate: PRTLEvent;
  412. var p:pintrtlevent;
  413. begin
  414. new(p);
  415. pthread_cond_init(@p^.condvar, nil);
  416. pthread_mutex_init(@p^.mutex, nil);
  417. result:=PRTLEVENT(p);
  418. end;
  419. procedure intRTLEventDestroy(AEvent: PRTLEvent);
  420. var p:pintrtlevent;
  421. begin
  422. p:=pintrtlevent(aevent);
  423. pthread_cond_destroy(@p^.condvar);
  424. pthread_mutex_destroy(@p^.mutex);
  425. dispose(p);
  426. end;
  427. procedure intRTLEventSetEvent(AEvent: PRTLEvent);
  428. var p:pintrtlevent;
  429. begin
  430. p:=pintrtlevent(aevent);
  431. pthread_mutex_lock(@p^.mutex);
  432. pthread_cond_signal(@p^.condvar);
  433. pthread_mutex_unlock(@p^.mutex);
  434. end;
  435. procedure intRTLEventResetEvent(AEvent: PRTLEvent);
  436. begin
  437. { events before startwait are ignored unix }
  438. end;
  439. procedure intRTLEventStartWait(AEvent: PRTLEvent);
  440. var p:pintrtlevent;
  441. begin
  442. p:=pintrtlevent(aevent);
  443. pthread_mutex_lock(@p^.mutex);
  444. end;
  445. procedure intRTLEventWaitFor(AEvent: PRTLEvent);
  446. var p:pintrtlevent;
  447. begin
  448. p:=pintrtlevent(aevent);
  449. pthread_cond_wait(@p^.condvar, @p^.mutex);
  450. pthread_mutex_unlock(@p^.mutex);
  451. end;
  452. procedure intRTLEventWaitForTimeout(AEvent: PRTLEvent;timeout : longint);
  453. var
  454. p : pintrtlevent;
  455. errres : cint;
  456. timespec : ttimespec;
  457. tnow : timeval;
  458. begin
  459. p:=pintrtlevent(aevent);
  460. fpgettimeofday(@tnow,nil);
  461. timespec.tv_sec:=tnow.tv_sec+(timeout div 1000);
  462. timespec.tv_nsec:=(timeout mod 1000)*1000000 + tnow.tv_usec*1000;
  463. if timespec.tv_nsec >= 1000000000 then
  464. begin
  465. inc(timespec.tv_sec);
  466. dec(timespec.tv_nsec, 1000000000);
  467. end;
  468. errres:=pthread_cond_timedwait(@p^.condvar, @p^.mutex, @timespec);
  469. if (errres=0) or (errres=ESysETIMEDOUT) then
  470. pthread_mutex_unlock(@p^.mutex);
  471. end;
  472. function cSemaphoreInit: Pointer;
  473. var
  474. s: PSemaphore;
  475. begin
  476. GetMem(s, SizeOf(TSemaphore));
  477. if sem_init(s, 0, 0) = 0 then
  478. cSemaphoreInit:=s
  479. else
  480. cSemaphoreInit:=nil;
  481. end;
  482. procedure cSemaphoreWait(const FSem: Pointer);
  483. begin
  484. sem_wait(PSemaphore(FSem));
  485. end;
  486. procedure cSemaphorePost(const FSem: Pointer);
  487. begin
  488. sem_post(PSemaphore(FSem));
  489. end;
  490. procedure cSemaphoreDestroy(const FSem: Pointer);
  491. var
  492. s: PSemaphore;
  493. begin
  494. s:=FSem;
  495. sem_destroy(PSemaphore(FSem));
  496. FreeMem(s);
  497. end;
  498. type
  499. threadmethod = procedure of object;
  500. Function CInitThreads : Boolean;
  501. begin
  502. {$ifdef DEBUG_MT}
  503. Writeln('Entering InitThreads.');
  504. {$endif}
  505. {$ifndef dynpthreads}
  506. Result:=True;
  507. {$else}
  508. Result:=LoadPthreads;
  509. {$endif}
  510. ThreadID := TThreadID (pthread_self);
  511. {$ifdef DEBUG_MT}
  512. Writeln('InitThreads : ',Result);
  513. {$endif DEBUG_MT}
  514. end;
  515. Function CDoneThreads : Boolean;
  516. begin
  517. {$ifndef dynpthreads}
  518. Result:=True;
  519. {$else}
  520. Result:=UnloadPthreads;
  521. {$endif}
  522. end;
  523. Var
  524. CThreadManager : TThreadManager;
  525. Procedure SetCThreadManager;
  526. begin
  527. With CThreadManager do begin
  528. InitManager :=@CInitThreads;
  529. DoneManager :=@CDoneThreads;
  530. BeginThread :=@CBeginThread;
  531. EndThread :=@CEndThread;
  532. SuspendThread :=@CSuspendThread;
  533. ResumeThread :=@CResumeThread;
  534. KillThread :=@CKillThread;
  535. ThreadSwitch :=@CThreadSwitch;
  536. WaitForThreadTerminate :=@CWaitForThreadTerminate;
  537. ThreadSetPriority :=@CThreadSetPriority;
  538. ThreadGetPriority :=@CThreadGetPriority;
  539. GetCurrentThreadId :=@CGetCurrentThreadId;
  540. InitCriticalSection :=@CInitCriticalSection;
  541. DoneCriticalSection :=@CDoneCriticalSection;
  542. EnterCriticalSection :=@CEnterCriticalSection;
  543. LeaveCriticalSection :=@CLeaveCriticalSection;
  544. InitThreadVar :=@CInitThreadVar;
  545. RelocateThreadVar :=@CRelocateThreadVar;
  546. AllocateThreadVars :=@CAllocateThreadVars;
  547. ReleaseThreadVars :=@CReleaseThreadVars;
  548. BasicEventCreate :=@intBasicEventCreate;
  549. BasicEventDestroy :=@intBasicEventDestroy;
  550. BasicEventResetEvent :=@intBasicEventResetEvent;
  551. BasicEventSetEvent :=@intBasicEventSetEvent;
  552. BasiceventWaitFor :=@intBasiceventWaitFor;
  553. rtlEventCreate :=@intrtlEventCreate;
  554. rtlEventDestroy :=@intrtlEventDestroy;
  555. rtlEventSetEvent :=@intrtlEventSetEvent;
  556. rtlEventResetEvent :=@intrtlEventResetEvent;
  557. rtlEventStartWait :=@intrtlEventStartWait;
  558. rtleventWaitForTimeout :=@intrtleventWaitForTimeout;
  559. rtleventWaitFor :=@intrtleventWaitFor;
  560. // semaphores
  561. SemaphoreInit :=@cSemaphoreInit;
  562. SemaphoreDestroy :=@cSemaphoreDestroy;
  563. SemaphoreWait :=@cSemaphoreWait;
  564. SemaphorePost :=@cSemaphorePost;
  565. end;
  566. SetThreadManager(CThreadManager);
  567. InitHeapMutexes;
  568. end;
  569. initialization
  570. if ThreadingAlreadyUsed then
  571. begin
  572. writeln('Threading has been used before cthreads was initialized.');
  573. writeln('Make cthreads one of the first units in your uses clause.');
  574. runerror(211);
  575. end;
  576. SetCThreadManager;
  577. finalization
  578. end.