| File: System\Windows\Input\Stylus\Wisp\PenThreadPool.cs | Web Access |
| Project: src\wpf\src\Microsoft.DotNet.Wpf\src\PresentationCore\PresentationCore.csproj (PresentationCore) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Windows.Input.Tracing; namespace System.Windows.Input { ///////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> internal class PenThreadPool { /// <summary> /// Limits the number of times to attempt getting a PenThread after /// the selected one does not add the context successfully. /// /// Each PenThread can hold 30 contexts so this limit allows for 300 /// re-entrant context adds after the initial AddPenContext call. /// </summary> private const int MAX_PENTHREAD_RETRIES = 10; static PenThreadPool() { } ///////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> [ThreadStatic] private static PenThreadPool _penThreadPool; ///////////////////////////////////////////////////////////////////// /// <summary> /// </summary> internal static PenThread GetPenThreadForPenContext(PenContext penContext) { // Create the threadstatic DynamicRendererThreadManager as needed for calling thread. // It only creates one if (_penThreadPool == null) { _penThreadPool = new PenThreadPool(); } return _penThreadPool.GetPenThreadForPenContextHelper(penContext); // Adds to weak ref list if creating new one. } ///////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> private List<WeakReference<PenThread>> _penThreadWeakRefList; ///////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> internal PenThreadPool() { _penThreadWeakRefList = new List<WeakReference<PenThread>>(); } /// <summary> /// DevDiv:1192272 /// /// This function has been changed to avoid re-entrancy issues. Previously, the /// PenThread selection depended on calls to AddPenContext in the selection mechanism /// or when creating a new PenThread. Since AddPenContext will wait on operations done /// on a PenThread, this would allow re-entrant calls to occur. These calls had the /// potential to generate redundant PenThreads that could cause performance and functional /// issues in touch-enabled applications. /// /// By removing calls to AddPenContext from the selection loops, we can be certain that there is /// no re-entrancy possible during this part of the code. The call to AddPenContext is now done /// post thread selection/creation. While this is still re-entrant, we handle the possible issues /// from that case by retrying thread selection for any failed AddPenContext calls, ignoring the /// specific thread that failed. After MAX_PENTHREAD_RETRIES, we exit and log an error. /// </summary> private PenThread GetPenThreadForPenContextHelper(PenContext penContext) { // A list of full PenThreads that we should ignore when attempting to select a thread // for this context. List<PenThread> ignoredThreads = new List<PenThread>(); PenThread selectedPenThread = null; // We have gone over the max retries, something is forcing a huge amount // of re-entrancy. In this case, break the loop and exit even if we might // have some issues with missing touch contexts and bad touch behavior. while (ignoredThreads.Count < MAX_PENTHREAD_RETRIES) { // Scan existing penthreads to find one to add the context to // We scan back to front to enable list cleanup. for (int i = _penThreadWeakRefList.Count - 1; i >= 0; i--) { PenThread candidatePenThread = null; // Select a thread if it's a valid WeakReference and we're not ignoring it // Allow selection to happen multiple times so we get the first valid candidate // in forward order. if (_penThreadWeakRefList[i].TryGetTarget(out candidatePenThread) && !ignoredThreads.Contains(candidatePenThread)) { selectedPenThread = candidatePenThread; } // This is an invalid WeakReference and should be removed else if (candidatePenThread == null) { _penThreadWeakRefList.RemoveAt(i); } } // If no valid thread was found, create a new one and add to the pool if (selectedPenThread == null) { selectedPenThread = new PenThread(); _penThreadWeakRefList.Add(new WeakReference<PenThread>(selectedPenThread)); } // If we have no context or we can successfully add to it, then end with this thread if (penContext == null || selectedPenThread.AddPenContext(penContext)) { break; } // If the add wasn't successful, this thread is full, so try again and ignore it else { ignoredThreads.Add(selectedPenThread); selectedPenThread = null; // Log re-entrant calls StylusTraceLogger.LogReentrancy(); } } // If we're here due to max retries, log errors appropriately if (selectedPenThread == null) { StylusTraceLogger.LogReentrancyRetryLimitReached(); Debug.Fail("Retry limit reached when acquiring PenThread"); } return selectedPenThread; } } }