systhrd.inc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510
  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. { lazy initialize thread support }
  130. LazyInitThreading;
  131. IsMultithread:=true;
  132. end;
  133. { the only way to pass data to the newly created thread }
  134. { in a MT safe way, is to use the heap }
  135. new(ti);
  136. ti^.f:=ThreadFunction;
  137. ti^.p:=p;
  138. ti^.stklen:=stacksize;
  139. {$ifdef DEBUG_MT}
  140. ConsolePrintf(#13'Starting new thread'#13#10);
  141. {$endif DEBUG_MT}
  142. SysBeginThread :=
  143. _BeginThread (@ThreadMain,NIL,Stacksize,ti);
  144. end;
  145. procedure SysEndThread(ExitCode : DWord);
  146. begin
  147. {$ifdef DEBUG_MT}
  148. ConsolePrintf (#13'SysEndThread %x'#13#10,_GetThreadID);
  149. {$endif}
  150. DoneThread;
  151. ExitThread(ExitCode , TSR_THREAD);
  152. end;
  153. {*****************************************************************************
  154. Thread handling
  155. *****************************************************************************}
  156. function __SuspendThread (threadId : dword) : dword; cdecl; external 'clib' name 'SuspendThread';
  157. function __ResumeThread (threadId : dword) : dword; cdecl; external 'clib' name 'ResumeThread';
  158. procedure __ThreadSwitchWithDelay; cdecl; external 'clib' name 'ThreadSwitchWithDelay';
  159. procedure SysThreadSwitch;
  160. begin
  161. __ThreadSwitchWithDelay;
  162. end;
  163. {redefined because the interface has not cdecl calling convention}
  164. function SysSuspendThread (threadHandle : dword) : dword;
  165. begin
  166. SysSuspendThread := __SuspendThread (threadHandle);
  167. end;
  168. function SysResumeThread (threadHandle : dword) : dword;
  169. begin
  170. SysResumeThread := __ResumeThread (threadHandle);
  171. end;
  172. function SysKillThread (threadHandle : dword) : dword;
  173. begin
  174. SysKillThread := 1; {not supported for netware}
  175. end;
  176. function GetThreadName (threadId : longint; var threadName) : longint; cdecl; external 'clib' name 'GetThreadName';
  177. function CGetThreadID : dword; cdecl; external 'clib' name 'GetThreadID';
  178. //function __RenameThread (threadId : longint; threadName:pchar) : longint; cdecl; external 'clib' name 'RenameThread';
  179. function SysWaitForThreadTerminate (threadHandle : dword; TimeoutMs : longint) : dword;
  180. var
  181. status : longint;
  182. buf : array [0..50] of char;
  183. begin
  184. {$warning timeout needs to be implemented}
  185. {$ifdef DEBUG_MT}
  186. ConsolePrintf (#13'SysWaitForThreadTerminate ThreadID:%x Handle:%x'#13#10,GetThreadID,threadHandle);
  187. {$endif}
  188. repeat
  189. status := GetThreadName (ThreadHandle,Buf); {should return EBADHNDL if thread is terminated}
  190. ThreadSwitch;
  191. until status <> 0;
  192. SysWaitForThreadTerminate:=0;
  193. end;
  194. function SysThreadSetPriority (threadHandle : dword; Prio: longint): boolean; {-15..+15, 0=normal}
  195. begin
  196. SysThreadSetPriority := true;
  197. end;
  198. function SysThreadGetPriority (threadHandle : dword): Longint;
  199. begin
  200. SysThreadGetPriority := 0;
  201. end;
  202. function SysGetCurrentThreadId : dword;
  203. begin
  204. SysGetCurrentThreadId := CGetThreadID;
  205. end;
  206. procedure SysSetThreadDebugNameA(threadHandle: TThreadID; const ThreadName: AnsiString);
  207. begin
  208. {$Warning SetThreadDebugName needs to be implemented}
  209. end;
  210. procedure SysSetThreadDebugNameU(threadHandle: TThreadID; const ThreadName: UnicodeString);
  211. begin
  212. {$Warning SetThreadDebugName needs to be implemented}
  213. end;
  214. { netware requires all allocated semaphores }
  215. { to be closed before terminating the nlm, otherwise }
  216. { the server will abend (except for netware 6 i think) }
  217. TYPE TSemaList = ARRAY [1..1000] OF LONGINT;
  218. PSemaList = ^TSemaList;
  219. CONST NumSemaOpen : LONGINT = 0;
  220. NumEntriesMax : LONGINT = 0;
  221. SemaList : PSemaList = NIL;
  222. PROCEDURE SaveSema (Handle : LONGINT);
  223. BEGIN
  224. {$ifdef DEBUG_MT}
  225. ConsolePrintf(#13'new Semaphore allocated (%x)'#13#10,Handle);
  226. {$endif DEBUG_MT}
  227. _EnterCritSec;
  228. IF NumSemaOpen = NumEntriesMax THEN
  229. BEGIN
  230. IF SemaList = NIL THEN
  231. BEGIN
  232. SemaList := _malloc (32 * SIZEOF (TSemaList[0]));
  233. NumEntriesMax := 32;
  234. END ELSE
  235. BEGIN
  236. INC (NumEntriesMax, 16);
  237. SemaList := _realloc (SemaList, NumEntriesMax * SIZEOF (TSemaList[0]));
  238. END;
  239. END;
  240. INC (NumSemaOpen);
  241. SemaList^[NumSemaOpen] := Handle;
  242. _ExitCritSec;
  243. END;
  244. PROCEDURE ReleaseSema (Handle : LONGINT);
  245. VAR I : LONGINT;
  246. BEGIN
  247. {$ifdef DEBUG_MT}
  248. ConsolePrintf(#13'Semaphore released (%x)'#13#10,Handle);
  249. {$endif DEBUG_MT}
  250. _EnterCritSec;
  251. IF SemaList <> NIL then
  252. if NumSemaOpen > 0 then
  253. begin
  254. for i := 1 to NumSemaOpen do
  255. if SemaList^[i] = Handle then
  256. begin
  257. if i < NumSemaOpen then
  258. SemaList^[i] := SemaList^[NumSemaOpen];
  259. dec (NumSemaOpen);
  260. _ExitCritSec;
  261. exit;
  262. end;
  263. end;
  264. _ExitCritSec;
  265. ConsolePrintf (#13'fpc-rtl: ReleaseSema, Handle not found'#13#10,0);
  266. END;
  267. PROCEDURE SysCloseAllRemainingSemaphores;
  268. var i : LONGINT;
  269. begin
  270. IF SemaList <> NIL then
  271. begin
  272. if NumSemaOpen > 0 then
  273. for i := 1 to NumSemaOpen do
  274. _CloseLocalSemaphore (SemaList^[i]);
  275. _free (SemaList);
  276. SemaList := NIL;
  277. NumSemaOpen := 0;
  278. NumEntriesMax := 0;
  279. end;
  280. end;
  281. { this allows to do a lot of things in MT safe way }
  282. { it is also used to make the heap management }
  283. { thread safe }
  284. procedure SysInitCriticalSection(var cs);// : TRTLCriticalSection);
  285. begin
  286. with PRTLCriticalSection(@cs)^ do
  287. begin
  288. SemaHandle := _OpenLocalSemaphore (1);
  289. if SemaHandle <> 0 then
  290. begin
  291. SemaIsOpen := true;
  292. SaveSema (SemaHandle);
  293. end else
  294. begin
  295. SemaIsOpen := false;
  296. ConsolePrintf (#13'fpc-rtl: InitCriticalsection, OpenLocalSemaphore returned error'#13#10,0);
  297. end;
  298. end;
  299. end;
  300. procedure SysDoneCriticalsection(var cs);
  301. begin
  302. with PRTLCriticalSection(@cs)^ do
  303. begin
  304. if SemaIsOpen then
  305. begin
  306. _CloseLocalSemaphore (SemaHandle);
  307. ReleaseSema (SemaHandle);
  308. SemaIsOpen := FALSE;
  309. end;
  310. end;
  311. end;
  312. procedure SysEnterCriticalsection(var cs);
  313. begin
  314. with PRTLCriticalSection(@cs)^ do
  315. begin
  316. if SemaIsOpen then
  317. _WaitOnLocalSemaphore (SemaHandle)
  318. else
  319. ConsolePrintf (#13'fpc-rtl: EnterCriticalsection, TRTLCriticalSection not open'#13#10,0);
  320. end;
  321. end;
  322. procedure SysLeaveCriticalSection(var cs);
  323. begin
  324. with PRTLCriticalSection(@cs)^ do
  325. begin
  326. if SemaIsOpen then
  327. _SignalLocalSemaphore (SemaHandle)
  328. else
  329. ConsolePrintf (#13'fpc-rtl: LeaveCriticalsection, TRTLCriticalSection not open'#13#10,0);
  330. end;
  331. end;
  332. function SysSetThreadDataAreaPtr (newPtr:pointer):pointer;
  333. begin
  334. SysSetThreadDataAreaPtr := _GetThreadDataAreaPtr;
  335. if newPtr = nil then
  336. newPtr := thredvarsmainthread;
  337. _SaveThreadDataAreaPtr (newPtr);
  338. end;
  339. function intBasicEventCreate(EventAttributes : Pointer;
  340. AManualReset,InitialState : Boolean;const Name : ansistring):pEventState;
  341. begin
  342. {$WARNING TODO! intBasicEventCreate}
  343. end;
  344. procedure intbasiceventdestroy(state:peventstate);
  345. begin
  346. {$WARNING TODO! intbasiceventdestroy}
  347. end;
  348. procedure intbasiceventResetEvent(state:peventstate);
  349. begin
  350. {$WARNING TODO! intbasiceventResetEvent}
  351. end;
  352. procedure intbasiceventSetEvent(state:peventstate);
  353. begin
  354. {$WARNING TODO! intbasiceventSetEvent}
  355. end;
  356. function intbasiceventWaitFor(Timeout : Cardinal;state:peventstate) : longint;
  357. begin
  358. {$WARNING TODO! intbasiceventWaitFor}
  359. end;
  360. function intRTLEventCreate: PRTLEvent;
  361. begin
  362. {$WARNING TODO! intRTLEventCreate}
  363. end;
  364. procedure intRTLEventDestroy(AEvent: PRTLEvent);
  365. begin
  366. {$WARNING TODO! intRTLEventDestroy}
  367. end;
  368. procedure intRTLEventSetEvent(AEvent: PRTLEvent);
  369. begin
  370. {$WARNING TODO! intRTLEventSetEvent}
  371. end;
  372. procedure intRTLEventResetEvent(AEvent: PRTLEvent);
  373. begin
  374. {$WARNING TODO! intRTLEventResetEvent}
  375. end;
  376. procedure intRTLEventWaitFor(AEvent: PRTLEvent);
  377. begin
  378. {$WARNING TODO! intRTLEventWaitFor}
  379. end;
  380. procedure intRTLEventWaitForTimeout(AEvent: PRTLEvent;timeout : longint);
  381. begin
  382. {$WARNING TODO! intRTLEventWaitForTimeout}
  383. end;
  384. Var
  385. NWThreadManager : TThreadManager;
  386. Procedure InitSystemThreads;
  387. begin
  388. With NWThreadManager do
  389. begin
  390. InitManager :=Nil;
  391. DoneManager :=Nil;
  392. BeginThread :=@SysBeginThread;
  393. EndThread :=@SysEndThread;
  394. SuspendThread :=@SysSuspendThread;
  395. ResumeThread :=@SysResumeThread;
  396. KillThread :=@SysKillThread;
  397. ThreadSwitch :=@SysThreadSwitch;
  398. WaitForThreadTerminate :=@SysWaitForThreadTerminate;
  399. ThreadSetPriority :=@SysThreadSetPriority;
  400. ThreadGetPriority :=@SysThreadGetPriority;
  401. GetCurrentThreadId :=@SysGetCurrentThreadId;
  402. SetThreadDebugNameA :=@SysSetThreadDebugNameA;
  403. SetThreadDebugNameU :=@SysSetThreadDebugNameU;
  404. InitCriticalSection :=@SysInitCriticalSection;
  405. DoneCriticalSection :=@SysDoneCriticalSection;
  406. EnterCriticalSection :=@SysEnterCriticalSection;
  407. LeaveCriticalSection :=@SysLeaveCriticalSection;
  408. InitThreadVar :=@SysInitThreadVar;
  409. RelocateThreadVar :=@SysRelocateThreadVar;
  410. AllocateThreadVars :=@SysAllocateThreadVars;
  411. ReleaseThreadVars :=@SysReleaseThreadVars;
  412. BasicEventCreate :=@intBasicEventCreate;
  413. basiceventdestroy :=@intbasiceventdestroy;
  414. basiceventResetEvent :=@intbasiceventResetEvent;
  415. basiceventSetEvent :=@intbasiceventSetEvent;
  416. basiceventWaitFor :=@intbasiceventWaitFor;
  417. RTLEventCreate :=@intRTLEventCreate;
  418. RTLEventDestroy :=@intRTLEventDestroy;
  419. RTLEventSetEvent :=@intRTLEventSetEvent;
  420. RTLEventResetEvent :=@intRTLEventResetEvent;
  421. RTLEventWaitFor :=@intRTLEventWaitFor;
  422. RTLEventWaitForTimeout :=@intRTLEventWaitForTimeout;
  423. end;
  424. SetThreadManager(NWThreadManager);
  425. NWSysSetThreadFunctions (@SysCloseAllRemainingSemaphores,
  426. @SysReleaseThreadVars,
  427. @SysSetThreadDataAreaPtr);
  428. end;