cthreads.pp 17 KB

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