cthreads.pp 18 KB

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