BeginInvoke / EndInvoke race condition

S

stork

Because the results to BeginInvoke occur async'ly, it stands to reason
that each call of execute is actually a record on a queue, and
therefor, each asyncResult must actually be a separate item on that
queue. Conceptually, the nicest place to put the IAsyncResult to use
for EndInvoke is with the queued item itself, that is, in the execute
method that BeginInvoke enqueued. BeginInvoke fires as needed, and the
"invoked" method cleans itself up by calling the matching EndInvoke.

But, there's no way to actually make this work as above. BeginInvoke
can hit execute before any possible asyncResult assignment takes place.
Consider this pseudocode example:

class MyForm{ }

class MyWorkerThread
{
MyForm form;
IAsyncResult asyncResult;

void execute()
{
form.EndInvoke( asyncResult );
}

void start(int 42)
{
asyncResult = form.BeginInvoke( new executeDelegate( execute ) );
}
}

In the above, asyncResult will be null or invalid inside of execute()
because the assignment to asyncResult will not be completed yet until
the execute() is reached. Short of locking, is there another way to
call form.BeginInvoke so that the asyncResult is passed with it?
Thanks!
 
J

Jon Skeet [C# MVP]

stork said:
Because the results to BeginInvoke occur async'ly, it stands to reason
that each call of execute is actually a record on a queue, and
therefor, each asyncResult must actually be a separate item on that
queue. Conceptually, the nicest place to put the IAsyncResult to use
for EndInvoke is with the queued item itself, that is, in the execute
method that BeginInvoke enqueued. BeginInvoke fires as needed, and the
"invoked" method cleans itself up by calling the matching EndInvoke.

But, there's no way to actually make this work as above. BeginInvoke
can hit execute before any possible asyncResult assignment takes place.
Consider this pseudocode example:

class MyForm{ }

class MyWorkerThread
{
MyForm form;
IAsyncResult asyncResult;

void execute()
{
form.EndInvoke( asyncResult );
}

void start(int 42)
{
asyncResult = form.BeginInvoke( new executeDelegate( execute ) );
}
}

In the above, asyncResult will be null or invalid inside of execute()
because the assignment to asyncResult will not be completed yet until
the execute() is reached. Short of locking, is there another way to
call form.BeginInvoke so that the asyncResult is passed with it?

I can't immediately see one, no. Do you definitely *need* to call
EndInvoke in your real code? There's no requirement for it from the
framework's point of view (unlike with other, similar EndXXX methods).
 
S

stork

TJB replied to:
I can't immediately see one, no. Do you definitely *need* to call
EndInvoke in your real code? There's no requirement for it from the
framework's point of view (unlike with other, similar EndXXX
methods).

I thought that EndInvoke did some sort of cleanup. I'm reading
Control.*Invoke as being a different PostMessage based animal than the
normal asynch delegates *Invoke, which use the worker thread pool. So
are you saying that it is not necessary to call Control.EndInvoke?
 
J

Jon Skeet [C# MVP]

stork said:
methods).

I thought that EndInvoke did some sort of cleanup. I'm reading
Control.*Invoke as being a different PostMessage based animal than the
normal asynch delegates *Invoke, which use the worker thread pool. So
are you saying that it is not necessary to call Control.EndInvoke?

Exactly. Note that this doesn't apply to other things (BeginRead,
Delegate.BeginInvoke etc) - it *only* applies to Control.BeginInvoke.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top