bethreads.pp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  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. IsMultiThread:=true;
  143. end;
  144. { the only way to pass data to the newly created thread
  145. in a MT safe way, is to use the heap }
  146. new(ti);
  147. ti^.f:=ThreadFunction;
  148. ti^.p:=p;
  149. ti^.stklen:=stacksize;
  150. { call pthread_create }
  151. {$ifdef DEBUG_MT}
  152. writeln('Starting new thread');
  153. {$endif DEBUG_MT}
  154. pthread_attr_init(@thread_attr);
  155. pthread_attr_setinheritsched(@thread_attr, PTHREAD_EXPLICIT_SCHED);
  156. // will fail under linux -- apparently unimplemented
  157. pthread_attr_setscope(@thread_attr, PTHREAD_SCOPE_PROCESS);
  158. // don't create detached, we need to be able to join (waitfor) on
  159. // the newly created thread!
  160. //pthread_attr_setdetachstate(@thread_attr, PTHREAD_CREATE_DETACHED);
  161. if pthread_create(@threadid, @thread_attr, @ThreadMain,ti) <> 0 then begin
  162. threadid := 0;
  163. end;
  164. BeBeginThread:=threadid;
  165. {$ifdef DEBUG_MT}
  166. writeln('BeginThread returning ',BeBeginThread);
  167. {$endif DEBUG_MT}
  168. end;
  169. procedure BeEndThread(ExitCode : DWord);
  170. begin
  171. DoneThread;
  172. pthread_detach(pthread_t(pthread_self()));
  173. pthread_exit(pointer(ExitCode));
  174. end;
  175. {$warning threadhandle can be larger than a dword}
  176. function BeSuspendThread (threadHandle : dword) : dword;
  177. begin
  178. {$Warning SuspendThread needs to be implemented}
  179. end;
  180. {$warning threadhandle can be larger than a dword}
  181. function BeResumeThread (threadHandle : dword) : dword;
  182. begin
  183. {$Warning ResumeThread needs to be implemented}
  184. end;
  185. procedure CThreadSwitch; {give time to other threads}
  186. begin
  187. {extern int pthread_yield (void) __THROW;}
  188. {$Warning ThreadSwitch needs to be implemented}
  189. end;
  190. {$warning threadhandle can be larger than a dword}
  191. function BeKillThread (threadHandle : dword) : dword;
  192. begin
  193. pthread_detach(pthread_t(threadHandle));
  194. CKillThread := pthread_cancel(pthread_t(threadHandle));
  195. end;
  196. {$warning threadhandle can be larger than a dword}
  197. function BeWaitForThreadTerminate (threadHandle : dword; TimeoutMs : longint) : dword; {0=no timeout}
  198. var
  199. LResultP: Pointer;
  200. LResult: DWord;
  201. begin
  202. LResult := 0;
  203. LResultP := @LResult;
  204. pthread_join(pthread_t(threadHandle), @LResultP);
  205. CWaitForThreadTerminate := LResult;
  206. end;
  207. {$warning threadhandle can be larger than a dword}
  208. function BeThreadSetPriority (threadHandle : dword; Prio: longint): boolean; {-15..+15, 0=normal}
  209. begin
  210. {$Warning ThreadSetPriority needs to be implemented}
  211. end;
  212. {$warning threadhandle can be larger than a dword}
  213. function BeThreadGetPriority (threadHandle : dword): Integer;
  214. begin
  215. {$Warning ThreadGetPriority needs to be implemented}
  216. end;
  217. {$warning threadhandle can be larger than a dword}
  218. function BeGetCurrentThreadId : dword;
  219. begin
  220. CGetCurrentThreadId:=dword(pthread_self());
  221. end;
  222. procedure BeSetThreadDebugNameA(threadHandle: TThreadID; const ThreadName: AnsiString);
  223. begin
  224. {$Warning SetThreadDebugName needs to be implemented}
  225. end;
  226. procedure BeSetThreadDebugNameU(threadHandle: TThreadID; const ThreadName: UnicodeString);
  227. begin
  228. {$Warning SetThreadDebugName needs to be implemented}
  229. end;
  230. {*****************************************************************************
  231. Delphi/Win32 compatibility
  232. *****************************************************************************}
  233. procedure BeInitCriticalSection(var CS);
  234. var
  235. MAttr : pthread_mutexattr_t;
  236. res: longint;
  237. begin
  238. res:=pthread_mutexattr_init(@MAttr);
  239. if res=0 then
  240. begin
  241. res:=pthread_mutexattr_settype(@MAttr,longint(_PTHREAD_MUTEX_RECURSIVE));
  242. if res=0 then
  243. res := pthread_mutex_init(@CS,@MAttr)
  244. else
  245. { No recursive mutex support :/ }
  246. res := pthread_mutex_init(@CS,NIL);
  247. end
  248. else
  249. res:= pthread_mutex_init(@CS,NIL);
  250. pthread_mutexattr_destroy(@MAttr);
  251. if res <> 0 then
  252. runerror(6);
  253. end;
  254. procedure BeEnterCriticalSection(var CS);
  255. begin
  256. if pthread_mutex_lock(@CS) <> 0 then
  257. runerror(6);
  258. end;
  259. procedure BeLeaveCriticalSection(var CS);
  260. begin
  261. if pthread_mutex_unlock(@CS) <> 0 then
  262. runerror(6)
  263. end;
  264. procedure BeDoneCriticalSection(var CS);
  265. begin
  266. if pthread_mutex_destroy(@CS) <> 0 then
  267. runerror(6);
  268. end;
  269. {*****************************************************************************
  270. Heap Mutex Protection
  271. *****************************************************************************}
  272. var
  273. HeapMutex : pthread_mutex_t;
  274. procedure BeThreadHeapMutexInit;
  275. begin
  276. pthread_mutex_init(@heapmutex,nil);
  277. end;
  278. procedure BeThreadHeapMutexDone;
  279. begin
  280. pthread_mutex_destroy(@heapmutex);
  281. end;
  282. procedure BeThreadHeapMutexLock;
  283. begin
  284. pthread_mutex_lock(@heapmutex);
  285. end;
  286. procedure BeThreadHeapMutexUnlock;
  287. begin
  288. pthread_mutex_unlock(@heapmutex);
  289. end;
  290. const
  291. BeThreadMemoryMutexManager : TMemoryMutexManager = (
  292. MutexInit : @BeThreadHeapMutexInit;
  293. MutexDone : @BeThreadHeapMutexDone;
  294. MutexLock : @BeThreadHeapMutexLock;
  295. MutexUnlock : @BeThreadHeapMutexUnlock;
  296. );
  297. procedure InitHeapMutexes;
  298. begin
  299. SetMemoryMutexManager(BeThreadMemoryMutexManager);
  300. end;
  301. Function BeInitThreads : Boolean;
  302. begin
  303. {$ifdef DEBUG_MT}
  304. Writeln('Entering InitThreads.');
  305. {$endif}
  306. {$ifndef dynpthreads}
  307. Result:=True;
  308. {$else}
  309. Result:=LoadPthreads;
  310. {$endif}
  311. ThreadID := SizeUInt (pthread_self);
  312. {$ifdef DEBUG_MT}
  313. Writeln('InitThreads : ',Result);
  314. {$endif DEBUG_MT}
  315. end;
  316. Function BeDoneThreads : Boolean;
  317. begin
  318. {$ifndef dynpthreads}
  319. Result:=True;
  320. {$else}
  321. Result:=UnloadPthreads;
  322. {$endif}
  323. end;
  324. type
  325. TPthreadMutex = pthread_mutex_t;
  326. Tbasiceventstate=record
  327. FSem: Pointer;
  328. FManualReset: Boolean;
  329. FEventSection: TPthreadMutex;
  330. end;
  331. plocaleventstate = ^tbasiceventstate;
  332. // peventstate=pointer;
  333. Const
  334. wrSignaled = 0;
  335. wrTimeout = 1;
  336. wrAbandoned= 2;
  337. wrError = 3;
  338. function IntBasicEventCreate(EventAttributes : Pointer; AManualReset,InitialState : Boolean;const Name : ansistring):pEventState;
  339. var
  340. MAttr : pthread_mutexattr_t;
  341. res : cint;
  342. begin
  343. new(plocaleventstate(result));
  344. plocaleventstate(result)^.FManualReset:=AManualReset;
  345. plocaleventstate(result)^.FSem:=New(PSemaphore); //sem_t.
  346. // plocaleventstate(result)^.feventsection:=nil;
  347. res:=pthread_mutexattr_init(@MAttr);
  348. if res=0 then
  349. begin
  350. res:=pthread_mutexattr_settype(@MAttr,longint(_PTHREAD_MUTEX_RECURSIVE));
  351. if Res=0 then
  352. Res:=pthread_mutex_init(@plocaleventstate(result)^.feventsection,@MAttr)
  353. else
  354. res:=pthread_mutex_init(@plocaleventstate(result)^.feventsection,nil);
  355. end
  356. else
  357. res:=pthread_mutex_init(@plocaleventstate(result)^.feventsection,nil);
  358. pthread_mutexattr_destroy(@MAttr);
  359. if res <> 0 then
  360. runerror(6);
  361. if sem_init(psem_t(plocaleventstate(result)^.FSem),ord(False),Ord(InitialState)) <> 0 then
  362. runerror(6);
  363. end;
  364. procedure Intbasiceventdestroy(state:peventstate);
  365. begin
  366. sem_destroy(psem_t( plocaleventstate(state)^.FSem));
  367. end;
  368. procedure IntbasiceventResetEvent(state:peventstate);
  369. begin
  370. While sem_trywait(psem_t( plocaleventstate(state)^.FSem))=0 do
  371. ;
  372. end;
  373. procedure IntbasiceventSetEvent(state:peventstate);
  374. Var
  375. Value : Longint;
  376. begin
  377. pthread_mutex_lock(@plocaleventstate(state)^.feventsection);
  378. Try
  379. sem_getvalue(plocaleventstate(state)^.FSem,@value);
  380. if Value=0 then
  381. sem_post(psem_t( plocaleventstate(state)^.FSem));
  382. finally
  383. pthread_mutex_unlock(@plocaleventstate(state)^.feventsection);
  384. end;
  385. end;
  386. function IntbasiceventWaitFor(Timeout : Cardinal;state:peventstate) : longint;
  387. begin
  388. If TimeOut<>Cardinal($FFFFFFFF) then
  389. result:=wrError
  390. else
  391. begin
  392. sem_wait(psem_t(plocaleventstate(state)^.FSem));
  393. result:=wrSignaled;
  394. if plocaleventstate(state)^.FManualReset then
  395. begin
  396. pthread_mutex_lock(@plocaleventstate(state)^.feventsection);
  397. Try
  398. intbasiceventresetevent(State);
  399. sem_post(psem_t( plocaleventstate(state)^.FSem));
  400. Finally
  401. pthread_mutex_unlock(@plocaleventstate(state)^.feventsection);
  402. end;
  403. end;
  404. end;
  405. end;
  406. Var
  407. BeThreadManager : TThreadManager;
  408. Procedure SetBeThreadManager;
  409. begin
  410. With BeThreadManager do
  411. begin
  412. InitManager :=@BeInitThreads;
  413. DoneManager :=@BeDoneThreads;
  414. BeginThread :=@BeBeginThread;
  415. EndThread :=@BeEndThread;
  416. SuspendThread :=@BeSuspendThread;
  417. ResumeThread :=@BeResumeThread;
  418. KillThread :=@BeKillThread;
  419. ThreadSwitch :=@BeThreadSwitch;
  420. WaitForThreadTerminate :=@BeWaitForThreadTerminate;
  421. ThreadSetPriority :=@BeThreadSetPriority;
  422. ThreadGetPriority :=@BeThreadGetPriority;
  423. GetCurrentThreadId :=@BeGetCurrentThreadId;
  424. SetThreadDebugNameA :=@BeSetThreadDebugNameA;
  425. SetThreadDebugNameU :=@BeSetThreadDebugNameU;
  426. InitCriticalSection :=@BeInitCriticalSection;
  427. DoneCriticalSection :=@BeDoneCriticalSection;
  428. EnterCriticalSection :=@BeEnterCriticalSection;
  429. LeaveCriticalSection :=@BeLeaveCriticalSection;
  430. {$ifdef hasthreadvar}
  431. InitThreadVar :=@BeInitThreadVar;
  432. RelocateThreadVar :=@BeRelocateThreadVar;
  433. AllocateThreadVars :=@BeAllocateThreadVars;
  434. ReleaseThreadVars :=@BeReleaseThreadVars;
  435. {$endif}
  436. BasicEventCreate :=@intBasicEventCreate;
  437. BasicEventDestroy :=@intBasicEventDestroy;
  438. BasicEventResetEvent :=@intBasicEventResetEvent;
  439. BasicEventSetEvent :=@intBasicEventSetEvent;
  440. BasiceventWaitFor :=@intBasiceventWaitFor;
  441. end;
  442. SetThreadManager(BeThreadManager);
  443. InitHeapMutexes;
  444. end;
  445. initialization
  446. SetBeThreadManager;
  447. end.