cthreads.pp 17 KB

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