[Mono-bugs] [Bug 327608] New: BackgroundWorker: is UI-thread unsafe!

bugzilla_noreply at novell.com bugzilla_noreply at novell.com
Sun Sep 23 17:36:05 EDT 2007


https://bugzilla.novell.com/show_bug.cgi?id=327608#c1

           Summary: BackgroundWorker: is UI-thread unsafe!
           Product: Mono: Class Libraries
           Version: 1.2
          Platform: Other
        OS/Version: All
            Status: NEW
          Severity: Critical
          Priority: P5 - None
         Component: Windows.Forms
        AssignedTo: mono-bugs at ximian.com
        ReportedBy: andyhume32 at yahoo.co.uk
         QAContact: mono-bugs at ximian.com
          Found By: ---


Created an attachment (id=174082)
 --> (https://bugzilla.novell.com/attachment.cgi?id=174082)
Repro #1

BackgroundWorker's purpose is to ensure that its ReportProgress and
RunWorkerCompleted event methods are run strictly on the UI thread.  It is
easily shown however that these methods run on arbitrary threads, and also thus
that thread-unsafe UI access results.  On Win32 this causes crashing in many
cases, on Linux (Suse VM) UI corruption is more common.

This is likely the real cause of bug 325356 "ListView: Win32 hang on infinite
recalc (?when both scrollbars needed)" and maybe others.


This fault is actually likely a general problem with SynchronizationContext
being unitialized for Windows Forms applications. 
SynchronizationContext.Current is found to be null in this case, when it should
not -- BackgroundWorker presumably relies on it to run the events safely.  Its
events are therefore just run on any available thread pool thread.

In fact, on further investigation that isn't quite the case.  There is _no_
WinForms implementation of SynchronizationContext.  No use of
SynchronizationContext.Current is made either, the code just does [new
SynchronizationContext();], so creating one that uses the thread-pool.

See e.g. http://msdn.microsoft.com/msdnmag/issues/06/06/NETMatters/default.aspx
for information on SynchronizationContext in WinForms and as used by
BackgroundWorker.


Two repro samples are attached.  Both have a simple BackgroundWorker where the
'work' is to simply count from 1 to N.  Both display the following on the
console: their 'counting' progress, whether the UI-safe action is correctly
being run on the UI thread, the ManagedThreadId being use for each action, and
whether an 'illegal' thread action has occured -- watch for "!!!".

The first sample does nothing further and the console output shows that
arbitrary threads are used.  The second application adds a textbox to the form
and writes simple text to it from the 'thread-safe' report-progress method. 
This application crashes, hangs, or displays corrupt text in 99% of cases.

Sample#1 expected:
[[
Form..ctor thrd: 1
syncCtx: System.Windows.Forms.WindowsFormsSynchronizationContext

Form_Shown thrd: 1
syncCtx: System.Windows.Forms.WindowsFormsSynchronizationContext
DoWork thrd: 4
Progress thrd: 1
Progress thrd: 1
Progress thrd: 1
..
..
Progress thrd: 1
Progress thrd: 1
RWCompleted thrd: 1
]]

The following is seen on SVN head on Win32.  Note the multiplicity of threads
being used, UI thread=7176, but report-progress runs on 6288, 8012, 7420 etc. 
Note that Control.InvokeRequired is frequently true.  Note that progress events
keep occuring even after the 'completed' event has fired.
[[
$ mono --debug BkgndWkr1.exe
Form..ctor thrd: 7176
syncCtx: NULL !!!

Form_Shown thrd: 7176
syncCtx: NULL !!!
DoWork thrd: 3412
Progress thrd: 6288
Progress thrd: 8012
Progress thrd: 7420
Progress !!! InvokeRequired !!!
Progress thrd: 3980
Progress !!! InvokeRequired !!!
i != m_expectedProgress
Progress thrd: 6308
Progress !!! InvokeRequired !!!
i != m_expectedProgress
percentage != 10 * m_expectedProgress
Progress thrd: 6980
Progress !!! InvokeRequired !!!
i != m_expectedProgress
percentage != 10 * m_expectedProgress
percentage != 10 * m_expectedProgress
Progress thrd: 7976
Progress !!! InvokeRequired !!!
i != m_expectedProgress
Progress thrd: 1208
Progress !!! InvokeRequired !!!
Progress !!! InvokeRequired !!!
i != m_expectedProgress
percentage != 10 * m_expectedProgress
Progress thrd: 1336
Progress thrd: 7412
Progress !!! InvokeRequired !!!
i != m_expectedProgress
percentage != 10 * m_expectedProgress
Progress thrd: 7412
Progress !!! InvokeRequired !!!
Progress thrd: 3412
Progress thrd: 3980
Progress thrd: 6128
Progress !!! InvokeRequired !!!
Progress after Completion !!!
i != m_expectedProgress
Progress !!! InvokeRequired !!!
Progress after Completion !!!
i != m_expectedProgress
percentage != 10 * m_expectedProgress
Progress thrd: 3872
Progress !!! InvokeRequired !!!
Progress after Completion !!!
i != m_expectedProgress
Progress !!! InvokeRequired !!!
Progress after Completion !!!
i != m_expectedProgress
percentage != 10 * m_expectedProgress
i != m_expectedProgress
percentage != 10 * m_expectedProgress
percentage != 10 * m_expectedProgress
Progress thrd: 6288
Progress !!! InvokeRequired !!!
Progress after Completion !!!
Progress thrd: 1336
RWCompleted thrd: 4160
RWCompleted !!! InvokeRequired !!!
Progress !!! InvokeRequired !!!
Progress after Completion !!!
Progress !!! InvokeRequired !!!
Progress after Completion !!!
Progress !!! InvokeRequired !!!
Progress after Completion !!!
i != m_expectedProgress
percentage != 10 * m_expectedProgress
Progress thrd: 6308
i != m_expectedProgress
percentage != 10 * m_expectedProgress
i != m_expectedProgress
percentage != 10 * m_expectedProgress
i != m_expectedProgress
Progress after Completion !!!
i != m_expectedProgress
Progress thrd: 7040
Progress !!! InvokeRequired !!!
Progress after Completion !!!
i != m_expectedProgress
percentage != 10 * m_expectedProgress
percentage != 10 * m_expectedProgress
percentage != 10 * m_expectedProgress
percentage != 10 * m_expectedProgress
Progress !!! InvokeRequired !!!
Progress after Completion !!!
i != m_expectedProgress
i != m_expectedProgress
percentage != 10 * m_expectedProgress
percentage != 10 * m_expectedProgress
Progress thrd: 6980
Progress !!! InvokeRequired !!!
Progress after Completion !!!
i != m_expectedProgress
percentage != 10 * m_expectedProgress
percentage != 10 * m_expectedProgress
]]


Sample #2 on MSFT shows similar good console output as above, and the textbox
shows good text in repeated instances of:  "<IN--1----1--out>" where '1' is the
UI thread id.

On Mono on Windows the sample generally crashes.  On Linux the sample does some
sometimes crash, but often only bad text-box text is seen: mis-ordered output
e.g. many "<IN"s in a sequence with no "out>"s, or even Chinese characters --
presumably bad text concatenation occuring.


Finally a third repro, this one checks the value of
SynchronizationContext.Current before opening a WinForms windows, when WinForms
'Application' is running, and aftersward.  The value is always null on Mono,
but on MSFT one sees:
[[
At Main -- before Application.Run
  SyncCtx.Current: NULL
At Form_Shown
  SyncCtx.Current: System.Windows.Forms.WindowsFormsSynchronizationContext
At Main -- after Application.Run
  SyncCtx.Current: System.Threading.SynchronizationContext
]]


-- 
Configure bugmail: https://bugzilla.novell.com/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are the QA contact for the bug.
You are the assignee for the bug.


More information about the mono-bugs mailing list