bethreads.pp 15 KB

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