cthreads.pp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920
  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(ThreadMain);
  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. { pthread_kill(SIGSTOP) cannot be used, because posix-compliant
  234. implementations then freeze the entire process instead of only
  235. the target thread. Suspending a particular thread is not
  236. supported by posix nor by most *nix implementations, presumably
  237. because of concerns mentioned in E.4 at
  238. http://pauillac.inria.fr/~xleroy/linuxthreads/faq.html#E and in
  239. http://java.sun.com/j2se/1.4.2/docs/guide/misc/threadPrimitiveDeprecation.html
  240. }
  241. // result := pthread_kill(threadHandle,SIGSTOP);
  242. end;
  243. function CResumeThread (threadHandle : TThreadID) : dword;
  244. begin
  245. // result := pthread_kill(threadHandle,SIGCONT);
  246. end;
  247. procedure sched_yield; cdecl; external 'c' name 'sched_yield';
  248. procedure CThreadSwitch; {give time to other threads}
  249. begin
  250. { At least on Mac OS X, the pthread_yield_np calls through to this. }
  251. { Further, sched_yield is in POSIX and supported on FreeBSD 4+, }
  252. { Linux, Mac OS X and Solaris, while the thread-specific yield }
  253. { routines are called differently everywhere and non-standard. }
  254. sched_yield;
  255. end;
  256. function CKillThread (threadHandle : TThreadID) : dword;
  257. begin
  258. pthread_detach(pthread_t(threadHandle));
  259. CKillThread := pthread_cancel(pthread_t(threadHandle));
  260. end;
  261. function CWaitForThreadTerminate (threadHandle : TThreadID; TimeoutMs : longint) : dword; {0=no timeout}
  262. var
  263. LResultP: Pointer;
  264. begin
  265. pthread_join(pthread_t(threadHandle), @LResultP);
  266. CWaitForThreadTerminate := dword(LResultP);
  267. end;
  268. {$warning threadhandle can be larger than a dword}
  269. function CThreadSetPriority (threadHandle : TThreadID; Prio: longint): boolean; {-15..+15, 0=normal}
  270. begin
  271. {$Warning ThreadSetPriority needs to be implemented}
  272. end;
  273. {$warning threadhandle can be larger than a dword}
  274. function CThreadGetPriority (threadHandle : TThreadID): Integer;
  275. begin
  276. {$Warning ThreadGetPriority needs to be implemented}
  277. end;
  278. function CGetCurrentThreadId : TThreadID;
  279. begin
  280. CGetCurrentThreadId := TThreadID (pthread_self());
  281. end;
  282. {*****************************************************************************
  283. Delphi/Win32 compatibility
  284. *****************************************************************************}
  285. procedure CInitCriticalSection(var CS);
  286. var
  287. MAttr : pthread_mutexattr_t;
  288. res: longint;
  289. begin
  290. res:=pthread_mutexattr_init(@MAttr);
  291. if res=0 then
  292. begin
  293. res:=pthread_mutexattr_settype(@MAttr,longint(_PTHREAD_MUTEX_RECURSIVE));
  294. if res=0 then
  295. res := pthread_mutex_init(@CS,@MAttr)
  296. else
  297. { No recursive mutex support :/ }
  298. res := pthread_mutex_init(@CS,NIL);
  299. end
  300. else
  301. res:= pthread_mutex_init(@CS,NIL);
  302. pthread_mutexattr_destroy(@MAttr);
  303. if res <> 0 then
  304. runerror(6);
  305. end;
  306. procedure CEnterCriticalSection(var CS);
  307. begin
  308. if pthread_mutex_lock(@CS) <> 0 then
  309. runerror(6);
  310. end;
  311. procedure CLeaveCriticalSection(var CS);
  312. begin
  313. if pthread_mutex_unlock(@CS) <> 0 then
  314. runerror(6)
  315. end;
  316. procedure CDoneCriticalSection(var CS);
  317. begin
  318. { unlock as long as unlocking works to unlock it if it is recursive
  319. some Delphi code might call this function with a locked mutex }
  320. while pthread_mutex_unlock(@CS)=0 do
  321. ;
  322. if pthread_mutex_destroy(@CS) <> 0 then
  323. runerror(6);
  324. end;
  325. {*****************************************************************************
  326. Semaphore routines
  327. *****************************************************************************}
  328. procedure cSemaphoreWait(const FSem: Pointer);
  329. var
  330. res: cint;
  331. err: cint;
  332. {$if not defined(has_sem_init) and not defined(has_sem_open)}
  333. b: byte;
  334. {$endif}
  335. begin
  336. {$if defined(has_sem_init) or defined(has_sem_open)}
  337. repeat
  338. res:=sem_wait(PSemaphore(FSem));
  339. err:=fpgetCerrno;
  340. until (res<>-1) or (err<>ESysEINTR);
  341. {$else}
  342. repeat
  343. res:=fpread(PFilDes(FSem)^[0], b, 1);
  344. err:=fpgeterrno;
  345. until (res<>-1) or ((err<>ESysEINTR) and (err<>ESysEAgain));
  346. {$endif}
  347. end;
  348. procedure cSemaphorePost(const FSem: Pointer);
  349. {$if defined(has_sem_init) or defined(has_sem_open)}
  350. begin
  351. sem_post(PSemaphore(FSem));
  352. end;
  353. {$else}
  354. var
  355. writeres: cint;
  356. err: cint;
  357. b : byte;
  358. begin
  359. b:=0;
  360. repeat
  361. writeres:=fpwrite(PFilDes(FSem)^[1], b, 1);
  362. err:=fpgeterrno;
  363. until (writeres<>-1) or ((err<>ESysEINTR) and (err<>ESysEAgain));
  364. end;
  365. {$endif}
  366. {$if defined(has_sem_open) and not defined(has_sem_init)}
  367. function cIntSemaphoreOpen(const name: pchar; initvalue: boolean): Pointer;
  368. var
  369. err: cint;
  370. begin
  371. repeat
  372. cIntSemaphoreOpen := sem_open(name,O_CREAT,0,ord(initvalue));
  373. err:=fpgetCerrno;
  374. until (ptrint(cIntSemaphoreOpen) <> SEM_FAILED) or (err <> ESysEINTR);
  375. if (ptrint(cIntSemaphoreOpen) <> SEM_FAILED) then
  376. { immediately unlink so the semaphore will be destroyed when the }
  377. { the process exits }
  378. sem_unlink(name)
  379. else
  380. cIntSemaphoreOpen:=NIL;
  381. end;
  382. {$endif}
  383. function cIntSemaphoreInit(initvalue: boolean): Pointer;
  384. {$if defined(has_sem_open) and not defined(has_sem_init)}
  385. var
  386. tid: string[31];
  387. semname: string[63];
  388. err: cint;
  389. {$endif}
  390. begin
  391. {$ifdef has_sem_init}
  392. cIntSemaphoreInit := GetMem(SizeOf(TSemaphore));
  393. if sem_init(PSemaphore(cIntSemaphoreInit), 0, ord(initvalue)) <> 0 then
  394. begin
  395. FreeMem(cIntSemaphoreInit);
  396. cIntSemaphoreInit:=NIL;
  397. end;
  398. {$else}
  399. {$ifdef has_sem_open}
  400. { avoid a potential temporary nameclash with another process/thread }
  401. str(fpGetPid,semname);
  402. str(ptruint(pthread_self),tid);
  403. semname:='/FPC'+semname+'T'+tid+#0;
  404. cIntSemaphoreInit:=cIntSemaphoreOpen(@semname[1],initvalue);
  405. {$else}
  406. cIntSemaphoreInit := GetMem(SizeOf(TFilDes));
  407. if (fppipe(PFilDes(cIntSemaphoreInit)^) <> 0) then
  408. begin
  409. FreeMem(cIntSemaphoreInit);
  410. cIntSemaphoreInit:=nil;
  411. end
  412. else if initvalue then
  413. cSemaphorePost(cIntSemaphoreInit);
  414. {$endif}
  415. {$endif}
  416. end;
  417. function cSemaphoreInit: Pointer;
  418. begin
  419. cSemaphoreInit:=cIntSemaphoreInit(false);
  420. end;
  421. procedure cSemaphoreDestroy(const FSem: Pointer);
  422. begin
  423. {$ifdef has_sem_init}
  424. sem_destroy(PSemaphore(FSem));
  425. FreeMem(FSem);
  426. {$else}
  427. {$ifdef has_sem_open}
  428. sem_close(PSemaphore(FSem));
  429. {$else has_sem_init}
  430. fpclose(PFilDes(FSem)^[0]);
  431. fpclose(PFilDes(FSem)^[1]);
  432. FreeMem(FSem);
  433. {$endif}
  434. {$endif}
  435. end;
  436. {*****************************************************************************
  437. Heap Mutex Protection
  438. *****************************************************************************}
  439. var
  440. HeapMutex : pthread_mutex_t;
  441. procedure PThreadHeapMutexInit;
  442. begin
  443. pthread_mutex_init(@heapmutex,nil);
  444. end;
  445. procedure PThreadHeapMutexDone;
  446. begin
  447. pthread_mutex_destroy(@heapmutex);
  448. end;
  449. procedure PThreadHeapMutexLock;
  450. begin
  451. pthread_mutex_lock(@heapmutex);
  452. end;
  453. procedure PThreadHeapMutexUnlock;
  454. begin
  455. pthread_mutex_unlock(@heapmutex);
  456. end;
  457. const
  458. PThreadMemoryMutexManager : TMemoryMutexManager = (
  459. MutexInit : @PThreadHeapMutexInit;
  460. MutexDone : @PThreadHeapMutexDone;
  461. MutexLock : @PThreadHeapMutexLock;
  462. MutexUnlock : @PThreadHeapMutexUnlock;
  463. );
  464. procedure InitHeapMutexes;
  465. begin
  466. SetMemoryMutexManager(PThreadMemoryMutexManager);
  467. end;
  468. type
  469. TPthreadMutex = pthread_mutex_t;
  470. Tbasiceventstate=record
  471. FSem: Pointer;
  472. FEventSection: TPthreadMutex;
  473. FManualReset: Boolean;
  474. end;
  475. plocaleventstate = ^tbasiceventstate;
  476. // peventstate=pointer;
  477. Const
  478. wrSignaled = 0;
  479. wrTimeout = 1;
  480. wrAbandoned= 2;
  481. wrError = 3;
  482. function IntBasicEventCreate(EventAttributes : Pointer; AManualReset,InitialState : Boolean;const Name : ansistring):pEventState;
  483. var
  484. MAttr : pthread_mutexattr_t;
  485. res : cint;
  486. begin
  487. new(plocaleventstate(result));
  488. plocaleventstate(result)^.FManualReset:=AManualReset;
  489. {$ifdef has_sem_init}
  490. plocaleventstate(result)^.FSem:=cIntSemaphoreInit(true);
  491. if plocaleventstate(result)^.FSem=nil then
  492. begin
  493. FreeMem(result);
  494. runerror(6);
  495. end;
  496. {$else}
  497. {$ifdef has_sem_open}
  498. plocaleventstate(result)^.FSem:=cIntSemaphoreOpen(PChar(Name),InitialState);
  499. if (plocaleventstate(result)^.FSem = NIL) then
  500. begin
  501. FreeMem(result);
  502. runerror(6);
  503. end;
  504. {$else}
  505. plocaleventstate(result)^.FSem:=cSemaphoreInit;
  506. if (plocaleventstate(result)^.FSem = NIL) then
  507. begin
  508. FreeMem(result);
  509. runerror(6);
  510. end;
  511. if InitialState then
  512. cSemaphorePost(plocaleventstate(result)^.FSem);
  513. {$endif}
  514. {$endif}
  515. // plocaleventstate(result)^.feventsection:=nil;
  516. res:=pthread_mutexattr_init(@MAttr);
  517. if res=0 then
  518. begin
  519. res:=pthread_mutexattr_settype(@MAttr,longint(_PTHREAD_MUTEX_RECURSIVE));
  520. if Res=0 then
  521. Res:=pthread_mutex_init(@plocaleventstate(result)^.feventsection,@MAttr)
  522. else
  523. res:=pthread_mutex_init(@plocaleventstate(result)^.feventsection,nil);
  524. end
  525. else
  526. res:=pthread_mutex_init(@plocaleventstate(result)^.feventsection,nil);
  527. pthread_mutexattr_destroy(@MAttr);
  528. if res <> 0 then
  529. begin
  530. cSemaphoreDestroy(plocaleventstate(result)^.FSem);
  531. FreeMem(result);
  532. runerror(6);
  533. end;
  534. end;
  535. procedure Intbasiceventdestroy(state:peventstate);
  536. begin
  537. cSemaphoreDestroy(plocaleventstate(state)^.FSem);
  538. FreeMem(state);
  539. end;
  540. procedure IntbasiceventResetEvent(state:peventstate);
  541. {$if defined(has_sem_init) or defined(has_sem_open)}
  542. var
  543. res: cint;
  544. err: cint;
  545. begin
  546. repeat
  547. res:=sem_trywait(psem_t(plocaleventstate(state)^.FSem));
  548. err:=fpgeterrno;
  549. until (res<>0) and ((res<>-1) or (err<>ESysEINTR));
  550. {$else has_sem_init or has_sem_open}
  551. var
  552. fds: TFDSet;
  553. tv : timeval;
  554. begin
  555. tv.tv_sec:=0;
  556. tv.tv_usec:=0;
  557. fpFD_ZERO(fds);
  558. fpFD_SET(PFilDes(plocaleventstate(state)^.FSem)^[0],fds);
  559. pthread_mutex_lock(@plocaleventstate(state)^.feventsection);
  560. Try
  561. while fpselect(PFilDes(plocaleventstate(state)^.FSem)^[0],@fds,nil,nil,@tv) > 0 do
  562. cSemaphoreWait(plocaleventstate(state)^.FSem);
  563. finally
  564. pthread_mutex_unlock(@plocaleventstate(state)^.feventsection);
  565. end;
  566. {$endif has_sem_init or has_sem_open}
  567. end;
  568. procedure IntbasiceventSetEvent(state:peventstate);
  569. Var
  570. {$if defined(has_sem_init) or defined(has_sem_open)}
  571. Value : Longint;
  572. res : cint;
  573. err : cint;
  574. {$else}
  575. fds: TFDSet;
  576. tv : timeval;
  577. {$endif}
  578. begin
  579. pthread_mutex_lock(@plocaleventstate(state)^.feventsection);
  580. Try
  581. {$if defined(has_sem_init) or defined(has_sem_open)}
  582. if (sem_getvalue(plocaleventstate(state)^.FSem,@value) <> -1) then
  583. begin
  584. if Value=0 then
  585. cSemaphorePost(plocaleventstate(state)^.FSem);
  586. end
  587. else if (fpgetCerrno = ESysENOSYS) then
  588. { not yet implemented on Mac OS X 10.4.8 }
  589. begin
  590. repeat
  591. res:=sem_trywait(psem_t(plocaleventstate(state)^.FSem));
  592. err:=fpgetCerrno;
  593. until ((res<>-1) or (err<>ESysEINTR));
  594. { now we've either decreased the semaphore by 1 (if it was }
  595. { not zero), or we've done nothing (if it was already zero) }
  596. { -> increase by 1 and we have the same result as }
  597. { increasing by 1 only if it was 0 }
  598. cSemaphorePost(plocaleventstate(state)^.FSem);
  599. end
  600. else
  601. runerror(6);
  602. {$else has_sem_init or has_sem_open}
  603. tv.tv_sec:=0;
  604. tv.tv_usec:=0;
  605. fpFD_ZERO(fds);
  606. fpFD_SET(PFilDes(plocaleventstate(state)^.FSem)^[0],fds);
  607. if fpselect(PFilDes(plocaleventstate(state)^.FSem)^[0],@fds,nil,nil,@tv)=0 then
  608. cSemaphorePost(plocaleventstate(state)^.FSem);
  609. {$endif has_sem_init or has_sem_open}
  610. finally
  611. pthread_mutex_unlock(@plocaleventstate(state)^.feventsection);
  612. end;
  613. end;
  614. function IntbasiceventWaitFor(Timeout : Cardinal;state:peventstate) : longint;
  615. begin
  616. If TimeOut<>Cardinal($FFFFFFFF) then
  617. result:=wrError
  618. else
  619. begin
  620. cSemaphoreWait(plocaleventstate(state)^.FSem);
  621. result:=wrSignaled;
  622. if plocaleventstate(state)^.FManualReset then
  623. begin
  624. pthread_mutex_lock(@plocaleventstate(state)^.feventsection);
  625. Try
  626. intbasiceventresetevent(State);
  627. cSemaphorePost(plocaleventstate(state)^.FSem);
  628. Finally
  629. pthread_mutex_unlock(@plocaleventstate(state)^.feventsection);
  630. end;
  631. end;
  632. end;
  633. end;
  634. function intRTLEventCreate: PRTLEvent;
  635. var p:pintrtlevent;
  636. begin
  637. new(p);
  638. pthread_cond_init(@p^.condvar, nil);
  639. pthread_mutex_init(@p^.mutex, nil);
  640. p^.isset:=false;
  641. result:=PRTLEVENT(p);
  642. end;
  643. procedure intRTLEventDestroy(AEvent: PRTLEvent);
  644. var p:pintrtlevent;
  645. begin
  646. p:=pintrtlevent(aevent);
  647. pthread_cond_destroy(@p^.condvar);
  648. pthread_mutex_destroy(@p^.mutex);
  649. dispose(p);
  650. end;
  651. procedure intRTLEventSetEvent(AEvent: PRTLEvent);
  652. var p:pintrtlevent;
  653. begin
  654. p:=pintrtlevent(aevent);
  655. pthread_mutex_lock(@p^.mutex);
  656. p^.isset:=true;
  657. pthread_cond_signal(@p^.condvar);
  658. pthread_mutex_unlock(@p^.mutex);
  659. end;
  660. procedure intRTLEventResetEvent(AEvent: PRTLEvent);
  661. var p:pintrtlevent;
  662. begin
  663. p:=pintrtlevent(aevent);
  664. pthread_mutex_lock(@p^.mutex);
  665. p^.isset:=false;
  666. pthread_mutex_unlock(@p^.mutex);
  667. end;
  668. procedure intRTLEventWaitFor(AEvent: PRTLEvent);
  669. var p:pintrtlevent;
  670. begin
  671. p:=pintrtlevent(aevent);
  672. pthread_mutex_lock(@p^.mutex);
  673. while not p^.isset do pthread_cond_wait(@p^.condvar, @p^.mutex);
  674. p^.isset:=false;
  675. pthread_mutex_unlock(@p^.mutex);
  676. end;
  677. procedure intRTLEventWaitForTimeout(AEvent: PRTLEvent;timeout : longint);
  678. var
  679. p : pintrtlevent;
  680. errres : cint;
  681. timespec : ttimespec;
  682. tnow : timeval;
  683. begin
  684. p:=pintrtlevent(aevent);
  685. fpgettimeofday(@tnow,nil);
  686. timespec.tv_sec:=tnow.tv_sec+(timeout div 1000);
  687. timespec.tv_nsec:=(timeout mod 1000)*1000000 + tnow.tv_usec*1000;
  688. if timespec.tv_nsec >= 1000000000 then
  689. begin
  690. inc(timespec.tv_sec);
  691. dec(timespec.tv_nsec, 1000000000);
  692. end;
  693. errres:=0;
  694. pthread_mutex_lock(@p^.mutex);
  695. while (not p^.isset) and
  696. (errres <> ESysETIMEDOUT) do
  697. begin
  698. errres:=pthread_cond_timedwait(@p^.condvar, @p^.mutex, @timespec);
  699. end;
  700. p^.isset:=false;
  701. pthread_mutex_unlock(@p^.mutex);
  702. end;
  703. type
  704. threadmethod = procedure of object;
  705. Function CInitThreads : Boolean;
  706. begin
  707. {$ifdef DEBUG_MT}
  708. Writeln('Entering InitThreads.');
  709. {$endif}
  710. {$ifndef dynpthreads}
  711. Result:=True;
  712. {$else}
  713. Result:=LoadPthreads;
  714. {$endif}
  715. ThreadID := TThreadID (pthread_self);
  716. {$ifdef DEBUG_MT}
  717. Writeln('InitThreads : ',Result);
  718. {$endif DEBUG_MT}
  719. end;
  720. Function CDoneThreads : Boolean;
  721. begin
  722. {$ifndef dynpthreads}
  723. Result:=True;
  724. {$else}
  725. Result:=UnloadPthreads;
  726. {$endif}
  727. end;
  728. Var
  729. CThreadManager : TThreadManager;
  730. Procedure SetCThreadManager;
  731. begin
  732. With CThreadManager do begin
  733. InitManager :=@CInitThreads;
  734. DoneManager :=@CDoneThreads;
  735. BeginThread :=@CBeginThread;
  736. EndThread :=@CEndThread;
  737. SuspendThread :=@CSuspendThread;
  738. ResumeThread :=@CResumeThread;
  739. KillThread :=@CKillThread;
  740. ThreadSwitch :=@CThreadSwitch;
  741. WaitForThreadTerminate :=@CWaitForThreadTerminate;
  742. ThreadSetPriority :=@CThreadSetPriority;
  743. ThreadGetPriority :=@CThreadGetPriority;
  744. GetCurrentThreadId :=@CGetCurrentThreadId;
  745. InitCriticalSection :=@CInitCriticalSection;
  746. DoneCriticalSection :=@CDoneCriticalSection;
  747. EnterCriticalSection :=@CEnterCriticalSection;
  748. LeaveCriticalSection :=@CLeaveCriticalSection;
  749. InitThreadVar :=@CInitThreadVar;
  750. RelocateThreadVar :=@CRelocateThreadVar;
  751. AllocateThreadVars :=@CAllocateThreadVars;
  752. ReleaseThreadVars :=@CReleaseThreadVars;
  753. BasicEventCreate :=@intBasicEventCreate;
  754. BasicEventDestroy :=@intBasicEventDestroy;
  755. BasicEventResetEvent :=@intBasicEventResetEvent;
  756. BasicEventSetEvent :=@intBasicEventSetEvent;
  757. BasiceventWaitFor :=@intBasiceventWaitFor;
  758. rtlEventCreate :=@intrtlEventCreate;
  759. rtlEventDestroy :=@intrtlEventDestroy;
  760. rtlEventSetEvent :=@intrtlEventSetEvent;
  761. rtlEventResetEvent :=@intrtlEventResetEvent;
  762. rtleventWaitForTimeout :=@intrtleventWaitForTimeout;
  763. rtleventWaitFor :=@intrtleventWaitFor;
  764. // semaphores
  765. SemaphoreInit :=@cSemaphoreInit;
  766. SemaphoreDestroy :=@cSemaphoreDestroy;
  767. SemaphoreWait :=@cSemaphoreWait;
  768. SemaphorePost :=@cSemaphorePost;
  769. end;
  770. SetThreadManager(CThreadManager);
  771. InitHeapMutexes;
  772. end;
  773. initialization
  774. if ThreadingAlreadyUsed then
  775. begin
  776. writeln('Threading has been used before cthreads was initialized.');
  777. writeln('Make cthreads one of the first units in your uses clause.');
  778. runerror(211);
  779. end;
  780. SetCThreadManager;
  781. finalization
  782. end.