cthreads.pp 17 KB

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