cthreads.pp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645
  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. if pthread_mutex_destroy(@CS) <> 0 then
  278. runerror(6);
  279. end;
  280. {*****************************************************************************
  281. Heap Mutex Protection
  282. *****************************************************************************}
  283. var
  284. HeapMutex : pthread_mutex_t;
  285. procedure PThreadHeapMutexInit;
  286. begin
  287. pthread_mutex_init(@heapmutex,nil);
  288. end;
  289. procedure PThreadHeapMutexDone;
  290. begin
  291. pthread_mutex_destroy(@heapmutex);
  292. end;
  293. procedure PThreadHeapMutexLock;
  294. begin
  295. pthread_mutex_lock(@heapmutex);
  296. end;
  297. procedure PThreadHeapMutexUnlock;
  298. begin
  299. pthread_mutex_unlock(@heapmutex);
  300. end;
  301. const
  302. PThreadMemoryMutexManager : TMemoryMutexManager = (
  303. MutexInit : @PThreadHeapMutexInit;
  304. MutexDone : @PThreadHeapMutexDone;
  305. MutexLock : @PThreadHeapMutexLock;
  306. MutexUnlock : @PThreadHeapMutexUnlock;
  307. );
  308. procedure InitHeapMutexes;
  309. begin
  310. SetMemoryMutexManager(PThreadMemoryMutexManager);
  311. end;
  312. type
  313. TPthreadMutex = pthread_mutex_t;
  314. Tbasiceventstate=record
  315. FSem: Pointer;
  316. FManualReset: Boolean;
  317. FEventSection: TPthreadMutex;
  318. end;
  319. plocaleventstate = ^tbasiceventstate;
  320. // peventstate=pointer;
  321. Const
  322. wrSignaled = 0;
  323. wrTimeout = 1;
  324. wrAbandoned= 2;
  325. wrError = 3;
  326. function IntBasicEventCreate(EventAttributes : Pointer; AManualReset,InitialState : Boolean;const Name : ansistring):pEventState;
  327. var
  328. MAttr : pthread_mutexattr_t;
  329. res : cint;
  330. begin
  331. new(plocaleventstate(result));
  332. plocaleventstate(result)^.FManualReset:=AManualReset;
  333. plocaleventstate(result)^.FSem:=New(PSemaphore); //sem_t.
  334. // plocaleventstate(result)^.feventsection:=nil;
  335. res:=pthread_mutexattr_init(@MAttr);
  336. if res=0 then
  337. begin
  338. res:=pthread_mutexattr_settype(@MAttr,longint(_PTHREAD_MUTEX_RECURSIVE));
  339. if Res=0 then
  340. Res:=pthread_mutex_init(@plocaleventstate(result)^.feventsection,@MAttr)
  341. else
  342. res:=pthread_mutex_init(@plocaleventstate(result)^.feventsection,nil);
  343. end
  344. else
  345. res:=pthread_mutex_init(@plocaleventstate(result)^.feventsection,nil);
  346. pthread_mutexattr_destroy(@MAttr);
  347. if res <> 0 then
  348. runerror(6);
  349. if sem_init(psem_t(plocaleventstate(result)^.FSem),ord(False),Ord(InitialState)) <> 0 then
  350. runerror(6);
  351. end;
  352. procedure Intbasiceventdestroy(state:peventstate);
  353. begin
  354. sem_destroy(psem_t( plocaleventstate(state)^.FSem));
  355. end;
  356. procedure IntbasiceventResetEvent(state:peventstate);
  357. begin
  358. While sem_trywait(psem_t( plocaleventstate(state)^.FSem))=0 do
  359. ;
  360. end;
  361. procedure IntbasiceventSetEvent(state:peventstate);
  362. Var
  363. Value : Longint;
  364. begin
  365. pthread_mutex_lock(@plocaleventstate(state)^.feventsection);
  366. Try
  367. sem_getvalue(plocaleventstate(state)^.FSem,@value);
  368. if Value=0 then
  369. sem_post(psem_t( plocaleventstate(state)^.FSem));
  370. finally
  371. pthread_mutex_unlock(@plocaleventstate(state)^.feventsection);
  372. end;
  373. end;
  374. function IntbasiceventWaitFor(Timeout : Cardinal;state:peventstate) : longint;
  375. begin
  376. If TimeOut<>Cardinal($FFFFFFFF) then
  377. result:=wrError
  378. else
  379. begin
  380. sem_wait(psem_t(plocaleventstate(state)^.FSem));
  381. result:=wrSignaled;
  382. if plocaleventstate(state)^.FManualReset then
  383. begin
  384. pthread_mutex_lock(@plocaleventstate(state)^.feventsection);
  385. Try
  386. intbasiceventresetevent(State);
  387. sem_post(psem_t( plocaleventstate(state)^.FSem));
  388. Finally
  389. pthread_mutex_unlock(@plocaleventstate(state)^.feventsection);
  390. end;
  391. end;
  392. end;
  393. end;
  394. function intRTLEventCreate: PRTLEvent;
  395. var p:pintrtlevent;
  396. begin
  397. new(p);
  398. pthread_cond_init(@p^.condvar, nil);
  399. pthread_mutex_init(@p^.mutex, nil);
  400. result:=PRTLEVENT(p);
  401. end;
  402. procedure intRTLEventDestroy(AEvent: PRTLEvent);
  403. var p:pintrtlevent;
  404. begin
  405. p:=pintrtlevent(aevent);
  406. pthread_cond_destroy(@p^.condvar);
  407. pthread_mutex_destroy(@p^.mutex);
  408. dispose(p);
  409. end;
  410. procedure intRTLEventSetEvent(AEvent: PRTLEvent);
  411. var p:pintrtlevent;
  412. begin
  413. p:=pintrtlevent(aevent);
  414. pthread_mutex_lock(@p^.mutex);
  415. pthread_cond_signal(@p^.condvar);
  416. pthread_mutex_unlock(@p^.mutex);
  417. end;
  418. procedure intRTLEventResetEvent(AEvent: PRTLEvent);
  419. begin
  420. { events before startwait are ignored unix }
  421. end;
  422. procedure intRTLEventStartWait(AEvent: PRTLEvent);
  423. var p:pintrtlevent;
  424. begin
  425. p:=pintrtlevent(aevent);
  426. pthread_mutex_lock(@p^.mutex);
  427. end;
  428. procedure intRTLEventWaitFor(AEvent: PRTLEvent);
  429. var p:pintrtlevent;
  430. begin
  431. p:=pintrtlevent(aevent);
  432. pthread_cond_wait(@p^.condvar, @p^.mutex);
  433. pthread_mutex_unlock(@p^.mutex);
  434. end;
  435. procedure intRTLEventWaitForTimeout(AEvent: PRTLEvent;timeout : longint);
  436. var
  437. p : pintrtlevent;
  438. errres : cint;
  439. timespec : ttimespec;
  440. begin
  441. p:=pintrtlevent(aevent);
  442. timespec.tv_sec:=timeout div 1000;
  443. timespec.tv_nsec:=(timeout mod 1000)*1000000;
  444. errres:=pthread_cond_timedwait(@p^.condvar, @p^.mutex, @timespec);
  445. if (errres=0) or (errres=ESysETIMEDOUT) then
  446. pthread_mutex_unlock(@p^.mutex);
  447. end;
  448. type
  449. threadmethod = procedure of object;
  450. Function CInitThreads : Boolean;
  451. begin
  452. {$ifdef DEBUG_MT}
  453. Writeln('Entering InitThreads.');
  454. {$endif}
  455. {$ifndef dynpthreads}
  456. Result:=True;
  457. {$else}
  458. Result:=LoadPthreads;
  459. {$endif}
  460. ThreadID := TThreadID (pthread_self);
  461. {$ifdef DEBUG_MT}
  462. Writeln('InitThreads : ',Result);
  463. {$endif DEBUG_MT}
  464. end;
  465. Function CDoneThreads : Boolean;
  466. begin
  467. {$ifndef dynpthreads}
  468. Result:=True;
  469. {$else}
  470. Result:=UnloadPthreads;
  471. {$endif}
  472. end;
  473. Var
  474. CThreadManager : TThreadManager;
  475. Procedure SetCThreadManager;
  476. begin
  477. With CThreadManager do
  478. begin
  479. InitManager :=@CInitThreads;
  480. DoneManager :=@CDoneThreads;
  481. BeginThread :=@CBeginThread;
  482. EndThread :=@CEndThread;
  483. SuspendThread :=@CSuspendThread;
  484. ResumeThread :=@CResumeThread;
  485. KillThread :=@CKillThread;
  486. ThreadSwitch :=@CThreadSwitch;
  487. WaitForThreadTerminate :=@CWaitForThreadTerminate;
  488. ThreadSetPriority :=@CThreadSetPriority;
  489. ThreadGetPriority :=@CThreadGetPriority;
  490. GetCurrentThreadId :=@CGetCurrentThreadId;
  491. InitCriticalSection :=@CInitCriticalSection;
  492. DoneCriticalSection :=@CDoneCriticalSection;
  493. EnterCriticalSection :=@CEnterCriticalSection;
  494. LeaveCriticalSection :=@CLeaveCriticalSection;
  495. InitThreadVar :=@CInitThreadVar;
  496. RelocateThreadVar :=@CRelocateThreadVar;
  497. AllocateThreadVars :=@CAllocateThreadVars;
  498. ReleaseThreadVars :=@CReleaseThreadVars;
  499. BasicEventCreate :=@intBasicEventCreate;
  500. BasicEventDestroy :=@intBasicEventDestroy;
  501. BasicEventResetEvent :=@intBasicEventResetEvent;
  502. BasicEventSetEvent :=@intBasicEventSetEvent;
  503. BasiceventWaitFor :=@intBasiceventWaitFor;
  504. rtlEventCreate :=@intrtlEventCreate;
  505. rtlEventDestroy :=@intrtlEventDestroy;
  506. rtlEventSetEvent :=@intrtlEventSetEvent;
  507. rtlEventResetEvent :=@intrtlEventResetEvent;
  508. rtlEventStartWait :=@intrtlEventStartWait;
  509. rtleventWaitForTimeout :=@intrtleventWaitForTimeout;
  510. rtleventWaitFor :=@intrtleventWaitFor;
  511. end;
  512. SetThreadManager(CThreadManager);
  513. InitHeapMutexes;
  514. end;
  515. initialization
  516. if ThreadingAlreadyUsed then
  517. begin
  518. writeln('Threading has been used before cthreads was initialized.');
  519. writeln('Make cthreads one of the first units in your uses clause.');
  520. runerror(211);
  521. end;
  522. SetCThreadManager;
  523. finalization
  524. end.