mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-05-02 16:53:41 +02:00
285 lines
7.2 KiB
PHP
285 lines
7.2 KiB
PHP
{
|
|
$Id$
|
|
This file is part of the Free Pascal run time library.
|
|
Copyright (c) 1999-2000 by the Free Pascal development team.
|
|
|
|
Multithreading implementation for Win32
|
|
|
|
See the file COPYING.FPC, included in this distribution,
|
|
for details about the copyright.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
**********************************************************************}
|
|
{$ifdef MT}
|
|
const
|
|
threadvarblocksize : dword = 0;
|
|
|
|
type
|
|
tthreadinfo = record
|
|
f : tthreadfunc;
|
|
p : pointer;
|
|
end;
|
|
pthreadinfo = ^tthreadinfo;
|
|
|
|
var
|
|
dataindex : dword;
|
|
|
|
{ import the necessary stuff from windows }
|
|
function TlsAlloc : DWord;
|
|
external 'kernel32' name 'TlsAlloc';
|
|
function TlsGetValue(dwTlsIndex : DWord) : pointer;
|
|
external 'kernel32' name 'TlsGetValue';
|
|
function TlsSetValue(dwTlsIndex : DWord;lpTlsValue : pointer) : LongBool;
|
|
external 'kernel32' name 'TlsSetValue';
|
|
function TlsFree(dwTlsIndex : DWord) : LongBool;
|
|
external 'kernel32' name 'TlsFree';
|
|
function CreateThread(lpThreadAttributes : pointer;
|
|
dwStackSize : DWord; lpStartAddress : pointer;lpParameter : pointer;
|
|
dwCreationFlags : DWord;var lpThreadId : DWord) : THandle;
|
|
external 'kernel32' name 'CreateThread';
|
|
procedure ExitThread(dwExitCode : DWord);
|
|
external 'kernel32' name 'ExitThread';
|
|
function GlobalAlloc(uFlags:UINT; dwBytes:DWORD):Pointer;
|
|
external 'kernel32' name 'GlobalAlloc';
|
|
function GlobalFree(hMem : Pointer):Pointer; external 'kernel32' name 'GlobalFree';
|
|
|
|
const
|
|
{ GlobalAlloc, GlobalFlags }
|
|
GMEM_FIXED = 0;
|
|
GMEM_ZEROINIT = 64;
|
|
|
|
procedure init_threadvar(var offset : dword;size : dword);[public,alias: 'FPC_INIT_THREADVAR'];
|
|
|
|
begin
|
|
offset:=threadvarblocksize;
|
|
inc(threadvarblocksize,size);
|
|
end;
|
|
|
|
|
|
type
|
|
ltvInitEntry = packed record
|
|
varaddr : pdword;
|
|
size : longint;
|
|
end;
|
|
pltvInitEntry = ^ltvInitEntry;
|
|
|
|
procedure init_unit_threadvars (tableEntry : pltvInitEntry);
|
|
begin
|
|
while tableEntry^.varaddr <> nil do
|
|
begin
|
|
init_threadvar (tableEntry^.varaddr^, tableEntry^.size);
|
|
inc (pchar (tableEntry), sizeof (tableEntry^));
|
|
end;
|
|
end;
|
|
|
|
type TltvInitTablesTable =
|
|
record
|
|
count : dword;
|
|
tables: array [1..32767] of pltvInitEntry;
|
|
end;
|
|
|
|
var
|
|
ThreadvarTablesTable : TltvInitTablesTable; external name 'FPC_LOCALTHREADVARTABLES';
|
|
|
|
procedure init_all_unit_threadvars; [public,alias: 'FPC_INITIALIZELOCALTHREADVARS'];
|
|
var i : integer;
|
|
begin
|
|
{$ifdef DEBUG_MT}
|
|
WriteLn ('init_all_unit_threadvars (%d) units',ThreadvarTablesTable.count);
|
|
{$endif}
|
|
for i := 1 to ThreadvarTablesTable.count do
|
|
init_unit_threadvars (ThreadvarTablesTable.tables[i]);
|
|
end;
|
|
|
|
|
|
function relocate_threadvar(offset : dword) : pointer;[public,alias: 'FPC_RELOCATE_THREADVAR'];
|
|
|
|
begin
|
|
asm
|
|
pushal
|
|
end;
|
|
relocate_threadvar:=TlsGetValue(dataindex)+offset;
|
|
asm
|
|
popal
|
|
end;
|
|
end;
|
|
|
|
procedure AllocateThreadVars;
|
|
|
|
var
|
|
threadvars : pointer;
|
|
|
|
begin
|
|
{ we've to allocate the memory from windows }
|
|
{ because the FPC heap management uses }
|
|
{ exceptions which use threadvars but }
|
|
{ these aren't allocated yet ... }
|
|
{ allocate room on the heap for the thread vars }
|
|
threadvars:=pointer(GlobalAlloc(GMEM_FIXED or GMEM_ZEROINIT,
|
|
threadvarblocksize));
|
|
TlsSetValue(dataindex,threadvars);
|
|
end;
|
|
|
|
procedure ReleaseThreadVars;
|
|
|
|
var
|
|
threadvars : pointer;
|
|
|
|
begin
|
|
{ release thread vars }
|
|
threadvars:=TlsGetValue(dataindex);
|
|
GlobalFree(threadvars);
|
|
end;
|
|
|
|
procedure InitThread;
|
|
|
|
begin
|
|
InitFPU;
|
|
{ we don't need to set the data to 0 because we did this with }
|
|
{ the fillchar above, but it looks nicer }
|
|
|
|
{ ExceptAddrStack and ExceptObjectStack are threadvars }
|
|
{ so every thread has its own exception handling capabilities }
|
|
InitExceptions;
|
|
InOutRes:=0;
|
|
// ErrNo:=0;
|
|
end;
|
|
|
|
procedure DoneThread;
|
|
|
|
begin
|
|
{ release thread vars }
|
|
ReleaseThreadVars;
|
|
end;
|
|
|
|
function ThreadMain(param : pointer) : dword;stdcall;
|
|
|
|
var
|
|
ti : tthreadinfo;
|
|
|
|
begin
|
|
{$ifdef DEBUG_MT}
|
|
writeln('New thread started, initialising ...');
|
|
{$endif DEBUG_MT}
|
|
AllocateThreadVars;
|
|
InitThread;
|
|
ti:=pthreadinfo(param)^;
|
|
dispose(pthreadinfo(param));
|
|
{$ifdef DEBUG_MT}
|
|
writeln('Jumping to thread function');
|
|
{$endif DEBUG_MT}
|
|
ThreadMain:=ti.f(ti.p);
|
|
DoneThread;
|
|
end;
|
|
|
|
|
|
function BeginThread(sa : Pointer;stacksize : dword;
|
|
ThreadFunction : tthreadfunc;p : pointer;creationFlags : dword;
|
|
var ThreadId : DWord) : DWord;
|
|
|
|
var
|
|
ti : pthreadinfo;
|
|
|
|
begin
|
|
{$ifdef DEBUG_MT}
|
|
writeln('Creating new thread');
|
|
{$endif DEBUG_MT}
|
|
IsMultithread:=true;
|
|
{ the only way to pass data to the newly created thread }
|
|
{ in a MT safe way, is to use the heap }
|
|
new(ti);
|
|
ti^.f:=ThreadFunction;
|
|
ti^.p:=p;
|
|
{$ifdef DEBUG_MT}
|
|
writeln('Starting new thread');
|
|
{$endif DEBUG_MT}
|
|
BeginThread:=CreateThread(sa,stacksize,@ThreadMain,ti,
|
|
creationflags,threadid);
|
|
end;
|
|
|
|
function BeginThread(ThreadFunction : tthreadfunc) : DWord;
|
|
|
|
var
|
|
dummy : dword;
|
|
|
|
begin
|
|
BeginThread:=BeginThread(nil,0,ThreadFunction,nil,0,dummy);
|
|
end;
|
|
|
|
function BeginThread(ThreadFunction : tthreadfunc;p : pointer) : DWord;
|
|
var
|
|
dummy : dword;
|
|
begin
|
|
BeginThread:=BeginThread(nil,0,ThreadFunction,p,0,dummy);
|
|
end;
|
|
|
|
function BeginThread(ThreadFunction : tthreadfunc;p : pointer;
|
|
var ThreadId : DWord) : DWord;
|
|
begin
|
|
BeginThread:=BeginThread(nil,0,ThreadFunction,p,0,ThreadId);
|
|
end;
|
|
|
|
function BeginThread(ThreadFunction : tthreadfunc;p : pointer;
|
|
var ThreadId : Longint) : DWord;
|
|
begin
|
|
BeginThread:=BeginThread(nil,0,ThreadFunction,p,0,DWord(ThreadId));
|
|
end;
|
|
|
|
|
|
function BeginThread(sa : Pointer;stacksize : dword;
|
|
ThreadFunction : tthreadfunc;p : pointer;creationFlags : dword;
|
|
var ThreadId : Longint) : DWord;
|
|
begin
|
|
BeginThread:=BeginThread(sa,stacksize,ThreadFunction,p,creationflags,DWord(threadid));
|
|
end;
|
|
|
|
|
|
procedure EndThread(ExitCode : DWord);
|
|
|
|
begin
|
|
DoneThread;
|
|
ExitThread(ExitCode);
|
|
end;
|
|
|
|
procedure EndThread;
|
|
|
|
begin
|
|
EndThread(0);
|
|
end;
|
|
|
|
{ we implement these procedures for win32 by importing them }
|
|
{ directly from windows }
|
|
procedure InitCriticalSection(var cs : TRTLCriticalSection);
|
|
external 'kernel32' name 'InitializeCriticalSection';
|
|
|
|
procedure DoneCriticalSection(var cs : TRTLCriticalSection);
|
|
external 'kernel32' name 'DeleteCriticalSection';
|
|
|
|
procedure EnterCriticalSection(var cs : TRTLCriticalSection);
|
|
external 'kernel32' name 'EnterCriticalSection';
|
|
|
|
procedure LeaveCriticalSection(var cs : TRTLCriticalSection);
|
|
external 'kernel32' name 'LeaveCriticalSection';
|
|
|
|
{$endif MT}
|
|
|
|
{
|
|
$Log$
|
|
Revision 1.10 2002-09-07 16:01:29 peter
|
|
* old logs removed and tabs fixed
|
|
|
|
Revision 1.9 2002/07/28 20:43:50 florian
|
|
* several fixes for linux/powerpc
|
|
* several fixes to MT
|
|
|
|
Revision 1.8 2002/03/31 10:03:13 armin
|
|
+ call to DoneThread was missing
|
|
|
|
Revision 1.7 2002/03/28 16:31:35 armin
|
|
+ initialize threadvars defined local in units
|
|
|
|
}
|