-1

I have problem with termination of BackgroundWorker in OmniThreadLibrary. Everything is OK, but when I want to terminate BackgroundWorker, termination has failed and BackgroundWorker is still alive. So, whole application that run as batch process is still alive.

  procedure TEntityIndexer.StartReindex;
  begin
    if LoadTable then
    begin      
    // In a ProcessRecords method I schedule WorkItems for background tasks
      ProcessRecords;
      while FCounter > 0 do
          ProcessMessages;
    // In ProcessMessages I keep the main thread alive
      ProcessRecordsContinue;
    // In ProcessRecordsContinue method I process the results of  background tasks and OnRequestDone method
    end
    else
        TerminateBackgroundWorker;
  end;

  procedure ProcessMessages;
  var
    Msg: TMsg;
  begin
    while integer(PeekMessage(Msg, 0, 0, 0, PM_REMOVE)) <> 0 do begin
      TranslateMessage(Msg);
      DispatchMessage(Msg);
    end;
  end;

  constructor TEntityIndexer.Create;
  begin
    ...
    CreateBackgroundWorker;
  end;

  procedure TEntityIndexer.CreateBackgroundWorker;
  begin
    FBackgroundWorker := Parallel.BackgroundWorker
      .NumTasks(INITasksCount)
      .Initialize(InitializeTask)
      .Finalize(FinalizeTask)
      .OnRequestDone(HandleRequestDone)
      .Execute(ProcessSupportStrings);
  end;

  procedure TEntityIndexer.FinalizeTask(const taskState: TOmniValue);
  var
    _obj: TObject;
  begin
    if not(taskState.IsObject) then
        Exit;
    _obj := taskState.AsObject;
    if _obj is TServerSessionApp then
        TServerSessionApp(_obj).ParentApplication.Free;
    CoUninitialize;
  end;

  procedure TEntityIndexer.ProcessRecordsContinue;
  begin
    if FStack.Count = 0 then
        Exit;
   ...
    FStack.Clear;
    StartReindex;
  end;

  procedure TEntityIndexer.ProcessRecords;
  ...
  begin
    FVTable.First;
    while not FVTable.Eof do
    begin
      ...
      _omniValue := TOmniValue.CreateNamed(
        [ovIdKey, _id,
        ovXMLKey, FVTable.FieldByName('mx').AsString,
        ovGenKey, FVTable.FieldByName('created').AsString
        ]);
      FBackgroundWorker.Schedule(FBackgroundWorker.CreateWorkItem(_omniValue));
      Inc(FCounter);
      FVTable.Next;
    end;
  end;

  procedure TEntityIndexer.ProcessSupportStrings(const workItem: IOmniWorkItem);
  var
    ...
  begin
    if not(workItem.taskState.IsObject) then
    ...
    if not workItem.Data.IsArray then
        raise Exception.Create('Empty parameters!');
    ...
    //  make some JSON and XML strings
    ...
    try
      try
        workItem.Result := TOmniValue.CreateNamed(
          [... ]);
     ...
  end;

  procedure TEntityIndexer.HandleRequestDone(const Sender: IOmniBackgroundWorker;
    const workItem: IOmniWorkItem);
  var
    ...
  begin
    Dec(FCounter);
    if workItem.IsExceptional then
    begin
      //  Process the exception
    end
    else if workItem.Result.IsArray then
    begin          
        ...         
      FStack.AddToStack(_stackItem);
    end;
  end;

  procedure TEntityIndexer.InitializeTask(var taskState: TOmniValue);
  begin
    CoInitialize(nil);
    taskState.AsObject := CreateAnotherServerSession;
  end;

  procedure TEntityIndexer.TerminateBackgroundWorker;
  begin
  // Here is s problem - Termination of the BackgroundWorker doesn't work, but finalization 
  // of background tasks is done
    FBackgroundWorker.Terminate(INFINITE);
    FBackgroundWorker := nil;
  end;

end.
  • 2
    Where is the code in your task that responds to the cancellation token? – David Heffernan Nov 18 '16 at 18:17
  • I'm going to second that. We need to see the code that the background worker is executing to be able to answer this question. Please [edit] your question to include this code. – J... Nov 18 '16 at 20:23
  • I am just a beginer in parallel programming and OTL and I was confused so I try add line with CancelAll and I forgot delete it. But there is no more code to respond to the cancelation token. In the book (Parallel programming with OTL), there is no mention I have to add special code to cancel tasks, when I want to stop them all and without any conditions. But when I delete that line problem continues. – Michal Miškov Nov 18 '16 at 20:28
  • Is it necessary to add that code (for cancel), when I know that input queue is empty and background tasks do nothing and only wait for terminate? – Michal Miškov Nov 18 '16 at 20:34
  • Please show us minimal complete program that exhibits the problem. http://stackoverflow.com/help/mcve – gabr Nov 18 '16 at 21:02
  • Here it is. https://github.com/jeffo216/Indexer/issues/1 – Michal Miškov Nov 18 '16 at 21:29
  • 1
    If a task doesn't check for cancellation why would it ever stop? – David Heffernan Nov 18 '16 at 22:25
  • http://otl.17slon.com/book/chap04.html#leanpub-auto-cancellation-1 you said that there was no mention of checking a cancellation token. I suggest you read again. – David Heffernan Nov 18 '16 at 22:30
  • @michal That linked program is very far from minimal. If you want to contact gabr for personal support that's between you and him. However, if you want to ask a question here you need to fit into our standards. Show a proper MCVE here in the question. Not an off site link. – David Heffernan Nov 18 '16 at 22:48
  • I don't want to stop the tasks but I need to terminate a BackgroundWorker correctly, because application will be executed as a batch by task scheduler every 1 hour, So application must be started and must be ending correctly. – Michal Miškov Nov 18 '16 at 23:21

1 Answers1

0

Ok I find a bug. It wasn't the bug of the OTL. This one was caused by wrong destruction of the object in Finalize() method. Release of the objects in taskState parameter variable in not enough. TaskState parameter variable should be cleared too.

procedure TEntityIndexer.FinalizeTask(const taskState: TOmniValue);
var
  _obj: TObject;
begin
  if not(taskState.IsObject) then
      Exit;
  _obj := taskState.AsObject;
  if Assigned(_obj) then
      _obj.Free;
  if _obj is TServerSessionApp then
      TServerSessionApp(_obj).ParentApplication.Free;
  // release the objects and clear a taskState variable
  taskState.Clear;
  CoUninitialize;
end;