PC Review


Reply
Thread Tools Rating: Thread Rating: 2 votes, 1.00 average.

Control.BeginInvoke is NOT fire-and-forget

 
 
Ben Voigt [C++ MVP]
Guest
Posts: n/a
 
      8th Jul 2008
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?


 
Reply With Quote
 
 
 
 
Ciaran O''Donnell
Guest
Posts: n/a
 
      8th Jul 2008
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?
>
>
>

 
Reply With Quote
 
Linda Liu[MSFT]
Guest
Posts: n/a
 
      9th Jul 2008
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:
(E-Mail Removed).

==================================================
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.

 
Reply With Quote
 
Ben Voigt [C++ MVP]
Guest
Posts: n/a
 
      9th Jul 2008
Peter Duniho wrote:
> On Tue, 08 Jul 2008 08:25:37 -0700, Ben Voigt [C++ MVP]
> <(E-Mail Removed)> 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
>>
>> [...]
>> 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



 
Reply With Quote
 
Ben Voigt [C++ MVP]
Guest
Posts: n/a
 
      9th Jul 2008
Peter Duniho wrote:
> On Tue, 08 Jul 2008 08:25:37 -0700, Ben Voigt [C++ MVP]
> <(E-Mail Removed)> 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
>>
>> [...]
>> 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.


 
Reply With Quote
 
Ben Voigt [C++ MVP]
Guest
Posts: n/a
 
      9th Jul 2008
> 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).



Hmmm. In Win32, creating a message-only window is a pretty common paradigm.
And I'm not seeing how SynchronizationContext can help me, at a minimum it
looks an order of magnitude more complicated to use than Control.Invoke or
Control.BeginInvoke. 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? It sure isn't using the Win32 APC
mechanism, which would solve the problem nicely with no extra Control needed
if only the main WinForms message loop did an alertable wait... but it
doesn't. I filed a feature request on that over a year ago.

As for why, I'm trying to have my components present a single-threaded event
driven interface. Components implemented event-driven with a state machine,
can fire events directly from their event handlers. Components that need
background threads, the thread synchronization code is hidden in the
implementation where the consumer doesn't ever have to know extra threads
were created.


 
Reply With Quote
 
Ben Voigt [C++ MVP]
Guest
Posts: n/a
 
      10th Jul 2008
Peter Duniho wrote:
> On Wed, 09 Jul 2008 15:30:56 -0700, Ben Voigt [C++ MVP]
> <(E-Mail Removed)> wrote:
>
>> Hmmm. In Win32, creating a message-only window is a pretty common
>> paradigm.

>
> That's true. But AFAIK, a Control instance isn't a message-only
> window.
>> And I'm not seeing how SynchronizationContext can help me, at a
>> minimum it
>> looks an order of magnitude more complicated to use than
>> Control.Invoke or
>> Control.BeginInvoke.

>
> Why? "SynchronizationContext.Current.Post( /* your delegate here */
> );" seems simple enough. For the purpose of your anonymous method,
> you'll probably want to capture the "Current" context for use later.


Ok, that's not too bad. An extra anonymous method is required in order to
make a SendOrPostCallback delegate. And you must save the Current context.
So about equal.

I think I just didn't see any example of how to use it. The documentation
for that class is beyond horrible. It starts out by saying "The
SynchronizationContext class is a base class that provides a free-threaded
context with no synchronization." The IsWaitNotificationRequired method
(shouldn't it be a property?) -- well the method name is more informative
than the entire documentation page. And so on, with not a single example
linked from any of the class members.

>
> What about it do you feel is an order of magnitude more complicated?
> Seems to me that creating a whole new Control instance and forcing the
> handle to be created is twice as many lines of code.
>
>> 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.
>
>> It sure isn't using the Win32 APC
>> mechanism, which would solve the problem nicely with no extra Control
>> needed
>> if only the main WinForms message loop did an alertable wait... but
>> it doesn't. I filed a feature request on that over a year ago.

>
> Yes, there are still some things in the unmanaged API that would be
> nice to have in .NET.
>
>> As for why, I'm trying to have my components present a
>> single-threaded event
>> driven interface. Components implemented event-driven with a state
>> machine,
>> can fire events directly from their event handlers. Components that
>> need background threads, the thread synchronization code is hidden
>> in the implementation where the consumer doesn't ever have to know
>> extra threads were created.

>
> Personally, I don't mind a design that requires the consumer to know
> that an event or other callback might happen on a different thread. .NET
> certainly has plenty of examples of that, and they seem to work
> okay.
> Depending on how you expect the thing to be used, you _could_ look at
> the Target for the delegate being invoked to see if it implements
> ISynchronizeInvoke, and then use the Invoke/BeginInvoke on that
> target for the callback. This of course requires that the delegate
> be a member of the Control sub-class in situations when it must be
> called on the GUI thread. Obviously there's no way to guarantee that.
>
> Barring that, in this particular situation perhaps
> SynchronizationContext would work for you. Assuming, of course, I
> haven't missed something and it's not really an order of magnitude
> more complicated. I certainly would agree that if it takes ten times
> the code, it might be better to do it some other way.
>
> Pete



 
Reply With Quote
 
Ben Voigt [C++ MVP]
Guest
Posts: n/a
 
      10th Jul 2008
>> 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.


 
Reply With Quote
 
 
 
Reply

Thread Tools
Rate This Thread
Rate This Thread:

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
HTTPContext Session and worker thread (Fire and Forget) Braulio Diez Microsoft ASP .NET 4 9th Feb 2008 06:30 PM
Asynchronous Fire-And-Forget Call Stephen Barrett Microsoft ASP .NET 5 13th Apr 2006 02:11 PM
Control.BeginInvoke john doe Microsoft C# .NET 9 12th Jan 2006 01:36 PM
Control.BeginInvoke vs. delegate.BeginInvoke Valerie Hough Microsoft C# .NET 6 21st Oct 2005 11:14 AM
question anbout control.invoke and control.begininvoke C# Microsoft Dot NET Framework Forms 4 24th May 2004 09:45 AM


Features
 

Advertising
 

Newsgroups
 


All times are GMT +1. The time now is 07:29 AM.