bethreads.pp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543
  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. BeOS (bethreads) 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. {$IFNDEF FPC_DOTTEDUNITS}
  14. unit bethreads;
  15. {$ENDIF FPC_DOTTEDUNITS}
  16. interface
  17. {$S-}
  18. Procedure SetBeThreadManager;
  19. implementation
  20. {$IFDEF FPC_DOTTEDUNITS}
  21. Uses
  22. systhrds,
  23. UnixApi.Base,
  24. UnixApi.Unix,
  25. UnixApi.Types,
  26. System.SysUtils;
  27. {$ELSE FPC_DOTTEDUNITS}
  28. Uses
  29. systhrds,
  30. BaseUnix,
  31. unix,
  32. unixtype,
  33. sysutils;
  34. {$ENDIF FPC_DOTTEDUNITS}
  35. {*****************************************************************************
  36. Generic overloaded
  37. *****************************************************************************}
  38. { Include OS specific parts. }
  39. {*****************************************************************************
  40. Threadvar support
  41. *****************************************************************************}
  42. {$ifdef HASTHREADVAR}
  43. const
  44. threadvarblocksize : dword = 0;
  45. var
  46. TLSKey : pthread_key_t;
  47. procedure BeInitThreadvar(var offset : dword;size : dword);
  48. begin
  49. offset:=threadvarblocksize;
  50. inc(threadvarblocksize,size);
  51. end;
  52. function BeRelocateThreadvar(offset : dword) : pointer;
  53. begin
  54. BeRelocateThreadvar:=pthread_getspecific(tlskey)+Offset;
  55. end;
  56. procedure BeAllocateThreadVars;
  57. var
  58. dataindex : pointer;
  59. begin
  60. { we've to allocate the memory from system }
  61. { because the FPC heap management uses }
  62. { exceptions which use threadvars but }
  63. { these aren't allocated yet ... }
  64. { allocate room on the heap for the thread vars }
  65. DataIndex:=Pointer(Fpmmap(nil,threadvarblocksize,3,MAP_PRIVATE+MAP_ANONYMOUS,-1,0));
  66. FillChar(DataIndex^,threadvarblocksize,0);
  67. pthread_setspecific(tlskey,dataindex);
  68. end;
  69. procedure BeReleaseThreadVars;
  70. begin
  71. {$ifdef ver1_0}
  72. Fpmunmap(longint(pthread_getspecific(tlskey)),threadvarblocksize);
  73. {$else}
  74. Fpmunmap(pointer(pthread_getspecific(tlskey)),threadvarblocksize);
  75. {$endif}
  76. end;
  77. { Include OS independent Threadvar initialization }
  78. {$endif HASTHREADVAR}
  79. {*****************************************************************************
  80. Thread starting
  81. *****************************************************************************}
  82. type
  83. pthreadinfo = ^tthreadinfo;
  84. tthreadinfo = record
  85. f : tthreadfunc;
  86. p : pointer;
  87. stklen : cardinal;
  88. end;
  89. procedure DoneThread;
  90. begin
  91. { Release Threadvars }
  92. {$ifdef HASTHREADVAR}
  93. CReleaseThreadVars;
  94. {$endif HASTHREADVAR}
  95. end;
  96. function ThreadMain(param : pointer) : pointer;cdecl;
  97. var
  98. ti : tthreadinfo;
  99. {$ifdef DEBUG_MT}
  100. // in here, don't use write/writeln before having called
  101. // InitThread! I wonder if anyone ever debugged these routines,
  102. // because they will have crashed if DEBUG_MT was enabled!
  103. // this took me the good part of an hour to figure out
  104. // why it was crashing all the time!
  105. // this is kind of a workaround, we simply write(2) to fd 0
  106. s: string[100]; // not an ansistring
  107. {$endif DEBUG_MT}
  108. begin
  109. {$ifdef DEBUG_MT}
  110. s := 'New thread started, initing threadvars'#10;
  111. fpwrite(0,s[1],length(s));
  112. {$endif DEBUG_MT}
  113. {$ifdef HASTHREADVAR}
  114. { Allocate local thread vars, this must be the first thing,
  115. because the exception management and io depends on threadvars }
  116. CAllocateThreadVars;
  117. {$endif HASTHREADVAR}
  118. { Copy parameter to local data }
  119. {$ifdef DEBUG_MT}
  120. s := 'New thread started, initialising ...'#10;
  121. fpwrite(0,s[1],length(s));
  122. {$endif DEBUG_MT}
  123. ti:=pthreadinfo(param)^;
  124. dispose(pthreadinfo(param));
  125. { Initialize thread }
  126. InitThread(ti.stklen);
  127. { Start thread function }
  128. {$ifdef DEBUG_MT}
  129. writeln('Jumping to thread function');
  130. {$endif DEBUG_MT}
  131. ThreadMain:=pointer(ti.f(ti.p));
  132. DoneThread;
  133. pthread_detach(pthread_t(pthread_self()));
  134. end;
  135. function BeBeginThread(sa : Pointer;stacksize : dword;
  136. ThreadFunction : tthreadfunc;p : pointer;
  137. creationFlags : dword; var ThreadId : THandle) : DWord;
  138. var
  139. ti : pthreadinfo;
  140. thread_attr : pthread_attr_t;
  141. begin
  142. {$ifdef DEBUG_MT}
  143. writeln('Creating new thread');
  144. {$endif DEBUG_MT}
  145. { Initialize multithreading if not done }
  146. if not IsMultiThread then
  147. begin
  148. {$ifdef HASTHREADVAR}
  149. { We're still running in single thread mode, setup the TLS }
  150. pthread_key_create(@TLSKey,nil);
  151. InitThreadVars(@CRelocateThreadvar);
  152. {$endif HASTHREADVAR}
  153. { lazy initialize thread support }
  154. LazyInitThreading;
  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. BeBeginThread:=threadid;
  178. {$ifdef DEBUG_MT}
  179. writeln('BeginThread returning ',BeBeginThread);
  180. {$endif DEBUG_MT}
  181. end;
  182. procedure BeEndThread(ExitCode : DWord);
  183. begin
  184. DoneThread;
  185. pthread_detach(pthread_t(pthread_self()));
  186. pthread_exit(pointer(ExitCode));
  187. end;
  188. {$warning threadhandle can be larger than a dword}
  189. function BeSuspendThread (threadHandle : dword) : dword;
  190. begin
  191. {$Warning SuspendThread needs to be implemented}
  192. end;
  193. {$warning threadhandle can be larger than a dword}
  194. function BeResumeThread (threadHandle : dword) : dword;
  195. begin
  196. {$Warning ResumeThread needs to be implemented}
  197. end;
  198. procedure CThreadSwitch; {give time to other threads}
  199. begin
  200. {extern int pthread_yield (void) __THROW;}
  201. {$Warning ThreadSwitch needs to be implemented}
  202. end;
  203. {$warning threadhandle can be larger than a dword}
  204. function BeKillThread (threadHandle : dword) : dword;
  205. begin
  206. pthread_detach(pthread_t(threadHandle));
  207. CKillThread := pthread_cancel(pthread_t(threadHandle));
  208. end;
  209. {$warning threadhandle can be larger than a dword}
  210. function BeWaitForThreadTerminate (threadHandle : dword; TimeoutMs : longint) : dword; {0=no timeout}
  211. var
  212. LResultP: Pointer;
  213. LResult: DWord;
  214. begin
  215. LResult := 0;
  216. LResultP := @LResult;
  217. pthread_join(pthread_t(threadHandle), @LResultP);
  218. CWaitForThreadTerminate := LResult;
  219. end;
  220. {$warning threadhandle can be larger than a dword}
  221. function BeThreadSetPriority (threadHandle : dword; Prio: longint): boolean; {-15..+15, 0=normal}
  222. begin
  223. {$Warning ThreadSetPriority needs to be implemented}
  224. end;
  225. {$warning threadhandle can be larger than a dword}
  226. function BeThreadGetPriority (threadHandle : dword): Integer;
  227. begin
  228. {$Warning ThreadGetPriority needs to be implemented}
  229. end;
  230. {$warning threadhandle can be larger than a dword}
  231. function BeGetCurrentThreadId : dword;
  232. begin
  233. CGetCurrentThreadId:=dword(pthread_self());
  234. end;
  235. procedure BeSetThreadDebugNameA(threadHandle: TThreadID; const ThreadName: AnsiString);
  236. begin
  237. {$Warning SetThreadDebugName needs to be implemented}
  238. end;
  239. procedure BeSetThreadDebugNameU(threadHandle: TThreadID; const ThreadName: UnicodeString);
  240. begin
  241. {$Warning SetThreadDebugName needs to be implemented}
  242. end;
  243. {*****************************************************************************
  244. Delphi/Win32 compatibility
  245. *****************************************************************************}
  246. procedure BeInitCriticalSection(var CS);
  247. var
  248. MAttr : pthread_mutexattr_t;
  249. res: longint;
  250. begin
  251. res:=pthread_mutexattr_init(@MAttr);
  252. if res=0 then
  253. begin
  254. res:=pthread_mutexattr_settype(@MAttr,longint(_PTHREAD_MUTEX_RECURSIVE));
  255. if res=0 then
  256. res := pthread_mutex_init(@CS,@MAttr)
  257. else
  258. { No recursive mutex support :/ }
  259. res := pthread_mutex_init(@CS,NIL);
  260. end
  261. else
  262. res:= pthread_mutex_init(@CS,NIL);
  263. pthread_mutexattr_destroy(@MAttr);
  264. if res <> 0 then
  265. runerror(6);
  266. end;
  267. procedure BeEnterCriticalSection(var CS);
  268. begin
  269. if pthread_mutex_lock(@CS) <> 0 then
  270. runerror(6);
  271. end;
  272. procedure BeLeaveCriticalSection(var CS);
  273. begin
  274. if pthread_mutex_unlock(@CS) <> 0 then
  275. runerror(6)
  276. end;
  277. procedure BeDoneCriticalSection(var CS);
  278. begin
  279. if pthread_mutex_destroy(@CS) <> 0 then
  280. runerror(6);
  281. end;
  282. {*****************************************************************************
  283. Heap Mutex Protection
  284. *****************************************************************************}
  285. var
  286. HeapMutex : pthread_mutex_t;
  287. procedure BeThreadHeapMutexInit;
  288. begin
  289. pthread_mutex_init(@heapmutex,nil);
  290. end;
  291. procedure BeThreadHeapMutexDone;
  292. begin
  293. pthread_mutex_destroy(@heapmutex);
  294. end;
  295. procedure BeThreadHeapMutexLock;
  296. begin
  297. pthread_mutex_lock(@heapmutex);
  298. end;
  299. procedure BeThreadHeapMutexUnlock;
  300. begin
  301. pthread_mutex_unlock(@heapmutex);
  302. end;
  303. const
  304. BeThreadMemoryMutexManager : TMemoryMutexManager = (
  305. MutexInit : @BeThreadHeapMutexInit;
  306. MutexDone : @BeThreadHeapMutexDone;
  307. MutexLock : @BeThreadHeapMutexLock;
  308. MutexUnlock : @BeThreadHeapMutexUnlock;
  309. );
  310. procedure InitHeapMutexes;
  311. begin
  312. SetMemoryMutexManager(BeThreadMemoryMutexManager);
  313. end;
  314. Function BeInitThreads : Boolean;
  315. begin
  316. {$ifdef DEBUG_MT}
  317. Writeln('Entering InitThreads.');
  318. {$endif}
  319. {$ifndef dynpthreads}
  320. Result:=True;
  321. {$else}
  322. Result:=LoadPthreads;
  323. {$endif}
  324. ThreadID := SizeUInt (pthread_self);
  325. {$ifdef DEBUG_MT}
  326. Writeln('InitThreads : ',Result);
  327. {$endif DEBUG_MT}
  328. end;
  329. Function BeDoneThreads : Boolean;
  330. begin
  331. {$ifndef dynpthreads}
  332. Result:=True;
  333. {$else}
  334. Result:=UnloadPthreads;
  335. {$endif}
  336. end;
  337. type
  338. TPthreadMutex = pthread_mutex_t;
  339. Tbasiceventstate=record
  340. FSem: Pointer;
  341. FManualReset: Boolean;
  342. FEventSection: TPthreadMutex;
  343. end;
  344. plocaleventstate = ^tbasiceventstate;
  345. // peventstate=pointer;
  346. Const
  347. wrSignaled = 0;
  348. wrTimeout = 1;
  349. wrAbandoned= 2;
  350. wrError = 3;
  351. function IntBasicEventCreate(EventAttributes : Pointer; AManualReset,InitialState : Boolean;const Name : ansistring):pEventState;
  352. var
  353. MAttr : pthread_mutexattr_t;
  354. res : cint;
  355. begin
  356. new(plocaleventstate(result));
  357. plocaleventstate(result)^.FManualReset:=AManualReset;
  358. plocaleventstate(result)^.FSem:=New(PSemaphore); //sem_t.
  359. // plocaleventstate(result)^.feventsection:=nil;
  360. res:=pthread_mutexattr_init(@MAttr);
  361. if res=0 then
  362. begin
  363. res:=pthread_mutexattr_settype(@MAttr,longint(_PTHREAD_MUTEX_RECURSIVE));
  364. if Res=0 then
  365. Res:=pthread_mutex_init(@plocaleventstate(result)^.feventsection,@MAttr)
  366. else
  367. res:=pthread_mutex_init(@plocaleventstate(result)^.feventsection,nil);
  368. end
  369. else
  370. res:=pthread_mutex_init(@plocaleventstate(result)^.feventsection,nil);
  371. pthread_mutexattr_destroy(@MAttr);
  372. if res <> 0 then
  373. runerror(6);
  374. if sem_init(psem_t(plocaleventstate(result)^.FSem),ord(False),Ord(InitialState)) <> 0 then
  375. runerror(6);
  376. end;
  377. procedure Intbasiceventdestroy(state:peventstate);
  378. begin
  379. sem_destroy(psem_t( plocaleventstate(state)^.FSem));
  380. end;
  381. procedure IntbasiceventResetEvent(state:peventstate);
  382. begin
  383. While sem_trywait(psem_t( plocaleventstate(state)^.FSem))=0 do
  384. ;
  385. end;
  386. procedure IntbasiceventSetEvent(state:peventstate);
  387. Var
  388. Value : Longint;
  389. begin
  390. pthread_mutex_lock(@plocaleventstate(state)^.feventsection);
  391. Try
  392. sem_getvalue(plocaleventstate(state)^.FSem,@value);
  393. if Value=0 then
  394. sem_post(psem_t( plocaleventstate(state)^.FSem));
  395. finally
  396. pthread_mutex_unlock(@plocaleventstate(state)^.feventsection);
  397. end;
  398. end;
  399. function IntbasiceventWaitFor(Timeout : Cardinal;state:peventstate;FUseComWait : Boolean=False) : longint;
  400. begin
  401. If TimeOut<>Cardinal($FFFFFFFF) then
  402. result:=wrError
  403. else
  404. begin
  405. sem_wait(psem_t(plocaleventstate(state)^.FSem));
  406. result:=wrSignaled;
  407. if plocaleventstate(state)^.FManualReset then
  408. begin
  409. pthread_mutex_lock(@plocaleventstate(state)^.feventsection);
  410. Try
  411. intbasiceventresetevent(State);
  412. sem_post(psem_t( plocaleventstate(state)^.FSem));
  413. Finally
  414. pthread_mutex_unlock(@plocaleventstate(state)^.feventsection);
  415. end;
  416. end;
  417. end;
  418. end;
  419. Var
  420. BeThreadManager : TThreadManager;
  421. Procedure SetBeThreadManager;
  422. begin
  423. With BeThreadManager do
  424. begin
  425. InitManager :=@BeInitThreads;
  426. DoneManager :=@BeDoneThreads;
  427. BeginThread :=@BeBeginThread;
  428. EndThread :=@BeEndThread;
  429. SuspendThread :=@BeSuspendThread;
  430. ResumeThread :=@BeResumeThread;
  431. KillThread :=@BeKillThread;
  432. ThreadSwitch :=@BeThreadSwitch;
  433. WaitForThreadTerminate :=@BeWaitForThreadTerminate;
  434. ThreadSetPriority :=@BeThreadSetPriority;
  435. ThreadGetPriority :=@BeThreadGetPriority;
  436. GetCurrentThreadId :=@BeGetCurrentThreadId;
  437. SetThreadDebugNameA :=@BeSetThreadDebugNameA;
  438. SetThreadDebugNameU :=@BeSetThreadDebugNameU;
  439. InitCriticalSection :=@BeInitCriticalSection;
  440. DoneCriticalSection :=@BeDoneCriticalSection;
  441. EnterCriticalSection :=@BeEnterCriticalSection;
  442. LeaveCriticalSection :=@BeLeaveCriticalSection;
  443. {$ifdef hasthreadvar}
  444. InitThreadVar :=@BeInitThreadVar;
  445. RelocateThreadVar :=@BeRelocateThreadVar;
  446. AllocateThreadVars :=@BeAllocateThreadVars;
  447. ReleaseThreadVars :=@BeReleaseThreadVars;
  448. {$endif}
  449. BasicEventCreate :=@intBasicEventCreate;
  450. BasicEventDestroy :=@intBasicEventDestroy;
  451. BasicEventResetEvent :=@intBasicEventResetEvent;
  452. BasicEventSetEvent :=@intBasicEventSetEvent;
  453. BasiceventWaitFor :=@intBasiceventWaitFor;
  454. end;
  455. SetThreadManager(BeThreadManager);
  456. InitHeapMutexes;
  457. end;
  458. initialization
  459. SetBeThreadManager;
  460. end.