cthreads.pp 31 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073
  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. 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. { we can combine both compile-time linking and dynamic loading, in order to:
  15. a) solve a problem on some systems with dynamically loading libpthread if
  16. it's not linked at compile time
  17. b) still enabling dynamically checking whether or not certain functions
  18. are available (could also be implemented via weak linking)
  19. }
  20. {$linklib pthread}
  21. {$define dynpthreads} // Useless on BSD, since they are in libc
  22. {$endif}
  23. { sem_init is best, since it does not consume any file descriptors. }
  24. { sem_open is second best, since it consumes only one file descriptor }
  25. { per semaphore. }
  26. { If neither is available, pipe is used as fallback, which consumes 2 }
  27. { file descriptors per semaphore. }
  28. { Darwin doesn't support nameless semaphores in at least }
  29. { Mac OS X 10.4.8/Darwin 8.8 }
  30. {$if not defined(darwin) and not defined(iphonesim)}
  31. {$define has_sem_init}
  32. {$define has_sem_getvalue}
  33. {$else }
  34. {$if defined(darwin) or defined(iphonesim)}
  35. {$define has_sem_open}
  36. {$endif}
  37. {$endif}
  38. {$if defined(linux) or defined(aix) or defined(android)}
  39. {$define has_sem_timedwait}
  40. {$endif}
  41. unit cthreads;
  42. interface
  43. {$S-}
  44. {$ifndef dynpthreads} // If you have problems compiling this on FreeBSD 5.x
  45. {$linklib c} // try adding -Xf
  46. {$if not defined(Darwin) and not defined(iphonesim) and not defined(Android)}
  47. {$ifndef haiku}
  48. {$linklib pthread}
  49. {$endif haiku}
  50. {$endif darwin}
  51. {$endif}
  52. {$define basicevents_with_pthread_cond}
  53. Procedure SetCThreadManager;
  54. implementation
  55. Uses
  56. BaseUnix,
  57. unix,
  58. unixtype,
  59. initc
  60. {$ifdef dynpthreads}
  61. ,dl
  62. {$endif}
  63. ;
  64. {*****************************************************************************
  65. System unit import
  66. *****************************************************************************}
  67. procedure fpc_threaderror; [external name 'FPC_THREADERROR'];
  68. {*****************************************************************************
  69. Generic overloaded
  70. *****************************************************************************}
  71. { Include OS specific parts. }
  72. {$i pthread.inc}
  73. Type PINTRTLEvent = ^TINTRTLEvent;
  74. TINTRTLEvent = record
  75. condvar: pthread_cond_t;
  76. mutex: pthread_mutex_t;
  77. isset: boolean;
  78. end;
  79. TTryWaitResult = (tw_error, tw_semwasunlocked, tw_semwaslocked);
  80. {*****************************************************************************
  81. Threadvar support
  82. *****************************************************************************}
  83. const
  84. threadvarblocksize : dword = 0;
  85. var
  86. TLSKey,
  87. CleanupKey : pthread_key_t;
  88. procedure CInitThreadvar(var offset : dword;size : dword);
  89. begin
  90. {$ifdef cpusparc}
  91. threadvarblocksize:=align(threadvarblocksize,16);
  92. {$endif cpusparc}
  93. {$ifdef cpupowerpc}
  94. threadvarblocksize:=align(threadvarblocksize,8);
  95. {$endif cpupowerc}
  96. {$ifdef cpui386}
  97. threadvarblocksize:=align(threadvarblocksize,8);
  98. {$endif cpui386}
  99. {$ifdef cpuarm}
  100. threadvarblocksize:=align(threadvarblocksize,4);
  101. {$endif cpuarm}
  102. {$ifdef cpum68k}
  103. threadvarblocksize:=align(threadvarblocksize,2);
  104. {$endif cpum68k}
  105. {$ifdef cpux86_64}
  106. threadvarblocksize:=align(threadvarblocksize,16);
  107. {$endif cpux86_64}
  108. {$ifdef cpupowerpc64}
  109. threadvarblocksize:=align(threadvarblocksize,16);
  110. {$endif cpupowerpc64}
  111. offset:=threadvarblocksize;
  112. inc(threadvarblocksize,size);
  113. end;
  114. procedure CAllocateThreadVars;
  115. var
  116. dataindex : pointer;
  117. begin
  118. { we've to allocate the memory from system }
  119. { because the FPC heap management uses }
  120. { exceptions which use threadvars but }
  121. { these aren't allocated yet ... }
  122. { allocate room on the heap for the thread vars }
  123. DataIndex:=Pointer(Fpmmap(nil,threadvarblocksize,3,MAP_PRIVATE+MAP_ANONYMOUS,-1,0));
  124. FillChar(DataIndex^,threadvarblocksize,0);
  125. pthread_setspecific(tlskey,dataindex);
  126. end;
  127. procedure CthreadCleanup(p: pointer); cdecl;
  128. {$ifdef DEBUG_MT}
  129. var
  130. s: string[100]; // not an ansistring
  131. {$endif DEBUG_MT}
  132. begin
  133. {$ifdef DEBUG_MT}
  134. s := 'finishing externally started thread'#10;
  135. fpwrite(0,s[1],length(s));
  136. {$endif DEBUG_MT}
  137. { Restore tlskey value as it may already have been set to null,
  138. in which case
  139. a) DoneThread can't release the memory
  140. b) accesses to threadvars from DoneThread or anything it
  141. calls would allocate new threadvar memory
  142. }
  143. pthread_setspecific(tlskey,p);
  144. { clean up }
  145. DoneThread;
  146. { the pthread routine that calls us is supposed to do this, but doesn't
  147. at least on Mac OS X 10.6 }
  148. pthread_setspecific(CleanupKey,nil);
  149. pthread_setspecific(tlskey,nil);
  150. end;
  151. procedure HookThread;
  152. begin
  153. { Allocate local thread vars, this must be the first thing,
  154. because the exception management and io depends on threadvars }
  155. CAllocateThreadVars;
  156. { we cannot know the stack size of the current thread, so pretend it
  157. is really large to prevent spurious stack overflow errors }
  158. InitThread(1000000000);
  159. { instruct the pthreads system to clean up this thread when it exits.
  160. Use current tlskey as value so that if tlskey is cleared before
  161. CleanupKey is called, we still know its value (the order in which
  162. pthread tls data is zeroed by pthreads is undefined, and under some
  163. systems the tlskey is cleared first) }
  164. pthread_setspecific(CleanupKey,pthread_getspecific(tlskey));
  165. end;
  166. function CRelocateThreadvar(offset : dword) : pointer;
  167. var
  168. P : Pointer;
  169. begin
  170. P:=pthread_getspecific(tlskey);
  171. { a thread which we did not create? }
  172. if (P=Nil) then
  173. begin
  174. HookThread;
  175. // If this also goes wrong: bye bye threadvars...
  176. P:=pthread_getspecific(tlskey);
  177. end;
  178. CRelocateThreadvar:=P+Offset;
  179. end;
  180. procedure CReleaseThreadVars;
  181. begin
  182. Fpmunmap(pointer(pthread_getspecific(tlskey)),threadvarblocksize);
  183. end;
  184. { Include OS independent Threadvar initialization }
  185. {*****************************************************************************
  186. Thread starting
  187. *****************************************************************************}
  188. type
  189. pthreadinfo = ^tthreadinfo;
  190. tthreadinfo = record
  191. f : tthreadfunc;
  192. p : pointer;
  193. stklen : cardinal;
  194. end;
  195. function ThreadMain(param : pointer) : pointer;cdecl;
  196. var
  197. ti : tthreadinfo;
  198. nset: tsigset;
  199. {$if defined(linux) and not defined(FPC_USE_LIBC)}
  200. nlibcset: tlibc_sigset;
  201. {$endif linux/no FPC_USE_LIBC}
  202. {$ifdef DEBUG_MT}
  203. // in here, don't use write/writeln before having called
  204. // InitThread! I wonder if anyone ever debugged these routines,
  205. // because they will have crashed if DEBUG_MT was enabled!
  206. // this took me the good part of an hour to figure out
  207. // why it was crashing all the time!
  208. // this is kind of a workaround, we simply write(2) to fd 0
  209. s: string[100]; // not an ansistring
  210. {$endif DEBUG_MT}
  211. begin
  212. {$ifdef DEBUG_MT}
  213. s := 'New thread started, initing threadvars'#10;
  214. fpwrite(0,s[1],length(s));
  215. {$endif DEBUG_MT}
  216. { unblock all signals we are interested in (may be blocked by }
  217. { default in new threads on some OSes, see #9073) }
  218. fpsigemptyset(nset);
  219. fpsigaddset(nset,SIGSEGV);
  220. fpsigaddset(nset,SIGBUS);
  221. fpsigaddset(nset,SIGFPE);
  222. fpsigaddset(nset,SIGILL);
  223. {$if defined(linux) and not defined(FPC_USE_LIBC)}
  224. { sigset_t has a different size for linux/kernel and linux/libc }
  225. fillchar(nlibcset,sizeof(nlibcset),0);
  226. if (sizeof(nlibcset)>sizeof(nset)) then
  227. move(nset,nlibcset,sizeof(nset))
  228. else
  229. move(nset,nlibcset,sizeof(nlibcset));
  230. pthread_sigmask(SIG_UNBLOCK,@nlibcset,nil);
  231. {$else linux}
  232. pthread_sigmask(SIG_UNBLOCK,@nset,nil);
  233. {$endif linux}
  234. { Allocate local thread vars, this must be the first thing,
  235. because the exception management and io depends on threadvars }
  236. CAllocateThreadVars;
  237. { Copy parameter to local data }
  238. {$ifdef DEBUG_MT}
  239. s := 'New thread started, initialising ...'#10;
  240. fpwrite(0,s[1],length(s));
  241. {$endif DEBUG_MT}
  242. ti:=pthreadinfo(param)^;
  243. dispose(pthreadinfo(param));
  244. { Initialize thread }
  245. InitThread(ti.stklen);
  246. { Start thread function }
  247. {$ifdef DEBUG_MT}
  248. writeln('Jumping to thread function');
  249. {$endif DEBUG_MT}
  250. ThreadMain:=pointer(ti.f(ti.p));
  251. DoneThread;
  252. pthread_exit(ThreadMain);
  253. end;
  254. Procedure InitCThreading;
  255. begin
  256. if (InterLockedExchange(longint(IsMultiThread),ord(true)) = 0) then
  257. begin
  258. { We're still running in single thread mode, setup the TLS }
  259. pthread_key_create(@TLSKey,nil);
  260. InitThreadVars(@CRelocateThreadvar);
  261. { used to clean up threads that we did not create ourselves:
  262. a) the default value for a key (and hence also this one) in
  263. new threads is NULL, and if it's still like that when the
  264. thread terminates, nothing will happen
  265. b) if it's non-NULL, the destructor routine will be called
  266. when the thread terminates
  267. -> we will set it to 1 if the threadvar relocation routine is
  268. called from a thread we did not create, so that we can
  269. clean up everything at the end }
  270. pthread_key_create(@CleanupKey,@CthreadCleanup);
  271. end
  272. end;
  273. function CBeginThread(sa : Pointer;stacksize : PtrUInt;
  274. ThreadFunction : tthreadfunc;p : pointer;
  275. creationFlags : dword; var ThreadId : TThreadId) : TThreadID;
  276. var
  277. ti : pthreadinfo;
  278. thread_attr : pthread_attr_t;
  279. begin
  280. {$ifdef DEBUG_MT}
  281. writeln('Creating new thread');
  282. {$endif DEBUG_MT}
  283. { Initialize multithreading if not done }
  284. if not IsMultiThread then
  285. InitCThreading;
  286. { the only way to pass data to the newly created thread
  287. in a MT safe way, is to use the heap }
  288. new(ti);
  289. ti^.f:=ThreadFunction;
  290. ti^.p:=p;
  291. ti^.stklen:=stacksize;
  292. { call pthread_create }
  293. {$ifdef DEBUG_MT}
  294. writeln('Starting new thread');
  295. {$endif DEBUG_MT}
  296. pthread_attr_init(@thread_attr);
  297. {$if not defined(HAIKU) and not defined(ANDROID)}
  298. {$if defined (solaris) or defined (netbsd) }
  299. pthread_attr_setinheritsched(@thread_attr, PTHREAD_INHERIT_SCHED);
  300. {$else not solaris}
  301. pthread_attr_setinheritsched(@thread_attr, PTHREAD_EXPLICIT_SCHED);
  302. {$endif not solaris}
  303. {$ifend}
  304. // will fail under linux -- apparently unimplemented
  305. pthread_attr_setscope(@thread_attr, PTHREAD_SCOPE_PROCESS);
  306. // don't create detached, we need to be able to join (waitfor) on
  307. // the newly created thread!
  308. //pthread_attr_setdetachstate(@thread_attr, PTHREAD_CREATE_DETACHED);
  309. // set the stack size
  310. if (pthread_attr_setstacksize(@thread_attr, stacksize)<>0) or
  311. // and create the thread
  312. (pthread_create(ppthread_t(@threadid), @thread_attr, @ThreadMain,ti) <> 0) then
  313. begin
  314. dispose(ti);
  315. threadid := TThreadID(0);
  316. end;
  317. CBeginThread:=threadid;
  318. pthread_attr_destroy(@thread_attr);
  319. {$ifdef DEBUG_MT}
  320. writeln('BeginThread returning ',ptrint(CBeginThread));
  321. {$endif DEBUG_MT}
  322. end;
  323. procedure CEndThread(ExitCode : DWord);
  324. begin
  325. DoneThread;
  326. pthread_detach(pthread_t(pthread_self()));
  327. pthread_exit(pointer(ptrint(ExitCode)));
  328. end;
  329. function CSuspendThread (threadHandle : TThreadID) : dword;
  330. begin
  331. { pthread_kill(SIGSTOP) cannot be used, because posix-compliant
  332. implementations then freeze the entire process instead of only
  333. the target thread. Suspending a particular thread is not
  334. supported by posix nor by most *nix implementations, presumably
  335. because of concerns mentioned in E.4 at
  336. http://pauillac.inria.fr/~xleroy/linuxthreads/faq.html#E and in
  337. http://java.sun.com/j2se/1.4.2/docs/guide/misc/threadPrimitiveDeprecation.html
  338. }
  339. // result := pthread_kill(threadHandle,SIGSTOP);
  340. result:=dword(-1);
  341. end;
  342. function CResumeThread (threadHandle : TThreadID) : dword;
  343. begin
  344. result:=dword(-1);
  345. end;
  346. procedure sched_yield; cdecl; external 'c' name 'sched_yield';
  347. procedure CThreadSwitch; {give time to other threads}
  348. begin
  349. { At least on Mac OS X, the pthread_yield_np calls through to this. }
  350. { Further, sched_yield is in POSIX and supported on FreeBSD 4+, }
  351. { Linux, Mac OS X and Solaris, while the thread-specific yield }
  352. { routines are called differently everywhere and non-standard. }
  353. sched_yield;
  354. end;
  355. function CKillThread (threadHandle : TThreadID) : dword;
  356. begin
  357. pthread_detach(pthread_t(threadHandle));
  358. {$ifndef android}
  359. CKillThread := pthread_cancel(pthread_t(threadHandle));
  360. {$else}
  361. CKillThread := dword(-1);
  362. {$endif}
  363. end;
  364. function CCloseThread (threadHandle : TThreadID) : dword;
  365. begin
  366. result:=0;
  367. end;
  368. function CWaitForThreadTerminate (threadHandle : TThreadID; TimeoutMs : longint) : dword; {0=no timeout}
  369. var
  370. LResultP: Pointer;
  371. begin
  372. pthread_join(pthread_t(threadHandle), @LResultP);
  373. CWaitForThreadTerminate := dword(LResultP);
  374. end;
  375. function CThreadSetPriority (threadHandle : TThreadID; Prio: longint): boolean; {-15..+15, 0=normal}
  376. begin
  377. {$Warning ThreadSetPriority needs to be implemented}
  378. result:=false;
  379. end;
  380. function CThreadGetPriority (threadHandle : TThreadID): Integer;
  381. begin
  382. {$Warning ThreadGetPriority needs to be implemented}
  383. result:=0;
  384. end;
  385. function CGetCurrentThreadId : TThreadID;
  386. begin
  387. CGetCurrentThreadId := TThreadID (pthread_self());
  388. end;
  389. {*****************************************************************************
  390. Delphi/Win32 compatibility
  391. *****************************************************************************}
  392. procedure CInitCriticalSection(var CS);
  393. var
  394. MAttr : pthread_mutexattr_t;
  395. res: longint;
  396. begin
  397. res:=pthread_mutexattr_init(@MAttr);
  398. if res=0 then
  399. begin
  400. res:=pthread_mutexattr_settype(@MAttr,longint(_PTHREAD_MUTEX_RECURSIVE));
  401. if res=0 then
  402. res := pthread_mutex_init(@CS,@MAttr)
  403. else
  404. { No recursive mutex support :/ }
  405. fpc_threaderror
  406. end
  407. else
  408. res:= pthread_mutex_init(@CS,NIL);
  409. pthread_mutexattr_destroy(@MAttr);
  410. if res <> 0 then
  411. fpc_threaderror;
  412. end;
  413. procedure CEnterCriticalSection(var CS);
  414. begin
  415. if pthread_mutex_lock(@CS) <> 0 then
  416. fpc_threaderror
  417. end;
  418. function CTryEnterCriticalSection(var CS):longint;
  419. begin
  420. if pthread_mutex_Trylock(@CS)=0 then
  421. result:=1 // succes
  422. else
  423. result:=0; // failure
  424. end;
  425. procedure CLeaveCriticalSection(var CS);
  426. begin
  427. if pthread_mutex_unlock(@CS) <> 0 then
  428. fpc_threaderror
  429. end;
  430. procedure CDoneCriticalSection(var CS);
  431. begin
  432. { unlock as long as unlocking works to unlock it if it is recursive
  433. some Delphi code might call this function with a locked mutex }
  434. while pthread_mutex_unlock(@CS)=0 do
  435. ;
  436. if pthread_mutex_destroy(@CS) <> 0 then
  437. fpc_threaderror;
  438. end;
  439. {*****************************************************************************
  440. Semaphore routines
  441. *****************************************************************************}
  442. procedure cSemaphoreWait(const FSem: Pointer);
  443. var
  444. res: cint;
  445. err: cint;
  446. {$if not defined(has_sem_init) and not defined(has_sem_open)}
  447. b: byte;
  448. {$endif}
  449. begin
  450. {$if defined(has_sem_init) or defined(has_sem_open)}
  451. repeat
  452. res:=sem_wait(PSemaphore(FSem));
  453. err:=fpgetCerrno;
  454. until (res<>-1) or (err<>ESysEINTR);
  455. {$else}
  456. repeat
  457. res:=fpread(PFilDes(FSem)^[0], b, 1);
  458. err:=fpgeterrno;
  459. until (res<>-1) or ((err<>ESysEINTR) and (err<>ESysEAgain));
  460. {$endif}
  461. end;
  462. {$if defined(has_sem_timedwait)}
  463. function cSemaphoreTimedWait(const FSem: Pointer; const Timeout: ttimespec): cint;
  464. var
  465. res: cint;
  466. err: cint;
  467. begin
  468. repeat
  469. res:=sem_timedwait(PSemaphore(FSem), @Timeout);
  470. if res=0 then exit(0);
  471. err:=fpgetCerrno;
  472. until err<>ESysEINTR;
  473. result:=err;
  474. end;
  475. {$endif}
  476. procedure cSemaphorePost(const FSem: Pointer);
  477. {$if defined(has_sem_init) or defined(has_sem_open)}
  478. begin
  479. sem_post(PSemaphore(FSem));
  480. end;
  481. {$else}
  482. var
  483. writeres: cint;
  484. err: cint;
  485. b : byte;
  486. begin
  487. b:=0;
  488. repeat
  489. writeres:=fpwrite(PFilDes(FSem)^[1], b, 1);
  490. err:=fpgeterrno;
  491. until (writeres<>-1) or ((err<>ESysEINTR) and (err<>ESysEAgain));
  492. end;
  493. {$endif}
  494. function cSemaphoreTryWait(const FSem: pointer): TTryWaitResult;
  495. var
  496. res: cint;
  497. err: cint;
  498. {$if defined(has_sem_init) or defined(has_sem_open)}
  499. begin
  500. repeat
  501. res:=sem_trywait(FSem);
  502. err:=fpgetCerrno;
  503. until (res<>-1) or (err<>ESysEINTR);
  504. if (res=0) then
  505. result:=tw_semwasunlocked
  506. else if (err=ESysEAgain) then
  507. result:=tw_semwaslocked
  508. else
  509. result:=tw_error;
  510. {$else has_sem_init or has_sem_open}
  511. var
  512. fds: TFDSet;
  513. tv : timeval;
  514. begin
  515. tv.tv_sec:=0;
  516. tv.tv_usec:=0;
  517. fpFD_ZERO(fds);
  518. fpFD_SET(PFilDes(FSem)^[0],fds);
  519. repeat
  520. res:=fpselect(PFilDes(FSem)^[0]+1,@fds,nil,nil,@tv);
  521. err:=fpgeterrno;
  522. until (res>=0) or ((res=-1) and (err<>ESysEIntr));
  523. if (res>0) then
  524. begin
  525. cSemaphoreWait(FSem);
  526. result:=tw_semwasunlocked
  527. end
  528. else if (res=0) then
  529. result:=tw_semwaslocked
  530. else
  531. result:=tw_error;
  532. {$endif has_sem_init or has_sem_open}
  533. end;
  534. {$if defined(has_sem_open) and not defined(has_sem_init)}
  535. function cIntSemaphoreOpen(const name: pchar; initvalue: boolean): Pointer;
  536. var
  537. err: cint;
  538. begin
  539. repeat
  540. cIntSemaphoreOpen := sem_open(name,O_CREAT,0,ord(initvalue));
  541. err:=fpgetCerrno;
  542. until (ptrint(cIntSemaphoreOpen) <> SEM_FAILED) or (err <> ESysEINTR);
  543. if (ptrint(cIntSemaphoreOpen) <> SEM_FAILED) then
  544. { immediately unlink so the semaphore will be destroyed when the }
  545. { the process exits }
  546. sem_unlink(name)
  547. else
  548. { 0 is a valid for sem_open on some platforms; pointer(-1) shouldn't
  549. be valid anywhere, even for sem_init }
  550. cIntSemaphoreOpen:=pointer(-1);
  551. end;
  552. {$endif}
  553. function cIntSemaphoreInit(initvalue: boolean): Pointer;
  554. {$if defined(has_sem_open) and not defined(has_sem_init)}
  555. var
  556. tid: string[31];
  557. semname: string[63];
  558. {$endif}
  559. begin
  560. {$ifdef has_sem_init}
  561. cIntSemaphoreInit := GetMem(SizeOf(TSemaphore));
  562. if sem_init(PSemaphore(cIntSemaphoreInit), 0, ord(initvalue)) <> 0 then
  563. begin
  564. FreeMem(cIntSemaphoreInit);
  565. { 0 is a valid for sem_open on some platforms; pointer(-1) shouldn't
  566. be valid anywhere, even for sem_init }
  567. cIntSemaphoreInit:=pointer(-1);
  568. end;
  569. {$else}
  570. {$ifdef has_sem_open}
  571. { avoid a potential temporary nameclash with another process/thread }
  572. str(fpGetPid,semname);
  573. str(ptruint(pthread_self()),tid);
  574. semname:='/FPC'+semname+'T'+tid+#0;
  575. cIntSemaphoreInit:=cIntSemaphoreOpen(@semname[1],initvalue);
  576. {$else}
  577. cIntSemaphoreInit := GetMem(SizeOf(TFilDes));
  578. if (fppipe(PFilDes(cIntSemaphoreInit)^) <> 0) then
  579. begin
  580. FreeMem(cIntSemaphoreInit);
  581. { 0 is a valid for sem_open on some platforms; pointer(-1) shouldn't
  582. be valid anywhere, even for sem_init }
  583. cIntSemaphoreInit:=pointer(-1);
  584. end
  585. else if initvalue then
  586. cSemaphorePost(cIntSemaphoreInit);
  587. {$endif}
  588. {$endif}
  589. end;
  590. function cSemaphoreInit: Pointer;
  591. begin
  592. cSemaphoreInit:=cIntSemaphoreInit(false);
  593. end;
  594. procedure cSemaphoreDestroy(const FSem: Pointer);
  595. begin
  596. {$ifdef has_sem_init}
  597. sem_destroy(PSemaphore(FSem));
  598. FreeMem(FSem);
  599. {$else}
  600. {$ifdef has_sem_open}
  601. sem_close(PSemaphore(FSem));
  602. {$else has_sem_init}
  603. fpclose(PFilDes(FSem)^[0]);
  604. fpclose(PFilDes(FSem)^[1]);
  605. FreeMem(FSem);
  606. {$endif}
  607. {$endif}
  608. end;
  609. type
  610. TPthreadCondition = pthread_cond_t;
  611. TPthreadMutex = pthread_mutex_t;
  612. Tbasiceventstate=record
  613. FCondVar: TPthreadCondition;
  614. FEventSection: TPthreadMutex;
  615. FWaiters: longint;
  616. FIsSet,
  617. FManualReset,
  618. FDestroying : Boolean;
  619. end;
  620. plocaleventstate = ^tbasiceventstate;
  621. // peventstate=pointer;
  622. Const
  623. wrSignaled = 0;
  624. wrTimeout = 1;
  625. wrAbandoned= 2;
  626. wrError = 3;
  627. function IntBasicEventCreate(EventAttributes : Pointer; AManualReset,InitialState : Boolean;const Name : ansistring):pEventState;
  628. var
  629. MAttr : pthread_mutexattr_t;
  630. res : cint;
  631. begin
  632. new(plocaleventstate(result));
  633. plocaleventstate(result)^.FManualReset:=AManualReset;
  634. plocaleventstate(result)^.FWaiters:=0;
  635. plocaleventstate(result)^.FDestroying:=False;
  636. plocaleventstate(result)^.FIsSet:=InitialState;
  637. res := pthread_cond_init(@plocaleventstate(result)^.FCondVar, nil);
  638. if (res <> 0) then
  639. begin
  640. FreeMem(result);
  641. fpc_threaderror;
  642. end;
  643. res:=pthread_mutexattr_init(@MAttr);
  644. if res=0 then
  645. begin
  646. res:=pthread_mutexattr_settype(@MAttr,longint(_PTHREAD_MUTEX_RECURSIVE));
  647. if Res=0 then
  648. Res:=pthread_mutex_init(@plocaleventstate(result)^.feventsection,@MAttr)
  649. else
  650. res:=pthread_mutex_init(@plocaleventstate(result)^.feventsection,nil);
  651. end
  652. else
  653. res:=pthread_mutex_init(@plocaleventstate(result)^.feventsection,nil);
  654. pthread_mutexattr_destroy(@MAttr);
  655. if res <> 0 then
  656. begin
  657. pthread_cond_destroy(@plocaleventstate(result)^.FCondVar);
  658. FreeMem(result);
  659. fpc_threaderror;
  660. end;
  661. end;
  662. procedure Intbasiceventdestroy(state:peventstate);
  663. begin
  664. { safely mark that we are destroying this event }
  665. pthread_mutex_lock(@plocaleventstate(state)^.feventsection);
  666. plocaleventstate(state)^.FDestroying:=true;
  667. { send a signal to all threads that are waiting }
  668. pthread_cond_broadcast(@plocaleventstate(state)^.FCondVar);
  669. pthread_mutex_unlock(@plocaleventstate(state)^.feventsection);
  670. { now wait until they've finished their business }
  671. while (plocaleventstate(state)^.FWaiters <> 0) do
  672. cThreadSwitch;
  673. { and clean up }
  674. pthread_cond_destroy(@plocaleventstate(state)^.Fcondvar);
  675. pthread_mutex_destroy(@plocaleventstate(state)^.FEventSection);
  676. dispose(plocaleventstate(state));
  677. end;
  678. procedure IntbasiceventResetEvent(state:peventstate);
  679. begin
  680. pthread_mutex_lock(@plocaleventstate(state)^.feventsection);
  681. plocaleventstate(state)^.fisset:=false;
  682. pthread_mutex_unlock(@plocaleventstate(state)^.feventsection);
  683. end;
  684. procedure IntbasiceventSetEvent(state:peventstate);
  685. begin
  686. pthread_mutex_lock(@plocaleventstate(state)^.feventsection);
  687. plocaleventstate(state)^.Fisset:=true;
  688. if not(plocaleventstate(state)^.FManualReset) then
  689. pthread_cond_signal(@plocaleventstate(state)^.Fcondvar)
  690. else
  691. pthread_cond_broadcast(@plocaleventstate(state)^.Fcondvar);
  692. pthread_mutex_unlock(@plocaleventstate(state)^.feventsection);
  693. end;
  694. function IntbasiceventWaitFor(Timeout : Cardinal;state:peventstate) : longint;
  695. var
  696. timespec: ttimespec;
  697. errres: cint;
  698. isset: boolean;
  699. tnow : timeval;
  700. begin
  701. { safely check whether we are being destroyed, if so immediately return. }
  702. { otherwise (under the same mutex) increase the number of waiters }
  703. pthread_mutex_lock(@plocaleventstate(state)^.feventsection);
  704. if (plocaleventstate(state)^.FDestroying) then
  705. begin
  706. pthread_mutex_unlock(@plocaleventstate(state)^.feventsection);
  707. result := wrAbandoned;
  708. exit;
  709. end;
  710. { not a regular inc() because it may happen simulatneously with the }
  711. { interlockeddecrement() at the end }
  712. interlockedincrement(plocaleventstate(state)^.FWaiters);
  713. //Wait without timeout using pthread_cond_wait
  714. if Timeout = $FFFFFFFF then
  715. begin
  716. while (not plocaleventstate(state)^.FIsSet) and (not plocaleventstate(state)^.FDestroying) do
  717. pthread_cond_wait(@plocaleventstate(state)^.Fcondvar, @plocaleventstate(state)^.feventsection);
  718. end
  719. else
  720. begin
  721. //Wait with timeout using pthread_cont_timedwait
  722. fpgettimeofday(@tnow,nil);
  723. timespec.tv_sec := tnow.tv_sec + (clong(timeout) div 1000);
  724. timespec.tv_nsec := (clong(timeout) mod 1000)*1000000 + tnow.tv_usec*1000;
  725. if timespec.tv_nsec >= 1000000000 then
  726. begin
  727. inc(timespec.tv_sec);
  728. dec(timespec.tv_nsec, 1000000000);
  729. end;
  730. errres:=0;
  731. while (not plocaleventstate(state)^.FDestroying) and (not plocaleventstate(state)^.FIsSet) and (errres<>ESysETIMEDOUT) do
  732. errres:=pthread_cond_timedwait(@plocaleventstate(state)^.Fcondvar, @plocaleventstate(state)^.feventsection, @timespec);
  733. end;
  734. isset := plocaleventstate(state)^.FIsSet;
  735. { if ManualReset=false, reset the event immediately. }
  736. if (plocaleventstate(state)^.FManualReset=false) then
  737. plocaleventstate(state)^.FIsSet := false;
  738. //check the results...
  739. if plocaleventstate(state)^.FDestroying then
  740. Result := wrAbandoned
  741. else
  742. if IsSet then
  743. Result := wrSignaled
  744. else
  745. begin
  746. if errres=ESysETIMEDOUT then
  747. Result := wrTimeout
  748. else
  749. Result := wrError;
  750. end;
  751. pthread_mutex_unlock(@plocaleventstate(state)^.feventsection);
  752. { don't put this above the previous pthread_mutex_unlock, because }
  753. { otherwise we can get errors in case an object is destroyed between }
  754. { end of the wait/sleep loop and the signalling above. }
  755. { The pthread_mutex_unlock above takes care of the memory barrier }
  756. interlockeddecrement(plocaleventstate(state)^.FWaiters);
  757. end;
  758. function intRTLEventCreate: PRTLEvent;
  759. var p:pintrtlevent;
  760. begin
  761. new(p);
  762. if not assigned(p) then
  763. fpc_threaderror;
  764. if pthread_cond_init(@p^.condvar, nil)<>0 then
  765. begin
  766. dispose(p);
  767. fpc_threaderror;
  768. end;
  769. if pthread_mutex_init(@p^.mutex, nil)<>0 then
  770. begin
  771. pthread_cond_destroy(@p^.condvar);
  772. dispose(p);
  773. fpc_threaderror;
  774. end;
  775. p^.isset:=false;
  776. result:=PRTLEVENT(p);
  777. end;
  778. procedure intRTLEventDestroy(AEvent: PRTLEvent);
  779. var p:pintrtlevent;
  780. begin
  781. p:=pintrtlevent(aevent);
  782. pthread_cond_destroy(@p^.condvar);
  783. pthread_mutex_destroy(@p^.mutex);
  784. dispose(p);
  785. end;
  786. procedure intRTLEventSetEvent(AEvent: PRTLEvent);
  787. var p:pintrtlevent;
  788. begin
  789. p:=pintrtlevent(aevent);
  790. pthread_mutex_lock(@p^.mutex);
  791. p^.isset:=true;
  792. pthread_cond_signal(@p^.condvar);
  793. pthread_mutex_unlock(@p^.mutex);
  794. end;
  795. procedure intRTLEventResetEvent(AEvent: PRTLEvent);
  796. var p:pintrtlevent;
  797. begin
  798. p:=pintrtlevent(aevent);
  799. pthread_mutex_lock(@p^.mutex);
  800. p^.isset:=false;
  801. pthread_mutex_unlock(@p^.mutex);
  802. end;
  803. procedure intRTLEventWaitFor(AEvent: PRTLEvent);
  804. var p:pintrtlevent;
  805. begin
  806. p:=pintrtlevent(aevent);
  807. pthread_mutex_lock(@p^.mutex);
  808. while not p^.isset do pthread_cond_wait(@p^.condvar, @p^.mutex);
  809. p^.isset:=false;
  810. pthread_mutex_unlock(@p^.mutex);
  811. end;
  812. procedure intRTLEventWaitForTimeout(AEvent: PRTLEvent;timeout : longint);
  813. var
  814. p : pintrtlevent;
  815. errres : cint;
  816. timespec : ttimespec;
  817. tnow : timeval;
  818. begin
  819. p:=pintrtlevent(aevent);
  820. fpgettimeofday(@tnow,nil);
  821. timespec.tv_sec:=tnow.tv_sec+(timeout div 1000);
  822. timespec.tv_nsec:=(timeout mod 1000)*1000000 + tnow.tv_usec*1000;
  823. if timespec.tv_nsec >= 1000000000 then
  824. begin
  825. inc(timespec.tv_sec);
  826. dec(timespec.tv_nsec, 1000000000);
  827. end;
  828. errres:=0;
  829. pthread_mutex_lock(@p^.mutex);
  830. while (not p^.isset) and
  831. (errres <> ESysETIMEDOUT) do
  832. begin
  833. errres:=pthread_cond_timedwait(@p^.condvar, @p^.mutex, @timespec);
  834. end;
  835. p^.isset:=false;
  836. pthread_mutex_unlock(@p^.mutex);
  837. end;
  838. type
  839. threadmethod = procedure of object;
  840. Function CInitThreads : Boolean;
  841. begin
  842. {$ifdef DEBUG_MT}
  843. Writeln('Entering InitThreads.');
  844. {$endif}
  845. {$ifndef dynpthreads}
  846. Result:=True;
  847. {$else}
  848. Result:=LoadPthreads;
  849. {$endif}
  850. ThreadID := TThreadID (pthread_self());
  851. {$ifdef DEBUG_MT}
  852. Writeln('InitThreads : ',Result);
  853. {$endif DEBUG_MT}
  854. // We assume that if you set the thread manager, the application is multithreading.
  855. InitCThreading;
  856. end;
  857. Function CDoneThreads : Boolean;
  858. begin
  859. {$ifndef dynpthreads}
  860. Result:=True;
  861. {$else}
  862. Result:=UnloadPthreads;
  863. {$endif}
  864. end;
  865. Var
  866. CThreadManager : TThreadManager;
  867. Procedure SetCThreadManager;
  868. begin
  869. With CThreadManager do begin
  870. InitManager :=@CInitThreads;
  871. DoneManager :=@CDoneThreads;
  872. BeginThread :=@CBeginThread;
  873. EndThread :=@CEndThread;
  874. SuspendThread :=@CSuspendThread;
  875. ResumeThread :=@CResumeThread;
  876. KillThread :=@CKillThread;
  877. ThreadSwitch :=@CThreadSwitch;
  878. CloseThread :=@CCloseThread;
  879. WaitForThreadTerminate :=@CWaitForThreadTerminate;
  880. ThreadSetPriority :=@CThreadSetPriority;
  881. ThreadGetPriority :=@CThreadGetPriority;
  882. GetCurrentThreadId :=@CGetCurrentThreadId;
  883. InitCriticalSection :=@CInitCriticalSection;
  884. DoneCriticalSection :=@CDoneCriticalSection;
  885. EnterCriticalSection :=@CEnterCriticalSection;
  886. TryEnterCriticalSection:=@CTryEnterCriticalSection;
  887. LeaveCriticalSection :=@CLeaveCriticalSection;
  888. InitThreadVar :=@CInitThreadVar;
  889. RelocateThreadVar :=@CRelocateThreadVar;
  890. AllocateThreadVars :=@CAllocateThreadVars;
  891. ReleaseThreadVars :=@CReleaseThreadVars;
  892. BasicEventCreate :=@intBasicEventCreate;
  893. BasicEventDestroy :=@intBasicEventDestroy;
  894. BasicEventResetEvent :=@intBasicEventResetEvent;
  895. BasicEventSetEvent :=@intBasicEventSetEvent;
  896. BasiceventWaitFor :=@intBasiceventWaitFor;
  897. rtlEventCreate :=@intrtlEventCreate;
  898. rtlEventDestroy :=@intrtlEventDestroy;
  899. rtlEventSetEvent :=@intrtlEventSetEvent;
  900. rtlEventResetEvent :=@intrtlEventResetEvent;
  901. rtleventWaitForTimeout :=@intrtleventWaitForTimeout;
  902. rtleventWaitFor :=@intrtleventWaitFor;
  903. // semaphores
  904. SemaphoreInit :=@cSemaphoreInit;
  905. SemaphoreDestroy :=@cSemaphoreDestroy;
  906. SemaphoreWait :=@cSemaphoreWait;
  907. SemaphorePost :=@cSemaphorePost;
  908. end;
  909. SetThreadManager(CThreadManager);
  910. end;
  911. initialization
  912. if ThreadingAlreadyUsed then
  913. begin
  914. writeln('Threading has been used before cthreads was initialized.');
  915. writeln('Make cthreads one of the first units in your uses clause.');
  916. runerror(211);
  917. end;
  918. SetCThreadManager;
  919. finalization
  920. end.