cthreads.pp 17 KB

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