{Kooperatives Multithreading unter Turbo Pascal und DOS}
{(c) haftmann#software Chemnitz 1996}
{$A+,B-,D+,E-,F-,G-,I-,L+,N-,O-,P-,Q-,R-,S-,T-,V-,X+}
unit threads;
interface
const
RTE_Thread=220; {Laufzeitfehler-Code dieser Unit}
type
PThread=^TThread;
TThread=record {Thread-"Deskriptor" (hnlich wie TSS)}
next: PThread; {Nchster der Einfach Verketteten (Ring-)Liste (EVL)}
stack: Pointer; {Stackbereich von GetMem fr FreeMem}
case integer of
1: (_sp,_ss: Word); {Speicher fr SS und SP - mehr ist gar nicht ntig!}
2: (sssp: Pointer);
end;
var
FirstThread: TThread; {Hauptprogramm; das Feld "stack" ist ungltig}
CurThread: PThread; {Zeiger auf "Deskriptor" des lfd. Threads}
function CreateThread(Entry:Pointer; Param:LongInt; Stacksize:Word):PThread;
{Einen neuen Thread erzeugen. Entry zeigt auf eine globale, FAR-codierte
Prozedur mit hchstens einem Argument mit max. 4 Bytes Gre, z.B.
kein Arg, Char, Integer, LongInt, Pointer, PChar. Der bergabeparameter
ist Param, der ggf. zu auf LongInt zu typecasten ist. Auf diese Weise
kann ein und dieselbe Thread-Prozedur mehrfach gestartet werden. Der
Parameter Stacksize gibt die Gre des Stacks in Bytes an. Er wird auf
dem Heap angelegt. Achtung: Vermutlich funktioniert mit dieser Unit
die Stackprfung ($S) innerhalb der Threads nicht mehr!}
procedure Yield; far;
{Zum nchsten Thread schalten. Mu aufgrund der Kooperativitt gelegentlich -
mglichst hufig - aufgerufen bzw. eingebaut werden. Zerstrt in blicher
Pascal-Manier alle Register auer CS:IP,SS:SP,DS und BP. DS mu wie immer
in Pascal aufs Datensegment zeigen}
procedure _Yield; far;
{Zum nchsten Thread schalten, ohne BP zu retten. Fr die Erweiterung zum
premptiven Multitasking oder zur Rettung weiterer Register vorgesehene
Funktion. Nicht geeignet fr Aufruf in gewhnlichen Pascal-Programmen}
procedure KillThread(Thread:PThread);
{Lscht den angegebenen Thread. Darf nicht der aktuelle Thread und nicht
FirstThread (das Hauptprogramm) sein, sonst Laufzeitfehler!}
procedure KillCurThread;
{Lscht den momentan laufenden Thread. Darf nicht im Hauptprogramm gerufen
werden, sonst Laufzeitfehler! "Voreilige" Alternative zum Beenden des Threads
mit dem Beenden der Threadprozedur selbst}
implementation
procedure _Yield; assembler; {Zum nchsten Thread schalten}
{Alle Register auer DS,CS,IP,SS,SP werden zerstrt}
{DS mu - wie immer in Pascal - ins Standard-Datensegment zeigen}
asm
les di,[CurThread]
mov es:TThread [di]._ss,ss {Stackzeiger retten}
mov es:TThread [di]._sp,sp
les di,es:TThread [di].next {Nchster Thread}
mov word [CurThread],di {Neuer aktueller Thread}
mov word [CurThread+2],es
mov ss,es:TThread [di]._ss {Stackzeiger setzen}
mov sp,es:TThread [di]._sp
end; {springt zum Thread oder zu "pop bp"}
procedure Yield; assembler; {Zum nchsten Thread schalten}
{BP wird zustzlich gerettet - Pascal-freundlich}
asm
push bp
call _Yield
pop bp
end;
function CreateThread(Entry:Pointer; Param:LongInt; Stacksize:Word):PThread;
var
P: PThread;
SP: Pointer; {Stack-Ersatzzeiger}
begin
{Ende der verketteten Liste suchen}
P:=@FirstThread;
while P^.next<>@FirstThread do P:=P^.next;
New(P^.next); {Neuen EVL-Knoten erzeugen}
P:=P^.next; {Diesen adressieren}
P^.next:=@FirstThread;{Nchster Knoten ist der Anfang}
GetMem(SP,StackSize); {Stack-Speicher anfordern}
P^.stack:=SP;
Word(SP^):=StackSize; {Stack-Gre zur Freigabe vorn vermerken}
Inc(PChar(SP),Stacksize); {ans Stack-Speicher-Ende}
Dec(PChar(SP),sizeof(LongInt));
LongInt(SP^):=Param; {Simulate_Push Argument}
Dec(PChar(SP),sizeof(Pointer));
Pointer(SP^):=@KillCurThread; {Simulate_Push Return-CS:IP}
Dec(PChar(SP),sizeof(Pointer));
Pointer(SP^):=Entry; {Simulate_Push CS:IP}
P^.sssp:=SP; {Thread-Stackspitze abspeichern}
CreateThread:=P;
end;
procedure ThreadError;
begin
RunError(RTE_Thread);
end;
function GetPrevThread(Thread:PThread):PThread;
var
P:PThread;
begin
P:=@FirstThread;
repeat
if P^.next=nil then ThreadError; {Thread-Zeigerfehler}
if P^.next=@FirstThread then ThreadError; {Ende der Liste}
if P^.next=Thread then break;
P:=P^.next;
until false;
GetPrevThread:=P;
end;
procedure KillThread(Thread:PThread);
begin
if Thread=CurThread then ThreadError; {Nicht fr laufenden Thread!}
if Thread=@FirstThread then ThreadError; {Nicht fr Hauptprogramm}
GetPrevThread(Thread)^.next:=Thread^.next; {Aktuellen Thread aushngen}
FreeMem(Thread^.stack,Word(Thread^.stack^)); {Speicher freigeben}
Dispose(Thread); {EVL-Knoten freigeben}
end;
procedure KillCurThread; assembler;
asm
les bx,[CurThread] {Retten des Stackpointers kann entfallen}
mov cx,es {Zeiger retten (nicht auf dem Stack!)}
les di,es:TThread [bx].next {Nchster Thread}
mov word [CurThread],di {Neuer aktueller Thread}
mov word [CurThread+2],es
mov ss,es:TThread [di]._ss {Stackzeiger setzen}
mov sp,es:TThread [di]._sp
push cx
push bx {Thread, nun nicht mehr CurThread!}
call KillThread
end;
{Initialisierungsteil}
begin
FirstThread.next:=@FirstThread;{das Hauptprogramm allein}
CurThread:=@FirstThread; {Momentan luft nur dieses}
end.
Vorgefundene Kodierung: UTF-8 | 0
|