OmniThreadLibrary icon indicating copy to clipboard operation
OmniThreadLibrary copied to clipboard

Improve or warn about ReceiveWait and creating task with function

Open geskill opened this issue 10 years ago • 4 comments

Hello, i just figured out that ReceiveWait will also receive internal messages. Lets say i create a task and say execute the Execute procedure:

CreateTask(TMyOmniWorker.Create(True)).MonitorWith(FOmniEM).Run(@TMyOmniWorker.Execute);

Inside the Initialize function i make a checkup if the task is allowed to process:

  LFound := False;

  repeat
    // This will also receive the @TMyOmniWorker.Execute message!
    task.Comm.ReceiveWait(LMessage, 1000 * 5);

    LFound := (((LMessage.MsgID = MSG_CONTINUE_TASK) or (LMessage.MsgID = MSG_QUIT_TASK)) and (LMessage.MsgData[0].AsInt64 = task.UniqueID));

    /// START Execute Fix
    if not LFound then
      task.Comm.OtherEndpoint.Send(LMessage);

  until LFound;

Here the problem appears. It took some time to figure it out. The ReceiveWait function catches the Execute message and removes it from the internal list. In the end the Initialize function is finished, but the Execute procedure gets never called. Obviously this is really clear if you read this, but you have to know that the same message channel is used.

In order to "fix" the problem i receive the Execute message and send it again to the TMyOmniWorker/OtherEndpoint.

http://geskill.bplaced.net/hp/other/OmniWorkerTestwithMonitor.zip

best regards

geskill avatar Oct 18 '15 13:10 geskill

Good catch! I think this should be considered a bug and should be fixed.

gabr42 avatar Oct 18 '15 14:10 gabr42

BTW, your approach looks a bit weird to me. Why do you call the Execute if you then immediately check if you can run it at all? Can't you just call Run without a parameter and then do 'task.Invoke(Execute)' when you actually need it?

gabr42 avatar Oct 18 '15 18:10 gabr42

I don't want to make the demo too complex ;)

Just add the IOmniThreadPool and set MaxExecuting := 1. Followed by:

CreateTask(TMyOmniWorker.Create(True)).MonitorWith(FOmniEM).
  Schedule(FThreadPool).Invoke(@TMyOmniWorker.Execute)

Same result. Now, the tasks are inside the queue and at the time a specific task is able to run (because all previous tasks have been finished), the time has passed and the task caller is maybe gone and this specific task is unnecessary to be done.

http://geskill.bplaced.net/hp/other/OmniWorkerTestwithMonitorandThreadPool.zip

PS: Now even more internal calls have to be redirected from ReceiveWait ^_^

geskill avatar Oct 18 '15 18:10 geskill

OK, that is a sensible argument.

gabr42 avatar Oct 18 '15 18:10 gabr42