Cannot create more Dispatcher. Runs out of resource?

0

In our application, we are using PngBitmapEncoder to encode and save PNG image in a separate thread\task. After few days of running the application we are seeing Dispatcher cannot be created from Encoder and throws error

Not enough storage is available to process the command

And has the below call stack

System.ComponentModel.Win32Exception (0x80004005): Not enough storage is available to process this command
   at MS.Win32.HwndWrapper..ctor(Int32 classStyle, Int32 style, Int32 exStyle, Int32 x, Int32 y, Int32 width, Int32 height, String name, IntPtr parent, HwndWrapperHook[] hooks)
   at System.Windows.Threading.Dispatcher..ctor()
   at System.Windows.Threading.DispatcherObject..ctor()
   at System.Windows.Media.Imaging.BitmapEncoder..ctor(Boolean isBuiltIn)

As .Net is available open source, got curious of what line inside Dispatcher constructor is throwing the error

    [SecurityCritical, SecurityTreatAsSafe]
    private Dispatcher()
    {
        _queue = new PriorityQueue<DispatcherOperation>();

        _tlsDispatcher = this; // use TLS for ownership only
        _dispatcherThread = Thread.CurrentThread;

        // Add ourselves to the map of dispatchers to threads.
        lock(_globalLock)
        {
            _dispatchers.Add(new WeakReference(this));
        }

        _unhandledExceptionEventArgs = new DispatcherUnhandledExceptionEventArgs(this);
        _exceptionFilterEventArgs = new DispatcherUnhandledExceptionFilterEventArgs(this);

        _defaultDispatcherSynchronizationContext = new DispatcherSynchronizationContext(this);

        // Create the message-only window we use to receive messages
        // that tell us to process the queue.
        MessageOnlyHwndWrapper window = new MessageOnlyHwndWrapper();
        _window = new SecurityCriticalData<MessageOnlyHwndWrapper>( window );

        _hook = new HwndWrapperHook(WndProcHook);
        _window.Value.AddHook(_hook);

        // DDVSO:447590
        // Verify that the accessibility switches are set prior to any major UI code running.
        AccessibilitySwitches.VerifySwitches(this);
    }

Update

Updated the constructor code from .net open source. The dispatcher.cs is available here https://referencesource.microsoft.com/#WindowsBase/Base/System/Windows/Threading/Dispatcher.cs,078d6b27d9837a35

After some investigation we found that issue occurs after some 15000 iterations(each iteration creates a new thread and calls PngBitmapEncoder). Then found that this is linked to Global Atom Table limit (0x4000 or 16384). More details on Global Atom Table here https://docs.microsoft.com/en-us/archive/blogs/ntdebugging/identifying-global-atom-table-leaks

The dispatcher created each time makes an entry in the Global atom table and on thread exit this entry is not cleared. This leads to leak in Global atom table and when it reaches the max limit, it throws "Not enough storage...." error. This seems like a issue with Microsoft's handling of Dispatcher. Even the PngBitmapEncoder documentation, I do not see any remark with respect to Dispatcher handling and any explicit shutdown of dispatcher.

.net
dispatcher
bitmapencoder
asked on Stack Overflow Aug 16, 2020 by Rahul Sundar • edited Aug 27, 2020 by Uwe Keim

1 Answer

1

I had this issue years ago when doing background processing using objects in the System.Windows.Media.Imaging namespace too. I came across a blog entry from a Microsoft engineer admitting that it was an issue, but that there wasn't enough interest to resolve it or something to that effect. I remember hoping they'd fix it in revisions to the framework. The engineer posted a solution that works for me.

I should mention that I've tried using the solution within the thread pool by using System.Threading.ThreadPool.QueueUserWorkItem() or System.Threading.Tasks.Task.Run(), but have found the solution does not work in the thread pool; perhaps because the thread is reused. The only way that I have been able to resolve the issue is by using a System.Threading.Thread to do the work. Below is the basic idea on how to get around the issue and force the resource to release.

new System.Threading.Thread(new System.Threading.ThreadStart(() =>
{
    // Do some imaging work.

    // This asks the dispatcher associated with this thread to shut down right away.
    System.Windows.Threading.Dispatcher.CurrentDispatcher.BeginInvokeShutdown(System.Windows.Threading.DispatcherPriority.Normal);
    System.Windows.Threading.Dispatcher.Run();
})).Start();
answered on Stack Overflow Aug 28, 2020 by Rich

User contributions licensed under CC BY-SA 3.0