cthreads.pp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910
  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. { sem_init is best, since it does not consume any file descriptors. }
  17. { sem_open is second best, since it consumes only one file descriptor }
  18. { per semaphore. }
  19. { If neither is available, pipe is used as fallback, which consumes 2 }
  20. { file descriptors per semaphore. }
  21. { Darwin doesn't support nameless semaphores in at least }
  22. { Mac OS X 10.4.8/Darwin 8.8 }
  23. {$ifndef darwin}
  24. {$define has_sem_init}
  25. {$define has_sem_getvalue}
  26. {$else }
  27. {$ifdef darwin}
  28. {$define has_sem_open}
  29. {$endif}
  30. {$endif}
  31. unit cthreads;
  32. interface
  33. {$S-}
  34. {$ifndef dynpthreads} // If you have problems compiling this on FreeBSD 5.x
  35. {$linklib c} // try adding -Xf
  36. {$ifndef Darwin}
  37. {$linklib pthread}
  38. {$endif darwin}
  39. {$endif}
  40. Procedure SetCThreadManager;
  41. implementation
  42. Uses
  43. BaseUnix,
  44. unix,
  45. unixtype,
  46. initc
  47. {$ifdef dynpthreads}
  48. ,dl
  49. {$endif}
  50. ;
  51. {*****************************************************************************
  52. Generic overloaded
  53. *****************************************************************************}
  54. { Include OS specific parts. }
  55. {$i pthread.inc}
  56. Type PINTRTLEvent = ^TINTRTLEvent;
  57. TINTRTLEvent = record
  58. condvar: pthread_cond_t;
  59. mutex: pthread_mutex_t;
  60. isset: boolean;
  61. end;
  62. {*****************************************************************************
  63. Threadvar support
  64. *****************************************************************************}
  65. const
  66. threadvarblocksize : dword = 0;
  67. var
  68. TLSKey : pthread_key_t;
  69. procedure CInitThreadvar(var offset : dword;size : dword);
  70. begin
  71. {$ifdef cpusparc}
  72. threadvarblocksize:=align(threadvarblocksize,16);
  73. {$endif cpusparc}
  74. {$ifdef cpupowerpc}
  75. threadvarblocksize:=align(threadvarblocksize,8);
  76. {$endif cpupowerc}
  77. {$ifdef cpui386}
  78. threadvarblocksize:=align(threadvarblocksize,8);
  79. {$endif cpui386}
  80. {$ifdef cpuarm}
  81. threadvarblocksize:=align(threadvarblocksize,4);
  82. {$endif cpuarm}
  83. {$ifdef cpum68k}
  84. threadvarblocksize:=align(threadvarblocksize,2);
  85. {$endif cpum68k}
  86. {$ifdef cpux86_64}
  87. threadvarblocksize:=align(threadvarblocksize,16);
  88. {$endif cpux86_64}
  89. {$ifdef cpupowerpc64}
  90. threadvarblocksize:=align(threadvarblocksize,16);
  91. {$endif cpupowerpc64}
  92. offset:=threadvarblocksize;
  93. inc(threadvarblocksize,size);
  94. end;
  95. procedure CAllocateThreadVars;
  96. var
  97. dataindex : pointer;
  98. begin
  99. { we've to allocate the memory from system }
  100. { because the FPC heap management uses }
  101. { exceptions which use threadvars but }
  102. { these aren't allocated yet ... }
  103. { allocate room on the heap for the thread vars }
  104. DataIndex:=Pointer(Fpmmap(nil,threadvarblocksize,3,MAP_PRIVATE+MAP_ANONYMOUS,-1,0));
  105. FillChar(DataIndex^,threadvarblocksize,0);
  106. pthread_setspecific(tlskey,dataindex);
  107. end;
  108. function CRelocateThreadvar(offset : dword) : pointer;
  109. var
  110. P : Pointer;
  111. begin
  112. P:=pthread_getspecific(tlskey);
  113. if (P=Nil) then
  114. begin
  115. CAllocateThreadvars;
  116. // If this also goes wrong: bye bye threadvars...
  117. P:=pthread_getspecific(tlskey);
  118. end;
  119. CRelocateThreadvar:=P+Offset;
  120. end;
  121. procedure CReleaseThreadVars;
  122. begin
  123. Fpmunmap(pointer(pthread_getspecific(tlskey)),threadvarblocksize);
  124. end;
  125. { Include OS independent Threadvar initialization }
  126. {*****************************************************************************
  127. Thread starting
  128. *****************************************************************************}
  129. type
  130. pthreadinfo = ^tthreadinfo;
  131. tthreadinfo = record
  132. f : tthreadfunc;
  133. p : pointer;
  134. stklen : cardinal;
  135. end;
  136. procedure DoneThread;
  137. begin
  138. { Release Threadvars }
  139. CReleaseThreadVars;
  140. end;
  141. function ThreadMain(param : pointer) : pointer;cdecl;
  142. var
  143. ti : tthreadinfo;
  144. {$ifdef DEBUG_MT}
  145. // in here, don't use write/writeln before having called
  146. // InitThread! I wonder if anyone ever debugged these routines,
  147. // because they will have crashed if DEBUG_MT was enabled!
  148. // this took me the good part of an hour to figure out
  149. // why it was crashing all the time!
  150. // this is kind of a workaround, we simply write(2) to fd 0
  151. s: string[100]; // not an ansistring
  152. {$endif DEBUG_MT}
  153. begin
  154. {$ifdef DEBUG_MT}
  155. s := 'New thread started, initing threadvars'#10;
  156. fpwrite(0,s[1],length(s));
  157. {$endif DEBUG_MT}
  158. { Allocate local thread vars, this must be the first thing,
  159. because the exception management and io depends on threadvars }
  160. CAllocateThreadVars;
  161. { Copy parameter to local data }
  162. {$ifdef DEBUG_MT}
  163. s := 'New thread started, initialising ...'#10;
  164. fpwrite(0,s[1],length(s));
  165. {$endif DEBUG_MT}
  166. ti:=pthreadinfo(param)^;
  167. dispose(pthreadinfo(param));
  168. { Initialize thread }
  169. InitThread(ti.stklen);
  170. { Start thread function }
  171. {$ifdef DEBUG_MT}
  172. writeln('Jumping to thread function');
  173. {$endif DEBUG_MT}
  174. ThreadMain:=pointer(ti.f(ti.p));
  175. DoneThread;
  176. pthread_exit(nil);
  177. end;
  178. function CBeginThread(sa : Pointer;stacksize : PtrUInt;
  179. ThreadFunction : tthreadfunc;p : pointer;
  180. creationFlags : dword; var ThreadId : TThreadId) : TThreadID;
  181. var
  182. ti : pthreadinfo;
  183. thread_attr : pthread_attr_t;
  184. begin
  185. {$ifdef DEBUG_MT}
  186. writeln('Creating new thread');
  187. {$endif DEBUG_MT}
  188. { Initialize multithreading if not done }
  189. if not IsMultiThread then
  190. begin
  191. if (InterLockedExchange(longint(IsMultiThread),ord(true)) = 0) then
  192. begin
  193. { We're still running in single thread mode, setup the TLS }
  194. pthread_key_create(@TLSKey,nil);
  195. InitThreadVars(@CRelocateThreadvar);
  196. end
  197. end;
  198. { the only way to pass data to the newly created thread
  199. in a MT safe way, is to use the heap }
  200. new(ti);
  201. ti^.f:=ThreadFunction;
  202. ti^.p:=p;
  203. ti^.stklen:=stacksize;
  204. { call pthread_create }
  205. {$ifdef DEBUG_MT}
  206. writeln('Starting new thread');
  207. {$endif DEBUG_MT}
  208. pthread_attr_init(@thread_attr);
  209. pthread_attr_setinheritsched(@thread_attr, PTHREAD_EXPLICIT_SCHED);
  210. // will fail under linux -- apparently unimplemented
  211. pthread_attr_setscope(@thread_attr, PTHREAD_SCOPE_PROCESS);
  212. // don't create detached, we need to be able to join (waitfor) on
  213. // the newly created thread!
  214. //pthread_attr_setdetachstate(@thread_attr, PTHREAD_CREATE_DETACHED);
  215. if pthread_create(ppthread_t(@threadid), @thread_attr, @ThreadMain,ti) <> 0 then
  216. begin
  217. dispose(ti);
  218. threadid := TThreadID(0);
  219. end;
  220. CBeginThread:=threadid;
  221. {$ifdef DEBUG_MT}
  222. writeln('BeginThread returning ',ptrint(CBeginThread));
  223. {$endif DEBUG_MT}
  224. end;
  225. procedure CEndThread(ExitCode : DWord);
  226. begin
  227. DoneThread;
  228. pthread_detach(pthread_t(pthread_self()));
  229. pthread_exit(pointer(ptrint(ExitCode)));
  230. end;
  231. function CSuspendThread (threadHandle : TThreadID) : dword;
  232. begin
  233. result := pthread_kill(threadHandle,SIGSTOP);
  234. end;
  235. function CResumeThread (threadHandle : TThreadID) : dword;
  236. begin
  237. result := pthread_kill(threadHandle,SIGCONT);
  238. end;
  239. procedure CThreadSwitch; {give time to other threads}
  240. begin
  241. {extern int pthread_yield (void) __THROW;}
  242. {$Warning ThreadSwitch needs to be implemented}
  243. end;
  244. function CKillThread (threadHandle : TThreadID) : dword;
  245. begin
  246. pthread_detach(pthread_t(threadHandle));
  247. CKillThread := pthread_cancel(pthread_t(threadHandle));
  248. end;
  249. function CWaitForThreadTerminate (threadHandle : TThreadID; TimeoutMs : longint) : dword; {0=no timeout}
  250. var
  251. LResultP: Pointer;
  252. LResult: DWord;
  253. begin
  254. LResult := 0;
  255. LResultP := @LResult;
  256. pthread_join(pthread_t(threadHandle), @LResultP);
  257. CWaitForThreadTerminate := LResult;
  258. end;
  259. {$warning threadhandle can be larger than a dword}
  260. function CThreadSetPriority (threadHandle : TThreadID; Prio: longint): boolean; {-15..+15, 0=normal}
  261. begin
  262. {$Warning ThreadSetPriority needs to be implemented}
  263. end;
  264. {$warning threadhandle can be larger than a dword}
  265. function CThreadGetPriority (threadHandle : TThreadID): Integer;
  266. begin
  267. {$Warning ThreadGetPriority needs to be implemented}
  268. end;
  269. function CGetCurrentThreadId : TThreadID;
  270. begin
  271. CGetCurrentThreadId := TThreadID (pthread_self());
  272. end;
  273. {*****************************************************************************
  274. Delphi/Win32 compatibility
  275. *****************************************************************************}
  276. procedure CInitCriticalSection(var CS);
  277. var
  278. MAttr : pthread_mutexattr_t;
  279. res: longint;
  280. begin
  281. res:=pthread_mutexattr_init(@MAttr);
  282. if res=0 then
  283. begin
  284. res:=pthread_mutexattr_settype(@MAttr,longint(_PTHREAD_MUTEX_RECURSIVE));
  285. if res=0 then
  286. res := pthread_mutex_init(@CS,@MAttr)
  287. else
  288. { No recursive mutex support :/ }
  289. res := pthread_mutex_init(@CS,NIL);
  290. end
  291. else
  292. res:= pthread_mutex_init(@CS,NIL);
  293. pthread_mutexattr_destroy(@MAttr);
  294. if res <> 0 then
  295. runerror(6);
  296. end;
  297. procedure CEnterCriticalSection(var CS);
  298. begin
  299. if pthread_mutex_lock(@CS) <> 0 then
  300. runerror(6);
  301. end;
  302. procedure CLeaveCriticalSection(var CS);
  303. begin
  304. if pthread_mutex_unlock(@CS) <> 0 then
  305. runerror(6)
  306. end;
  307. procedure CDoneCriticalSection(var CS);
  308. begin
  309. { unlock as long as unlocking works to unlock it if it is recursive
  310. some Delphi code might call this function with a locked mutex }
  311. while pthread_mutex_unlock(@CS)=0 do
  312. ;
  313. if pthread_mutex_destroy(@CS) <> 0 then
  314. runerror(6);
  315. end;
  316. {*****************************************************************************
  317. Semaphore routines
  318. *****************************************************************************}
  319. procedure cSemaphoreWait(const FSem: Pointer);
  320. var
  321. res: cint;
  322. err: cint;
  323. {$if not defined(has_sem_init) and not defined(has_sem_open)}
  324. b: byte;
  325. {$endif}
  326. begin
  327. {$if defined(has_sem_init) or defined(has_sem_open)}
  328. repeat
  329. res:=sem_wait(PSemaphore(FSem));
  330. err:=fpgetCerrno;
  331. until (res<>-1) or (err<>ESysEINTR);
  332. {$else}
  333. repeat
  334. res:=fpread(PFilDes(FSem)^[0], b, 1);
  335. err:=fpgeterrno;
  336. until (res<>-1) or ((err<>ESysEINTR) and (err<>ESysEAgain));
  337. {$endif}
  338. end;
  339. procedure cSemaphorePost(const FSem: Pointer);
  340. {$if defined(has_sem_init) or defined(has_sem_open)}
  341. begin
  342. sem_post(PSemaphore(FSem));
  343. end;
  344. {$else}
  345. var
  346. writeres: cint;
  347. err: cint;
  348. b : byte;
  349. begin
  350. b:=0;
  351. repeat
  352. writeres:=fpwrite(PFilDes(FSem)^[1], b, 1);
  353. err:=fpgeterrno;
  354. until (writeres<>-1) or ((err<>ESysEINTR) and (err<>ESysEAgain));
  355. end;
  356. {$endif}
  357. {$if defined(has_sem_open) and not defined(has_sem_init)}
  358. function cIntSemaphoreOpen(const name: pchar; initvalue: boolean): Pointer;
  359. var
  360. err: cint;
  361. begin
  362. repeat
  363. cIntSemaphoreOpen := sem_open(name,O_CREAT,0,ord(initvalue));
  364. err:=fpgetCerrno;
  365. until (ptrint(cIntSemaphoreOpen) <> SEM_FAILED) or (err <> ESysEINTR);
  366. if (ptrint(cIntSemaphoreOpen) <> SEM_FAILED) then
  367. { immediately unlink so the semaphore will be destroyed when the }
  368. { the process exits }
  369. sem_unlink(name)
  370. else
  371. cIntSemaphoreOpen:=NIL;
  372. end;
  373. {$endif}
  374. function cIntSemaphoreInit(initvalue: boolean): Pointer;
  375. {$if defined(has_sem_open) and not defined(has_sem_init)}
  376. var
  377. tid: string[31];
  378. semname: string[63];
  379. err: cint;
  380. {$endif}
  381. begin
  382. {$ifdef has_sem_init}
  383. cIntSemaphoreInit := GetMem(SizeOf(TSemaphore));
  384. if sem_init(PSemaphore(cIntSemaphoreInit), 0, ord(initvalue)) <> 0 then
  385. begin
  386. FreeMem(cIntSemaphoreInit);
  387. cIntSemaphoreInit:=NIL;
  388. end;
  389. {$else}
  390. {$ifdef has_sem_open}
  391. { avoid a potential temporary nameclash with another process/thread }
  392. str(fpGetPid,semname);
  393. str(ptruint(pthread_self),tid);
  394. semname:='/FPC'+semname+'T'+tid+#0;
  395. cIntSemaphoreInit:=cIntSemaphoreOpen(@semname[1],initvalue);
  396. {$else}
  397. cIntSemaphoreInit := GetMem(SizeOf(TFilDes));
  398. if (fppipe(PFilDes(cIntSemaphoreInit)^) <> 0) then
  399. begin
  400. FreeMem(cIntSemaphoreInit);
  401. cIntSemaphoreInit:=nil;
  402. end
  403. else if initvalue then
  404. cSemaphorePost(cIntSemaphoreInit);
  405. {$endif}
  406. {$endif}
  407. end;
  408. function cSemaphoreInit: Pointer;
  409. begin
  410. cSemaphoreInit:=cIntSemaphoreInit(false);
  411. end;
  412. procedure cSemaphoreDestroy(const FSem: Pointer);
  413. begin
  414. {$ifdef has_sem_init}
  415. sem_destroy(PSemaphore(FSem));
  416. FreeMem(FSem);
  417. {$else}
  418. {$ifdef has_sem_open}
  419. sem_close(PSemaphore(FSem));
  420. {$else has_sem_init}
  421. fpclose(PFilDes(FSem)^[0]);
  422. fpclose(PFilDes(FSem)^[1]);
  423. FreeMem(FSem);
  424. {$endif}
  425. {$endif}
  426. end;
  427. {*****************************************************************************
  428. Heap Mutex Protection
  429. *****************************************************************************}
  430. var
  431. HeapMutex : pthread_mutex_t;
  432. procedure PThreadHeapMutexInit;
  433. begin
  434. pthread_mutex_init(@heapmutex,nil);
  435. end;
  436. procedure PThreadHeapMutexDone;
  437. begin
  438. pthread_mutex_destroy(@heapmutex);
  439. end;
  440. procedure PThreadHeapMutexLock;
  441. begin
  442. pthread_mutex_lock(@heapmutex);
  443. end;
  444. procedure PThreadHeapMutexUnlock;
  445. begin
  446. pthread_mutex_unlock(@heapmutex);
  447. end;
  448. const
  449. PThreadMemoryMutexManager : TMemoryMutexManager = (
  450. MutexInit : @PThreadHeapMutexInit;
  451. MutexDone : @PThreadHeapMutexDone;
  452. MutexLock : @PThreadHeapMutexLock;
  453. MutexUnlock : @PThreadHeapMutexUnlock;
  454. );
  455. procedure InitHeapMutexes;
  456. begin
  457. SetMemoryMutexManager(PThreadMemoryMutexManager);
  458. end;
  459. type
  460. TPthreadMutex = pthread_mutex_t;
  461. Tbasiceventstate=record
  462. FSem: Pointer;
  463. FEventSection: TPthreadMutex;
  464. FManualReset: Boolean;
  465. end;
  466. plocaleventstate = ^tbasiceventstate;
  467. // peventstate=pointer;
  468. Const
  469. wrSignaled = 0;
  470. wrTimeout = 1;
  471. wrAbandoned= 2;
  472. wrError = 3;
  473. function IntBasicEventCreate(EventAttributes : Pointer; AManualReset,InitialState : Boolean;const Name : ansistring):pEventState;
  474. var
  475. MAttr : pthread_mutexattr_t;
  476. res : cint;
  477. begin
  478. new(plocaleventstate(result));
  479. plocaleventstate(result)^.FManualReset:=AManualReset;
  480. {$ifdef has_sem_init}
  481. plocaleventstate(result)^.FSem:=cIntSemaphoreInit(true);
  482. if plocaleventstate(result)^.FSem=nil then
  483. begin
  484. FreeMem(result);
  485. runerror(6);
  486. end;
  487. {$else}
  488. {$ifdef has_sem_open}
  489. plocaleventstate(result)^.FSem:=cIntSemaphoreOpen(PChar(Name),InitialState);
  490. if (plocaleventstate(result)^.FSem = NIL) then
  491. begin
  492. FreeMem(result);
  493. runerror(6);
  494. end;
  495. {$else}
  496. plocaleventstate(result)^.FSem:=cSemaphoreInit;
  497. if (plocaleventstate(result)^.FSem = NIL) then
  498. begin
  499. FreeMem(result);
  500. runerror(6);
  501. end;
  502. if InitialState then
  503. cSemaphorePost(plocaleventstate(result)^.FSem);
  504. {$endif}
  505. {$endif}
  506. // plocaleventstate(result)^.feventsection:=nil;
  507. res:=pthread_mutexattr_init(@MAttr);
  508. if res=0 then
  509. begin
  510. res:=pthread_mutexattr_settype(@MAttr,longint(_PTHREAD_MUTEX_RECURSIVE));
  511. if Res=0 then
  512. Res:=pthread_mutex_init(@plocaleventstate(result)^.feventsection,@MAttr)
  513. else
  514. res:=pthread_mutex_init(@plocaleventstate(result)^.feventsection,nil);
  515. end
  516. else
  517. res:=pthread_mutex_init(@plocaleventstate(result)^.feventsection,nil);
  518. pthread_mutexattr_destroy(@MAttr);
  519. if res <> 0 then
  520. begin
  521. cSemaphoreDestroy(plocaleventstate(result)^.FSem);
  522. FreeMem(result);
  523. runerror(6);
  524. end;
  525. end;
  526. procedure Intbasiceventdestroy(state:peventstate);
  527. begin
  528. cSemaphoreDestroy(plocaleventstate(state)^.FSem);
  529. FreeMem(state);
  530. end;
  531. procedure IntbasiceventResetEvent(state:peventstate);
  532. {$if defined(has_sem_init) or defined(has_sem_open)}
  533. var
  534. res: cint;
  535. err: cint;
  536. begin
  537. repeat
  538. res:=sem_trywait(psem_t(plocaleventstate(state)^.FSem));
  539. err:=fpgeterrno;
  540. until (res<>0) and ((res<>-1) or (err<>ESysEINTR));
  541. {$else has_sem_init or has_sem_open}
  542. var
  543. fds: TFDSet;
  544. tv : timeval;
  545. begin
  546. tv.tv_sec:=0;
  547. tv.tv_usec:=0;
  548. fpFD_ZERO(fds);
  549. fpFD_SET(PFilDes(plocaleventstate(state)^.FSem)^[0],fds);
  550. pthread_mutex_lock(@plocaleventstate(state)^.feventsection);
  551. Try
  552. while fpselect(PFilDes(plocaleventstate(state)^.FSem)^[0],@fds,nil,nil,@tv) > 0 do
  553. cSemaphoreWait(plocaleventstate(state)^.FSem);
  554. finally
  555. pthread_mutex_unlock(@plocaleventstate(state)^.feventsection);
  556. end;
  557. {$endif has_sem_init or has_sem_open}
  558. end;
  559. procedure IntbasiceventSetEvent(state:peventstate);
  560. Var
  561. {$if defined(has_sem_init) or defined(has_sem_open)}
  562. Value : Longint;
  563. res : cint;
  564. err : cint;
  565. {$else}
  566. fds: TFDSet;
  567. tv : timeval;
  568. {$endif}
  569. begin
  570. pthread_mutex_lock(@plocaleventstate(state)^.feventsection);
  571. Try
  572. {$if defined(has_sem_init) or defined(has_sem_open)}
  573. if (sem_getvalue(plocaleventstate(state)^.FSem,@value) <> -1) then
  574. begin
  575. if Value=0 then
  576. cSemaphorePost(plocaleventstate(state)^.FSem);
  577. end
  578. else if (fpgetCerrno = ESysENOSYS) then
  579. { not yet implemented on Mac OS X 10.4.8 }
  580. begin
  581. repeat
  582. res:=sem_trywait(psem_t(plocaleventstate(state)^.FSem));
  583. err:=fpgetCerrno;
  584. until ((res<>-1) or (err<>ESysEINTR));
  585. { now we've either decreased the semaphore by 1 (if it was }
  586. { not zero), or we've done nothing (if it was already zero) }
  587. { -> increase by 1 and we have the same result as }
  588. { increasing by 1 only if it was 0 }
  589. cSemaphorePost(plocaleventstate(state)^.FSem);
  590. end
  591. else
  592. runerror(6);
  593. {$else has_sem_init or has_sem_open}
  594. tv.tv_sec:=0;
  595. tv.tv_usec:=0;
  596. fpFD_ZERO(fds);
  597. fpFD_SET(PFilDes(plocaleventstate(state)^.FSem)^[0],fds);
  598. if fpselect(PFilDes(plocaleventstate(state)^.FSem)^[0],@fds,nil,nil,@tv)=0 then
  599. cSemaphorePost(plocaleventstate(state)^.FSem);
  600. {$endif has_sem_init or has_sem_open}
  601. finally
  602. pthread_mutex_unlock(@plocaleventstate(state)^.feventsection);
  603. end;
  604. end;
  605. function IntbasiceventWaitFor(Timeout : Cardinal;state:peventstate) : longint;
  606. begin
  607. If TimeOut<>Cardinal($FFFFFFFF) then
  608. result:=wrError
  609. else
  610. begin
  611. cSemaphoreWait(plocaleventstate(state)^.FSem);
  612. result:=wrSignaled;
  613. if plocaleventstate(state)^.FManualReset then
  614. begin
  615. pthread_mutex_lock(@plocaleventstate(state)^.feventsection);
  616. Try
  617. intbasiceventresetevent(State);
  618. cSemaphorePost(plocaleventstate(state)^.FSem);
  619. Finally
  620. pthread_mutex_unlock(@plocaleventstate(state)^.feventsection);
  621. end;
  622. end;
  623. end;
  624. end;
  625. function intRTLEventCreate: PRTLEvent;
  626. var p:pintrtlevent;
  627. begin
  628. new(p);
  629. pthread_cond_init(@p^.condvar, nil);
  630. pthread_mutex_init(@p^.mutex, nil);
  631. p^.isset:=false;
  632. result:=PRTLEVENT(p);
  633. end;
  634. procedure intRTLEventDestroy(AEvent: PRTLEvent);
  635. var p:pintrtlevent;
  636. begin
  637. p:=pintrtlevent(aevent);
  638. pthread_cond_destroy(@p^.condvar);
  639. pthread_mutex_destroy(@p^.mutex);
  640. dispose(p);
  641. end;
  642. procedure intRTLEventSetEvent(AEvent: PRTLEvent);
  643. var p:pintrtlevent;
  644. begin
  645. p:=pintrtlevent(aevent);
  646. pthread_mutex_lock(@p^.mutex);
  647. p^.isset:=true;
  648. pthread_cond_signal(@p^.condvar);
  649. pthread_mutex_unlock(@p^.mutex);
  650. end;
  651. procedure intRTLEventResetEvent(AEvent: PRTLEvent);
  652. var p:pintrtlevent;
  653. begin
  654. p:=pintrtlevent(aevent);
  655. pthread_mutex_lock(@p^.mutex);
  656. p^.isset:=false;
  657. pthread_mutex_unlock(@p^.mutex);
  658. end;
  659. procedure intRTLEventWaitFor(AEvent: PRTLEvent);
  660. var p:pintrtlevent;
  661. begin
  662. p:=pintrtlevent(aevent);
  663. pthread_mutex_lock(@p^.mutex);
  664. while not p^.isset do pthread_cond_wait(@p^.condvar, @p^.mutex);
  665. p^.isset:=false;
  666. pthread_mutex_unlock(@p^.mutex);
  667. end;
  668. procedure intRTLEventWaitForTimeout(AEvent: PRTLEvent;timeout : longint);
  669. var
  670. p : pintrtlevent;
  671. errres : cint;
  672. timespec : ttimespec;
  673. tnow : timeval;
  674. begin
  675. p:=pintrtlevent(aevent);
  676. fpgettimeofday(@tnow,nil);
  677. timespec.tv_sec:=tnow.tv_sec+(timeout div 1000);
  678. timespec.tv_nsec:=(timeout mod 1000)*1000000 + tnow.tv_usec*1000;
  679. if timespec.tv_nsec >= 1000000000 then
  680. begin
  681. inc(timespec.tv_sec);
  682. dec(timespec.tv_nsec, 1000000000);
  683. end;
  684. errres:=0;
  685. pthread_mutex_lock(@p^.mutex);
  686. while (not p^.isset) and
  687. (errres <> ESysETIMEDOUT) do
  688. begin
  689. errres:=pthread_cond_timedwait(@p^.condvar, @p^.mutex, @timespec);
  690. end;
  691. p^.isset:=false;
  692. pthread_mutex_unlock(@p^.mutex);
  693. end;
  694. type
  695. threadmethod = procedure of object;
  696. Function CInitThreads : Boolean;
  697. begin
  698. {$ifdef DEBUG_MT}
  699. Writeln('Entering InitThreads.');
  700. {$endif}
  701. {$ifndef dynpthreads}
  702. Result:=True;
  703. {$else}
  704. Result:=LoadPthreads;
  705. {$endif}
  706. ThreadID := TThreadID (pthread_self);
  707. {$ifdef DEBUG_MT}
  708. Writeln('InitThreads : ',Result);
  709. {$endif DEBUG_MT}
  710. end;
  711. Function CDoneThreads : Boolean;
  712. begin
  713. {$ifndef dynpthreads}
  714. Result:=True;
  715. {$else}
  716. Result:=UnloadPthreads;
  717. {$endif}
  718. end;
  719. Var
  720. CThreadManager : TThreadManager;
  721. Procedure SetCThreadManager;
  722. begin
  723. With CThreadManager do begin
  724. InitManager :=@CInitThreads;
  725. DoneManager :=@CDoneThreads;
  726. BeginThread :=@CBeginThread;
  727. EndThread :=@CEndThread;
  728. SuspendThread :=@CSuspendThread;
  729. ResumeThread :=@CResumeThread;
  730. KillThread :=@CKillThread;
  731. ThreadSwitch :=@CThreadSwitch;
  732. WaitForThreadTerminate :=@CWaitForThreadTerminate;
  733. ThreadSetPriority :=@CThreadSetPriority;
  734. ThreadGetPriority :=@CThreadGetPriority;
  735. GetCurrentThreadId :=@CGetCurrentThreadId;
  736. InitCriticalSection :=@CInitCriticalSection;
  737. DoneCriticalSection :=@CDoneCriticalSection;
  738. EnterCriticalSection :=@CEnterCriticalSection;
  739. LeaveCriticalSection :=@CLeaveCriticalSection;
  740. InitThreadVar :=@CInitThreadVar;
  741. RelocateThreadVar :=@CRelocateThreadVar;
  742. AllocateThreadVars :=@CAllocateThreadVars;
  743. ReleaseThreadVars :=@CReleaseThreadVars;
  744. BasicEventCreate :=@intBasicEventCreate;
  745. BasicEventDestroy :=@intBasicEventDestroy;
  746. BasicEventResetEvent :=@intBasicEventResetEvent;
  747. BasicEventSetEvent :=@intBasicEventSetEvent;
  748. BasiceventWaitFor :=@intBasiceventWaitFor;
  749. rtlEventCreate :=@intrtlEventCreate;
  750. rtlEventDestroy :=@intrtlEventDestroy;
  751. rtlEventSetEvent :=@intrtlEventSetEvent;
  752. rtlEventResetEvent :=@intrtlEventResetEvent;
  753. rtleventWaitForTimeout :=@intrtleventWaitForTimeout;
  754. rtleventWaitFor :=@intrtleventWaitFor;
  755. // semaphores
  756. SemaphoreInit :=@cSemaphoreInit;
  757. SemaphoreDestroy :=@cSemaphoreDestroy;
  758. SemaphoreWait :=@cSemaphoreWait;
  759. SemaphorePost :=@cSemaphorePost;
  760. end;
  761. SetThreadManager(CThreadManager);
  762. InitHeapMutexes;
  763. end;
  764. initialization
  765. if ThreadingAlreadyUsed then
  766. begin
  767. writeln('Threading has been used before cthreads was initialized.');
  768. writeln('Make cthreads one of the first units in your uses clause.');
  769. runerror(211);
  770. end;
  771. SetCThreadManager;
  772. finalization
  773. end.