cthreads.pp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650
  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. systhrds,
  30. BaseUnix,
  31. unix,
  32. unixtype,
  33. sysutils
  34. {$ifdef dynpthreads}
  35. ,dl
  36. {$endif}
  37. ;
  38. {*****************************************************************************
  39. Generic overloaded
  40. *****************************************************************************}
  41. { Include OS specific parts. }
  42. {$i pthread.inc}
  43. {*****************************************************************************
  44. Threadvar support
  45. *****************************************************************************}
  46. {$ifdef HASTHREADVAR}
  47. const
  48. threadvarblocksize : dword = 0;
  49. var
  50. TLSKey : pthread_key_t;
  51. procedure CInitThreadvar(var offset : dword;size : dword);
  52. begin
  53. offset:=threadvarblocksize;
  54. inc(threadvarblocksize,size);
  55. end;
  56. function CRelocateThreadvar(offset : dword) : pointer;
  57. begin
  58. CRelocateThreadvar:=pthread_getspecific(tlskey)+Offset;
  59. end;
  60. procedure CAllocateThreadVars;
  61. var
  62. dataindex : pointer;
  63. begin
  64. { we've to allocate the memory from system }
  65. { because the FPC heap management uses }
  66. { exceptions which use threadvars but }
  67. { these aren't allocated yet ... }
  68. { allocate room on the heap for the thread vars }
  69. DataIndex:=Pointer(Fpmmap(nil,threadvarblocksize,3,MAP_PRIVATE+MAP_ANONYMOUS,-1,0));
  70. FillChar(DataIndex^,threadvarblocksize,0);
  71. pthread_setspecific(tlskey,dataindex);
  72. end;
  73. procedure CReleaseThreadVars;
  74. begin
  75. {$ifdef ver1_0}
  76. Fpmunmap(longint(pthread_getspecific(tlskey)),threadvarblocksize);
  77. {$else}
  78. Fpmunmap(pointer(pthread_getspecific(tlskey)),threadvarblocksize);
  79. {$endif}
  80. end;
  81. { Include OS independent Threadvar initialization }
  82. {$endif HASTHREADVAR}
  83. {*****************************************************************************
  84. Thread starting
  85. *****************************************************************************}
  86. type
  87. pthreadinfo = ^tthreadinfo;
  88. tthreadinfo = record
  89. f : tthreadfunc;
  90. p : pointer;
  91. stklen : cardinal;
  92. end;
  93. procedure DoneThread;
  94. begin
  95. { Release Threadvars }
  96. {$ifdef HASTHREADVAR}
  97. CReleaseThreadVars;
  98. {$endif HASTHREADVAR}
  99. end;
  100. function ThreadMain(param : pointer) : pointer;cdecl;
  101. var
  102. ti : tthreadinfo;
  103. {$ifdef DEBUG_MT}
  104. // in here, don't use write/writeln before having called
  105. // InitThread! I wonder if anyone ever debugged these routines,
  106. // because they will have crashed if DEBUG_MT was enabled!
  107. // this took me the good part of an hour to figure out
  108. // why it was crashing all the time!
  109. // this is kind of a workaround, we simply write(2) to fd 0
  110. s: string[100]; // not an ansistring
  111. {$endif DEBUG_MT}
  112. begin
  113. {$ifdef DEBUG_MT}
  114. s := 'New thread started, initing threadvars'#10;
  115. fpwrite(0,s[1],length(s));
  116. {$endif DEBUG_MT}
  117. {$ifdef HASTHREADVAR}
  118. { Allocate local thread vars, this must be the first thing,
  119. because the exception management and io depends on threadvars }
  120. CAllocateThreadVars;
  121. {$endif HASTHREADVAR}
  122. { Copy parameter to local data }
  123. {$ifdef DEBUG_MT}
  124. s := 'New thread started, initialising ...'#10;
  125. fpwrite(0,s[1],length(s));
  126. {$endif DEBUG_MT}
  127. ti:=pthreadinfo(param)^;
  128. dispose(pthreadinfo(param));
  129. { Initialize thread }
  130. InitThread(ti.stklen);
  131. { Start thread function }
  132. {$ifdef DEBUG_MT}
  133. writeln('Jumping to thread function');
  134. {$endif DEBUG_MT}
  135. ThreadMain:=pointer(ti.f(ti.p));
  136. DoneThread;
  137. pthread_detach(pthread_t(pthread_self()));
  138. end;
  139. function CBeginThread(sa : Pointer;stacksize : dword;
  140. ThreadFunction : tthreadfunc;p : pointer;
  141. creationFlags : dword; var ThreadId : THandle) : DWord;
  142. var
  143. ti : pthreadinfo;
  144. thread_attr : pthread_attr_t;
  145. begin
  146. {$ifdef DEBUG_MT}
  147. writeln('Creating new thread');
  148. {$endif DEBUG_MT}
  149. { Initialize multithreading if not done }
  150. if not IsMultiThread then
  151. begin
  152. {$ifdef HASTHREADVAR}
  153. { We're still running in single thread mode, setup the TLS }
  154. pthread_key_create(@TLSKey,nil);
  155. InitThreadVars(@CRelocateThreadvar);
  156. {$endif HASTHREADVAR}
  157. IsMultiThread:=true;
  158. end;
  159. { the only way to pass data to the newly created thread
  160. in a MT safe way, is to use the heap }
  161. new(ti);
  162. ti^.f:=ThreadFunction;
  163. ti^.p:=p;
  164. ti^.stklen:=stacksize;
  165. { call pthread_create }
  166. {$ifdef DEBUG_MT}
  167. writeln('Starting new thread');
  168. {$endif DEBUG_MT}
  169. pthread_attr_init(@thread_attr);
  170. pthread_attr_setinheritsched(@thread_attr, PTHREAD_EXPLICIT_SCHED);
  171. // will fail under linux -- apparently unimplemented
  172. pthread_attr_setscope(@thread_attr, PTHREAD_SCOPE_PROCESS);
  173. // don't create detached, we need to be able to join (waitfor) on
  174. // the newly created thread!
  175. //pthread_attr_setdetachstate(@thread_attr, PTHREAD_CREATE_DETACHED);
  176. if pthread_create(@threadid, @thread_attr, @ThreadMain,ti) <> 0 then begin
  177. threadid := 0;
  178. end;
  179. CBeginThread:=threadid;
  180. {$ifdef DEBUG_MT}
  181. writeln('BeginThread returning ',CBeginThread);
  182. {$endif DEBUG_MT}
  183. end;
  184. procedure CEndThread(ExitCode : DWord);
  185. begin
  186. DoneThread;
  187. pthread_detach(pthread_t(pthread_self()));
  188. pthread_exit(pointer(ExitCode));
  189. end;
  190. {$warning threadhandle can be larger than a dword}
  191. function CSuspendThread (threadHandle : dword) : dword;
  192. begin
  193. {$Warning SuspendThread needs to be implemented}
  194. end;
  195. {$warning threadhandle can be larger than a dword}
  196. function CResumeThread (threadHandle : dword) : dword;
  197. begin
  198. {$Warning ResumeThread needs to be implemented}
  199. end;
  200. procedure CThreadSwitch; {give time to other threads}
  201. begin
  202. {extern int pthread_yield (void) __THROW;}
  203. {$Warning ThreadSwitch needs to be implemented}
  204. end;
  205. {$warning threadhandle can be larger than a dword}
  206. function CKillThread (threadHandle : dword) : dword;
  207. begin
  208. pthread_detach(pthread_t(threadHandle));
  209. CKillThread := pthread_cancel(pthread_t(threadHandle));
  210. end;
  211. {$warning threadhandle can be larger than a dword}
  212. function CWaitForThreadTerminate (threadHandle : dword; TimeoutMs : longint) : dword; {0=no timeout}
  213. var
  214. LResultP: Pointer;
  215. LResult: DWord;
  216. begin
  217. LResult := 0;
  218. LResultP := @LResult;
  219. pthread_join(pthread_t(threadHandle), @LResultP);
  220. CWaitForThreadTerminate := LResult;
  221. end;
  222. {$warning threadhandle can be larger than a dword}
  223. function CThreadSetPriority (threadHandle : dword; Prio: longint): boolean; {-15..+15, 0=normal}
  224. begin
  225. {$Warning ThreadSetPriority needs to be implemented}
  226. end;
  227. {$warning threadhandle can be larger than a dword}
  228. function CThreadGetPriority (threadHandle : dword): Integer;
  229. begin
  230. {$Warning ThreadGetPriority needs to be implemented}
  231. end;
  232. {$warning threadhandle can be larger than a dword}
  233. function CGetCurrentThreadId : dword;
  234. begin
  235. CGetCurrentThreadId:=dword(pthread_self());
  236. end;
  237. {*****************************************************************************
  238. Delphi/Win32 compatibility
  239. *****************************************************************************}
  240. procedure CInitCriticalSection(var CS);
  241. var
  242. MAttr : pthread_mutexattr_t;
  243. res: longint;
  244. begin
  245. res:=pthread_mutexattr_init(@MAttr);
  246. if res=0 then
  247. begin
  248. res:=pthread_mutexattr_settype(@MAttr,longint(_PTHREAD_MUTEX_RECURSIVE));
  249. if res=0 then
  250. res := pthread_mutex_init(@CS,@MAttr)
  251. else
  252. { No recursive mutex support :/ }
  253. res := pthread_mutex_init(@CS,NIL);
  254. end
  255. else
  256. res:= pthread_mutex_init(@CS,NIL);
  257. pthread_mutexattr_destroy(@MAttr);
  258. if res <> 0 then
  259. runerror(6);
  260. end;
  261. procedure CEnterCriticalSection(var CS);
  262. begin
  263. if pthread_mutex_lock(@CS) <> 0 then
  264. runerror(6);
  265. end;
  266. procedure CLeaveCriticalSection(var CS);
  267. begin
  268. if pthread_mutex_unlock(@CS) <> 0 then
  269. runerror(6)
  270. end;
  271. procedure CDoneCriticalSection(var CS);
  272. begin
  273. if pthread_mutex_destroy(@CS) <> 0 then
  274. runerror(6);
  275. end;
  276. {*****************************************************************************
  277. Heap Mutex Protection
  278. *****************************************************************************}
  279. var
  280. HeapMutex : pthread_mutex_t;
  281. procedure PThreadHeapMutexInit;
  282. begin
  283. pthread_mutex_init(@heapmutex,nil);
  284. end;
  285. procedure PThreadHeapMutexDone;
  286. begin
  287. pthread_mutex_destroy(@heapmutex);
  288. end;
  289. procedure PThreadHeapMutexLock;
  290. begin
  291. pthread_mutex_lock(@heapmutex);
  292. end;
  293. procedure PThreadHeapMutexUnlock;
  294. begin
  295. pthread_mutex_unlock(@heapmutex);
  296. end;
  297. const
  298. PThreadMemoryMutexManager : TMemoryMutexManager = (
  299. MutexInit : @PThreadHeapMutexInit;
  300. MutexDone : @PThreadHeapMutexDone;
  301. MutexLock : @PThreadHeapMutexLock;
  302. MutexUnlock : @PThreadHeapMutexUnlock;
  303. );
  304. procedure InitHeapMutexes;
  305. begin
  306. SetMemoryMutexManager(PThreadMemoryMutexManager);
  307. end;
  308. Function CInitThreads : Boolean;
  309. begin
  310. {$ifdef DEBUG_MT}
  311. Writeln('Entering InitThreads.');
  312. {$endif}
  313. {$ifndef dynpthreads}
  314. Result:=True;
  315. {$else}
  316. Result:=LoadPthreads;
  317. {$endif}
  318. ThreadID := SizeUInt (pthread_self);
  319. {$ifdef DEBUG_MT}
  320. Writeln('InitThreads : ',Result);
  321. {$endif DEBUG_MT}
  322. end;
  323. Function CDoneThreads : Boolean;
  324. begin
  325. {$ifndef dynpthreads}
  326. Result:=True;
  327. {$else}
  328. Result:=UnloadPthreads;
  329. {$endif}
  330. end;
  331. type
  332. TPthreadMutex = pthread_mutex_t;
  333. Tbasiceventstate=record
  334. FSem: Pointer;
  335. FManualReset: Boolean;
  336. FEventSection: TPthreadMutex;
  337. end;
  338. plocaleventstate = ^tbasiceventstate;
  339. // peventstate=pointer;
  340. Const
  341. wrSignaled = 0;
  342. wrTimeout = 1;
  343. wrAbandoned= 2;
  344. wrError = 3;
  345. function IntBasicEventCreate(EventAttributes : Pointer; AManualReset,InitialState : Boolean;const Name : ansistring):pEventState;
  346. var
  347. MAttr : pthread_mutexattr_t;
  348. res : cint;
  349. begin
  350. new(plocaleventstate(result));
  351. plocaleventstate(result)^.FManualReset:=AManualReset;
  352. plocaleventstate(result)^.FSem:=New(PSemaphore); //sem_t.
  353. // plocaleventstate(result)^.feventsection:=nil;
  354. res:=pthread_mutexattr_init(@MAttr);
  355. if res=0 then
  356. begin
  357. res:=pthread_mutexattr_settype(@MAttr,longint(_PTHREAD_MUTEX_RECURSIVE));
  358. if Res=0 then
  359. Res:=pthread_mutex_init(@plocaleventstate(result)^.feventsection,@MAttr)
  360. else
  361. res:=pthread_mutex_init(@plocaleventstate(result)^.feventsection,nil);
  362. end
  363. else
  364. res:=pthread_mutex_init(@plocaleventstate(result)^.feventsection,nil);
  365. pthread_mutexattr_destroy(@MAttr);
  366. if res <> 0 then
  367. runerror(6);
  368. if sem_init(psem_t(plocaleventstate(result)^.FSem),ord(False),Ord(InitialState)) <> 0 then
  369. runerror(6);
  370. end;
  371. procedure Intbasiceventdestroy(state:peventstate);
  372. begin
  373. sem_destroy(psem_t( plocaleventstate(state)^.FSem));
  374. end;
  375. procedure IntbasiceventResetEvent(state:peventstate);
  376. begin
  377. While sem_trywait(psem_t( plocaleventstate(state)^.FSem))=0 do
  378. ;
  379. end;
  380. procedure IntbasiceventSetEvent(state:peventstate);
  381. Var
  382. Value : Longint;
  383. begin
  384. pthread_mutex_lock(@plocaleventstate(state)^.feventsection);
  385. Try
  386. sem_getvalue(plocaleventstate(state)^.FSem,@value);
  387. if Value=0 then
  388. sem_post(psem_t( plocaleventstate(state)^.FSem));
  389. finally
  390. pthread_mutex_unlock(@plocaleventstate(state)^.feventsection);
  391. end;
  392. end;
  393. function IntbasiceventWaitFor(Timeout : Cardinal;state:peventstate) : longint;
  394. begin
  395. If TimeOut<>Cardinal($FFFFFFFF) then
  396. result:=wrError
  397. else
  398. begin
  399. sem_wait(psem_t(plocaleventstate(state)^.FSem));
  400. result:=wrSignaled;
  401. if plocaleventstate(state)^.FManualReset then
  402. begin
  403. pthread_mutex_lock(@plocaleventstate(state)^.feventsection);
  404. Try
  405. intbasiceventresetevent(State);
  406. sem_post(psem_t( plocaleventstate(state)^.FSem));
  407. Finally
  408. pthread_mutex_unlock(@plocaleventstate(state)^.feventsection);
  409. end;
  410. end;
  411. end;
  412. end;
  413. Var
  414. CThreadManager : TThreadManager;
  415. Procedure SetCThreadManager;
  416. begin
  417. With CThreadManager do
  418. begin
  419. InitManager :=@CInitThreads;
  420. DoneManager :=@CDoneThreads;
  421. BeginThread :=@CBeginThread;
  422. EndThread :=@CEndThread;
  423. SuspendThread :=@CSuspendThread;
  424. ResumeThread :=@CResumeThread;
  425. KillThread :=@CKillThread;
  426. ThreadSwitch :=@CThreadSwitch;
  427. WaitForThreadTerminate :=@CWaitForThreadTerminate;
  428. ThreadSetPriority :=@CThreadSetPriority;
  429. ThreadGetPriority :=@CThreadGetPriority;
  430. GetCurrentThreadId :=@CGetCurrentThreadId;
  431. InitCriticalSection :=@CInitCriticalSection;
  432. DoneCriticalSection :=@CDoneCriticalSection;
  433. EnterCriticalSection :=@CEnterCriticalSection;
  434. LeaveCriticalSection :=@CLeaveCriticalSection;
  435. {$ifdef hasthreadvar}
  436. InitThreadVar :=@CInitThreadVar;
  437. RelocateThreadVar :=@CRelocateThreadVar;
  438. AllocateThreadVars :=@CAllocateThreadVars;
  439. ReleaseThreadVars :=@CReleaseThreadVars;
  440. {$endif}
  441. BasicEventCreate :=@intBasicEventCreate;
  442. BasicEventDestroy :=@intBasicEventDestroy;
  443. BasicEventResetEvent :=@intBasicEventResetEvent;
  444. BasicEventSetEvent :=@intBasicEventSetEvent;
  445. BasiceventWaitFor :=@intBasiceventWaitFor;
  446. end;
  447. SetThreadManager(CThreadManager);
  448. InitHeapMutexes;
  449. end;
  450. initialization
  451. SetCThreadManager;
  452. end.
  453. {
  454. $Log$
  455. Revision 1.12 2004-09-09 20:29:06 jonas
  456. * fixed definition of pthread_mutex_t for non-linux targets (and for
  457. linux as well, actually).
  458. * base libpthread definitions are now in ptypes.inc, included in unixtype
  459. They sometimes have an extra underscore in front of their name, in
  460. case they were also exported by the packages/base/pthreads unit, so
  461. they can keep their original name there
  462. * cthreadds unit now imports systuils, because it uses exceptions (it
  463. already did so before as well)
  464. * fixed many linux definitions of libpthread functions in pthrlinux.inc
  465. (integer -> cint etc)
  466. + added culonglong type to ctype.inc
  467. Revision 1.11 2004/05/23 15:30:42 marco
  468. * basicevent, still untested.
  469. Revision 1.10 2004/03/03 22:00:28 peter
  470. * $ifdef debug code
  471. Revision 1.9 2004/02/22 16:48:39 florian
  472. * several 64 bit issues fixed
  473. Revision 1.8 2004/02/15 16:33:32 marco
  474. * linklibs fixed for new pthread mechanism on FreeBSD
  475. Revision 1.7 2004/01/20 23:13:53 hajny
  476. * ExecuteProcess fixes, ProcessID and ThreadID added
  477. Revision 1.6 2004/01/07 17:40:56 jonas
  478. * Darwin does not have a lib_r, libc itself is already reentrant
  479. Revision 1.5 2003/12/16 09:43:04 daniel
  480. * Use of 0 instead of nil fixed
  481. Revision 1.4 2003/11/29 17:34:14 michael
  482. + Removed dummy variable from SetCthreadManager
  483. Revision 1.3 2003/11/27 20:24:53 michael
  484. + Compiles on BSD too now
  485. Revision 1.2 2003/11/27 20:16:59 michael
  486. + Make works with 1.0.10 too
  487. Revision 1.1 2003/11/26 20:10:59 michael
  488. + New threadmanager implementation
  489. Revision 1.20 2003/11/19 10:54:32 marco
  490. * some simple restructures
  491. Revision 1.19 2003/11/18 22:36:12 marco
  492. * Last patch was ok, problem was somewhere else. Moved *BSD part of pthreads to freebsd/pthreads.inc
  493. Revision 1.18 2003/11/18 22:35:09 marco
  494. * Last patch was ok, problem was somewhere else. Moved *BSD part of pthreads to freebsd/pthreads.inc
  495. Revision 1.17 2003/11/17 10:05:51 marco
  496. * threads for FreeBSD. Not working tho
  497. Revision 1.16 2003/11/17 08:27:50 marco
  498. * pthreads based ttread from Johannes Berg
  499. Revision 1.15 2003/10/01 21:00:09 peter
  500. * GetCurrentThreadHandle renamed to GetCurrentThreadId
  501. Revision 1.14 2003/10/01 20:53:08 peter
  502. * GetCurrentThreadId implemented
  503. Revision 1.13 2003/09/20 12:38:29 marco
  504. * FCL now compiles for FreeBSD with new 1.1. Now Linux.
  505. Revision 1.12 2003/09/16 13:17:03 marco
  506. * Wat cleanup, ouwe syscalls nu via baseunix e.d.
  507. Revision 1.11 2003/09/16 13:00:02 marco
  508. * small BSD gotcha removed (typing mmap params)
  509. Revision 1.10 2003/09/15 20:08:49 marco
  510. * small fixes. FreeBSD now cycles
  511. Revision 1.9 2003/09/14 20:15:01 marco
  512. * Unix reform stage two. Remove all calls from Unix that exist in Baseunix.
  513. Revision 1.8 2003/03/27 17:14:27 armin
  514. * more platform independent thread routines, needs to be implemented for unix
  515. Revision 1.7 2003/01/05 19:11:32 marco
  516. * small changes originating from introduction of Baseunix to FreeBSD
  517. Revision 1.6 2002/11/11 21:41:06 marco
  518. * syscall.inc -> syscallo.inc
  519. Revision 1.5 2002/10/31 13:45:21 carl
  520. * threadvar.inc -> threadvr.inc
  521. Revision 1.4 2002/10/26 18:27:52 marco
  522. * First series POSIX calls commits. Including getcwd.
  523. Revision 1.3 2002/10/18 18:05:06 marco
  524. * $I pthread.inc instead of pthreads.inc
  525. Revision 1.2 2002/10/18 12:19:59 marco
  526. * Fixes to get the generic *BSD RTL compiling again + fixes for thread
  527. support. Still problems left in fexpand. (inoutres?) Therefore fixed
  528. sysposix not yet commited
  529. Revision 1.1 2002/10/16 06:22:56 michael
  530. Threads renamed from threads to systhrds
  531. Revision 1.1 2002/10/14 19:39:17 peter
  532. * threads unit added for thread support
  533. }