Jump to content
MarkShark

Puzzling issue with passing CancellationToken to a function.

Recommended Posts

Summary:  Using a Parallel.BackgroundWorker if I check for WorkItem.CancellationToken.IsSignalled within the .OnExecute anonymous procedure I see that it is terminated immediately if I call FWorker.CancelAll from the main thread.  HOWEVER if I use a function called from the OnExecute handler it seems that the token is only signaled once the function completes.  I've included some code below, but if anyone could explain what I'm seeing it would be much appreciated!

 

Using a Paralel.BackgroundWorker created like:

  FWorker := Parallel.BackgroundWorker.Execute(nil);

 

I then schedule a work item like:

 

  FWorker.Schedule(
    FWorker.CreateWorkItem(TOmniValue.Null),
    FWorker.Config
      .OnExecute(
        procedure(const WorkItem: IOmniWorkItem)
        begin
          var LElapsed: Integer := 1;
          repeat
            Sleep(100);
            Inc(LElapsed, 100);
          until WorkItem.CancellationToken.IsSignalled or (LElapsed > 5000);
        end)
      .OnRequestDone(
        procedure (const Sender: IOmniBackgroundWorker; const WorkItem: IOmniWorkItem)
        begin
          if WorkItem.CancellationToken.IsSignalled then
            Memo1.Lines.Add('Task ' + WorkItem.UniqueID.ToString + ' onRequestDone and was cancelled.')
          else
            Memo1.Lines.Add('Task ' + WorkItem.UniqueID.ToString + ' onRequestDone');
        end
      )
  );

Calling FWorker.CancelAll; from the main thread cancels this workitem immediately.

 

However if instead of the "repeat until" above I call the following function, the token is never signaled "during the call", though it is signaled after the call returns.

function NiceWaitEx(const ACancelToken: IOmniCancellationToken; AWaitMs: Integer): Integer;
begin
  const LagMs = 100;
  var LElapsed: Integer := 1;
  repeat
    Sleep(LagMs);
    Inc(LElapsed, LagMs);
  until ACancelToken.IsSignalled or (LElapsed > AWaitMs);
  Result := LElapsed;
end;
  FWorker.Schedule(
    FWorker.CreateWorkItem(TOmniValue.Null),
    FWorker.Config
      .OnExecute(
        procedure(const WorkItem: IOmniWorkItem)
        begin
          NiceWaitEx(WorkItem.CancellationToken, 5000);
        end)
      .OnRequestDone(
        procedure (const Sender: IOmniBackgroundWorker; const WorkItem: IOmniWorkItem)
        begin
          if WorkItem.CancellationToken.IsSignalled then
            Memo1.Lines.Add('Task ' + WorkItem.UniqueID.ToString + ' onRequestDone and was cancelled.')
          else
            Memo1.Lines.Add('Task ' + WorkItem.UniqueID.ToString + ' onRequestDone');
        end
      )
  );

When I use the second version (with the call) the workitem always goes the full 5 seconds although shows that it was cancelled at the end.

 

I hope that's enough to explain the issue (and my confusion lol.)  Any thoughts appreciated!

 

-Mark

 

Edited by MarkShark
clarifying

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×