From 911d332c523848023e3c6564788b72b7f419fca1 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Sat, 9 Jun 2018 13:39:58 -0700 Subject: [PATCH] Avoid NativeOverlapped pinning by allocating unmanaged memory for it (#18360) It makes PinnableBufferCache unnecessary --- .../System.Private.CoreLib.csproj | 3 - .../Collections/Concurrent/ConcurrentStack.cs | 490 --------------------- .../src/System/PinnableBufferCache.cs | 445 ------------------- .../src/System/PinnableBufferCacheEventSource.cs | 36 -- .../src/System/Threading/Overlapped.cs | 208 +++------ src/classlibnative/bcltype/CMakeLists.txt | 1 - .../bcltype/windowsruntimebufferhelper.cpp | 44 -- .../bcltype/windowsruntimebufferhelper.h | 27 -- src/gc/gcinterface.h | 2 +- src/gc/objecthandle.cpp | 2 +- src/pal/inc/pal.h | 8 +- src/pal/inc/rt/palrt.h | 2 +- src/vm/appdomain.cpp | 66 --- src/vm/appdomain.hpp | 1 - src/vm/ceemain.cpp | 2 - src/vm/class.cpp | 16 +- src/vm/comcallablewrapper.cpp | 33 +- src/vm/comcallablewrapper.h | 44 +- src/vm/comthreadpool.cpp | 37 +- src/vm/gcenv.ee.cpp | 15 +- src/vm/mscorlib.cpp | 4 - src/vm/mscorlib.h | 19 +- src/vm/nativeoverlapped.cpp | 407 +---------------- src/vm/nativeoverlapped.h | 122 ++--- src/vm/threads.cpp | 3 - src/vm/win32threadpool.cpp | 8 +- 26 files changed, 155 insertions(+), 1890 deletions(-) delete mode 100644 src/System.Private.CoreLib/src/System/Collections/Concurrent/ConcurrentStack.cs delete mode 100644 src/System.Private.CoreLib/src/System/PinnableBufferCache.cs delete mode 100644 src/System.Private.CoreLib/src/System/PinnableBufferCacheEventSource.cs delete mode 100644 src/classlibnative/bcltype/windowsruntimebufferhelper.cpp delete mode 100644 src/classlibnative/bcltype/windowsruntimebufferhelper.h diff --git a/src/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/System.Private.CoreLib/System.Private.CoreLib.csproj index b2387ce..2c0f47a 100644 --- a/src/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -341,7 +341,6 @@ - @@ -557,7 +556,6 @@ - @@ -610,7 +608,6 @@ - diff --git a/src/System.Private.CoreLib/src/System/Collections/Concurrent/ConcurrentStack.cs b/src/System.Private.CoreLib/src/System/Collections/Concurrent/ConcurrentStack.cs deleted file mode 100644 index 019bc4e..0000000 --- a/src/System.Private.CoreLib/src/System/Collections/Concurrent/ConcurrentStack.cs +++ /dev/null @@ -1,490 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -// -// ConcurrentStack.cs -// -// A lock-free, concurrent stack primitive, and its associated debugger view type. -// -// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - -using System.Collections.Generic; -using System.Diagnostics; -using System.Threading; - -namespace System.Collections.Concurrent -{ - // A stack that uses CAS operations internally to maintain thread-safety in a lock-free - // manner. Attempting to push or pop concurrently from the stack will not trigger waiting, - // although some optimistic concurrency and retry is used, possibly leading to lack of - // fairness and/or livelock. The stack uses spinning and backoff to add some randomization, - // in hopes of statistically decreasing the possibility of livelock. - // - // Note that we currently allocate a new node on every push. This avoids having to worry - // about potential ABA issues, since the CLR GC ensures that a memory address cannot be - // reused before all references to it have died. - - /// - /// Represents a thread-safe last-in, first-out collection of objects. - /// - /// Specifies the type of elements in the stack. - /// - /// All public and protected members of are thread-safe and may be used - /// concurrently from multiple threads. - /// - [DebuggerDisplay("Count = {Count}")] - [DebuggerTypeProxy(typeof(IProducerConsumerCollectionDebugView<>))] - internal class ConcurrentStack : IProducerConsumerCollection, IReadOnlyCollection - { - /// - /// A simple (internal) node type used to store elements of concurrent stacks and queues. - /// - private class Node - { - internal readonly T _value; // Value of the node. - internal Node _next; // Next pointer. - - /// - /// Constructs a new node with the specified value and no next node. - /// - /// The value of the node. - internal Node(T value) - { - _value = value; - _next = null; - } - } - - private volatile Node _head; // The stack is a singly linked list, and only remembers the head. - private const int BACKOFF_MAX_YIELDS = 8; // Arbitrary number to cap backoff. - - /// - /// Initializes a new instance of the - /// class. - /// - public ConcurrentStack() - { - } - - /// - /// Gets the number of elements contained in the . - /// - /// The number of elements contained in the . - /// - /// For determining whether the collection contains any items, use of the - /// property is recommended rather than retrieving the number of items from the - /// property and comparing it to 0. - /// - public int Count - { - // Counts the number of entries in the stack. This is an O(n) operation. The answer may be out - // of date before returning, but guarantees to return a count that was once valid. Conceptually, - // the implementation snaps a copy of the list and then counts the entries, though physically - // this is not what actually happens. - get - { - int count = 0; - - // Just whip through the list and tally up the number of nodes. We rely on the fact that - // node next pointers are immutable after being enqueued for the first time, even as - // they are being dequeued. If we ever changed this (e.g. to pool nodes somehow), - // we'd need to revisit this implementation. - - for (Node curr = _head; curr != null; curr = curr._next) - { - count++; //we don't handle overflow, to be consistent with existing generic collection types in CLR - } - - return count; - } - } - - - /// - /// Gets a value indicating whether access to the is - /// synchronized with the SyncRoot. - /// - /// true if access to the is synchronized - /// with the SyncRoot; otherwise, false. For , this property always - /// returns false. - bool ICollection.IsSynchronized - { - // Gets a value indicating whether access to this collection is synchronized. Always returns - // false. The reason is subtle. While access is in face thread safe, it's not the case that - // locking on the SyncRoot would have prevented concurrent pushes and pops, as this property - // would typically indicate; that's because we internally use CAS operations vs. true locks. - get { return false; } - } - - /// - /// Gets an object that can be used to synchronize access to the . This property is not supported. - /// - /// The SyncRoot property is not supported - object ICollection.SyncRoot - { - get - { - ThrowHelper.ThrowNotSupportedException(ExceptionResource.ConcurrentCollection_SyncRoot_NotSupported); - return default; - } - } - - /// - /// Copies the elements of the to an , starting at a particular - /// index. - /// - /// The one-dimensional that is the destination of - /// the elements copied from the - /// . The must - /// have zero-based indexing. - /// The zero-based index in at which copying - /// begins. - /// is a null reference (Nothing in - /// Visual Basic). - /// is less than - /// zero. - /// - /// is multidimensional. -or- - /// does not have zero-based indexing. -or- - /// is equal to or greater than the length of the - /// -or- The number of elements in the source is - /// greater than the available space from to the end of the destination - /// . -or- The type of the source cannot be cast automatically to the type of the - /// destination . - /// - void ICollection.CopyTo(Array array, int index) - { - // Validate arguments. - if (array == null) - { - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); - } - - // We must be careful not to corrupt the array, so we will first accumulate an - // internal list of elements that we will then copy to the array. This requires - // some extra allocation, but is necessary since we don't know up front whether - // the array is sufficiently large to hold the stack's contents. - ((ICollection)ToList()).CopyTo(array, index); - } - - /// - /// Copies the elements to an existing one-dimensional , starting at the specified array index. - /// - /// The one-dimensional that is the destination of - /// the elements copied from the - /// . The must have zero-based - /// indexing. - /// The zero-based index in at which copying - /// begins. - /// is a null reference (Nothing in - /// Visual Basic). - /// is less than - /// zero. - /// is equal to or greater than the - /// length of the - /// -or- The number of elements in the source is greater than the - /// available space from to the end of the destination . - /// - public void CopyTo(T[] array, int index) - { - if (array == null) - { - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); - } - - // We must be careful not to corrupt the array, so we will first accumulate an - // internal list of elements that we will then copy to the array. This requires - // some extra allocation, but is necessary since we don't know up front whether - // the array is sufficiently large to hold the stack's contents. - ToList().CopyTo(array, index); - } - -#pragma warning disable 0420 // No warning for Interlocked.xxx if compiled with new managed compiler (Roslyn) - /// - /// Inserts an object at the top of the . - /// - /// The object to push onto the . The value can be - /// a null reference (Nothing in Visual Basic) for reference types. - /// - public void Push(T item) - { - // Pushes a node onto the front of the stack thread-safely. Internally, this simply - // swaps the current head pointer using a (thread safe) CAS operation to accomplish - // lock freedom. If the CAS fails, we add some back off to statistically decrease - // contention at the head, and then go back around and retry. - - Node newNode = new Node(item); - newNode._next = _head; - if (Interlocked.CompareExchange(ref _head, newNode, newNode._next) == newNode._next) - { - return; - } - - // If we failed, go to the slow path and loop around until we succeed. - PushCore(newNode, newNode); - } - - - /// - /// Push one or many nodes into the stack, if head and tails are equal then push one node to the stack other wise push the list between head - /// and tail to the stack - /// - /// The head pointer to the new list - /// The tail pointer to the new list - private void PushCore(Node head, Node tail) - { - SpinWait spin = new SpinWait(); - - // Keep trying to CAS the existing head with the new node until we succeed. - do - { - spin.SpinOnce(); - // Reread the head and link our new node. - tail._next = _head; - } - while (Interlocked.CompareExchange( - ref _head, head, tail._next) != tail._next); - } - - /// - /// Attempts to pop and return the object at the top of the . - /// - /// - /// When this method returns, if the operation was successful, contains the - /// object removed. If no object was available to be removed, the value is unspecified. - /// - /// true if an element was removed and returned from the top of the - /// successfully; otherwise, false. - public bool TryPop(out T result) - { - Node head = _head; - //stack is empty - if (head == null) - { - result = default; - return false; - } - if (Interlocked.CompareExchange(ref _head, head._next, head) == head) - { - result = head._value; - return true; - } - - // Fall through to the slow path. - return TryPopCore(out result); - } - - /// - /// Local helper function to Pop an item from the stack, slow path - /// - /// The popped item - /// True if succeeded, false otherwise - private bool TryPopCore(out T result) - { - Node poppedNode; - - if (TryPopCore(1, out poppedNode) == 1) - { - result = poppedNode._value; - return true; - } - - result = default; - return false; - } - - /// - /// Slow path helper for TryPop. This method assumes an initial attempt to pop an element - /// has already occurred and failed, so it begins spinning right away. - /// - /// The number of items to pop. - /// - /// When this method returns, if the pop succeeded, contains the removed object. If no object was - /// available to be removed, the value is unspecified. This parameter is passed uninitialized. - /// - /// The number of objects successfully popped from the top of - /// the . - private int TryPopCore(int count, out Node poppedHead) - { - SpinWait spin = new SpinWait(); - - // Try to CAS the head with its current next. We stop when we succeed or - // when we notice that the stack is empty, whichever comes first. - Node head; - Node next; - int backoff = 1; - Random r = null; - while (true) - { - head = _head; - // Is the stack empty? - if (head == null) - { - poppedHead = null; - return 0; - } - next = head; - int nodesCount = 1; - for (; nodesCount < count && next._next != null; nodesCount++) - { - next = next._next; - } - - // Try to swap the new head. If we succeed, break out of the loop. - if (Interlocked.CompareExchange(ref _head, next._next, head) == head) - { - // Return the popped Node. - poppedHead = head; - return nodesCount; - } - - // We failed to CAS the new head. Spin briefly and retry. - for (int i = 0; i < backoff; i++) - { - spin.SpinOnce(); - } - - if (spin.NextSpinWillYield) - { - if (r == null) - { - r = new Random(); - } - backoff = r.Next(1, BACKOFF_MAX_YIELDS); - } - else - { - backoff *= 2; - } - } - } -#pragma warning restore 0420 - - /// - /// Copies the items stored in the to a new array. - /// - /// A new array containing a snapshot of elements copied from the . - public T[] ToArray() - { - Node curr = _head; - return curr == null ? - Array.Empty() : - ToList(curr).ToArray(); - } - - /// - /// Returns an array containing a snapshot of the list's contents, using - /// the target list node as the head of a region in the list. - /// - /// A list of the stack's contents. - private List ToList() - { - return ToList(_head); - } - - /// - /// Returns an array containing a snapshot of the list's contents starting at the specified node. - /// - /// A list of the stack's contents starting at the specified node. - private List ToList(Node curr) - { - List list = new List(); - - while (curr != null) - { - list.Add(curr._value); - curr = curr._next; - } - - return list; - } - - /// - /// Attempts to add an object to the . - /// - /// The object to add to the . The value can be a null - /// reference (Nothing in Visual Basic) for reference types. - /// - /// true if the object was added successfully; otherwise, false. - /// For , this operation will always insert the object onto the - /// top of the - /// and return true. - bool IProducerConsumerCollection.TryAdd(T item) - { - Push(item); - return true; - } - - /// - /// Attempts to remove and return an object from the . - /// - /// - /// When this method returns, if the operation was successful, contains the - /// object removed. If no object was available to be removed, the value is unspecified. - /// - /// true if an element was removed and returned successfully; otherwise, false. - /// For , this operation will attempt to pop the object at - /// the top of the . - /// - bool IProducerConsumerCollection.TryTake(out T item) => TryPop(out item); - - /// - /// Returns an enumerator that iterates through the . - /// - /// An enumerator for the . - /// - /// The enumeration represents a moment-in-time snapshot of the contents - /// of the stack. It does not reflect any updates to the collection after - /// was called. The enumerator is safe to use - /// concurrently with reads from and writes to the stack. - /// - public IEnumerator GetEnumerator() - { - // Returns an enumerator for the stack. This effectively takes a snapshot - // of the stack's contents at the time of the call, i.e. subsequent modifications - // (pushes or pops) will not be reflected in the enumerator's contents. - - //If we put yield-return here, the iterator will be lazily evaluated. As a result a snapshot of - //the stack is not taken when GetEnumerator is initialized but when MoveNext() is first called. - //This is inconsistent with existing generic collections. In order to prevent it, we capture the - //value of _head in a buffer and call out to a helper method - return GetEnumerator(_head); - } - - private IEnumerator GetEnumerator(Node head) - { - Node current = head; - while (current != null) - { - yield return current._value; - current = current._next; - } - } - - /// - /// Returns an enumerator that iterates through a collection. - /// - /// An that can be used to iterate through - /// the collection. - /// - /// The enumeration represents a moment-in-time snapshot of the contents of the stack. It does not - /// reflect any updates to the collection after - /// was called. The enumerator is safe to use concurrently with reads - /// from and writes to the stack. - /// - IEnumerator IEnumerable.GetEnumerator() - { - return ((IEnumerable)this).GetEnumerator(); - } - } -} diff --git a/src/System.Private.CoreLib/src/System/PinnableBufferCache.cs b/src/System.Private.CoreLib/src/System/PinnableBufferCache.cs deleted file mode 100644 index 1bfd2cf..0000000 --- a/src/System.Private.CoreLib/src/System/PinnableBufferCache.cs +++ /dev/null @@ -1,445 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. -#define ENABLE -#define MINBUFFERS - -using System; -using System.Runtime.InteropServices; -using System.Runtime.ConstrainedExecution; -using System.Collections.Generic; -using System.Collections.Concurrent; -using System.Threading; -using System.Runtime.CompilerServices; -using System.Diagnostics; - -#if PINNABLEBUFFERCACHE_MSCORLIB -namespace System.Threading -#else -namespace System -#endif -{ - internal sealed class PinnableBufferCache - { - /// - /// Create a PinnableBufferCache that works on any object (it is intended for OverlappedData) - /// This is only used in mscorlib. - /// - internal PinnableBufferCache(string cacheName, Func factory) - { - m_NotGen2 = new List(DefaultNumberOfBuffers); - m_factory = factory; -#if ENABLE - // Check to see if we should disable the cache. - string envVarName = "PinnableBufferCache_" + cacheName + "_Disabled"; - try - { - string envVar = Environment.GetEnvironmentVariable(envVarName); - if (envVar != null) - { - PinnableBufferCacheEventSource.Log.DebugMessage("Creating " + cacheName + " PinnableBufferCacheDisabled=" + envVar); - int index = envVar.IndexOf(cacheName, StringComparison.OrdinalIgnoreCase); - if (0 <= index) - { - // The cache is disabled because we haven't set the cache name. - PinnableBufferCacheEventSource.Log.DebugMessage("Disabling " + cacheName); - return; - } - } - } - catch - { - // Ignore failures when reading the environment variable. - } -#endif -#if MINBUFFERS - // Allow the environment to specify a minimum buffer count. - string minEnvVarName = "PinnableBufferCache_" + cacheName + "_MinCount"; - try - { - string minEnvVar = Environment.GetEnvironmentVariable(minEnvVarName); - if (minEnvVar != null) - { - if (int.TryParse(minEnvVar, out m_minBufferCount)) - CreateNewBuffers(); - } - } - catch - { - // Ignore failures when reading the environment variable. - } -#endif - - PinnableBufferCacheEventSource.Log.Create(cacheName); - m_CacheName = cacheName; - } - - /// - /// Get a object from the buffer manager. If no buffers exist, allocate a new one. - /// - internal object Allocate() - { -#if ENABLE - // Check to see whether or not the cache is disabled. - if (m_CacheName == null) - return m_factory(); -#endif - // Fast path, get it from our Gen2 aged m_FreeList. - object returnBuffer; - if (!m_FreeList.TryPop(out returnBuffer)) - Restock(out returnBuffer); - - // Computing free count is expensive enough that we don't want to compute it unless logging is on. - if (PinnableBufferCacheEventSource.Log.IsEnabled()) - { - int numAllocCalls = Interlocked.Increment(ref m_numAllocCalls); - if (numAllocCalls >= 1024) - { - lock (this) - { - int previousNumAllocCalls = Interlocked.Exchange(ref m_numAllocCalls, 0); - if (previousNumAllocCalls >= 1024) - { - int nonGen2Count = 0; - foreach (object o in m_FreeList) - { - if (GC.GetGeneration(o) < GC.MaxGeneration) - { - nonGen2Count++; - } - } - - PinnableBufferCacheEventSource.Log.WalkFreeListResult(m_CacheName, m_FreeList.Count, nonGen2Count); - } - } - } - - PinnableBufferCacheEventSource.Log.AllocateBuffer(m_CacheName, PinnableBufferCacheEventSource.AddressOf(returnBuffer), returnBuffer.GetHashCode(), GC.GetGeneration(returnBuffer), m_FreeList.Count); - } - return returnBuffer; - } - - /// - /// Return a buffer back to the buffer manager. - /// - internal void Free(object buffer) - { -#if ENABLE - // Check to see whether or not the cache is disabled. - if (m_CacheName == null) - return; -#endif - if (PinnableBufferCacheEventSource.Log.IsEnabled()) - PinnableBufferCacheEventSource.Log.FreeBuffer(m_CacheName, PinnableBufferCacheEventSource.AddressOf(buffer), buffer.GetHashCode(), m_FreeList.Count); - - - // After we've done 3 gen1 GCs, assume that all buffers have aged into gen2 on the free path. - if ((m_gen1CountAtLastRestock + 3) > GC.CollectionCount(GC.MaxGeneration - 1)) - { - lock (this) - { - if (GC.GetGeneration(buffer) < GC.MaxGeneration) - { - // The buffer is not aged, so put it in the non-aged free list. - m_moreThanFreeListNeeded = true; - PinnableBufferCacheEventSource.Log.FreeBufferStillTooYoung(m_CacheName, m_NotGen2.Count); - m_NotGen2.Add(buffer); - m_gen1CountAtLastRestock = GC.CollectionCount(GC.MaxGeneration - 1); - return; - } - } - } - - // If we discovered that it is indeed Gen2, great, put it in the Gen2 list. - m_FreeList.Push(buffer); - } - - #region Private - - /// - /// Called when we don't have any buffers in our free list to give out. - /// - /// - private void Restock(out object returnBuffer) - { - lock (this) - { - // Try again after getting the lock as another thread could have just filled the free list. If we don't check - // then we unnecessarily grab a new set of buffers because we think we are out. - if (m_FreeList.TryPop(out returnBuffer)) - return; - - // Lazy init, Ask that TrimFreeListIfNeeded be called on every Gen 2 GC. - if (m_restockSize == 0) - Gen2GcCallback.Register(Gen2GcCallbackFunc, this); - - // Indicate to the trimming policy that the free list is insufficent. - m_moreThanFreeListNeeded = true; - PinnableBufferCacheEventSource.Log.AllocateBufferFreeListEmpty(m_CacheName, m_NotGen2.Count); - - // Get more buffers if needed. - if (m_NotGen2.Count == 0) - CreateNewBuffers(); - - // We have no buffers in the aged freelist, so get one from the newer list. Try to pick the best one. - // Debug.Assert(m_NotGen2.Count != 0); - int idx = m_NotGen2.Count - 1; - if (GC.GetGeneration(m_NotGen2[idx]) < GC.MaxGeneration && GC.GetGeneration(m_NotGen2[0]) == GC.MaxGeneration) - idx = 0; - returnBuffer = m_NotGen2[idx]; - m_NotGen2.RemoveAt(idx); - - // Remember any sub-optimial buffer so we don't put it on the free list when it gets freed. - if (PinnableBufferCacheEventSource.Log.IsEnabled() && GC.GetGeneration(returnBuffer) < GC.MaxGeneration) - { - PinnableBufferCacheEventSource.Log.AllocateBufferFromNotGen2(m_CacheName, m_NotGen2.Count); - } - - // If we have a Gen1 collection, then everything on m_NotGen2 should have aged. Move them to the m_Free list. - if (!AgePendingBuffers()) - { - // Before we could age at set of buffers, we have handed out half of them. - // This implies we should be proactive about allocating more (since we will trim them if we over-allocate). - if (m_NotGen2.Count == m_restockSize / 2) - { - PinnableBufferCacheEventSource.Log.DebugMessage("Proactively adding more buffers to aging pool"); - CreateNewBuffers(); - } - } - } - } - - /// - /// See if we can promote the buffers to the free list. Returns true if successful. - /// - private bool AgePendingBuffers() - { - if (m_gen1CountAtLastRestock < GC.CollectionCount(GC.MaxGeneration - 1)) - { - // Allocate a temp list of buffers that are not actually in gen2, and swap it in once - // we're done scanning all buffers. - int promotedCount = 0; - List notInGen2 = new List(); - PinnableBufferCacheEventSource.Log.AllocateBufferAged(m_CacheName, m_NotGen2.Count); - for (int i = 0; i < m_NotGen2.Count; i++) - { - // We actually check every object to ensure that we aren't putting non-aged buffers into the free list. - object currentBuffer = m_NotGen2[i]; - if (GC.GetGeneration(currentBuffer) >= GC.MaxGeneration) - { - m_FreeList.Push(currentBuffer); - promotedCount++; - } - else - { - notInGen2.Add(currentBuffer); - } - } - PinnableBufferCacheEventSource.Log.AgePendingBuffersResults(m_CacheName, promotedCount, notInGen2.Count); - m_NotGen2 = notInGen2; - - return true; - } - return false; - } - - /// - /// Generates some buffers to age into Gen2. - /// - private void CreateNewBuffers() - { - // We choose a very modest number of buffers initially because for the client case. This is often enough. - if (m_restockSize == 0) - m_restockSize = 4; - else if (m_restockSize < DefaultNumberOfBuffers) - m_restockSize = DefaultNumberOfBuffers; - else if (m_restockSize < 256) - m_restockSize = m_restockSize * 2; // Grow quickly at small sizes - else if (m_restockSize < 4096) - m_restockSize = m_restockSize * 3 / 2; // Less agressively at large ones - else - m_restockSize = 4096; // Cap how agressive we are - - // Ensure we hit our minimums - if (m_minBufferCount > m_buffersUnderManagement) - m_restockSize = Math.Max(m_restockSize, m_minBufferCount - m_buffersUnderManagement); - - PinnableBufferCacheEventSource.Log.AllocateBufferCreatingNewBuffers(m_CacheName, m_buffersUnderManagement, m_restockSize); - for (int i = 0; i < m_restockSize; i++) - { - // Make a new buffer. - object newBuffer = m_factory(); - - // Create space between the objects. We do this because otherwise it forms a single plug (group of objects) - // and the GC pins the entire plug making them NOT move to Gen1 and Gen2. by putting space between them - // we ensure that object get a chance to move independently (even if some are pinned). - var dummyObject = new object(); - m_NotGen2.Add(newBuffer); - } - m_buffersUnderManagement += m_restockSize; - m_gen1CountAtLastRestock = GC.CollectionCount(GC.MaxGeneration - 1); - } - - /// - /// This is the static function that is called from the gen2 GC callback. - /// The input object is the cache itself. - /// NOTE: The reason that we make this functionstatic and take the cache as a parameter is that - /// otherwise, we root the cache to the Gen2GcCallback object, and leak the cache even when - /// the application no longer needs it. - /// - private static bool Gen2GcCallbackFunc(object targetObj) - { - return ((PinnableBufferCache)(targetObj)).TrimFreeListIfNeeded(); - } - - /// - /// This is called on every gen2 GC to see if we need to trim the free list. - /// NOTE: DO NOT CALL THIS DIRECTLY FROM THE GEN2GCCALLBACK. INSTEAD CALL IT VIA A STATIC FUNCTION (SEE ABOVE). - /// If you register a non-static function as a callback, then this object will be leaked. - /// - private bool TrimFreeListIfNeeded() - { - int curMSec = Environment.TickCount; - int deltaMSec = curMSec - m_msecNoUseBeyondFreeListSinceThisTime; - PinnableBufferCacheEventSource.Log.TrimCheck(m_CacheName, m_buffersUnderManagement, m_moreThanFreeListNeeded, deltaMSec); - - // If we needed more than just the set of aged buffers since the last time we were called, - // we obviously should not be trimming any memory, so do nothing except reset the flag - if (m_moreThanFreeListNeeded) - { - m_moreThanFreeListNeeded = false; - m_trimmingExperimentInProgress = false; - m_msecNoUseBeyondFreeListSinceThisTime = curMSec; - return true; - } - - // We require a minimum amount of clock time to pass (10 seconds) before we trim. Ideally this time - // is larger than the typical buffer hold time. - if (0 <= deltaMSec && deltaMSec < 10000) - return true; - - // If we got here we have spend the last few second without needing to lengthen the free list. Thus - // we have 'enough' buffers, but maybe we have too many. - // See if we can trim - lock (this) - { - // Hit a race, try again later. - if (m_moreThanFreeListNeeded) - { - m_moreThanFreeListNeeded = false; - m_trimmingExperimentInProgress = false; - m_msecNoUseBeyondFreeListSinceThisTime = curMSec; - return true; - } - - var freeCount = m_FreeList.Count; // This is expensive to fetch, do it once. - - // If there is something in m_NotGen2 it was not used for the last few seconds, it is trimable. - if (m_NotGen2.Count > 0) - { - // If we are not performing an experiment and we have stuff that is waiting to go into the - // free list but has not made it there, it could be becasue the 'slow path' of restocking - // has not happened, so force this (which should flush the list) and start over. - if (!m_trimmingExperimentInProgress) - { - PinnableBufferCacheEventSource.Log.TrimFlush(m_CacheName, m_buffersUnderManagement, freeCount, m_NotGen2.Count); - AgePendingBuffers(); - m_trimmingExperimentInProgress = true; - return true; - } - - PinnableBufferCacheEventSource.Log.TrimFree(m_CacheName, m_buffersUnderManagement, freeCount, m_NotGen2.Count); - m_buffersUnderManagement -= m_NotGen2.Count; - - // Possibly revise the restocking down. We don't want to grow agressively if we are trimming. - var newRestockSize = m_buffersUnderManagement / 4; - if (newRestockSize < m_restockSize) - m_restockSize = Math.Max(newRestockSize, DefaultNumberOfBuffers); - - m_NotGen2.Clear(); - m_trimmingExperimentInProgress = false; - return true; - } - - // Set up an experiment where we use 25% less buffers in our free list. We put them in - // m_NotGen2, and if they are needed they will be put back in the free list again. - var trimSize = freeCount / 4 + 1; - - // We are OK with a 15% overhead, do nothing in that case. - if (freeCount * 15 <= m_buffersUnderManagement || m_buffersUnderManagement - trimSize <= m_minBufferCount) - { - PinnableBufferCacheEventSource.Log.TrimFreeSizeOK(m_CacheName, m_buffersUnderManagement, freeCount); - return true; - } - - // Move buffers from the free list back to the non-aged list. If we don't use them by next time, then we'll consider trimming them. - PinnableBufferCacheEventSource.Log.TrimExperiment(m_CacheName, m_buffersUnderManagement, freeCount, trimSize); - object buffer; - for (int i = 0; i < trimSize; i++) - { - if (m_FreeList.TryPop(out buffer)) - m_NotGen2.Add(buffer); - } - m_msecNoUseBeyondFreeListSinceThisTime = curMSec; - m_trimmingExperimentInProgress = true; - } - - // Indicate that we want to be called back on the next Gen 2 GC. - return true; - } - - private const int DefaultNumberOfBuffers = 16; - private string m_CacheName; - private Func m_factory; - - /// - /// Contains 'good' buffers to reuse. They are guaranteed to be Gen 2 ENFORCED! - /// - private ConcurrentStack m_FreeList = new ConcurrentStack(); - /// - /// Contains buffers that are not gen 2 and thus we do not wish to give out unless we have to. - /// To implement trimming we sometimes put aged buffers in here as a place to 'park' them - /// before true deletion. - /// - private List m_NotGen2; - /// - /// What was the gen 1 count the last time re restocked? If it is now greater, then - /// we know that all objects are in Gen 2 so we don't have to check. Should be updated - /// every time something gets added to the m_NotGen2 list. - /// - private int m_gen1CountAtLastRestock; - - /// - /// Used to ensure we have a minimum time between trimmings. - /// - private int m_msecNoUseBeyondFreeListSinceThisTime; - /// - /// To trim, we remove things from the free list (which is Gen 2) and see if we 'hit bottom' - /// This flag indicates that we hit bottom (we really needed a bigger free list). - /// - private bool m_moreThanFreeListNeeded; - /// - /// The total number of buffers that this cache has ever allocated. - /// Used in trimming heuristics. - /// - private int m_buffersUnderManagement; - /// - /// The number of buffers we added the last time we restocked. - /// - private int m_restockSize; - /// - /// Did we put some buffers into m_NotGen2 to see if we can trim? - /// - private bool m_trimmingExperimentInProgress; - /// - /// A forced minimum number of buffers. - /// - private int m_minBufferCount; - /// - /// The number of calls to Allocate. - /// - private int m_numAllocCalls; - - #endregion - } -} diff --git a/src/System.Private.CoreLib/src/System/PinnableBufferCacheEventSource.cs b/src/System.Private.CoreLib/src/System/PinnableBufferCacheEventSource.cs deleted file mode 100644 index 7d382f3..0000000 --- a/src/System.Private.CoreLib/src/System/PinnableBufferCacheEventSource.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics.Tracing; - -namespace System -{ - internal sealed class PinnableBufferCacheEventSource - { - public static readonly PinnableBufferCacheEventSource Log = new PinnableBufferCacheEventSource(); - - public bool IsEnabled() { return false; } - public void DebugMessage(string message) { } - public void Create(string cacheName) { } - public void AllocateBuffer(string cacheName, ulong objectId, int objectHash, int objectGen, int freeCountAfter) { } - public void AllocateBufferFromNotGen2(string cacheName, int notGen2CountAfter) { } - public void AllocateBufferCreatingNewBuffers(string cacheName, int totalBuffsBefore, int objectCount) { } - public void AllocateBufferAged(string cacheName, int agedCount) { } - public void AllocateBufferFreeListEmpty(string cacheName, int notGen2CountBefore) { } - public void FreeBuffer(string cacheName, ulong objectId, int objectHash, int freeCountBefore) { } - public void FreeBufferStillTooYoung(string cacheName, int notGen2CountBefore) { } - public void TrimCheck(string cacheName, int totalBuffs, bool neededMoreThanFreeList, int deltaMSec) { } - public void TrimFree(string cacheName, int totalBuffs, int freeListCount, int toBeFreed) { } - public void TrimExperiment(string cacheName, int totalBuffs, int freeListCount, int numTrimTrial) { } - public void TrimFreeSizeOK(string cacheName, int totalBuffs, int freeListCount) { } - public void TrimFlush(string cacheName, int totalBuffs, int freeListCount, int notGen2CountBefore) { } - public void AgePendingBuffersResults(string cacheName, int promotedToFreeListCount, int heldBackCount) { } - public void WalkFreeListResult(string cacheName, int freeListCount, int gen0BuffersInFreeList) { } - - internal static ulong AddressOf(object obj) - { - return 0; - } - } -} diff --git a/src/System.Private.CoreLib/src/System/Threading/Overlapped.cs b/src/System.Private.CoreLib/src/System/Threading/Overlapped.cs index 51df27f..1d6d682 100644 --- a/src/System.Private.CoreLib/src/System/Threading/Overlapped.cs +++ b/src/System.Private.CoreLib/src/System/Threading/Overlapped.cs @@ -10,7 +10,6 @@ * - _IOCompletionCallback * - OverlappedData * - Overlapped - * - OverlappedDataCache */ /*============================================================================= @@ -62,7 +61,7 @@ namespace System.Threading private ExecutionContext _executionContext; private uint _errorCode; // Error code private uint _numBytes; // No. of bytes transferred - private NativeOverlapped* _pOVERLAP; + private NativeOverlapped* _pNativeOverlapped; internal _IOCompletionCallback(IOCompletionCallback ioCompletionCallback, ExecutionContext executionContext) { @@ -71,46 +70,39 @@ namespace System.Threading } // Context callback: same sig for SendOrPostCallback and ContextCallback internal static ContextCallback _ccb = new ContextCallback(IOCompletionCallback_Context); - internal static void IOCompletionCallback_Context(Object state) + internal static void IOCompletionCallback_Context(object state) { _IOCompletionCallback helper = (_IOCompletionCallback)state; Debug.Assert(helper != null, "_IOCompletionCallback cannot be null"); - helper._ioCompletionCallback(helper._errorCode, helper._numBytes, helper._pOVERLAP); + helper._ioCompletionCallback(helper._errorCode, helper._numBytes, helper._pNativeOverlapped); } // call back helper - internal static unsafe void PerformIOCompletionCallback(uint errorCode, // Error code - uint numBytes, // No. of bytes transferred - NativeOverlapped* pOVERLAP // ptr to OVERLAP structure - ) + internal static unsafe void PerformIOCompletionCallback(uint errorCode, uint numBytes, NativeOverlapped* pNativeOverlapped) { - Overlapped overlapped; - _IOCompletionCallback helper; - do { - overlapped = OverlappedData.GetOverlappedFromNative(pOVERLAP).m_overlapped; - helper = overlapped.iocbHelper; + OverlappedData overlapped = OverlappedData.GetOverlappedFromNative(pNativeOverlapped); - if (helper == null || helper._executionContext == null || helper._executionContext.IsDefault) + if (overlapped._callback is IOCompletionCallback iocb) { // We got here because of UnsafePack (or) Pack with EC flow suppressed - IOCompletionCallback callback = overlapped.UserCallback; - callback(errorCode, numBytes, pOVERLAP); + iocb(errorCode, numBytes, pNativeOverlapped); } else { // We got here because of Pack + var helper = (_IOCompletionCallback)overlapped._callback; helper._errorCode = errorCode; helper._numBytes = numBytes; - helper._pOVERLAP = pOVERLAP; + helper._pNativeOverlapped = pNativeOverlapped; ExecutionContext.RunInternal(helper._executionContext, _ccb, helper); } //Quickly check the VM again, to see if a packet has arrived. - OverlappedData.CheckVMForIOPacket(out pOVERLAP, out errorCode, out numBytes); - } while (pOVERLAP != null); + OverlappedData.CheckVMForIOPacket(out pNativeOverlapped, out errorCode, out numBytes); + } while (pNativeOverlapped != null); } } @@ -119,50 +111,28 @@ namespace System.Threading #region class OverlappedData - sealed internal class OverlappedData + sealed internal unsafe class OverlappedData { // ! If you make any change to the layout here, you need to make matching change - // ! to OverlappedObject in vm\nativeoverlapped.h - internal IAsyncResult m_asyncResult; - internal IOCompletionCallback m_iocb; - internal _IOCompletionCallback m_iocbHelper; - internal Overlapped m_overlapped; - private Object m_userObject; - private IntPtr m_pinSelf; - private IntPtr m_userObjectInternal; - private int m_AppDomainId; -#pragma warning disable 414 // Field is not used from managed. -#pragma warning disable 169 - private byte m_isArray; - private byte m_toBeCleaned; -#pragma warning restore 414 -#pragma warning restore 169 - internal NativeOverlapped m_nativeOverlapped; - - // Adding an empty default ctor for annotation purposes - internal OverlappedData() { } - - internal void ReInitialize() - { - m_asyncResult = null; - m_iocb = null; - m_iocbHelper = null; - m_overlapped = null; - m_userObject = null; - Debug.Assert(m_pinSelf == IntPtr.Zero, "OverlappedData has not been freed: m_pinSelf"); - m_pinSelf = IntPtr.Zero; - m_userObjectInternal = IntPtr.Zero; - Debug.Assert(m_AppDomainId == 0 || m_AppDomainId == AppDomain.CurrentDomain.Id, "OverlappedData is not in the current domain"); - m_AppDomainId = 0; - m_nativeOverlapped.EventHandle = IntPtr.Zero; - m_isArray = 0; - m_nativeOverlapped.InternalLow = IntPtr.Zero; - m_nativeOverlapped.InternalHigh = IntPtr.Zero; - } + // ! to OverlappedDataObject in vm\nativeoverlapped.h + internal IAsyncResult _asyncResult; + internal object _callback; // IOCompletionCallback or _IOCompletionCallback + internal Overlapped _overlapped; + private object _userObject; + private NativeOverlapped * _pNativeOverlapped; + private IntPtr _eventHandle; + private int _offsetLow; + private int _offsetHigh; + + internal ref IAsyncResult AsyncResult => ref _asyncResult; + + internal ref int OffsetLow => ref (_pNativeOverlapped != null) ? ref _pNativeOverlapped->OffsetLow : ref _offsetLow; + internal ref int OffsetHigh => ref (_pNativeOverlapped != null) ? ref _pNativeOverlapped->OffsetHigh : ref _offsetHigh; + internal ref IntPtr EventHandle => ref (_pNativeOverlapped != null) ? ref _pNativeOverlapped->EventHandle : ref _eventHandle; internal unsafe NativeOverlapped* Pack(IOCompletionCallback iocb, Object userData) { - if (m_pinSelf != IntPtr.Zero) + if (_pNativeOverlapped != null) { throw new InvalidOperationException(SR.InvalidOperation_Overlapped_Pack); } @@ -170,69 +140,38 @@ namespace System.Threading if (iocb != null) { ExecutionContext ec = ExecutionContext.Capture(); - m_iocbHelper = ec != null ? new _IOCompletionCallback(iocb, ec) : null; - m_iocb = iocb; + _callback = (ec != null && !ec.IsDefault) ? new _IOCompletionCallback(iocb, ec) : (object)iocb; } else { - m_iocbHelper = null; - m_iocb = null; - } - m_userObject = userData; - if (m_userObject != null) - { - if (m_userObject.GetType() == typeof(Object[])) - { - m_isArray = 1; - } - else - { - m_isArray = 0; - } + _callback = null; } + _userObject = userData; return AllocateNativeOverlapped(); } internal unsafe NativeOverlapped* UnsafePack(IOCompletionCallback iocb, Object userData) { - if (m_pinSelf != IntPtr.Zero) + if (_pNativeOverlapped != null) { throw new InvalidOperationException(SR.InvalidOperation_Overlapped_Pack); } - m_userObject = userData; - if (m_userObject != null) - { - if (m_userObject.GetType() == typeof(Object[])) - { - m_isArray = 1; - } - else - { - m_isArray = 0; - } - } - m_iocb = iocb; - m_iocbHelper = null; + _userObject = userData; + _callback = iocb; return AllocateNativeOverlapped(); } - internal IntPtr UserHandle - { - get { return m_nativeOverlapped.EventHandle; } - set { m_nativeOverlapped.EventHandle = value; } - } - [MethodImplAttribute(MethodImplOptions.InternalCall)] - private extern unsafe NativeOverlapped* AllocateNativeOverlapped(); + private extern NativeOverlapped* AllocateNativeOverlapped(); [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal static extern unsafe void FreeNativeOverlapped(NativeOverlapped* nativeOverlappedPtr); + internal static extern void FreeNativeOverlapped(NativeOverlapped* nativeOverlappedPtr); [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal static extern unsafe OverlappedData GetOverlappedFromNative(NativeOverlapped* nativeOverlappedPtr); + internal static extern OverlappedData GetOverlappedFromNative(NativeOverlapped* nativeOverlappedPtr); [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal static extern unsafe void CheckVMForIOPacket(out NativeOverlapped* pOVERLAP, out uint errorCode, out uint numBytes); + internal static extern void CheckVMForIOPacket(out NativeOverlapped* pNativeOverlapped, out uint errorCode, out uint numBytes); } #endregion class OverlappedData @@ -242,23 +181,22 @@ namespace System.Threading public class Overlapped { - private OverlappedData m_overlappedData; - private static PinnableBufferCache s_overlappedDataCache = new PinnableBufferCache("System.Threading.OverlappedData", () => new OverlappedData()); + private OverlappedData _overlappedData; public Overlapped() { - m_overlappedData = (OverlappedData)s_overlappedDataCache.Allocate(); - m_overlappedData.m_overlapped = this; + // The split between Overlapped and OverlappedData should not be needed. It is required by the implementation of + // async GC handles currently. It expects OverlappedData to be a sealed type. + _overlappedData = new OverlappedData(); + _overlappedData._overlapped = this; } - public Overlapped(int offsetLo, int offsetHi, IntPtr hEvent, IAsyncResult ar) + public Overlapped(int offsetLo, int offsetHi, IntPtr hEvent, IAsyncResult ar) : this() { - m_overlappedData = (OverlappedData)s_overlappedDataCache.Allocate(); - m_overlappedData.m_overlapped = this; - m_overlappedData.m_nativeOverlapped.OffsetLow = offsetLo; - m_overlappedData.m_nativeOverlapped.OffsetHigh = offsetHi; - m_overlappedData.UserHandle = hEvent; - m_overlappedData.m_asyncResult = ar; + _overlappedData.OffsetLow = offsetLo; + _overlappedData.OffsetHigh = offsetHi; + _overlappedData.EventHandle = hEvent; + _overlappedData.AsyncResult = ar; } [Obsolete("This constructor is not 64-bit compatible. Use the constructor that takes an IntPtr for the event handle. http://go.microsoft.com/fwlink/?linkid=14202")] @@ -268,43 +206,33 @@ namespace System.Threading public IAsyncResult AsyncResult { - get { return m_overlappedData.m_asyncResult; } - set { m_overlappedData.m_asyncResult = value; } + get { return _overlappedData.AsyncResult; } + set { _overlappedData.AsyncResult = value; } } public int OffsetLow { - get { return m_overlappedData.m_nativeOverlapped.OffsetLow; } - set { m_overlappedData.m_nativeOverlapped.OffsetLow = value; } + get { return _overlappedData.OffsetLow; } + set { _overlappedData.OffsetLow = value; } } public int OffsetHigh { - get { return m_overlappedData.m_nativeOverlapped.OffsetHigh; } - set { m_overlappedData.m_nativeOverlapped.OffsetHigh = value; } + get { return _overlappedData.OffsetHigh; } + set { _overlappedData.OffsetHigh = value; } } [Obsolete("This property is not 64-bit compatible. Use EventHandleIntPtr instead. http://go.microsoft.com/fwlink/?linkid=14202")] public int EventHandle { - get { return m_overlappedData.UserHandle.ToInt32(); } - set { m_overlappedData.UserHandle = new IntPtr(value); } + get { return EventHandleIntPtr.ToInt32(); } + set { EventHandleIntPtr = new IntPtr(value); } } public IntPtr EventHandleIntPtr { - get { return m_overlappedData.UserHandle; } - set { m_overlappedData.UserHandle = value; } - } - - internal _IOCompletionCallback iocbHelper - { - get { return m_overlappedData.m_iocbHelper; } - } - - internal IOCompletionCallback UserCallback - { - get { return m_overlappedData.m_iocb; } + get { return _overlappedData.EventHandle; } + set { _overlappedData.EventHandle = value; } } /*==================================================================== @@ -320,9 +248,9 @@ namespace System.Threading } [CLSCompliant(false)] - public unsafe NativeOverlapped* Pack(IOCompletionCallback iocb, Object userData) + public unsafe NativeOverlapped* Pack(IOCompletionCallback iocb, object userData) { - return m_overlappedData.Pack(iocb, userData); + return _overlappedData.Pack(iocb, userData); } [Obsolete("This method is not safe. Use UnsafePack (iocb, userData) instead. http://go.microsoft.com/fwlink/?linkid=14202")] @@ -333,9 +261,9 @@ namespace System.Threading } [CLSCompliant(false)] - public unsafe NativeOverlapped* UnsafePack(IOCompletionCallback iocb, Object userData) + public unsafe NativeOverlapped* UnsafePack(IOCompletionCallback iocb, object userData) { - return m_overlappedData.UnsafePack(iocb, userData); + return _overlappedData.UnsafePack(iocb, userData); } /*==================================================================== @@ -348,9 +276,7 @@ namespace System.Threading if (nativeOverlappedPtr == null) throw new ArgumentNullException(nameof(nativeOverlappedPtr)); - Overlapped overlapped = OverlappedData.GetOverlappedFromNative(nativeOverlappedPtr).m_overlapped; - - return overlapped; + return OverlappedData.GetOverlappedFromNative(nativeOverlappedPtr)._overlapped; } [CLSCompliant(false)] @@ -359,14 +285,10 @@ namespace System.Threading if (nativeOverlappedPtr == null) throw new ArgumentNullException(nameof(nativeOverlappedPtr)); - Overlapped overlapped = OverlappedData.GetOverlappedFromNative(nativeOverlappedPtr).m_overlapped; + OverlappedData.GetOverlappedFromNative(nativeOverlappedPtr)._overlapped._overlappedData = null; OverlappedData.FreeNativeOverlapped(nativeOverlappedPtr); - OverlappedData overlappedData = overlapped.m_overlappedData; - overlapped.m_overlappedData = null; - overlappedData.ReInitialize(); - s_overlappedDataCache.Free(overlappedData); } } #endregion class Overlapped -} // namespace +} diff --git a/src/classlibnative/bcltype/CMakeLists.txt b/src/classlibnative/bcltype/CMakeLists.txt index 785f6b5..62cf968 100644 --- a/src/classlibnative/bcltype/CMakeLists.txt +++ b/src/classlibnative/bcltype/CMakeLists.txt @@ -12,7 +12,6 @@ set(BCLTYPE_SOURCES decimal.cpp diyfp.cpp grisu3.cpp - windowsruntimebufferhelper.cpp number.cpp oavariant.cpp objectnative.cpp diff --git a/src/classlibnative/bcltype/windowsruntimebufferhelper.cpp b/src/classlibnative/bcltype/windowsruntimebufferhelper.cpp deleted file mode 100644 index c99730e..0000000 --- a/src/classlibnative/bcltype/windowsruntimebufferhelper.cpp +++ /dev/null @@ -1,44 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#ifdef FEATURE_COMINTEROP - -#include "common.h" -#include "ComCallableWrapper.h" -#include "WindowsRuntimeBufferHelper.h" - -void QCALLTYPE WindowsRuntimeBufferHelper::StoreOverlappedPtrInCCW(QCall::ObjectHandleOnStack winRtBuffer, LPOVERLAPPED lpOverlapped) { - - QCALL_CONTRACT; - - BEGIN_QCALL; - - GCX_COOP(); - OBJECTREF buffer = ObjectToOBJECTREF(*winRtBuffer.m_ppObject); - - ComCallWrapper *ccw = ComCallWrapper::GetWrapperForObject(buffer); - SimpleComCallWrapper *simpleCCW = ccw->GetSimpleWrapper(); - - simpleCCW->StoreOverlappedPointer(lpOverlapped); - - END_QCALL; -} - - -void WindowsRuntimeBufferHelper::ReleaseOverlapped(LPOVERLAPPED lpOverlapped) { - - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - SO_TOLERANT; - } - CONTRACTL_END; - - GCX_COOP(); - OverlappedDataObject::GetOverlapped(lpOverlapped)->FreeAsyncPinHandles(); -} - -#endif // ifdef FEATURE_COMINTEROP diff --git a/src/classlibnative/bcltype/windowsruntimebufferhelper.h b/src/classlibnative/bcltype/windowsruntimebufferhelper.h deleted file mode 100644 index 4259651..0000000 --- a/src/classlibnative/bcltype/windowsruntimebufferhelper.h +++ /dev/null @@ -1,27 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#ifndef _WINDOWSRUNTIMEBUFFERHELPER_H_ -#define _WINDOWSRUNTIMEBUFFERHELPER_H_ - -#ifdef FEATURE_COMINTEROP - -#include "nativeoverlapped.h" -#include "qcall.h" - -class WindowsRuntimeBufferHelper { - -private: - - -public: - - static void QCALLTYPE StoreOverlappedPtrInCCW(QCall::ObjectHandleOnStack winRtBuffer, LPOVERLAPPED lpOverlapped); - static void ReleaseOverlapped(LPOVERLAPPED lpOverlapped); - -}; - -#endif // ifdef FEATURE_COMINTEROP - -#endif // _WINDOWSRUNTIMEBUFFERHELPER_H_ diff --git a/src/gc/gcinterface.h b/src/gc/gcinterface.h index 40812d5..58482b8 100644 --- a/src/gc/gcinterface.h +++ b/src/gc/gcinterface.h @@ -7,7 +7,7 @@ // The major version of the GC/EE interface. Breaking changes to this interface // require bumps in the major version number. -#define GC_INTERFACE_MAJOR_VERSION 1 +#define GC_INTERFACE_MAJOR_VERSION 2 // The minor version of the GC/EE interface. Non-breaking changes are required // to bump the minor version number. GCs and EEs with minor version number diff --git a/src/gc/objecthandle.cpp b/src/gc/objecthandle.cpp index ab39589..2e26476 100644 --- a/src/gc/objecthandle.cpp +++ b/src/gc/objecthandle.cpp @@ -285,7 +285,7 @@ void CALLBACK AsyncPinObject(_UNCHECKED_OBJECTREF *pObjRef, uintptr_t *pExtraInf Object **pRef = (Object **)pObjRef; _ASSERTE(lp2); promote_func* callback = (promote_func*)lp2; - callback(pRef, (ScanContext *)lp1, GC_CALL_PINNED); + callback(pRef, (ScanContext *)lp1, 0); Object* pPinnedObj = *pRef; if (!HndIsNullOrDestroyedHandle(pPinnedObj)) { diff --git a/src/pal/inc/pal.h b/src/pal/inc/pal.h index f89798c..d5587b9 100644 --- a/src/pal/inc/pal.h +++ b/src/pal/inc/pal.h @@ -873,7 +873,13 @@ SetFileAttributesW( #define SetFileAttributes SetFileAttributesA #endif -typedef LPVOID LPOVERLAPPED; // diff from winbase.h +typedef struct _OVERLAPPED { + ULONG_PTR Internal; + ULONG_PTR InternalHigh; + DWORD Offset; + DWORD OffsetHigh; + HANDLE hEvent; +} OVERLAPPED, *LPOVERLAPPED; PALIMPORT BOOL diff --git a/src/pal/inc/rt/palrt.h b/src/pal/inc/rt/palrt.h index 1360a81..e262b0d 100644 --- a/src/pal/inc/rt/palrt.h +++ b/src/pal/inc/rt/palrt.h @@ -1273,7 +1273,7 @@ interface IMoniker; typedef VOID (WINAPI *LPOVERLAPPED_COMPLETION_ROUTINE)( DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, - LPVOID lpOverlapped); + LPOVERLAPPED lpOverlapped); // // Debug APIs diff --git a/src/vm/appdomain.cpp b/src/vm/appdomain.cpp index 8a69a99..3766ed5 100644 --- a/src/vm/appdomain.cpp +++ b/src/vm/appdomain.cpp @@ -8966,76 +8966,10 @@ void AppDomain::ClearGCHandles() GCHeapUtilities::GetGCHeap()->WaitUntilConcurrentGCComplete(); - // Keep async pin handles alive by moving them to default domain - HandleAsyncPinHandles(); - // Remove our handle store as a source of GC roots m_handleStore->Uproot(); } -// When an AD is unloaded, we will release all objects in this AD. -// If a future asynchronous operation, like io completion port function, -// we need to keep the memory space fixed so that the gc heap is not corrupted. -void AppDomain::HandleAsyncPinHandles() -{ - CONTRACTL - { - GC_TRIGGERS; - MODE_COOPERATIVE; - NOTHROW; - } - CONTRACTL_END; - - IGCHandleStore *pBucket = m_handleStore; - - // IO completion port picks IO job using FIFO. Here is how we know which AsyncPinHandle can be freed. - // 1. We mark all non-pending AsyncPinHandle with READYTOCLEAN. - // 2. We queue a dump Overlapped to the IO completion as a marker. - // 3. When the Overlapped is picked up by completion port, we wait until all previous IO jobs are processed. - // 4. Then we can delete all AsyncPinHandle marked with READYTOCLEAN. - IGCHandleStore *pBucketInDefault = SystemDomain::System()->DefaultDomain()->m_handleStore; - - auto clearIfComplete = [](Object* object) - { - LIMITED_METHOD_CONTRACT; - - assert(object != nullptr); - if (object->GetGCSafeMethodTable() != g_pOverlappedDataClass) - { - return; - } - - OVERLAPPEDDATAREF overlapped = (OVERLAPPEDDATAREF)(ObjectToOBJECTREF((Object*)object)); - if (overlapped->HasCompleted()) - { - // IO has finished. We don't need to pin the user buffer any longer. - overlapped->m_userObject = NULL; - } - - BashMTForPinnedObject(ObjectToOBJECTREF(object)); - }; - - auto setHandle = [](Object* object, OBJECTHANDLE handle) - { - LIMITED_METHOD_CONTRACT; - - assert(object != nullptr); - assert(handle); - - if (object->GetGCSafeMethodTable() != g_pOverlappedDataClass) - { - return; - } - - OverlappedDataObject* overlapped = (OverlappedDataObject*)object; - overlapped->m_pinSelf = handle; - }; - - pBucket->RelocateAsyncPinnedHandles(pBucketInDefault, clearIfComplete, setHandle); - - OverlappedDataObject::RequestCleanup(); -} - void AppDomain::ClearGCRoots() { CONTRACTL diff --git a/src/vm/appdomain.hpp b/src/vm/appdomain.hpp index d92aabb..501e215 100644 --- a/src/vm/appdomain.hpp +++ b/src/vm/appdomain.hpp @@ -3438,7 +3438,6 @@ private: void Close(); void ClearGCRoots(); void ClearGCHandles(); - void HandleAsyncPinHandles(); void UnwindThreads(); // Return TRUE if EE is stopped // Return FALSE if more work is needed diff --git a/src/vm/ceemain.cpp b/src/vm/ceemain.cpp index f599a2a..fddc127 100644 --- a/src/vm/ceemain.cpp +++ b/src/vm/ceemain.cpp @@ -1012,8 +1012,6 @@ void EEStartupHelper(COINITIEE fFlags) // Now we really have fully initialized the garbage collector SetGarbageCollectorFullyInitialized(); - InitializePinHandleTable(); - #ifdef DEBUGGING_SUPPORTED // Make a call to publish the DefaultDomain for the debugger // This should be done before assemblies/modules are loaded into it (i.e. SystemDomain::Init) diff --git a/src/vm/class.cpp b/src/vm/class.cpp index f66a719..4375a34 100644 --- a/src/vm/class.cpp +++ b/src/vm/class.cpp @@ -2943,21 +2943,7 @@ void EEClass::Save(DataImage *image, MethodTable *pMT) LOG((LF_ZAP, LL_INFO10000, "EEClass::Save %s (%p)\n", m_szDebugClassName, this)); - // Optimize packable fields before saving into ngen image (the packable fields are located at the end of - // the EEClass or sub-type instance and packing will transform them into a space-efficient format which - // should reduce the result returned by the GetSize() call below). Packing will fail if the compression - // algorithm would result in an increase in size. We track this in the m_fFieldsArePacked data member - // which we use to determine whether to access the fields in their packed or unpacked format. - // Special case: we don't attempt to pack fields for the System.Threading.OverlappedData class since a - // host can change the size of this at runtime. This requires modifying one of the packable fields and we - // don't support updates to such fields if they were successfully packed. - if (g_pOverlappedDataClass == NULL) - { - g_pOverlappedDataClass = MscorlibBinder::GetClass(CLASS__OVERLAPPEDDATA); - _ASSERTE(g_pOverlappedDataClass); - } - if (this != g_pOverlappedDataClass->GetClass()) - m_fFieldsArePacked = GetPackedFields()->PackFields(); + m_fFieldsArePacked = GetPackedFields()->PackFields(); DWORD cbSize = GetSize(); diff --git a/src/vm/comcallablewrapper.cpp b/src/vm/comcallablewrapper.cpp index e2ba05f..f6ad119 100644 --- a/src/vm/comcallablewrapper.cpp +++ b/src/vm/comcallablewrapper.cpp @@ -44,7 +44,6 @@ #include "caparser.h" #include "appdomain.inl" #include "rcwwalker.h" -#include "windowsruntimebufferhelper.h" #include "winrttypenameconverter.h" #include "typestring.h" @@ -1085,25 +1084,13 @@ VOID SimpleComCallWrapper::Cleanup() m_pWrap = NULL; m_pMT = NULL; - if (HasOverlappedRef()) + if (m_pCPList) { - if (m_operlappedPtr) - { - WindowsRuntimeBufferHelper::ReleaseOverlapped(m_operlappedPtr); - m_operlappedPtr = NULL; - } - UnMarkOverlappedRef(); - } - else - { - if (m_pCPList) // enum_HasOverlappedRef - { - for (UINT i = 0; i < m_pCPList->Size(); i++) - delete (*m_pCPList)[i]; + for (UINT i = 0; i < m_pCPList->Size(); i++) + delete (*m_pCPList)[i]; - delete m_pCPList; - m_pCPList = NULL; - } + delete m_pCPList; + m_pCPList = NULL; } // if this object was made agile, then we will have stashed away the original handle @@ -1426,8 +1413,6 @@ void SimpleComCallWrapper::SetUpCPList() CQuickArray SrcItfList; - _ASSERTE(!HasOverlappedRef()); - // If the list has already been set up, then return. if (m_pCPList) return; @@ -1450,8 +1435,6 @@ void SimpleComCallWrapper::SetUpCPListHelper(MethodTable **apSrcItfMTs, int cSrc PRECONDITION(CheckPointer(apSrcItfMTs)); } CONTRACTL_END; - - _ASSERTE(!HasOverlappedRef()); CPListHolder pCPList = NULL; ComCallWrapper *pWrap = GetMainWrapper(); @@ -2046,9 +2029,7 @@ BOOL SimpleComCallWrapper::FindConnectionPoint(REFIID riid, IConnectionPoint **p PRECONDITION(CheckPointer(ppCP)); } CONTRACTL_END; - - _ASSERTE(!HasOverlappedRef()); - + // If the connection point list hasn't been set up yet, then set it up now. if (!m_pCPList) SetUpCPList(); @@ -2085,8 +2066,6 @@ void SimpleComCallWrapper::EnumConnectionPoints(IEnumConnectionPoints **ppEnumCP } CONTRACTL_END; - _ASSERTE(!HasOverlappedRef()); - // If the connection point list hasn't been set up yet, then set it up now. if (!m_pCPList) SetUpCPList(); diff --git a/src/vm/comcallablewrapper.h b/src/vm/comcallablewrapper.h index 1a68135..152830e 100644 --- a/src/vm/comcallablewrapper.h +++ b/src/vm/comcallablewrapper.h @@ -1493,7 +1493,7 @@ private: enum_IsObjectTP = 0x8, enum_IsAgile = 0x10, enum_IsPegged = 0x80, - enum_HasOverlappedRef = 0x100, + // unused = 0x100, enum_CustomQIRespondsToIMarshal = 0x200, enum_CustomQIRespondsToIMarshal_Inited = 0x400, }; @@ -1795,14 +1795,7 @@ public: FastInterlockAnd((ULONG*)&m_flags, ~enum_IsPegged); } - - inline BOOL HasOverlappedRef() - { - LIMITED_METHOD_DAC_CONTRACT; - - return m_flags & enum_HasOverlappedRef; - } - + // Used for the creation and deletion of simple wrappers static SimpleComCallWrapper* CreateSimpleWrapper(); @@ -2161,14 +2154,6 @@ public: return pWeakRef; } - void StoreOverlappedPointer(LPOVERLAPPED lpOverlapped) - { - LIMITED_METHOD_CONTRACT; - - this->m_operlappedPtr = lpOverlapped; - MarkOverlappedRef(); - } - // Returns TRUE if the ICustomQI implementation returns Handled or Failed for IID_IMarshal. BOOL CustomQIRespondsToIMarshal(); @@ -2210,14 +2195,7 @@ private: // QI for well known interfaces from within the runtime based on an IID. IUnknown* QIStandardInterface(REFIID riid); - // These values are never used at the same time, so we can save a few bytes for each CCW by using a union. - // Use the inline methods HasOverlappedRef(), MarkOverlappedRef(), and UnMarkOverlappedRef() to differentiate - // how this union is to be interpreted. - union - { - CQuickArray* m_pCPList; - LPOVERLAPPED m_operlappedPtr; - }; + CQuickArray* m_pCPList; // syncblock for the ObjecRef SyncBlock* m_pSyncBlock; @@ -2250,21 +2228,7 @@ private: // This maintains both COM ref and Jupiter ref in 64-bit LONGLONG m_llRefCount; - - inline void MarkOverlappedRef() - { - LIMITED_METHOD_CONTRACT; - - FastInterlockOr((ULONG*)&m_flags, enum_HasOverlappedRef); - } - - inline void UnMarkOverlappedRef() - { - LIMITED_METHOD_CONTRACT; - - FastInterlockAnd((ULONG*)&m_flags, ~enum_HasOverlappedRef); - } -}; + }; inline OBJECTHANDLE ComCallWrapper::GetObjectHandle() { diff --git a/src/vm/comthreadpool.cpp b/src/vm/comthreadpool.cpp index fcb51dc..b6d4106 100644 --- a/src/vm/comthreadpool.cpp +++ b/src/vm/comthreadpool.cpp @@ -559,7 +559,6 @@ struct BindIoCompletion_Args DWORD ErrorCode; DWORD numBytesTransferred; LPOVERLAPPED lpOverlapped; - BOOL *pfProcessed; }; void SetAsyncResultProperties( @@ -589,7 +588,6 @@ VOID BindIoCompletionCallBack_Worker(LPVOID args) OVERLAPPEDDATAREF overlapped = ObjectToOVERLAPPEDDATAREF(OverlappedDataObject::GetOverlapped(lpOverlapped)); GCPROTECT_BEGIN(overlapped); - *(((BindIoCompletion_Args *)args)->pfProcessed) = TRUE; // we set processed to TRUE, now it's our responsibility to guarantee proper cleanup #ifdef _DEBUG @@ -597,7 +595,7 @@ VOID BindIoCompletionCallBack_Worker(LPVOID args) LogCall(pMeth,"IOCallback"); #endif - if (overlapped->m_iocb != NULL) + if (overlapped->m_callback != NULL) { // Caution: the args are not protected, we have to garantee there's no GC from here till PREPARE_NONVIRTUAL_CALLSITE(METHOD__IOCB_HELPER__PERFORM_IOCOMPLETION_CALLBACK); @@ -612,7 +610,7 @@ VOID BindIoCompletionCallBack_Worker(LPVOID args) else { // no user delegate to callback - _ASSERTE((overlapped->m_iocbHelper == NULL) || !"This is benign, but should be optimized"); + _ASSERTE((overlapped->m_callback == NULL) || !"This is benign, but should be optimized"); SetAsyncResultProperties(overlapped, ErrorCode, numBytesTransferred); @@ -652,33 +650,8 @@ void __stdcall BindIoCompletionCallbackStubEx(DWORD ErrorCode, GCX_COOP(); - // NOTE: there is a potential race between the time we retrieve the app domain pointer, - // and the time which this thread enters the domain. - // - // To solve the race, we rely on the fact that there is a thread sync (via GC) - // between releasing an app domain's handle, and destroying the app domain. Thus - // it is important that we not go into preemptive gc mode in that window. - // - - //IMPORTANT - do not gc protect overlapped here - it belongs to another appdomain - //so if it stops being pinned it should be able to go away - OVERLAPPEDDATAREF overlapped = ObjectToOVERLAPPEDDATAREF(OverlappedDataObject::GetOverlapped(lpOverlapped)); - AppDomainFromIDHolder appDomain(ADID(overlapped->GetAppDomainId()), TRUE); - BOOL fProcessed = FALSE; - if (!appDomain.IsUnloaded()) - { - // this holder resets our thread's security state when exiting this scope, - // but only if setStack is TRUE. - Thread* pHolderThread = NULL; - if (setStack) - { - pHolderThread = pThread; - } - - BindIoCompletion_Args args = {ErrorCode, numBytesTransferred, lpOverlapped, &fProcessed}; - appDomain.Release(); - ManagedThreadBase::ThreadPool(ADID(overlapped->GetAppDomainId()), BindIoCompletionCallBack_Worker, &args); - } + BindIoCompletion_Args args = {ErrorCode, numBytesTransferred, lpOverlapped}; + ManagedThreadBase::ThreadPool((ADID)DefaultADID, BindIoCompletionCallBack_Worker, &args); LOG((LF_INTEROP, LL_INFO10000, "Leaving IO_CallBackStub thread 0x%x retCode 0x%x, overlap 0x%x\n", pThread, ErrorCode, lpOverlapped)); // We should have released all locks. @@ -751,7 +724,7 @@ FCIMPL1(FC_BOOL_RET, ThreadPoolNative::CorPostQueuedCompletionStatus, LPOVERLAPP HELPER_METHOD_FRAME_BEGIN_RET_1(overlapped); // Eventually calls BEGIN_SO_INTOLERANT_CODE_NOTHROW // OS doesn't signal handle, so do it here - overlapped->Internal = 0; + lpOverlapped->Internal = 0; if (ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, ThreadPoolIOEnqueue)) FireEtwThreadPoolIOEnqueue(lpOverlapped, OBJECTREFToObject(overlapped), false, GetClrInstanceId()); diff --git a/src/vm/gcenv.ee.cpp b/src/vm/gcenv.ee.cpp index daf6fe8..7f9d269 100644 --- a/src/vm/gcenv.ee.cpp +++ b/src/vm/gcenv.ee.cpp @@ -1371,17 +1371,13 @@ void GCToEEInterface::WalkAsyncPinnedForPromotion(Object* object, ScanContext* s OverlappedDataObject *pOverlapped = (OverlappedDataObject *)object; if (pOverlapped->m_userObject != NULL) { - //callback(OBJECTREF_TO_UNCHECKED_OBJECTREF(pOverlapped->m_userObject), (ScanContext *)lp1, GC_CALL_PINNED); - if (pOverlapped->m_isArray) + if (pOverlapped->m_userObject->GetGCSafeMethodTable() == g_pPredefinedArrayTypes[ELEMENT_TYPE_OBJECT]->GetMethodTable()) { // OverlappedDataObject is very special. An async pin handle keeps it alive. // During GC, we also make sure // 1. m_userObject itself does not move if m_userObject is not array // 2. Every object pointed by m_userObject does not move if m_userObject is array - // We do not want to pin m_userObject if it is array. But m_userObject may be updated - // during relocation phase before OverlappedDataObject is doing relocation. - // m_userObjectInternal is used to track the location of the m_userObject before it is updated. - pOverlapped->m_userObjectInternal = static_cast(OBJECTREFToObject(pOverlapped->m_userObject)); + // We do not want to pin m_userObject if it is array. ArrayBase* pUserObject = (ArrayBase*)OBJECTREFToObject(pOverlapped->m_userObject); Object **ppObj = (Object**)pUserObject->GetDataPtr(TRUE); size_t num = pUserObject->GetNumComponents(); @@ -1395,11 +1391,6 @@ void GCToEEInterface::WalkAsyncPinnedForPromotion(Object* object, ScanContext* s callback(&OBJECTREF_TO_UNCHECKED_OBJECTREF(pOverlapped->m_userObject), (ScanContext *)sc, GC_CALL_PINNED); } } - - if (pOverlapped->GetAppDomainId() != DefaultADID && pOverlapped->GetAppDomainIndex().m_dwIndex == DefaultADID) - { - OverlappedDataObject::MarkCleanupNeededFromGC(); - } } void GCToEEInterface::WalkAsyncPinned(Object* object, void* context, void (*callback)(Object*, Object*, void*)) @@ -1419,7 +1410,7 @@ void GCToEEInterface::WalkAsyncPinned(Object* object, void* context, void (*call { Object * pUserObject = OBJECTREFToObject(pOverlapped->m_userObject); callback(object, pUserObject, context); - if (pOverlapped->m_isArray) + if (pOverlapped->m_userObject->GetGCSafeMethodTable() == g_pPredefinedArrayTypes[ELEMENT_TYPE_OBJECT]->GetMethodTable()) { ArrayBase* pUserArrayObject = (ArrayBase*)pUserObject; Object **pObj = (Object**)pUserArrayObject->GetDataPtr(TRUE); diff --git a/src/vm/mscorlib.cpp b/src/vm/mscorlib.cpp index 53c3044..6cd93ad 100644 --- a/src/vm/mscorlib.cpp +++ b/src/vm/mscorlib.cpp @@ -86,10 +86,6 @@ #include "multicorejit.h" #endif -#ifdef FEATURE_COMINTEROP -#include "windowsruntimebufferhelper.h" -#endif - #if defined(FEATURE_EVENTSOURCE_XPLAT) #include "nativeeventsource.h" #include "eventpipe.h" diff --git a/src/vm/mscorlib.h b/src/vm/mscorlib.h index f66931a..c9715e1 100644 --- a/src/vm/mscorlib.h +++ b/src/vm/mscorlib.h @@ -677,16 +677,15 @@ DEFINE_CLASS(OLE_AUT_BINDER, System, OleAutBinder) DEFINE_CLASS(MONITOR, Threading, Monitor) DEFINE_METHOD(MONITOR, ENTER, Enter, SM_Obj_RetVoid) -// Note: The size of the OverlappedData can be inflated by the CLR host -DEFINE_CLASS_U(Threading, OverlappedData, NoClass) -DEFINE_FIELD_U(m_asyncResult, OverlappedDataObject, m_asyncResult) -DEFINE_FIELD_U(m_iocb, OverlappedDataObject, m_iocb) -DEFINE_FIELD_U(m_iocbHelper, OverlappedDataObject, m_iocbHelper) -DEFINE_FIELD_U(m_overlapped, OverlappedDataObject, m_overlapped) -DEFINE_FIELD_U(m_userObject, OverlappedDataObject, m_userObject) -DEFINE_FIELD_U(m_pinSelf, OverlappedDataObject, m_pinSelf) -DEFINE_FIELD_U(m_AppDomainId, OverlappedDataObject, m_AppDomainId) -DEFINE_FIELD_U(m_isArray, OverlappedDataObject, m_isArray) +DEFINE_CLASS_U(Threading, OverlappedData, OverlappedDataObject) +DEFINE_FIELD_U(_asyncResult, OverlappedDataObject, m_asyncResult) +DEFINE_FIELD_U(_callback, OverlappedDataObject, m_callback) +DEFINE_FIELD_U(_overlapped, OverlappedDataObject, m_overlapped) +DEFINE_FIELD_U(_userObject, OverlappedDataObject, m_userObject) +DEFINE_FIELD_U(_pNativeOverlapped, OverlappedDataObject, m_pNativeOverlapped) +DEFINE_FIELD_U(_offsetLow, OverlappedDataObject, m_offsetLow) +DEFINE_FIELD_U(_offsetHigh, OverlappedDataObject, m_offsetHigh) +DEFINE_FIELD_U(_eventHandle, OverlappedDataObject, m_eventHandle) DEFINE_CLASS(OVERLAPPEDDATA, Threading, OverlappedData) DEFINE_CLASS(NATIVEOVERLAPPED, Threading, NativeOverlapped) diff --git a/src/vm/nativeoverlapped.cpp b/src/vm/nativeoverlapped.cpp index 6f053ea..94e61c6 100644 --- a/src/vm/nativeoverlapped.cpp +++ b/src/vm/nativeoverlapped.cpp @@ -22,11 +22,6 @@ #include "comthreadpool.h" #include "marshalnative.h" -LONG OverlappedDataObject::s_CleanupRequestCount = 0; -BOOL OverlappedDataObject::s_CleanupInProgress = FALSE; -BOOL OverlappedDataObject::s_GCDetectsCleanup = FALSE; -BOOL OverlappedDataObject::s_CleanupFreeHandle = FALSE; - // //The function is called from managed code to quicly check if a packet is available. //This is a perf-critical function. Even helper method frames are not created. We fall @@ -38,7 +33,6 @@ FCIMPL3(void, CheckVMForIOPacket, LPOVERLAPPED* lpOverlapped, DWORD* errorCode, #ifndef FEATURE_PAL Thread *pThread = GetThread(); - DWORD adid = pThread->GetDomain()->GetId().m_dwId; size_t key=0; _ASSERTE(pThread); @@ -46,7 +40,7 @@ FCIMPL3(void, CheckVMForIOPacket, LPOVERLAPPED* lpOverlapped, DWORD* errorCode, //Poll and wait if GC is in progress, to avoid blocking GC for too long. FC_GC_POLL(); - *lpOverlapped = ThreadpoolMgr::CompletionPortDispatchWorkWithinAppDomain(pThread, errorCode, numBytes, &key, adid); + *lpOverlapped = ThreadpoolMgr::CompletionPortDispatchWorkWithinAppDomain(pThread, errorCode, numBytes, &key, DefaultADID); if(*lpOverlapped == NULL) { return; @@ -54,17 +48,10 @@ FCIMPL3(void, CheckVMForIOPacket, LPOVERLAPPED* lpOverlapped, DWORD* errorCode, OVERLAPPEDDATAREF overlapped = ObjectToOVERLAPPEDDATAREF(OverlappedDataObject::GetOverlapped(*lpOverlapped)); - _ASSERTE(overlapped->GetAppDomainId() == adid); - - if(overlapped->m_iocb == NULL) + if (overlapped->m_callback == NULL) { - // no user delegate to callback - _ASSERTE((overlapped->m_iocbHelper == NULL) || !"This is benign, but should be optimized"); - - { - //We're not initialized yet, go back to the Vm, and process the packet there. - ThreadpoolMgr::StoreOverlappedInfoInThread(pThread, *errorCode, *numBytes, key, *lpOverlapped); - } + //We're not initialized yet, go back to the Vm, and process the packet there. + ThreadpoolMgr::StoreOverlappedInfoInThread(pThread, *errorCode, *numBytes, key, *lpOverlapped); *lpOverlapped = NULL; return; @@ -94,7 +81,7 @@ FCIMPL3(void, CheckVMForIOPacket, LPOVERLAPPED* lpOverlapped, DWORD* errorCode, // if this will be "dispatched" to the managed callback fire the IODequeue event: if (*lpOverlapped != NULL && ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, ThreadPoolIODequeue)) - FireEtwThreadPoolIODequeue(*lpOverlapped, (BYTE*)(*lpOverlapped) - offsetof(OverlappedDataObject, Internal), GetClrInstanceId()); + FireEtwThreadPoolIODequeue(*lpOverlapped, OverlappedDataObject::GetOverlapped(*lpOverlapped), GetClrInstanceId()); #else // !FEATURE_PAL *lpOverlapped = NULL; @@ -104,17 +91,17 @@ FCIMPL3(void, CheckVMForIOPacket, LPOVERLAPPED* lpOverlapped, DWORD* errorCode, } FCIMPLEND -FCIMPL1(void*, AllocateNativeOverlapped, OverlappedDataObject* overlappedUNSAFE) +FCIMPL1(LPOVERLAPPED, AllocateNativeOverlapped, OverlappedDataObject* overlappedUNSAFE) { FCALL_CONTRACT; + LPOVERLAPPED lpOverlapped; + OVERLAPPEDDATAREF overlapped = ObjectToOVERLAPPEDDATAREF(overlappedUNSAFE); OBJECTREF userObject = overlapped->m_userObject; HELPER_METHOD_FRAME_BEGIN_RET_ATTRIB_2(Frame::FRAME_ATTR_NONE, overlapped, userObject); - AsyncPinningHandleHolder handle; - if (g_pOverlappedDataClass == NULL) { g_pOverlappedDataClass = MscorlibBinder::GetClass(CLASS__OVERLAPPEDDATA); @@ -126,11 +113,9 @@ FCIMPL1(void*, AllocateNativeOverlapped, OverlappedDataObject* overlappedUNSAFE) CONSISTENCY_CHECK(overlapped->GetMethodTable() == g_pOverlappedDataClass); - overlapped->m_AppDomainId = GetAppDomain()->GetId().m_dwId; - if (userObject != NULL) { - if (overlapped->m_isArray == 1) + if (userObject->GetMethodTable() == g_pPredefinedArrayTypes[ELEMENT_TYPE_OBJECT]->GetMethodTable()) { BASEARRAYREF asArray = (BASEARRAYREF) userObject; OBJECTREF *pObj = (OBJECTREF*)(asArray->GetDataPtr()); @@ -140,33 +125,32 @@ FCIMPL1(void*, AllocateNativeOverlapped, OverlappedDataObject* overlappedUNSAFE) { ValidatePinnedObject(pObj[i]); } - for (i = 0; i < num; i ++) - { - asArray = (BASEARRAYREF) userObject; - AddMTForPinHandle(pObj[i]); - } } else { ValidatePinnedObject(userObject); - AddMTForPinHandle(userObject); } - } - handle = GetAppDomain()->CreateTypedHandle(overlapped, HNDTYPE_ASYNCPINNED); + NewHolder overlappedHolder(new NATIVEOVERLAPPED_AND_HANDLE()); + overlappedHolder->m_handle = GetAppDomain()->CreateTypedHandle(overlapped, HNDTYPE_ASYNCPINNED); + lpOverlapped = &(overlappedHolder.Extract()->m_overlapped); + lpOverlapped->Internal = 0; + lpOverlapped->InternalHigh = 0; + lpOverlapped->Offset = overlapped->m_offsetLow; + lpOverlapped->OffsetHigh = overlapped->m_offsetHigh; + lpOverlapped->hEvent = (HANDLE)overlapped->m_eventHandle; - handle.SuppressRelease(); - overlapped->m_pinSelf = handle; + overlapped->m_pNativeOverlapped = lpOverlapped; HELPER_METHOD_FRAME_END(); LOG((LF_INTEROP, LL_INFO10000, "In AllocNativeOperlapped thread 0x%x\n", GetThread())); if (ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, ThreadPoolIODequeue)) - FireEtwThreadPoolIOPack(&overlapped->Internal, overlappedUNSAFE, GetClrInstanceId()); + FireEtwThreadPoolIOPack(lpOverlapped, overlappedUNSAFE, GetClrInstanceId()); - return &overlapped->Internal; + return lpOverlapped; } FCIMPLEND @@ -176,28 +160,11 @@ FCIMPL1(void, FreeNativeOverlapped, LPOVERLAPPED lpOverlapped) HELPER_METHOD_FRAME_BEGIN_0(); - OVERLAPPEDDATAREF overlapped = ObjectToOVERLAPPEDDATAREF(OverlappedDataObject::GetOverlapped(lpOverlapped)); - CONSISTENCY_CHECK(g_pOverlappedDataClass && (overlapped->GetMethodTable() == g_pOverlappedDataClass)); + CONSISTENCY_CHECK(g_pOverlappedDataClass && (OverlappedDataObject::GetOverlapped(lpOverlapped)->GetMethodTable() == g_pOverlappedDataClass)); - // We don't want to call HasCompleted in the default domain, because we don't have - // overlapped handle support. - if ((!overlapped->HasCompleted ())) - { -#ifdef MDA_SUPPORTED - MdaOverlappedFreeError *pFreeError = MDA_GET_ASSISTANT(OverlappedFreeError); - if (pFreeError) - { - pFreeError->ReportError((LPVOID) OVERLAPPEDDATAREFToObject(overlapped)); - - // If we entered ReportError then our overlapped OBJECTREF became technically invalid, - // since a gc can be triggered. That causes an assert from FreeAsyncPinHandles() below. - // (I say technically because the object is pinned and won't really move) - overlapped = ObjectToOVERLAPPEDDATAREF(OverlappedDataObject::GetOverlapped(lpOverlapped)); - } -#endif // MDA_SUPPORTED - } + DestroyAsyncPinningHandle(((NATIVEOVERLAPPED_AND_HANDLE*)lpOverlapped)->m_handle); + delete lpOverlapped; - overlapped->FreeAsyncPinHandles(); HELPER_METHOD_FRAME_END(); } FCIMPLEND @@ -211,331 +178,3 @@ FCIMPL1(OverlappedDataObject*, GetOverlappedFromNative, LPOVERLAPPED lpOverlappe return OverlappedDataObject::GetOverlapped(lpOverlapped); } FCIMPLEND - -namespace -{ - -// Sets up an enumeration of all async pinned handles, such that all enumerated -// async pinned handles are processed by calling HandleAsyncPinHandle on the -// underlying overlapped instance. -BOOL HandleAsyncPinHandles() -{ - auto callback = [](Object* value, void*) - { - _ASSERTE (value->GetMethodTable() == g_pOverlappedDataClass); - OVERLAPPEDDATAREF overlapped = (OVERLAPPEDDATAREF)(ObjectToOBJECTREF(value)); - if (overlapped->GetAppDomainId() != DefaultADID && overlapped->HasCompleted()) - { - overlapped->HandleAsyncPinHandle(); - return true; - } - - return false; - }; - - IGCHandleManager* mgr = GCHandleUtilities::GetGCHandleManager(); - return mgr->GetGlobalHandleStore()->EnumerateAsyncPinnedHandles(callback, nullptr); -} - -} // anonymous namespace - -void OverlappedDataObject::FreeAsyncPinHandles() -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_COOPERATIVE; - SO_TOLERANT; - } - CONTRACTL_END; - - // This cannot throw or return error, and cannot force SO because it is called - // from CCLRIoCompletionManager::OnComplete which probes. - CONTRACT_VIOLATION(SOToleranceViolation); - - CONSISTENCY_CHECK(g_pOverlappedDataClass && (this->GetMethodTable() == g_pOverlappedDataClass)); - - _ASSERTE(GetThread() != NULL); - - if (m_pinSelf) - { - OBJECTHANDLE h = m_pinSelf; - if (FastInterlockCompareExchangePointer(&m_pinSelf, static_cast(NULL), h) == h) - { - DestroyAsyncPinningHandle(h); - } - } - - EventHandle = 0; -} - - -void OverlappedDataObject::StartCleanup() -{ - CONTRACTL - { - NOTHROW; - if (GetThread()) {MODE_COOPERATIVE;} else {DISABLED(MODE_COOPERATIVE);} - GC_TRIGGERS; - } - CONTRACTL_END; - - if (s_CleanupRequestCount == 0) - { - return; - } - - LONG curCount = s_CleanupRequestCount; - if (FastInterlockExchange((LONG*)&s_CleanupInProgress, TRUE) == FALSE) - { - { - BOOL HasJob = HandleAsyncPinHandles(); - if (!HasJob) - { - s_CleanupInProgress = FALSE; - FastInterlockExchangeAdd (&s_CleanupRequestCount, -curCount); - return; - } - } - - if (!ThreadpoolMgr::DrainCompletionPortQueue()) - { - s_CleanupInProgress = FALSE; - } - else - { - FastInterlockExchangeAdd (&s_CleanupRequestCount, -curCount); - } - } -} - - -void OverlappedDataObject::FinishCleanup(bool wasDrained) -{ - WRAPPER_NO_CONTRACT; - - if (wasDrained) - { - GCX_COOP(); - - s_CleanupFreeHandle = TRUE; - HandleAsyncPinHandles(); - s_CleanupFreeHandle = FALSE; - - s_CleanupInProgress = FALSE; - if (s_CleanupRequestCount > 0) - { - StartCleanup(); - } - } - else - { - s_CleanupInProgress = FALSE; - } -} - - -void OverlappedDataObject::HandleAsyncPinHandle() -{ - WRAPPER_NO_CONTRACT; - - _ASSERTE (s_CleanupInProgress); - if (m_toBeCleaned || !ThreadpoolMgr::IsCompletionPortInitialized()) - { - OBJECTHANDLE h = m_pinSelf; - if (h) - { - if (FastInterlockCompareExchangePointer(&m_pinSelf, (OBJECTHANDLE)NULL, h) == h) - { - DestroyAsyncPinningHandle(h); - } - } - } - else if (!s_CleanupFreeHandle) - { - m_toBeCleaned = 1; - } -} - - -// A hash table to track size of objects that may be moved to default domain -typedef EEHashTable, FALSE> EEHashTableOfMT; -EEHashTableOfMT *s_pPinHandleTable; - -CrstStatic s_PinHandleTableCrst; - -void InitializePinHandleTable() -{ - WRAPPER_NO_CONTRACT; - - s_PinHandleTableCrst.Init(CrstPinHandle); - LockOwner lock = {&s_PinHandleTableCrst, IsOwnerOfCrst}; - s_pPinHandleTable = new EEHashTableOfMT(); - s_pPinHandleTable->Init(10, &lock); -} - -// We can not fail due to OOM when we move an object to default domain during AD unload. -// If we may need a dummy MethodTable later, we allocate the MethodTable here. -void AddMTForPinHandle(OBJECTREF obj) -{ - CONTRACTL - { - THROWS; - WRAPPER(GC_TRIGGERS); - } - CONTRACTL_END; - - if (obj == NULL) - { - return; - } - - _ASSERTE (g_pOverlappedDataClass != NULL); - - SSIZE_T size = 0; - MethodTable *pMT = obj->GetMethodTable(); - - if (pMT->GetLoaderModule()->IsSystem()) - { - return; - } - - if (pMT->IsArray()) - { -#ifdef _DEBUG - BASEARRAYREF asArray = (BASEARRAYREF) obj; - TypeHandle th = asArray->GetArrayElementTypeHandle(); - _ASSERTE (!th.IsTypeDesc()); - MethodTable *pElemMT = th.AsMethodTable(); - _ASSERTE (pElemMT->IsValueType() && pElemMT->IsBlittable()); - _ASSERTE (!pElemMT->GetLoaderModule()->IsSystem()); -#endif - - // Create an ArrayMethodTable that has the same element size - // Use negative number for arrays of structs - it assumes that - // the maximum type base size is less than 2GB. - size = - (SSIZE_T)pMT->GetComponentSize(); - _ASSERTE(size < 0); - } - else - { - size = pMT->GetBaseSize(); - _ASSERTE(size >= 0); - } - - HashDatum data; - if (s_pPinHandleTable->GetValue(size, &data) == FALSE) - { - CrstHolder csh(&s_PinHandleTableCrst); - if (s_pPinHandleTable->GetValue(size, &data) == FALSE) - { - // We do not need to include GCDescr here, since this - // methodtable does not contain pointers. - BYTE *buffer = new BYTE[sizeof(MethodTable)]; - memset (buffer, 0, sizeof(MethodTable)); - MethodTable *pNewMT = (MethodTable *)buffer; - NewArrayHolder pMTHolder(buffer); - pNewMT->SetIsAsyncPinType(); - if (size >= 0) - { - pNewMT->SetBaseSize(static_cast(size)); - } - else - { - pNewMT->SetBaseSize(ARRAYBASE_BASESIZE); - pNewMT->SetComponentSize(static_cast(-size)); - } - s_pPinHandleTable->InsertValue(size, (HashDatum)pNewMT); - pMTHolder.SuppressRelease(); - } - } -} - -// We need to ensure that the MethodTable of an object is valid in default domain when the object -// is move to default domain duing AD unload. -void BashMTForPinnedObject(OBJECTREF obj) -{ - CONTRACTL - { - GC_NOTRIGGER; - NOTHROW; - } - CONTRACTL_END; - - if (obj == NULL) - { - return; - } - - ADIndex adIndx = obj->GetAppDomainIndex(); - ADIndex defaultAdIndx = SystemDomain::System()->DefaultDomain()->GetIndex(); - if (adIndx.m_dwIndex != 0 && adIndx != defaultAdIndx) - { - obj->GetHeader()->ResetAppDomainIndexNoFailure(defaultAdIndx); - } - SSIZE_T size = 0; - MethodTable *pMT = obj->GetMethodTable(); - - if (pMT == g_pOverlappedDataClass) - { - // Managed Overlapped - OVERLAPPEDDATAREF overlapped = (OVERLAPPEDDATAREF)(obj); - overlapped->m_asyncResult = NULL; - overlapped->m_iocb = NULL; - overlapped->m_iocbHelper = NULL; - overlapped->m_overlapped = NULL; - - if (overlapped->m_userObject != NULL) - { - if (overlapped->m_isArray == 1) - { - BASEARRAYREF asArray = (BASEARRAYREF) (overlapped->m_userObject); - OBJECTREF *pObj = (OBJECTREF*)asArray->GetDataPtr (TRUE); - SIZE_T num = asArray->GetNumComponents(); - for (SIZE_T i = 0; i < num; i ++) - { - BashMTForPinnedObject(pObj[i]); - } - } - else - { - BashMTForPinnedObject(overlapped->m_userObject); - } - } - STRESS_LOG1 (LF_APPDOMAIN | LF_GC, LL_INFO100, "OverlappedData %p:MT is bashed\n", OBJECTREFToObject (overlapped)); - return; - } - - if (pMT->GetLoaderModule()->IsSystem()) - { - return; - } - - if (pMT->IsArray()) - { -#ifdef _DEBUG - BASEARRAYREF asArray = (BASEARRAYREF) obj; - TypeHandle th = asArray->GetArrayElementTypeHandle(); - _ASSERTE (!th.IsTypeDesc()); - MethodTable *pElemMT = th.AsMethodTable(); - _ASSERTE (pElemMT->IsValueType() && pElemMT->IsBlittable()); - _ASSERTE (!pElemMT->GetLoaderModule()->IsSystem()); -#endif - - // Create an ArrayMethodTable that has the same element size - size = - (SSIZE_T)pMT->GetComponentSize(); - } - else - { - _ASSERTE (pMT->IsBlittable()); - size = pMT->GetBaseSize(); - } - - HashDatum data = NULL; - BOOL fRet; - fRet = s_pPinHandleTable->GetValue(size, &data); - _ASSERTE(fRet); - PREFIX_ASSUME(data != NULL); - obj->SetMethodTable((MethodTable*)data); -} diff --git a/src/vm/nativeoverlapped.h b/src/vm/nativeoverlapped.h index 7f12f47..e1ffb88 100644 --- a/src/vm/nativeoverlapped.h +++ b/src/vm/nativeoverlapped.h @@ -16,118 +16,50 @@ #ifndef _OVERLAPPED_H #define _OVERLAPPED_H +struct NATIVEOVERLAPPED_AND_HANDLE +{ + OVERLAPPED m_overlapped; + OBJECTHANDLE m_handle; +}; + // This should match the managed Overlapped object. // If you make any change here, you need to change the managed part Overlapped. class OverlappedDataObject : public Object { public: - OBJECTREF m_asyncResult; - OBJECTREF m_iocb; - OBJECTREF m_iocbHelper; - OBJECTREF m_overlapped; - OBJECTREF m_userObject; - - // - // NOTE! WCF directly accesses m_pinSelf from managed code, using a hard-coded negative - // offset from the Internal member, below. They need this so they can modify the - // contents of m_userObject; after such modification, they need to update this handle - // to be in the correct GC generation. - // - // If you need to add or remove fields between this one and Internal, be sure that - // you also fix the hard-coded offsets in ndp\cdf\src\WCF\ServiceModel\System\ServiceModel\Channels\OverlappedContext.cs. - // - OBJECTHANDLE m_pinSelf; - // OverlappedDataObject is very special. An async pin handle keeps it alive. // During GC, we also make sure // 1. m_userObject itself does not move if m_userObject is not array // 2. Every object pointed by m_userObject does not move if m_userObject is array - // We do not want to pin m_userObject if it is array. But m_userObject may be updated - // during relocation phase before OverlappedDataObject is doing relocation. - // m_userObjectInternal is used to track the location of the m_userObject before it is updated. - void *m_userObjectInternal; - DWORD m_AppDomainId; - unsigned char m_isArray; - unsigned char m_toBeCleaned; - - ULONG_PTR Internal; - ULONG_PTR InternalHigh; - int OffsetLow; - int OffsetHigh; - ULONG_PTR EventHandle; - - static OverlappedDataObject* GetOverlapped (LPOVERLAPPED nativeOverlapped) + OBJECTREF m_asyncResult; + OBJECTREF m_callback; + OBJECTREF m_overlapped; + OBJECTREF m_userObject; + LPOVERLAPPED m_pNativeOverlapped; + ULONG_PTR m_eventHandle; + int m_offsetLow; + int m_offsetHigh; + +#ifndef DACCESS_COMPILE + static OverlappedDataObject* GetOverlapped(LPOVERLAPPED nativeOverlapped) { LIMITED_METHOD_CONTRACT; STATIC_CONTRACT_SO_TOLERANT; _ASSERTE (nativeOverlapped != NULL); - _ASSERTE (GCHeapUtilities::GetGCHeap()->IsHeapPointer((BYTE *) nativeOverlapped)); - - return (OverlappedDataObject*)((BYTE*)nativeOverlapped - offsetof(OverlappedDataObject, Internal)); + return (OverlappedDataObject*)OBJECTREFToObject(ObjectFromHandle(((NATIVEOVERLAPPED_AND_HANDLE*)nativeOverlapped)->m_handle)); } - DWORD GetAppDomainId() - { - return m_AppDomainId; - } - - void HandleAsyncPinHandle(); - - void FreeAsyncPinHandles(); - - BOOL HasCompleted() + // Return the raw OverlappedDataObject* without going into cooperative mode for tracing + static OverlappedDataObject* GetOverlappedForTracing(LPOVERLAPPED nativeOverlapped) { LIMITED_METHOD_CONTRACT; -#ifndef FEATURE_PAL - return HasOverlappedIoCompleted((LPOVERLAPPED) &Internal); -#else // !FEATURE_PAL - return FALSE; -#endif // !FEATURE_PAL - } - -private: - static LONG s_CleanupRequestCount; - static BOOL s_CleanupInProgress; - static BOOL s_GCDetectsCleanup; - static BOOL s_CleanupFreeHandle; - -public: - static void RequestCleanup() - { - WRAPPER_NO_CONTRACT; - - FastInterlockIncrement(&s_CleanupRequestCount); - if (!s_CleanupInProgress) - { - StartCleanup(); - } - } - static void StartCleanup(); - - static void FinishCleanup(bool wasDrained); - - static void MarkCleanupNeededFromGC() - { - LIMITED_METHOD_CONTRACT; - s_GCDetectsCleanup = TRUE; - } - - static BOOL CleanupNeededFromGC() - { - return s_GCDetectsCleanup; - } - - static void RequestCleanupFromGC() - { - WRAPPER_NO_CONTRACT; + STATIC_CONTRACT_SO_TOLERANT; - if (s_GCDetectsCleanup) - { - s_GCDetectsCleanup = FALSE; - RequestCleanup(); - } + _ASSERTE(nativeOverlapped != NULL); + return *(OverlappedDataObject**)(((NATIVEOVERLAPPED_AND_HANDLE*)nativeOverlapped)->m_handle); } +#endif // DACCESS_COMPILE }; #ifdef USE_CHECKED_OBJECTREFS @@ -145,12 +77,8 @@ typedef OverlappedDataObject* OVERLAPPEDDATAREF; #endif FCDECL3(void, CheckVMForIOPacket, LPOVERLAPPED* lpOverlapped, DWORD* errorCode, DWORD* numBytes); -FCDECL1(void*, AllocateNativeOverlapped, OverlappedDataObject* overlapped); +FCDECL1(LPOVERLAPPED, AllocateNativeOverlapped, OverlappedDataObject* overlapped); FCDECL1(void, FreeNativeOverlapped, LPOVERLAPPED lpOverlapped); FCDECL1(OverlappedDataObject*, GetOverlappedFromNative, LPOVERLAPPED lpOverlapped); -void InitializePinHandleTable(); -void AddMTForPinHandle(OBJECTREF obj); -void BashMTForPinnedObject(OBJECTREF obj); - #endif diff --git a/src/vm/threads.cpp b/src/vm/threads.cpp index 5042e52..a354d54 100644 --- a/src/vm/threads.cpp +++ b/src/vm/threads.cpp @@ -8546,7 +8546,6 @@ BOOL Thread::HaveExtraWorkForFinalizer() LIMITED_METHOD_CONTRACT; return m_ThreadTasks - || OverlappedDataObject::CleanupNeededFromGC() || ThreadpoolMgr::HaveTimerInfosToFlush() || ExecutionManager::IsCacheCleanupRequired() || Thread::CleanupNeededForFinalizedThread() @@ -8606,8 +8605,6 @@ void Thread::DoExtraWorkForFinalizer() ExecutionManager::ClearCaches(); } - OverlappedDataObject::RequestCleanupFromGC(); - // If there were any TimerInfos waiting to be released, they'll get flushed now ThreadpoolMgr::FlushQueueOfTimerInfos(); diff --git a/src/vm/win32threadpool.cpp b/src/vm/win32threadpool.cpp index 7f9953a..644e232 100644 --- a/src/vm/win32threadpool.cpp +++ b/src/vm/win32threadpool.cpp @@ -1297,7 +1297,6 @@ ThreadpoolMgr::CallbackForInitiateDrainageOfCompletionPortQueue( } FastInterlockAnd(&g_fCompletionPortDrainNeeded, 0); - OverlappedDataObject::FinishCleanup(!fTryNextTime); #endif // !FEATURE_PAL } @@ -3459,7 +3458,9 @@ Top: // abstraction level for managed IO we can remove the IODequeues fired here if (ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, ThreadPoolIODequeue) && !AreEtwIOQueueEventsSpeciallyHandled((LPOVERLAPPED_COMPLETION_ROUTINE)key) && pOverlapped != NULL) - FireEtwThreadPoolIODequeue(pOverlapped, (BYTE*)pOverlapped - offsetof(OverlappedDataObject, Internal), GetClrInstanceId()); + { + FireEtwThreadPoolIODequeue(pOverlapped, OverlappedDataObject::GetOverlappedForTracing(pOverlapped), GetClrInstanceId()); + } bool enterRetirement; @@ -3766,7 +3767,7 @@ LPOVERLAPPED ThreadpoolMgr::CompletionPortDispatchWorkWithinAppDomain( overlapped = ObjectToOVERLAPPEDDATAREF(OverlappedDataObject::GetOverlapped(lpOverlapped)); } - if (ManagedCallback && (overlapped->GetAppDomainId() == adid)) + if (ManagedCallback) { _ASSERTE(*pKey != 0); // should be a valid function address @@ -3775,7 +3776,6 @@ LPOVERLAPPED ThreadpoolMgr::CompletionPortDispatchWorkWithinAppDomain( //Application Bug. return NULL; } - } else { -- 2.7.4