systhrd.inc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  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. { netware requires all allocated semaphores }
  205. { to be closed before terminating the nlm, otherwise }
  206. { the server will abend (except for netware 6 i think) }
  207. TYPE TSemaList = ARRAY [1..1000] OF LONGINT;
  208. PSemaList = ^TSemaList;
  209. CONST NumSemaOpen : LONGINT = 0;
  210. NumEntriesMax : LONGINT = 0;
  211. SemaList : PSemaList = NIL;
  212. PROCEDURE SaveSema (Handle : LONGINT);
  213. BEGIN
  214. {$ifdef DEBUG_MT}
  215. ConsolePrintf(#13'new Semaphore allocated (%x)'#13#10,Handle);
  216. {$endif DEBUG_MT}
  217. _EnterCritSec;
  218. IF NumSemaOpen = NumEntriesMax THEN
  219. BEGIN
  220. IF SemaList = NIL THEN
  221. BEGIN
  222. SemaList := _malloc (32 * SIZEOF (TSemaList[0]));
  223. NumEntriesMax := 32;
  224. END ELSE
  225. BEGIN
  226. INC (NumEntriesMax, 16);
  227. SemaList := _realloc (SemaList, NumEntriesMax * SIZEOF (TSemaList[0]));
  228. END;
  229. END;
  230. INC (NumSemaOpen);
  231. SemaList^[NumSemaOpen] := Handle;
  232. _ExitCritSec;
  233. END;
  234. PROCEDURE ReleaseSema (Handle : LONGINT);
  235. VAR I : LONGINT;
  236. BEGIN
  237. {$ifdef DEBUG_MT}
  238. ConsolePrintf(#13'Semaphore released (%x)'#13#10,Handle);
  239. {$endif DEBUG_MT}
  240. _EnterCritSec;
  241. IF SemaList <> NIL then
  242. if NumSemaOpen > 0 then
  243. begin
  244. for i := 1 to NumSemaOpen do
  245. if SemaList^[i] = Handle then
  246. begin
  247. if i < NumSemaOpen then
  248. SemaList^[i] := SemaList^[NumSemaOpen];
  249. dec (NumSemaOpen);
  250. _ExitCritSec;
  251. exit;
  252. end;
  253. end;
  254. _ExitCritSec;
  255. ConsolePrintf (#13'fpc-rtl: ReleaseSema, Handle not found'#13#10,0);
  256. END;
  257. PROCEDURE SysCloseAllRemainingSemaphores;
  258. var i : LONGINT;
  259. begin
  260. IF SemaList <> NIL then
  261. begin
  262. if NumSemaOpen > 0 then
  263. for i := 1 to NumSemaOpen do
  264. _CloseLocalSemaphore (SemaList^[i]);
  265. _free (SemaList);
  266. SemaList := NIL;
  267. NumSemaOpen := 0;
  268. NumEntriesMax := 0;
  269. end;
  270. end;
  271. { this allows to do a lot of things in MT safe way }
  272. { it is also used to make the heap management }
  273. { thread safe }
  274. procedure SysInitCriticalSection(var cs);// : TRTLCriticalSection);
  275. begin
  276. with PRTLCriticalSection(@cs)^ do
  277. begin
  278. SemaHandle := _OpenLocalSemaphore (1);
  279. if SemaHandle <> 0 then
  280. begin
  281. SemaIsOpen := true;
  282. SaveSema (SemaHandle);
  283. end else
  284. begin
  285. SemaIsOpen := false;
  286. ConsolePrintf (#13'fpc-rtl: InitCriticalsection, OpenLocalSemaphore returned error'#13#10,0);
  287. end;
  288. end;
  289. end;
  290. procedure SysDoneCriticalsection(var cs);
  291. begin
  292. with PRTLCriticalSection(@cs)^ do
  293. begin
  294. if SemaIsOpen then
  295. begin
  296. _CloseLocalSemaphore (SemaHandle);
  297. ReleaseSema (SemaHandle);
  298. SemaIsOpen := FALSE;
  299. end;
  300. end;
  301. end;
  302. procedure SysEnterCriticalsection(var cs);
  303. begin
  304. with PRTLCriticalSection(@cs)^ do
  305. begin
  306. if SemaIsOpen then
  307. _WaitOnLocalSemaphore (SemaHandle)
  308. else
  309. ConsolePrintf (#13'fpc-rtl: EnterCriticalsection, TRTLCriticalSection not open'#13#10,0);
  310. end;
  311. end;
  312. procedure SysLeaveCriticalSection(var cs);
  313. begin
  314. with PRTLCriticalSection(@cs)^ do
  315. begin
  316. if SemaIsOpen then
  317. _SignalLocalSemaphore (SemaHandle)
  318. else
  319. ConsolePrintf (#13'fpc-rtl: LeaveCriticalsection, TRTLCriticalSection not open'#13#10,0);
  320. end;
  321. end;
  322. function SysSetThreadDataAreaPtr (newPtr:pointer):pointer;
  323. begin
  324. SysSetThreadDataAreaPtr := _GetThreadDataAreaPtr;
  325. if newPtr = nil then
  326. newPtr := thredvarsmainthread;
  327. _SaveThreadDataAreaPtr (newPtr);
  328. end;
  329. function intBasicEventCreate(EventAttributes : Pointer;
  330. AManualReset,InitialState : Boolean;const Name : ansistring):pEventState;
  331. begin
  332. {$WARNING TODO! intBasicEventCreate}
  333. end;
  334. procedure intbasiceventdestroy(state:peventstate);
  335. begin
  336. {$WARNING TODO! intbasiceventdestroy}
  337. end;
  338. procedure intbasiceventResetEvent(state:peventstate);
  339. begin
  340. {$WARNING TODO! intbasiceventResetEvent}
  341. end;
  342. procedure intbasiceventSetEvent(state:peventstate);
  343. begin
  344. {$WARNING TODO! intbasiceventSetEvent}
  345. end;
  346. function intbasiceventWaitFor(Timeout : Cardinal;state:peventstate) : longint;
  347. begin
  348. {$WARNING TODO! intbasiceventWaitFor}
  349. end;
  350. function intRTLEventCreate: PRTLEvent;
  351. begin
  352. {$WARNING TODO! intRTLEventCreate}
  353. end;
  354. procedure intRTLEventDestroy(AEvent: PRTLEvent);
  355. begin
  356. {$WARNING TODO! intRTLEventDestroy}
  357. end;
  358. procedure intRTLEventSetEvent(AEvent: PRTLEvent);
  359. begin
  360. {$WARNING TODO! intRTLEventSetEvent}
  361. end;
  362. procedure intRTLEventResetEvent(AEvent: PRTLEvent);
  363. begin
  364. {$WARNING TODO! intRTLEventResetEvent}
  365. end;
  366. procedure intRTLEventWaitFor(AEvent: PRTLEvent);
  367. begin
  368. {$WARNING TODO! intRTLEventWaitFor}
  369. end;
  370. procedure intRTLEventWaitForTimeout(AEvent: PRTLEvent;timeout : longint);
  371. begin
  372. {$WARNING TODO! intRTLEventWaitForTimeout}
  373. end;
  374. Var
  375. NWThreadManager : TThreadManager;
  376. Procedure InitSystemThreads;
  377. begin
  378. With NWThreadManager do
  379. begin
  380. InitManager :=Nil;
  381. DoneManager :=Nil;
  382. BeginThread :=@SysBeginThread;
  383. EndThread :=@SysEndThread;
  384. SuspendThread :=@SysSuspendThread;
  385. ResumeThread :=@SysResumeThread;
  386. KillThread :=@SysKillThread;
  387. ThreadSwitch :=@SysThreadSwitch;
  388. WaitForThreadTerminate :=@SysWaitForThreadTerminate;
  389. ThreadSetPriority :=@SysThreadSetPriority;
  390. ThreadGetPriority :=@SysThreadGetPriority;
  391. GetCurrentThreadId :=@SysGetCurrentThreadId;
  392. InitCriticalSection :=@SysInitCriticalSection;
  393. DoneCriticalSection :=@SysDoneCriticalSection;
  394. EnterCriticalSection :=@SysEnterCriticalSection;
  395. LeaveCriticalSection :=@SysLeaveCriticalSection;
  396. InitThreadVar :=@SysInitThreadVar;
  397. RelocateThreadVar :=@SysRelocateThreadVar;
  398. AllocateThreadVars :=@SysAllocateThreadVars;
  399. ReleaseThreadVars :=@SysReleaseThreadVars;
  400. BasicEventCreate :=@intBasicEventCreate;
  401. basiceventdestroy :=@intbasiceventdestroy;
  402. basiceventResetEvent :=@intbasiceventResetEvent;
  403. basiceventSetEvent :=@intbasiceventSetEvent;
  404. basiceventWaitFor :=@intbasiceventWaitFor;
  405. RTLEventCreate :=@intRTLEventCreate;
  406. RTLEventDestroy :=@intRTLEventDestroy;
  407. RTLEventSetEvent :=@intRTLEventSetEvent;
  408. RTLEventResetEvent :=@intRTLEventResetEvent;
  409. RTLEventWaitFor :=@intRTLEventWaitFor;
  410. RTLEventWaitForTimeout :=@intRTLEventWaitForTimeout;
  411. end;
  412. SetThreadManager(NWThreadManager);
  413. NWSysSetThreadFunctions (@SysCloseAllRemainingSemaphores,
  414. @SysReleaseThreadVars,
  415. @SysSetThreadDataAreaPtr);
  416. end;