A Lifecycle of an Abstraction

Typically, factories from the Parallel class return an interface. For example, Parallel implements five Join overloads which create a //Join// abstraction.

class function  Join: IOmniParallelJoin; overload;
class function  Join(const task1, task2: TProc): 
  IOmniParallelJoin; overload;
class function  Join(const task1, task2: TOmniJoinDelegate):
  IOmniParallelJoin; overload;
class function  Join(const tasks: array of TProc): 
  IOmniParallelJoin; overload;
class function  Join(const tasks: array of TOmniJoinDelegate):
  IOmniParallelJoin; overload;

Important fact to keep in mind is that the Join abstraction (or any other abstraction returned from any of the Parallel factories) will only be alive as long as this interface is not destroyed. For example, the following code fragment will function fine.

procedure Test_OK;
begin
  Parallel.Join(
    procedure
    begin
      Sleep(10000);
    end,
    procedure
    begin
      Sleep(15000);
    end;
  end
  ).Execute;
end;

The interface returned from the Parallel.Join call is stored in a hidden variable, which will only be destroyed while executing the end statement of the Test_OK procedure. As the Execute waits for all subtasks to complete, Join will complete its execution before the end is executed and before the interface is destroyed.

Modify the code slightly and it will not work anymore.

procedure Test_Fail;
begin
  Parallel.Join(
    procedure
    begin
      Sleep(10000);
    end,
    procedure
    begin
      Sleep(15000);
    end;
  end
  ).NoWait.Execute;
end;

Because of the NoWait modifier, Execute will not wait for subtasks to complete but will return immediately. Next, the code behind the end will be executed and will destroy the abstraction while the subtasks are still running.

In such cases, it is important to keep the interface in a global field (typically it will be stored inside a form or another object) which will stay alive until the abstraction has stopped.

procedure Test_OK_Again;
begin
  FJoin := Parallel.Join(
    procedure
    begin
      Sleep(10000);
    end,
    procedure
    begin
      Sleep(15000);
    end;
  end
  ).NoWait.Execute;
end;

This leads to a new problem – when should this interface be destroyed? The answer depends on the abstraction used. Some abstractions provide OnStop method which can be used for this purpose. For other abstractions, you should use termination handler of the task configuration block.

book/highlevel/intro/abstractionlifecycle.txt · Last modified: 2012/11/14 11:50 by gabr
Recent changes RSS feed Debian Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki