cthreads.pp 18 KB

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