systhrd.inc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508
  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. Linux (pthreads) 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. { Multithreading for netware, armin 16 Mar 2002
  13. - threads are basicly tested and working
  14. - TRTLCriticalSections are working but NEVER call Enter or
  15. LeaveCriticalSection with uninitialized CriticalSections.
  16. Critial Sections are based on local semaphores and the
  17. Server will abend if the semaphore handles are invalid. There
  18. are basic tests in the rtl but this will not work in every case.
  19. Not closed semaphores will be closed by the rtl on program
  20. termination because some versions of netware will abend if there
  21. are open semaphores on nlm unload.
  22. }
  23. {*****************************************************************************
  24. Threadvar support
  25. *****************************************************************************}
  26. const
  27. threadvarblocksize : dword = 0; // total size of allocated threadvars
  28. thredvarsmainthread: pointer = nil; // to free the threadvars in the signal handler
  29. procedure SysInitThreadvar (var offset : dword;size : dword);[public,alias: 'FPC_INIT_THREADVAR'];
  30. begin
  31. offset:=threadvarblocksize;
  32. inc(threadvarblocksize,size);
  33. {$ifdef DEBUG_MT}
  34. ConsolePrintf(#13'init_threadvar, new offset: (%d), Size:%d'#13#10,offset,size,0);
  35. {$endif DEBUG_MT}
  36. end;
  37. {$ifdef DEBUG_MT}
  38. var dummy_buff : array [0..255] of char; // to avoid abends (for current compiler error that not all threadvars are initialized)
  39. {$endif}
  40. function SysRelocateThreadvar (offset : dword) : pointer;
  41. var p : pointer;
  42. begin
  43. {$ifdef DEBUG_MT}
  44. // ConsolePrintf(#13'relocate_threadvar, offset: (%d)'#13#10,offset);
  45. if offset > threadvarblocksize then
  46. begin
  47. // ConsolePrintf(#13'relocate_threadvar, invalid offset'#13#10,0);
  48. SysRelocateThreadvar := @dummy_buff;
  49. exit;
  50. end;
  51. {$endif DEBUG_MT}
  52. SysRelocateThreadvar:= _GetThreadDataAreaPtr + offset;
  53. end;
  54. procedure SysAllocateThreadVars;
  55. var
  56. threadvars : pointer;
  57. begin
  58. { we've to allocate the memory from netware }
  59. { because the FPC heap management uses }
  60. { exceptions which use threadvars but }
  61. { these aren't allocated yet ... }
  62. { allocate room on the heap for the thread vars }
  63. threadvars := _malloc (threadvarblocksize);
  64. fillchar (threadvars^, threadvarblocksize, 0);
  65. _SaveThreadDataAreaPtr (threadvars);
  66. {$ifdef DEBUG_MT}
  67. ConsolePrintf(#13'threadvars allocated at (%x), size: %d'#13#10,longint(threadvars),threadvarblocksize,0);
  68. {$endif DEBUG_MT}
  69. if thredvarsmainthread = nil then
  70. thredvarsmainthread := threadvars;
  71. end;
  72. procedure SysReleaseThreadVars;
  73. var threadvars : pointer;
  74. begin
  75. { release thread vars }
  76. if threadvarblocksize > 0 then
  77. begin
  78. threadvars:=_GetThreadDataAreaPtr;
  79. if threadvars <> nil then
  80. begin
  81. {$ifdef DEBUG_MT}
  82. ConsolePrintf (#13'free threadvars'#13#10,0);
  83. {$endif DEBUG_MT}
  84. _Free (threadvars);
  85. _SaveThreadDataAreaPtr (nil);
  86. end;
  87. end;
  88. end;
  89. {*****************************************************************************
  90. Thread starting
  91. *****************************************************************************}
  92. type
  93. tthreadinfo = record
  94. f : tthreadfunc;
  95. p : pointer;
  96. stklen: cardinal;
  97. end;
  98. pthreadinfo = ^tthreadinfo;
  99. function ThreadMain(param : pointer) : dword; cdecl;
  100. var
  101. ti : tthreadinfo;
  102. begin
  103. { Allocate local thread vars, this must be the first thing,
  104. because the exception management and io depends on threadvars }
  105. SysAllocateThreadVars;
  106. {$ifdef DEBUG_MT}
  107. ConsolePrintf(#13'New thread %x started, initialising ...'#13#10,_GetThreadID);
  108. {$endif DEBUG_MT}
  109. ti:=pthreadinfo(param)^;
  110. InitThread(ti.stklen);
  111. dispose(pthreadinfo(param));
  112. {$ifdef DEBUG_MT}
  113. ConsolePrintf(#13'Jumping to thread function'#13#10);
  114. {$endif DEBUG_MT}
  115. ThreadMain:=ti.f(ti.p);
  116. DoneThread;
  117. end;
  118. function SysBeginThread(sa : Pointer;stacksize : SizeUInt;
  119. ThreadFunction : tthreadfunc;p : pointer;
  120. creationFlags : dword; var ThreadId : DWord) : DWord;
  121. var ti : pthreadinfo;
  122. begin
  123. {$ifdef DEBUG_MT}
  124. ConsolePrintf(#13'Creating new thread'#13#10);
  125. {$endif DEBUG_MT}
  126. if not IsMultiThread then
  127. begin
  128. InitThreadVars(@SysRelocateThreadvar);
  129. IsMultithread:=true;
  130. end;
  131. { the only way to pass data to the newly created thread }
  132. { in a MT safe way, is to use the heap }
  133. new(ti);
  134. ti^.f:=ThreadFunction;
  135. ti^.p:=p;
  136. ti^.stklen:=stacksize;
  137. {$ifdef DEBUG_MT}
  138. ConsolePrintf(#13'Starting new thread'#13#10);
  139. {$endif DEBUG_MT}
  140. SysBeginThread :=
  141. _BeginThread (@ThreadMain,NIL,Stacksize,ti);
  142. end;
  143. procedure SysEndThread(ExitCode : DWord);
  144. begin
  145. {$ifdef DEBUG_MT}
  146. ConsolePrintf (#13'SysEndThread %x'#13#10,_GetThreadID);
  147. {$endif}
  148. DoneThread;
  149. ExitThread(ExitCode , TSR_THREAD);
  150. end;
  151. {*****************************************************************************
  152. Thread handling
  153. *****************************************************************************}
  154. function __SuspendThread (threadId : dword) : dword; cdecl; external 'clib' name 'SuspendThread';
  155. function __ResumeThread (threadId : dword) : dword; cdecl; external 'clib' name 'ResumeThread';
  156. procedure __ThreadSwitchWithDelay; cdecl; external 'clib' name 'ThreadSwitchWithDelay';
  157. procedure SysThreadSwitch;
  158. begin
  159. __ThreadSwitchWithDelay;
  160. end;
  161. {redefined because the interface has not cdecl calling convention}
  162. function SysSuspendThread (threadHandle : dword) : dword;
  163. begin
  164. SysSuspendThread := __SuspendThread (threadHandle);
  165. end;
  166. function SysResumeThread (threadHandle : dword) : dword;
  167. begin
  168. SysResumeThread := __ResumeThread (threadHandle);
  169. end;
  170. function SysKillThread (threadHandle : dword) : dword;
  171. begin
  172. SysKillThread := 1; {not supported for netware}
  173. end;
  174. function GetThreadName (threadId : longint; var threadName) : longint; cdecl; external 'clib' name 'GetThreadName';
  175. function CGetThreadID : dword; cdecl; external 'clib' name 'GetThreadID';
  176. //function __RenameThread (threadId : longint; threadName:pchar) : longint; cdecl; external 'clib' name 'RenameThread';
  177. function SysWaitForThreadTerminate (threadHandle : dword; TimeoutMs : longint) : dword;
  178. var
  179. status : longint;
  180. buf : array [0..50] of char;
  181. begin
  182. {$warning timeout needs to be implemented}
  183. {$ifdef DEBUG_MT}
  184. ConsolePrintf (#13'SysWaitForThreadTerminate ThreadID:%x Handle:%x'#13#10,GetThreadID,threadHandle);
  185. {$endif}
  186. repeat
  187. status := GetThreadName (ThreadHandle,Buf); {should return EBADHNDL if thread is terminated}
  188. ThreadSwitch;
  189. until status <> 0;
  190. SysWaitForThreadTerminate:=0;
  191. end;
  192. function SysThreadSetPriority (threadHandle : dword; Prio: longint): boolean; {-15..+15, 0=normal}
  193. begin
  194. SysThreadSetPriority := true;
  195. end;
  196. function SysThreadGetPriority (threadHandle : dword): Longint;
  197. begin
  198. SysThreadGetPriority := 0;
  199. end;
  200. function SysGetCurrentThreadId : dword;
  201. begin
  202. SysGetCurrentThreadId := CGetThreadID;
  203. end;
  204. procedure SysSetThreadDebugNameA(threadHandle: TThreadID; const ThreadName: AnsiString);
  205. begin
  206. {$Warning SetThreadDebugName needs to be implemented}
  207. end;
  208. procedure SysSetThreadDebugNameU(threadHandle: TThreadID; const ThreadName: UnicodeString);
  209. begin
  210. {$Warning SetThreadDebugName needs to be implemented}
  211. end;
  212. { netware requires all allocated semaphores }
  213. { to be closed before terminating the nlm, otherwise }
  214. { the server will abend (except for netware 6 i think) }
  215. TYPE TSemaList = ARRAY [1..1000] OF LONGINT;
  216. PSemaList = ^TSemaList;
  217. CONST NumSemaOpen : LONGINT = 0;
  218. NumEntriesMax : LONGINT = 0;
  219. SemaList : PSemaList = NIL;
  220. PROCEDURE SaveSema (Handle : LONGINT);
  221. BEGIN
  222. {$ifdef DEBUG_MT}
  223. ConsolePrintf(#13'new Semaphore allocated (%x)'#13#10,Handle);
  224. {$endif DEBUG_MT}
  225. _EnterCritSec;
  226. IF NumSemaOpen = NumEntriesMax THEN
  227. BEGIN
  228. IF SemaList = NIL THEN
  229. BEGIN
  230. SemaList := _malloc (32 * SIZEOF (TSemaList[0]));
  231. NumEntriesMax := 32;
  232. END ELSE
  233. BEGIN
  234. INC (NumEntriesMax, 16);
  235. SemaList := _realloc (SemaList, NumEntriesMax * SIZEOF (TSemaList[0]));
  236. END;
  237. END;
  238. INC (NumSemaOpen);
  239. SemaList^[NumSemaOpen] := Handle;
  240. _ExitCritSec;
  241. END;
  242. PROCEDURE ReleaseSema (Handle : LONGINT);
  243. VAR I : LONGINT;
  244. BEGIN
  245. {$ifdef DEBUG_MT}
  246. ConsolePrintf(#13'Semaphore released (%x)'#13#10,Handle);
  247. {$endif DEBUG_MT}
  248. _EnterCritSec;
  249. IF SemaList <> NIL then
  250. if NumSemaOpen > 0 then
  251. begin
  252. for i := 1 to NumSemaOpen do
  253. if SemaList^[i] = Handle then
  254. begin
  255. if i < NumSemaOpen then
  256. SemaList^[i] := SemaList^[NumSemaOpen];
  257. dec (NumSemaOpen);
  258. _ExitCritSec;
  259. exit;
  260. end;
  261. end;
  262. _ExitCritSec;
  263. ConsolePrintf (#13'fpc-rtl: ReleaseSema, Handle not found'#13#10,0);
  264. END;
  265. PROCEDURE SysCloseAllRemainingSemaphores;
  266. var i : LONGINT;
  267. begin
  268. IF SemaList <> NIL then
  269. begin
  270. if NumSemaOpen > 0 then
  271. for i := 1 to NumSemaOpen do
  272. _CloseLocalSemaphore (SemaList^[i]);
  273. _free (SemaList);
  274. SemaList := NIL;
  275. NumSemaOpen := 0;
  276. NumEntriesMax := 0;
  277. end;
  278. end;
  279. { this allows to do a lot of things in MT safe way }
  280. { it is also used to make the heap management }
  281. { thread safe }
  282. procedure SysInitCriticalSection(var cs);// : TRTLCriticalSection);
  283. begin
  284. with PRTLCriticalSection(@cs)^ do
  285. begin
  286. SemaHandle := _OpenLocalSemaphore (1);
  287. if SemaHandle <> 0 then
  288. begin
  289. SemaIsOpen := true;
  290. SaveSema (SemaHandle);
  291. end else
  292. begin
  293. SemaIsOpen := false;
  294. ConsolePrintf (#13'fpc-rtl: InitCriticalsection, OpenLocalSemaphore returned error'#13#10,0);
  295. end;
  296. end;
  297. end;
  298. procedure SysDoneCriticalsection(var cs);
  299. begin
  300. with PRTLCriticalSection(@cs)^ do
  301. begin
  302. if SemaIsOpen then
  303. begin
  304. _CloseLocalSemaphore (SemaHandle);
  305. ReleaseSema (SemaHandle);
  306. SemaIsOpen := FALSE;
  307. end;
  308. end;
  309. end;
  310. procedure SysEnterCriticalsection(var cs);
  311. begin
  312. with PRTLCriticalSection(@cs)^ do
  313. begin
  314. if SemaIsOpen then
  315. _WaitOnLocalSemaphore (SemaHandle)
  316. else
  317. ConsolePrintf (#13'fpc-rtl: EnterCriticalsection, TRTLCriticalSection not open'#13#10,0);
  318. end;
  319. end;
  320. procedure SysLeaveCriticalSection(var cs);
  321. begin
  322. with PRTLCriticalSection(@cs)^ do
  323. begin
  324. if SemaIsOpen then
  325. _SignalLocalSemaphore (SemaHandle)
  326. else
  327. ConsolePrintf (#13'fpc-rtl: LeaveCriticalsection, TRTLCriticalSection not open'#13#10,0);
  328. end;
  329. end;
  330. function SysSetThreadDataAreaPtr (newPtr:pointer):pointer;
  331. begin
  332. SysSetThreadDataAreaPtr := _GetThreadDataAreaPtr;
  333. if newPtr = nil then
  334. newPtr := thredvarsmainthread;
  335. _SaveThreadDataAreaPtr (newPtr);
  336. end;
  337. function intBasicEventCreate(EventAttributes : Pointer;
  338. AManualReset,InitialState : Boolean;const Name : ansistring):pEventState;
  339. begin
  340. {$WARNING TODO! intBasicEventCreate}
  341. end;
  342. procedure intbasiceventdestroy(state:peventstate);
  343. begin
  344. {$WARNING TODO! intbasiceventdestroy}
  345. end;
  346. procedure intbasiceventResetEvent(state:peventstate);
  347. begin
  348. {$WARNING TODO! intbasiceventResetEvent}
  349. end;
  350. procedure intbasiceventSetEvent(state:peventstate);
  351. begin
  352. {$WARNING TODO! intbasiceventSetEvent}
  353. end;
  354. function intbasiceventWaitFor(Timeout : Cardinal;state:peventstate) : longint;
  355. begin
  356. {$WARNING TODO! intbasiceventWaitFor}
  357. end;
  358. function intRTLEventCreate: PRTLEvent;
  359. begin
  360. {$WARNING TODO! intRTLEventCreate}
  361. end;
  362. procedure intRTLEventDestroy(AEvent: PRTLEvent);
  363. begin
  364. {$WARNING TODO! intRTLEventDestroy}
  365. end;
  366. procedure intRTLEventSetEvent(AEvent: PRTLEvent);
  367. begin
  368. {$WARNING TODO! intRTLEventSetEvent}
  369. end;
  370. procedure intRTLEventResetEvent(AEvent: PRTLEvent);
  371. begin
  372. {$WARNING TODO! intRTLEventResetEvent}
  373. end;
  374. procedure intRTLEventWaitFor(AEvent: PRTLEvent);
  375. begin
  376. {$WARNING TODO! intRTLEventWaitFor}
  377. end;
  378. procedure intRTLEventWaitForTimeout(AEvent: PRTLEvent;timeout : longint);
  379. begin
  380. {$WARNING TODO! intRTLEventWaitForTimeout}
  381. end;
  382. Var
  383. NWThreadManager : TThreadManager;
  384. Procedure InitSystemThreads;
  385. begin
  386. With NWThreadManager do
  387. begin
  388. InitManager :=Nil;
  389. DoneManager :=Nil;
  390. BeginThread :=@SysBeginThread;
  391. EndThread :=@SysEndThread;
  392. SuspendThread :=@SysSuspendThread;
  393. ResumeThread :=@SysResumeThread;
  394. KillThread :=@SysKillThread;
  395. ThreadSwitch :=@SysThreadSwitch;
  396. WaitForThreadTerminate :=@SysWaitForThreadTerminate;
  397. ThreadSetPriority :=@SysThreadSetPriority;
  398. ThreadGetPriority :=@SysThreadGetPriority;
  399. GetCurrentThreadId :=@SysGetCurrentThreadId;
  400. SetThreadDebugNameA :=@SysSetThreadDebugNameA;
  401. SetThreadDebugNameU :=@SysSetThreadDebugNameU;
  402. InitCriticalSection :=@SysInitCriticalSection;
  403. DoneCriticalSection :=@SysDoneCriticalSection;
  404. EnterCriticalSection :=@SysEnterCriticalSection;
  405. LeaveCriticalSection :=@SysLeaveCriticalSection;
  406. InitThreadVar :=@SysInitThreadVar;
  407. RelocateThreadVar :=@SysRelocateThreadVar;
  408. AllocateThreadVars :=@SysAllocateThreadVars;
  409. ReleaseThreadVars :=@SysReleaseThreadVars;
  410. BasicEventCreate :=@intBasicEventCreate;
  411. basiceventdestroy :=@intbasiceventdestroy;
  412. basiceventResetEvent :=@intbasiceventResetEvent;
  413. basiceventSetEvent :=@intbasiceventSetEvent;
  414. basiceventWaitFor :=@intbasiceventWaitFor;
  415. RTLEventCreate :=@intRTLEventCreate;
  416. RTLEventDestroy :=@intRTLEventDestroy;
  417. RTLEventSetEvent :=@intRTLEventSetEvent;
  418. RTLEventResetEvent :=@intRTLEventResetEvent;
  419. RTLEventWaitFor :=@intRTLEventWaitFor;
  420. RTLEventWaitForTimeout :=@intRTLEventWaitForTimeout;
  421. end;
  422. SetThreadManager(NWThreadManager);
  423. NWSysSetThreadFunctions (@SysCloseAllRemainingSemaphores,
  424. @SysReleaseThreadVars,
  425. @SysSetThreadDataAreaPtr);
  426. end;