Ignore:
Timestamp:
Jan 27, 2011, 10:30:55 AM (13 years ago)
Author:
george
Message:
  • Fixed: Wrong context restoration after return from micro thread.
  • Add: Option of TMicroThreadScheduler to set use or not use main thread for scheduling.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • MicroThreading/UMicroThreading.pas

    r151 r152  
    1818  cthreads,
    1919  {$ENDIF}{$ENDIF}
    20   Classes, SysUtils, Contnrs, SyncObjs, DateUtils, Dialogs, Forms, UPlatform;
     20  Classes, ExtCtrls, SysUtils, Contnrs, SyncObjs, DateUtils, Dialogs, Forms, UPlatform;
    2121
    2222const
     
    9898  TMicroThreadThread = class(TThread)
    9999    Manager: TMicroThreadManager;
     100    ExecuteTerminated: Boolean;
    100101    procedure Execute; override;
    101102    constructor Create(CreateSuspended: Boolean;
     
    119120    function Execute(Count: Integer): Integer;
    120121  public
     122    Id: Integer;
    121123    procedure Yield;
    122124    constructor Create;
     
    139141    FLastId: Integer;
    140142    FFrequency: Int64;
    141     FTerminated: Boolean;
     143    FMainThreadTerminated: Boolean;
    142144    FMicroThreads: TObjectList; // TList<TMicroThread>
    143145    FMainThreadManager: TMicroThreadManager;
    144146    FMicroThreadsLock: TCriticalSection;
    145147    FState: TMicroThreadSchedulerState;
     148    FUseMainThread: Boolean;
     149    FMainThreadStarter: TTimer;
    146150    function GetMicroThreadCount: Integer;
    147151    function GetThreadPoolCount: Integer;
     
    150154    procedure SetThreadPoolSize(const AValue: Integer);
    151155    function GetNextMicroThread: TMicroThread;
     156    procedure ReleaseMicroThread(MicroThread: TMicroThread);
     157    procedure SetUseMainThread(const AValue: Boolean);
    152158    procedure Start;
    153159    procedure Stop;
    154160    procedure PoolThreadTerminated(Sender: TObject);
    155161    procedure UpdateThreadPoolSize;
     162    procedure MainThreadStart(Sender: TObject);
    156163  public
    157164    function Add(MicroThread: TMicroThread): Integer;
     
    168175    property MainThreadManager: TMicroThreadManager read FMainThreadManager;
    169176    property Active: Boolean read FActive write SetActive;
     177    property UseMainThread: Boolean read FUseMainThread write SetUseMainThread;
    170178  end;
    171179
     
    222230
    223231procedure TMicroThreadManager.Yield;
    224 var
    225   I: Integer;
    226   CurrentTime: TDateTime;
    227 begin
    228   CurrentTime := NowPrecise;
     232begin
    229233  if Assigned(FCurrentMicroThread) then begin
    230     FCurrentMicroThread.FExecutionEndTime := CurrentTime;
     234    FCurrentMicroThread.FExecutionEndTime := NowPrecise;
    231235    FCurrentMicroThread.FExecutionTime := FCurrentMicroThread.FExecutionTime +
    232236      (FCurrentMicroThread.FExecutionEndTime - FCurrentMicroThread.FExecutionStartTime);
    233     if FCurrentMicroThread.FState = tsRunning then
    234       FCurrentMicroThread.FState := tsWaiting;
    235237    asm
    236238      // Store microthread stack
     
    249251    end;
    250252    FCurrentMicroThread.FManager := nil;
     253    FScheduler.ReleaseMicroThread(FCurrentMicroThread);
    251254    FCurrentMicroThread := nil;
    252255  end;
    253256
    254   FCurrentMicroThread := FScheduler.GetNextMicroThread;
    255 
    256   if Assigned(FCurrentMicroThread) and (FExecutedCount < FExecuteCount) then begin
    257     FCurrentMicroThread.FManager := Self;
    258     Inc(FExecutedCount);
    259     asm
    260       // Store FScheduler stack
    261       mov eax, Self
    262       mov edx, esp
    263       mov [eax].TMicroThreadManager.FStackPointer, edx
    264       mov edx, ebp
    265       mov [eax].TMicroThreadManager.FBasePointer, edx
    266     end;
    267     if not FCurrentMicroThread.FExecuted then begin
    268       FCurrentMicroThread.FExecuted := True;
    269       FCurrentMicroThread.FState := tsRunning;
    270       FCurrentMicroThread.FExecutionStartTime := CurrentTime;
     257  if FExecutedCount < FExecuteCount then begin
     258    FCurrentMicroThread := FScheduler.GetNextMicroThread;
     259
     260    if Assigned(FCurrentMicroThread) then begin
     261      Inc(FExecutedCount);
     262      FCurrentMicroThread.FExecutionStartTime := NowPrecise;
     263      FCurrentMicroThread.FManager := Self;
    271264      asm
    272         // Restore microthread stack
    273         mov ecx, Self
    274         mov eax, [ecx].TMicroThreadManager.FCurrentMicroThread
    275         mov edx, [eax].TMicroThread.FStackPointer
    276         mov ecx, esp
    277         mov esp, edx
    278         push ebp // remember bp on micro thread stack for read back
    279         push ecx
    280         mov edx, [eax].TMicroThread.FBasePointer
    281         mov ebp, edx
    282         // We want to call virtual method Execute
    283         // but virtual methods can be called only statically
    284         // Then static method CallExecute is calling virtual method Execute
    285         call TMicroThread.CallExecute
    286 //      end;
    287 //      StaticMicroThread.Execute;
    288 //      asm
    289         pop edx
    290         pop ebp
    291         mov esp, edx
     265        // Store FScheduler stack
     266        mov eax, Self
     267        mov edx, esp
     268        mov [eax].TMicroThreadManager.FStackPointer, edx
     269        mov edx, ebp
     270        mov [eax].TMicroThreadManager.FBasePointer, edx
    292271      end;
    293       //FSelected.Method(FSelected);
    294   (*    StaticManager := FCurrentMicroThread.FManager;
    295       asm
    296         // Restore FScheduler stack
    297         mov eax, StaticManager // Self is invalid before BP restore
    298         mov edx, [eax].TMicroThreadManager.FStackPointer
    299         mov esp, edx
    300         mov edx, [eax].TMicroThreadManager.FBasePointer
    301         mov ebp, edx
    302       end;
    303 *)
    304       FCurrentMicroThread.FManager := nil;
    305       FCurrentMicroThread.FExecutionEndTime := CurrentTime;
    306       FCurrentMicroThread.FExecutionTime := FCurrentMicroThread.FExecutionTime +
    307        (FCurrentMicroThread.FExecutionEndTime - FCurrentMicroThread.FExecutionStartTime);
    308       FCurrentMicroThread.FFinished := True;
    309       if FCurrentMicroThread.FFreeOnTerminate then begin
    310         // Microthread is finished, remove it from queue
    311         with FScheduler do
    312         try
    313           FMicroThreadsLock.Acquire;
    314           FMicroThreads.Delete(FMicroThreads.IndexOf(FCurrentMicroThread));
    315         finally
    316           FMicroThreadsLock.Release;
     272      if not FCurrentMicroThread.FExecuted then begin
     273        FCurrentMicroThread.FExecuted := True;
     274        asm
     275          // Restore microthread stack
     276          mov ecx, Self
     277          mov eax, [ecx].TMicroThreadManager.FCurrentMicroThread
     278          mov edx, [eax].TMicroThread.FStackPointer
     279          mov esp, edx
     280          mov edx, [eax].TMicroThread.FBasePointer
     281          mov ebp, edx
     282          // We want to call virtual method Execute
     283          // but virtual methods can be called only statically
     284          // Then static method CallExecute is calling virtual method Execute
     285          call TMicroThread.CallExecute
     286
     287          // Restore FScheduler stack
     288          // ecx register is set by CallExecute to running micro thread
     289          mov eax, [ecx].TMicroThread.FManager
     290          mov edx, [eax].TMicroThreadManager.FStackPointer
     291          mov esp, edx
     292          mov edx, [eax].TMicroThreadManager.FBasePointer
     293          mov ebp, edx
     294        end;
     295
     296        FCurrentMicroThread.FExecutionEndTime := NowPrecise;
     297        FCurrentMicroThread.FExecutionTime := FCurrentMicroThread.FExecutionTime +
     298         (FCurrentMicroThread.FExecutionEndTime - FCurrentMicroThread.FExecutionStartTime);
     299        FCurrentMicroThread.FFinished := True;
     300        if FCurrentMicroThread.FFreeOnTerminate then begin
     301          // Microthread is finished, remove it from queue
     302          with FScheduler do
     303          try
     304            FMicroThreadsLock.Acquire;
     305            FMicroThreads.Delete(FMicroThreads.IndexOf(FCurrentMicroThread));
     306          finally
     307            FMicroThreadsLock.Release;
     308          end;
     309        end else begin
     310          FCurrentMicroThread.FManager := nil;
     311          FScheduler.ReleaseMicroThread(FCurrentMicroThread);
     312        end;
     313        //FCurrentMicroThread.FManager := nil;
     314        //FScheduler.ReleaseMicroThread(FCurrentMicroThread);
     315        FCurrentMicroThread := nil;
     316      end else
     317      //if FCurrentMicroThread.State = tsWaiting then
     318      begin
     319        // Execute selected thread
     320        asm
     321          // Restore microthread stack
     322          mov ecx, Self
     323          mov eax, [ecx].TMicroThreadManager.FCurrentMicroThread
     324          mov edx, [eax].TMicroThread.FStackPointer
     325          mov esp, edx
     326          mov edx, [eax].TMicroThread.FBasePointer
     327          mov ebp, edx
    317328        end;
    318329      end;
    319       FCurrentMicroThread := nil;
    320     end else
    321     if FCurrentMicroThread.State = tsWaiting then begin
    322       // Execute selected thread
    323       FCurrentMicroThread.FState := tsRunning;
    324       FCurrentMicroThread.FExecutionStartTime := CurrentTime;
    325       asm
    326         // Restore microthread stack
    327         mov ecx, Self
    328         mov eax, [ecx].TMicroThreadManager.FCurrentMicroThread
    329         mov edx, [eax].TMicroThread.FStackPointer
    330         mov esp, edx
    331         mov edx, [eax].TMicroThread.FBasePointer
    332         mov ebp, edx
    333       end;
    334330    end;
    335   end else begin
    336     FCurrentMicroThread := nil;
    337331  end;
    338332end;
     
    370364begin
    371365  inherited;
     366  ExecuteTerminated := False;
    372367  Manager := TMicroThreadManager.Create;
    373368end;
     
    375370destructor TMicroThreadThread.Destroy;
    376371begin
     372  Terminate;
     373  repeat
     374    Application.ProcessMessages;
     375    Sleep(1);
     376  until ExecuteTerminated;
     377
    377378  Manager.Free;
    378379  inherited Destroy;
     
    393394begin
    394395  Execute;
     396  asm
     397    mov ecx, Self
     398  end;
    395399end;
    396400
     
    513517constructor TMicroThreadScheduler.Create;
    514518begin
    515   FTerminated := True;
     519  FMainThreadStarter := TTimer.Create(nil);
     520  FMainThreadStarter.Enabled := False;
     521  FMainThreadStarter.Interval := 1;
     522  FMainThreadStarter.OnTimer := MainThreadStart;
     523  FMainThreadTerminated := True;
    516524  FMicroThreadsLock := TCriticalSection.Create;
    517525  FMicroThreads := TObjectList.Create;
     
    521529  FMainThreadManager := TMicroThreadManager.Create;
    522530  FMainThreadManager.FScheduler := Self;
     531  UseMainThread := False;
    523532end;
    524533
     
    526535begin
    527536  Active := False;
     537  FMainThreadStarter.Free;
    528538  FMainThreadManager.Free;
    529539  FThreadPool.Free;
     
    534544
    535545procedure TMicroThreadScheduler.Start;
    536 var
    537   Executed: Integer;
    538   I: Integer;
    539 begin
    540   FTerminated := False;
     546begin
     547  FMainThreadTerminated := False;
    541548  UpdateThreadPoolSize;
    542549  FState := ssRunning;
    543   repeat
    544     Executed := FMainThreadManager.Execute(10);
    545     Application.ProcessMessages;
    546     if Executed = 0 then Sleep(1);
    547   until FState <> ssRunning;
    548   FTerminated := True;
    549550end;
    550551
     
    567568    Application.ProcessMessages;
    568569    Sleep(1);
    569   until FTerminated and (ThreadPoolSize = 0);
     570  until FMainThreadTerminated and (ThreadPoolSize = 0);
    570571  FState := ssStopped;
    571572end;
    572573
    573574procedure TMicroThreadScheduler.PoolThreadTerminated(Sender: TObject);
    574 begin
     575var
     576  ThreadIndex: Integer;
     577begin
     578  TMicroThreadThread(Sender).ExecuteTerminated := True;
    575579  try
    576580    FThreadPoolLock.Acquire;
    577581    FThreadPool.OwnsObjects := False;
    578     FThreadPool.Delete(FThreadPool.IndexOf(Sender));
     582    ThreadIndex := FThreadPool.IndexOf(Sender);
     583    if ThreadIndex <> -1 then FThreadPool.Delete(ThreadIndex);
    579584    FThreadPool.OwnsObjects := True;
    580585  finally
     
    594599        NewThread := TMicroThreadThread.Create(True);
    595600        NewThread.Manager.FScheduler := Self;
     601        NewThread.Manager.Id := FThreadPool.Count;
    596602        NewThread.OnTerminate := PoolThreadTerminated;
    597603        ThreadPool.Add(NewThread);
    598604        NewThread.Resume;
    599605      end;
    600     end else
    601     ThreadPool.Count := FThreadPoolSize;
     606    end else begin
     607      while FThreadPool.Count > FThreadPoolSize do begin
     608        FThreadPool.Delete(FThreadPool.Count - 1);
     609      end;
     610    end;
    602611  finally
    603612    FThreadPoolLock.Release;
    604613  end;
     614end;
     615
     616procedure TMicroThreadScheduler.MainThreadStart(Sender: TObject);
     617var
     618  Executed: Integer;
     619begin
     620  FMainThreadStarter.Enabled := False;
     621  repeat
     622    Executed := FMainThreadManager.Execute(1);
     623    Application.ProcessMessages;
     624    if Executed = 0 then Sleep(1);
     625  until (FState <> ssRunning) or (not FUseMainThread);
     626  FMainThreadTerminated := True;
    605627end;
    606628
     
    619641      FRoundRobinIndex := 0;
    620642    while (I < FMicroThreads.Count) and
    621      (TMicroThread(FMicroThreads[FRoundRobinIndex]).State <> tsWaiting) do begin
     643     (TMicroThread(FMicroThreads[FRoundRobinIndex]).FState <> tsWaiting) do begin
    622644      // WakeUp sleeping threads
    623645      if (TMicroThread(FMicroThreads[FRoundRobinIndex]).FState = tsSleeping) and
     
    634656    if I < FMicroThreads.Count then begin
    635657      Result := TMicroThread(FMicroThreads[FRoundRobinIndex]);
     658      Result.FState := tsRunning;
    636659    end;
    637660  finally
    638661    FMicroThreadsLock.Release;
     662  end;
     663end;
     664
     665procedure TMicroThreadScheduler.ReleaseMicroThread(MicroThread: TMicroThread);
     666begin
     667  try
     668    FMicroThreadsLock.Acquire;
     669    if MicroThread.FState = tsRunning then begin
     670      MicroThread.FState := tsWaiting;
     671    end;
     672  finally
     673    FMicroThreadsLock.Release;
     674  end;
     675end;
     676
     677procedure TMicroThreadScheduler.SetUseMainThread(const AValue: Boolean);
     678begin
     679  if FUseMainThread = AValue then Exit;
     680  FUseMainThread := AValue;
     681  if FState = ssRunning then begin
     682    if AValue then FMainThreadStarter.Enabled := True;
    639683  end;
    640684end;
     
    686730
    687731//StaticManagers := TObjectList.Create;
    688 MainScheduler := TMicroThreadScheduler.Create;
     732//MainScheduler := TMicroThreadScheduler.Create;
    689733
    690734finalization
    691735
    692 MainScheduler.Free;
     736//MainScheduler.Free;
    693737//StaticManagers.Free;
    694738
Note: See TracChangeset for help on using the changeset viewer.