By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
455,900 Members | 1,368 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 455,900 IT Pros & Developers. It's quick & easy.

Control.BeginInvoke is NOT fire-and-forget

P: n/a
As much as the CLR team assures us that it's ok to fire-and-forget
Control.BeginInvoke, it seems it isn't. Maybe this is a bug.

See for example: the comments in
http://blogs.msdn.com/cbrumme/archiv.../06/51385.aspx

I was encountering a bug that disappeared when debugging. Not when a
debugger is attached, mind you, but when I placed a breakpoint near the
code. Adding Trace.WriteLine statements showed that the failing code was
not even being executed. Ok, what effects can a breakpoint have? Well,
evaluation of watch expressions, so I cleared the watch window... check,
same behavior. And occasionally (<10%) the code worked even with the
breakpoint removed or disabled. Must be a race condition, hitting a
breakpoint could definitely affect thread scheduling.

Turns out this code (now fixed) was the culprit:

if (postProcessing != null)
{
new UIPermission(UIPermissionWindow.AllWindows).Assert ();
Control c = new Control();
IntPtr forceHandleCreation = c.Handle;
MethodInvoker finalProcessing = postProcessing + c.Dispose;
helper.postProcessing = delegate {
c.Invoke(finalProcessing); };
}

new System.Threading.Thread(helper.UIThreadProc).Start ();

The thread procedure:

public void UIThreadProc()
{
new UIPermission(UIPermissionWindow.AllWindows).Assert ();
progressDialog = new
ProgressTracker((ushort)fileArray.Length, cumulativeSize, actionMsg);
IntPtr forceHandleCreation = progressDialog.Handle;
new System.Threading.Thread(WorkThreadProc).Start();
Application.Run(progressDialog);
if (postProcessing != null)
postProcessing();
}

I originally had c.BeginInvoke in the asynchronous method. Seems that if
you BeginInvoke and then the calling thread ends, the call never takes
place. Yuck!

Do you think this is a CLR bug or it is by design?
Jul 8 '08 #1
Share this Question
Share on Google+
7 Replies


P: n/a
I can understand why this might happen but I wouldnt think it would be by
design.
If it is by design then I guess they expected you to call EndInvoke after
doing more work or something.

--
Ciaran O''Donnell
http://wannabedeveloper.spaces.live.com
"Ben Voigt [C++ MVP]" wrote:
As much as the CLR team assures us that it's ok to fire-and-forget
Control.BeginInvoke, it seems it isn't. Maybe this is a bug.

See for example: the comments in
http://blogs.msdn.com/cbrumme/archiv.../06/51385.aspx

I was encountering a bug that disappeared when debugging. Not when a
debugger is attached, mind you, but when I placed a breakpoint near the
code. Adding Trace.WriteLine statements showed that the failing code was
not even being executed. Ok, what effects can a breakpoint have? Well,
evaluation of watch expressions, so I cleared the watch window... check,
same behavior. And occasionally (<10%) the code worked even with the
breakpoint removed or disabled. Must be a race condition, hitting a
breakpoint could definitely affect thread scheduling.

Turns out this code (now fixed) was the culprit:

if (postProcessing != null)
{
new UIPermission(UIPermissionWindow.AllWindows).Assert ();
Control c = new Control();
IntPtr forceHandleCreation = c.Handle;
MethodInvoker finalProcessing = postProcessing + c.Dispose;
helper.postProcessing = delegate {
c.Invoke(finalProcessing); };
}

new System.Threading.Thread(helper.UIThreadProc).Start ();

The thread procedure:

public void UIThreadProc()
{
new UIPermission(UIPermissionWindow.AllWindows).Assert ();
progressDialog = new
ProgressTracker((ushort)fileArray.Length, cumulativeSize, actionMsg);
IntPtr forceHandleCreation = progressDialog.Handle;
new System.Threading.Thread(WorkThreadProc).Start();
Application.Run(progressDialog);
if (postProcessing != null)
postProcessing();
}

I originally had c.BeginInvoke in the asynchronous method. Seems that if
you BeginInvoke and then the calling thread ends, the call never takes
place. Yuck!

Do you think this is a CLR bug or it is by design?
Jul 8 '08 #2

P: n/a
On Tue, 08 Jul 2008 08:25:37 -0700, Ben Voigt [C++ MVP]
<rb*@nospam.nospamwrote:
As much as the CLR team assures us that it's ok to fire-and-forget
Control.BeginInvoke, it seems it isn't. Maybe this is a bug.

See for example: the comments in
http://blogs.msdn.com/cbrumme/archiv.../06/51385.aspx

[...]
Do you think this is a CLR bug or it is by design?
First thing to keep in mind: the assertion about Control.EndInvoke() has
to do with resource cleanup and whether one is required to call that
method to ensure things are cleaned up. It's not about whether
Control.BeginInvoke() will work.

Second, it would be helpful if you'd actually post a concise-but-complete
code sample that reliably demonstrates the problem. Saying "I original
had c.BeginInvoke in the asynchronous method" doesn't tell us much about
how you actually used it or what might have been going wrong.

I am relatively confident that if you call BeginInvoke() from a thread
that exits before the invoked delegate gets to run, the invoked delegate
should still run. I would be very surprised if that wasn't actually what
happened. On the other hand, if the thread that _owns_ the control being
used to call BeginInvoke() exits or is otherwise terminated, I would _not_
expect the delegate being invoked to execute, since it has to execute on
that thread.

Again, a complete code sample would eliminate these ambiguities in your
comment. It's impossible to tell for sure from the code you posted what
exactly you were trying to do and what broke. It also doesn't help that
the code you posted is clearly a corner case, whatever else might have
been going on, and you didn't post enough to show us that you've correctly
set the threading model for whatever threads wind up with a message pump
(something that could also break things).

Given the evidence so far, I cannot imaging being able to confidently say
there's a bug, whether in the CLR or (as is probably more likely, assuming
this is a bug at all) in the framework.

Pete
Jul 8 '08 #3

P: n/a
Thanks Pete and Ciaran for your help!

Hi Ben,

The Control.BeginInvoke method executes the specified delegate
asynchronously on the thread that the control's underlying handle was
created on. It means that the calling thread doen't need to wait until the
UI thread finishes processing the request and will returns immediately.

It's true that you should always call a delegate's EndInvoke after a call
to a delegate's BeginInvoke. It's completely safe to call
Control.BeginInvoke without ever calling Control.EndInvoke, because it
doesn't create the same resources associated with a delegate's BeginInvoke
call.

Even if you do want the results from a call to Control.BeginInvoke, there's
no way to pass a callback, so you need to use the IAsyncResult
implementation as returned from Control.BeginInvoke. You keep checking the
IsCompleted property for true during your other worker thread processing
before calling Control.EndInvoke to harvest the result. This is such a pain
that, if you want results from the call to the UI thread, I suggest that
the worker thread use Control.Invoke instead.
As much as the CLR team assures us that it's ok to fire-and-forget
Control.BeginInvoke, it seems it isn't.

Could you please show us a complete code snippet to demonstrate the problem?

I look forward to your reply!

Sincerely,
Linda Liu
Microsoft Online Community Support

Delighting our customers is our #1 priority. We welcome your comments and
suggestions about how we can improve the support we provide to you. Please
feel free to let my manager know what you think of the level of service
provided. You can send feedback directly to my manager at:
ms****@microsoft.com.

==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscripti...ult.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscripti...t/default.aspx.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.

Jul 9 '08 #4

P: n/a
Peter Duniho wrote:
On Tue, 08 Jul 2008 08:25:37 -0700, Ben Voigt [C++ MVP]
<rb*@nospam.nospamwrote:
>As much as the CLR team assures us that it's ok to fire-and-forget
Control.BeginInvoke, it seems it isn't. Maybe this is a bug.

See for example: the comments in
http://blogs.msdn.com/cbrumme/archiv.../06/51385.aspx

[...]
Do you think this is a CLR bug or it is by design?

First thing to keep in mind: the assertion about Control.EndInvoke()
has to do with resource cleanup and whether one is required to call
that method to ensure things are cleaned up. It's not about whether
Control.BeginInvoke() will work.

Second, it would be helpful if you'd actually post a
concise-but-complete code sample that reliably demonstrates the
problem. Saying "I original had c.BeginInvoke in the asynchronous
method" doesn't tell us much about how you actually used it or what
might have been going wrong.
Did I write that? Sure enough.

I should have said "in the anonymous method".

i.e. changing the line from
helper.postProcessing = delegate { c.Invoke(finalProcessing); };
back to
helper.postProcessing = delegate { c.BeginInvoke(finalProcessing); };

breaks things, in that the finalProcessing MulticastDelegate never runs nor
throws an exception.
>
I am relatively confident that if you call BeginInvoke() from a thread
that exits before the invoked delegate gets to run, the invoked
delegate should still run. I would be very surprised if that wasn't
actually what happened. On the other hand, if the thread that _owns_
the control being used to call BeginInvoke() exits or is otherwise
terminated, I would _not_ expect the delegate being invoked to
execute, since it has to execute on that thread.
The control should be owned by the original thread which does not exit, I
read the Handle property for the explicit purpose of forcing it to be
created on that thread, before I spawn the worker.
>
Again, a complete code sample would eliminate these ambiguities in
your comment. It's impossible to tell for sure from the code you
posted what exactly you were trying to do and what broke. It also
doesn't help that the code you posted is clearly a corner case,
whatever else might have been going on, and you didn't post enough to
show us that you've correctly set the threading model for whatever
threads wind up with a message pump (something that could also break
things).
Changing the call in the asynchronous method which is called at the very end
of the worker threadproc from BeginInvoke to Invoke does cure the problem.
I think this demonstrates that the thread on which the control is created is
properly pumping messages.
>
Given the evidence so far, I cannot imaging being able to confidently
say there's a bug, whether in the CLR or (as is probably more likely,
assuming this is a bug at all) in the framework.
True, it's most likely a bug in the base class libraries, not the CLR. My
poor wording.
>
Pete

Jul 9 '08 #5

P: n/a
Peter Duniho wrote:
On Tue, 08 Jul 2008 08:25:37 -0700, Ben Voigt [C++ MVP]
<rb*@nospam.nospamwrote:
>As much as the CLR team assures us that it's ok to fire-and-forget
Control.BeginInvoke, it seems it isn't. Maybe this is a bug.

See for example: the comments in
http://blogs.msdn.com/cbrumme/archiv.../06/51385.aspx

[...]
Do you think this is a CLR bug or it is by design?

First thing to keep in mind: the assertion about Control.EndInvoke()
has to do with resource cleanup and whether one is required to call
that method to ensure things are cleaned up. It's not about whether
Control.BeginInvoke() will work.

Second, it would be helpful if you'd actually post a
concise-but-complete code sample that reliably demonstrates the
problem.
I tried and it doesn't reproduce with an unloaded system. The original
project has a few other threads of varying priorities doing unrelated tasks
but certainly affecting thread scheduling and I can definitely understand
why that can affect the appearance of a race condition. I'll just use
Invoke, the thread is dying anyway, it can stay around long enough to get
the delegate call completed message.
Jul 9 '08 #6

P: n/a
On Wed, 09 Jul 2008 06:41:10 -0700, Ben Voigt [C++ MVP]
<rb*@nospam.nospamwrote:
[...]
>Again, a complete code sample would eliminate these ambiguities in
your comment. It's impossible to tell for sure from the code you
posted what exactly you were trying to do and what broke. It also
doesn't help that the code you posted is clearly a corner case,
whatever else might have been going on, and you didn't post enough to
show us that you've correctly set the threading model for whatever
threads wind up with a message pump (something that could also break
things).

Changing the call in the asynchronous method which is called at the very
end
of the worker threadproc from BeginInvoke to Invoke does cure the
problem.
I think this demonstrates that the thread on which the control is
created is
properly pumping messages.
Given the code that was posted, at best it demonstrates that using
Invoke() treats the call differently than using BeginInvoke(). For
example, it could be that there's some logic in the Invoke() method that
winds up calling the delegate on the current thread.

You have all the code, so you can of course make your own judgments. But
absent a concise-but-complete code sample, I'm leery of making assumptions
about what is specifically happening.

The glimpse of the design that we've seen so far makes me think that
there's probably a much better way to approach whatever you're doing
anyway. Creating a dummy Control instance simply for the purpose of
marshalling execution back to some thread seems very odd to me. At the
very least, I'd think a SynchronizationContext would be more appropriate,
since it wouldn't carry all the extra unused baggage a Control has. And
usually, when you actually need code to be marshalled back to a specific
GUI thread, it's because you have a specific Control instance that
requires it, and you can just use that instance to do the marshalling (in
that case, the logic would be moved to whatever code is in your
"postProcessing" delegate...again, this seems more sensible to me than
requiring some other code to manage the marshalling arbitrarily).

But, all that said, even if we take as granted that the design is the very
best approach for managing this, no one here can answer the question as to
whether there's a bug in the runtime or not. We don't have all the code,
and so there's no way to know for sure that it's not just a bug in your
code.

Personally, I can count on the fingers of one hand with at least one
finger left over the number of times that someone has said "this is a .NET
bug" and they wound up being right. That's in spite of seeing such
statements many dozens of times. Granted, I give you a much higher chance
of being correct on such a statement than the average schmoe, but I'm
still hesitant to just take the assertion at face value. I'd want to see
the code.

But, as long as all you want is for your code to work, I think you've got
your solution. Based only on what you've posted, no one can say whether
it's a .NET bug, but it doesn't sound like that's an important
consideration for you anyway.

Pete
Jul 9 '08 #7

P: n/a
>As far as "extra baggage" of a Control, isn't
>cross-thread marshalling dependent on window messages anyway (hence
the concern for the message loop of the receiving thread), or has
.NET conflated
async calls with the UI message loop?

That I don't know. The point is that the SynchronizationContext
instance already exists, so you might as well use it rather than
creating a whole new Control instance. :)
Well, to answer whether window messages are used for SynchronizationContext,
I can tell you (from Reflector), that WindowsFormsSynchronizationContext is
implemented on top of Control.Invoke and Control.BeginInvoke.

Also, Control.MarshaledInvoke (the underlying implementation of Invoke and
BeginInvoke) does use a windows message to notify the target thread. The
delegate itself is not passed using the message data (as far as I know you
can't safely pass addresses of garbage collectable objects asynchronously --
ok, yes you can with GCHandle). And Invoke-ing a delegate on your own
thread looks like it causes all invokes you've received from other threads
to be processed immediately instead of waiting for the message loop.
There's explicit locking everywhere and I'm glad I came up with my own
solution for my C++/CLI IO components (where all requests are actually
processed with APCs on a native thread running an alertable wait loop).
Maybe I need to think about using that lockless message queue to pass
delegates and make my own SynchronizationContext implementation -- but since
I don't control the message loop it wouldn't be nearly as elegant.
Jul 10 '08 #8

This discussion thread is closed

Replies have been disabled for this discussion.