summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJan Kotas <jkotas@microsoft.com>2018-06-09 13:39:58 -0700
committerGitHub <noreply@github.com>2018-06-09 13:39:58 -0700
commit911d332c523848023e3c6564788b72b7f419fca1 (patch)
treeae979e6b048630ed0393983d14bde5d43942459f /src
parent618f9c2d18e88566ac61f93529bac58123c50cba (diff)
downloadcoreclr-911d332c523848023e3c6564788b72b7f419fca1.tar.gz
coreclr-911d332c523848023e3c6564788b72b7f419fca1.tar.bz2
coreclr-911d332c523848023e3c6564788b72b7f419fca1.zip
Avoid NativeOverlapped pinning by allocating unmanaged memory for it (#18360)
It makes PinnableBufferCache unnecessary
Diffstat (limited to 'src')
-rw-r--r--src/System.Private.CoreLib/System.Private.CoreLib.csproj3
-rw-r--r--src/System.Private.CoreLib/src/System/Collections/Concurrent/ConcurrentStack.cs490
-rw-r--r--src/System.Private.CoreLib/src/System/PinnableBufferCache.cs445
-rw-r--r--src/System.Private.CoreLib/src/System/PinnableBufferCacheEventSource.cs36
-rw-r--r--src/System.Private.CoreLib/src/System/Threading/Overlapped.cs208
-rw-r--r--src/classlibnative/bcltype/CMakeLists.txt1
-rw-r--r--src/classlibnative/bcltype/windowsruntimebufferhelper.cpp44
-rw-r--r--src/classlibnative/bcltype/windowsruntimebufferhelper.h27
-rw-r--r--src/gc/gcinterface.h2
-rw-r--r--src/gc/objecthandle.cpp2
-rw-r--r--src/pal/inc/pal.h8
-rw-r--r--src/pal/inc/rt/palrt.h2
-rw-r--r--src/vm/appdomain.cpp66
-rw-r--r--src/vm/appdomain.hpp1
-rw-r--r--src/vm/ceemain.cpp2
-rw-r--r--src/vm/class.cpp16
-rw-r--r--src/vm/comcallablewrapper.cpp33
-rw-r--r--src/vm/comcallablewrapper.h44
-rw-r--r--src/vm/comthreadpool.cpp37
-rw-r--r--src/vm/gcenv.ee.cpp15
-rw-r--r--src/vm/mscorlib.cpp4
-rw-r--r--src/vm/mscorlib.h19
-rw-r--r--src/vm/nativeoverlapped.cpp407
-rw-r--r--src/vm/nativeoverlapped.h122
-rw-r--r--src/vm/threads.cpp3
-rw-r--r--src/vm/win32threadpool.cpp8
26 files changed, 155 insertions, 1890 deletions
diff --git a/src/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/System.Private.CoreLib/System.Private.CoreLib.csproj
index b2387ceb79..2c0f47a6d7 100644
--- a/src/System.Private.CoreLib/System.Private.CoreLib.csproj
+++ b/src/System.Private.CoreLib/System.Private.CoreLib.csproj
@@ -341,7 +341,6 @@
<Compile Include="$(BclSourcesRoot)\System\mda.cs" />
<Compile Include="$(BclSourcesRoot)\System\MissingMemberException.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Number.CoreCLR.cs" />
- <Compile Include="$(BclSourcesRoot)\System\PinnableBufferCache.cs" />
<Compile Include="$(BclSourcesRoot)\System\RtType.cs" />
<Compile Include="$(BclSourcesRoot)\System\RuntimeArgumentHandle.cs" />
<Compile Include="$(BclSourcesRoot)\System\RuntimeHandles.cs" />
@@ -557,7 +556,6 @@
<Compile Include="$(BclSourcesRoot)\System\Collections\Generic\EqualityComparer.cs" />
<Compile Include="$(BclSourcesRoot)\System\Collections\Generic\ArraySortHelper.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Collections\ObjectModel\ReadOnlyDictionary.cs" />
- <Compile Include="$(BclSourcesRoot)\System\Collections\Concurrent\ConcurrentStack.cs" />
</ItemGroup>
<ItemGroup>
<Compile Include="$(BclSourcesRoot)\Microsoft\Win32\SafeHandles\SafeWaitHandle.cs" />
@@ -610,7 +608,6 @@
<Compile Include="$(BclSourcesRoot)\mscorlib.Friends.cs" Condition="'$(FeatureCominterop)' == 'true'" />
</ItemGroup>
<ItemGroup>
- <Compile Include="src\System\PinnableBufferCacheEventSource.cs" />
<Compile Include="src\System\Runtime\RuntimeImports.cs" />
</ItemGroup>
<Import Project="shared\System.Private.CoreLib.Shared.projitems" />
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 019bc4e545..0000000000
--- 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.
-
- /// <summary>
- /// Represents a thread-safe last-in, first-out collection of objects.
- /// </summary>
- /// <typeparam name="T">Specifies the type of elements in the stack.</typeparam>
- /// <remarks>
- /// All public and protected members of <see cref="ConcurrentStack{T}"/> are thread-safe and may be used
- /// concurrently from multiple threads.
- /// </remarks>
- [DebuggerDisplay("Count = {Count}")]
- [DebuggerTypeProxy(typeof(IProducerConsumerCollectionDebugView<>))]
- internal class ConcurrentStack<T> : IProducerConsumerCollection<T>, IReadOnlyCollection<T>
- {
- /// <summary>
- /// A simple (internal) node type used to store elements of concurrent stacks and queues.
- /// </summary>
- private class Node
- {
- internal readonly T _value; // Value of the node.
- internal Node _next; // Next pointer.
-
- /// <summary>
- /// Constructs a new node with the specified value and no next node.
- /// </summary>
- /// <param name="value">The value of the node.</param>
- 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.
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ConcurrentStack{T}"/>
- /// class.
- /// </summary>
- public ConcurrentStack()
- {
- }
-
- /// <summary>
- /// Gets the number of elements contained in the <see cref="ConcurrentStack{T}"/>.
- /// </summary>
- /// <value>The number of elements contained in the <see cref="ConcurrentStack{T}"/>.</value>
- /// <remarks>
- /// For determining whether the collection contains any items, use of the <see cref="IsEmpty"/>
- /// property is recommended rather than retrieving the number of items from the <see cref="Count"/>
- /// property and comparing it to 0.
- /// </remarks>
- 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;
- }
- }
-
-
- /// <summary>
- /// Gets a value indicating whether access to the <see cref="T:System.Collections.ICollection"/> is
- /// synchronized with the SyncRoot.
- /// </summary>
- /// <value>true if access to the <see cref="T:System.Collections.ICollection"/> is synchronized
- /// with the SyncRoot; otherwise, false. For <see cref="ConcurrentStack{T}"/>, this property always
- /// returns false.</value>
- 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; }
- }
-
- /// <summary>
- /// Gets an object that can be used to synchronize access to the <see
- /// cref="T:System.Collections.ICollection"/>. This property is not supported.
- /// </summary>
- /// <exception cref="T:System.NotSupportedException">The SyncRoot property is not supported</exception>
- object ICollection.SyncRoot
- {
- get
- {
- ThrowHelper.ThrowNotSupportedException(ExceptionResource.ConcurrentCollection_SyncRoot_NotSupported);
- return default;
- }
- }
-
- /// <summary>
- /// Copies the elements of the <see cref="T:System.Collections.ICollection"/> to an <see
- /// cref="T:System.Array"/>, starting at a particular
- /// <see cref="T:System.Array"/> index.
- /// </summary>
- /// <param name="array">The one-dimensional <see cref="T:System.Array"/> that is the destination of
- /// the elements copied from the
- /// <see cref="ConcurrentStack{T}"/>. The <see cref="T:System.Array"/> must
- /// have zero-based indexing.</param>
- /// <param name="index">The zero-based index in <paramref name="array"/> at which copying
- /// begins.</param>
- /// <exception cref="ArgumentNullException"><paramref name="array"/> is a null reference (Nothing in
- /// Visual Basic).</exception>
- /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is less than
- /// zero.</exception>
- /// <exception cref="ArgumentException">
- /// <paramref name="array"/> is multidimensional. -or-
- /// <paramref name="array"/> does not have zero-based indexing. -or-
- /// <paramref name="index"/> is equal to or greater than the length of the <paramref name="array"/>
- /// -or- The number of elements in the source <see cref="T:System.Collections.ICollection"/> is
- /// greater than the available space from <paramref name="index"/> to the end of the destination
- /// <paramref name="array"/>. -or- The type of the source <see
- /// cref="T:System.Collections.ICollection"/> cannot be cast automatically to the type of the
- /// destination <paramref name="array"/>.
- /// </exception>
- 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);
- }
-
- /// <summary>
- /// Copies the <see cref="ConcurrentStack{T}"/> elements to an existing one-dimensional <see
- /// cref="T:System.Array"/>, starting at the specified array index.
- /// </summary>
- /// <param name="array">The one-dimensional <see cref="T:System.Array"/> that is the destination of
- /// the elements copied from the
- /// <see cref="ConcurrentStack{T}"/>. The <see cref="T:System.Array"/> must have zero-based
- /// indexing.</param>
- /// <param name="index">The zero-based index in <paramref name="array"/> at which copying
- /// begins.</param>
- /// <exception cref="ArgumentNullException"><paramref name="array"/> is a null reference (Nothing in
- /// Visual Basic).</exception>
- /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is less than
- /// zero.</exception>
- /// <exception cref="ArgumentException"><paramref name="index"/> is equal to or greater than the
- /// length of the <paramref name="array"/>
- /// -or- The number of elements in the source <see cref="ConcurrentStack{T}"/> is greater than the
- /// available space from <paramref name="index"/> to the end of the destination <paramref
- /// name="array"/>.
- /// </exception>
- 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)
- /// <summary>
- /// Inserts an object at the top of the <see cref="ConcurrentStack{T}"/>.
- /// </summary>
- /// <param name="item">The object to push onto the <see cref="ConcurrentStack{T}"/>. The value can be
- /// a null reference (Nothing in Visual Basic) for reference types.
- /// </param>
- 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);
- }
-
-
- /// <summary>
- /// 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
- /// </summary>
- /// <param name="head">The head pointer to the new list</param>
- /// <param name="tail">The tail pointer to the new list</param>
- 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);
- }
-
- /// <summary>
- /// Attempts to pop and return the object at the top of the <see cref="ConcurrentStack{T}"/>.
- /// </summary>
- /// <param name="result">
- /// When this method returns, if the operation was successful, <paramref name="result"/> contains the
- /// object removed. If no object was available to be removed, the value is unspecified.
- /// </param>
- /// <returns>true if an element was removed and returned from the top of the <see
- /// cref="ConcurrentStack{T}"/>
- /// successfully; otherwise, false.</returns>
- 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);
- }
-
- /// <summary>
- /// Local helper function to Pop an item from the stack, slow path
- /// </summary>
- /// <param name="result">The popped item</param>
- /// <returns>True if succeeded, false otherwise</returns>
- private bool TryPopCore(out T result)
- {
- Node poppedNode;
-
- if (TryPopCore(1, out poppedNode) == 1)
- {
- result = poppedNode._value;
- return true;
- }
-
- result = default;
- return false;
- }
-
- /// <summary>
- /// 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.
- /// </summary>
- /// <param name="count">The number of items to pop.</param>
- /// <param name="poppedHead">
- /// 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.
- /// </param>
- /// <returns>The number of objects successfully popped from the top of
- /// the <see cref="ConcurrentStack{T}"/>.</returns>
- 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
-
- /// <summary>
- /// Copies the items stored in the <see cref="ConcurrentStack{T}"/> to a new array.
- /// </summary>
- /// <returns>A new array containing a snapshot of elements copied from the <see
- /// cref="ConcurrentStack{T}"/>.</returns>
- public T[] ToArray()
- {
- Node curr = _head;
- return curr == null ?
- Array.Empty<T>() :
- ToList(curr).ToArray();
- }
-
- /// <summary>
- /// 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.
- /// </summary>
- /// <returns>A list of the stack's contents.</returns>
- private List<T> ToList()
- {
- return ToList(_head);
- }
-
- /// <summary>
- /// Returns an array containing a snapshot of the list's contents starting at the specified node.
- /// </summary>
- /// <returns>A list of the stack's contents starting at the specified node.</returns>
- private List<T> ToList(Node curr)
- {
- List<T> list = new List<T>();
-
- while (curr != null)
- {
- list.Add(curr._value);
- curr = curr._next;
- }
-
- return list;
- }
-
- /// <summary>
- /// Attempts to add an object to the <see cref="T:System.Collections.Concurrent.IProducerConsumerCollection{T}"/>.
- /// </summary>
- /// <param name="item">The object to add to the <see
- /// cref="T:System.Collections.Concurrent.IProducerConsumerCollection{T}"/>. The value can be a null
- /// reference (Nothing in Visual Basic) for reference types.
- /// </param>
- /// <returns>true if the object was added successfully; otherwise, false.</returns>
- /// <remarks>For <see cref="ConcurrentStack{T}"/>, this operation will always insert the object onto the
- /// top of the <see cref="ConcurrentStack{T}"/>
- /// and return true.</remarks>
- bool IProducerConsumerCollection<T>.TryAdd(T item)
- {
- Push(item);
- return true;
- }
-
- /// <summary>
- /// Attempts to remove and return an object from the <see cref="T:System.Collections.Concurrent.IProducerConsumerCollection{T}"/>.
- /// </summary>
- /// <param name="item">
- /// When this method returns, if the operation was successful, <paramref name="item"/> contains the
- /// object removed. If no object was available to be removed, the value is unspecified.
- /// </param>
- /// <returns>true if an element was removed and returned successfully; otherwise, false.</returns>
- /// <remarks>For <see cref="ConcurrentStack{T}"/>, this operation will attempt to pop the object at
- /// the top of the <see cref="ConcurrentStack{T}"/>.
- /// </remarks>
- bool IProducerConsumerCollection<T>.TryTake(out T item) => TryPop(out item);
-
- /// <summary>
- /// Returns an enumerator that iterates through the <see cref="ConcurrentStack{T}"/>.
- /// </summary>
- /// <returns>An enumerator for the <see cref="ConcurrentStack{T}"/>.</returns>
- /// <remarks>
- /// The enumeration represents a moment-in-time snapshot of the contents
- /// of the stack. It does not reflect any updates to the collection after
- /// <see cref="GetEnumerator"/> was called. The enumerator is safe to use
- /// concurrently with reads from and writes to the stack.
- /// </remarks>
- public IEnumerator<T> 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<T> GetEnumerator(Node head)
- {
- Node current = head;
- while (current != null)
- {
- yield return current._value;
- current = current._next;
- }
- }
-
- /// <summary>
- /// Returns an enumerator that iterates through a collection.
- /// </summary>
- /// <returns>An <see cref="T:System.Collections.IEnumerator"/> that can be used to iterate through
- /// the collection.</returns>
- /// <remarks>
- /// The enumeration represents a moment-in-time snapshot of the contents of the stack. It does not
- /// reflect any updates to the collection after
- /// <see cref="GetEnumerator"/> was called. The enumerator is safe to use concurrently with reads
- /// from and writes to the stack.
- /// </remarks>
- IEnumerator IEnumerable.GetEnumerator()
- {
- return ((IEnumerable<T>)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 1bfd2cf07b..0000000000
--- 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
- {
- /// <summary>
- /// Create a PinnableBufferCache that works on any object (it is intended for OverlappedData)
- /// This is only used in mscorlib.
- /// </summary>
- internal PinnableBufferCache(string cacheName, Func<object> factory)
- {
- m_NotGen2 = new List<object>(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;
- }
-
- /// <summary>
- /// Get a object from the buffer manager. If no buffers exist, allocate a new one.
- /// </summary>
- 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;
- }
-
- /// <summary>
- /// Return a buffer back to the buffer manager.
- /// </summary>
- 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
-
- /// <summary>
- /// Called when we don't have any buffers in our free list to give out.
- /// </summary>
- /// <returns></returns>
- 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();
- }
- }
- }
- }
-
- /// <summary>
- /// See if we can promote the buffers to the free list. Returns true if successful.
- /// </summary>
- 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<object> notInGen2 = new List<object>();
- 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;
- }
-
- /// <summary>
- /// Generates some buffers to age into Gen2.
- /// </summary>
- 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);
- }
-
- /// <summary>
- /// 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.
- /// </summary>
- private static bool Gen2GcCallbackFunc(object targetObj)
- {
- return ((PinnableBufferCache)(targetObj)).TrimFreeListIfNeeded();
- }
-
- /// <summary>
- /// 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.
- /// </summary>
- 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<object> m_factory;
-
- /// <summary>
- /// Contains 'good' buffers to reuse. They are guaranteed to be Gen 2 ENFORCED!
- /// </summary>
- private ConcurrentStack<object> m_FreeList = new ConcurrentStack<object>();
- /// <summary>
- /// 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.
- /// </summary>
- private List<object> m_NotGen2;
- /// <summary>
- /// 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.
- /// </summary>
- private int m_gen1CountAtLastRestock;
-
- /// <summary>
- /// Used to ensure we have a minimum time between trimmings.
- /// </summary>
- private int m_msecNoUseBeyondFreeListSinceThisTime;
- /// <summary>
- /// 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).
- /// </summary>
- private bool m_moreThanFreeListNeeded;
- /// <summary>
- /// The total number of buffers that this cache has ever allocated.
- /// Used in trimming heuristics.
- /// </summary>
- private int m_buffersUnderManagement;
- /// <summary>
- /// The number of buffers we added the last time we restocked.
- /// </summary>
- private int m_restockSize;
- /// <summary>
- /// Did we put some buffers into m_NotGen2 to see if we can trim?
- /// </summary>
- private bool m_trimmingExperimentInProgress;
- /// <summary>
- /// A forced minimum number of buffers.
- /// </summary>
- private int m_minBufferCount;
- /// <summary>
- /// The number of calls to Allocate.
- /// </summary>
- 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 7d382f3c12..0000000000
--- 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 51df27f735..1d6d682d54 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 785f6b5b44..62cf9682c3 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 c99730e3aa..0000000000
--- 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 42596515a0..0000000000
--- 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 40812d5860..58482b8c89 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 ab3958921f..2e26476e99 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 f89798ceab..d5587b9f41 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 1360a81c43..e262b0dd95 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 8a69a997a7..3766ed528a 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 d92aabb535..501e2151e4 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 f599a2aa23..fddc12768f 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 f66a719bb0..4375a340cd 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 e2ba05fb37..f6ad1191ae 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<MethodTable *> 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 1a68135e77..152830e907 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<ConnectionPoint*>* m_pCPList;
- LPOVERLAPPED m_operlappedPtr;
- };
+ CQuickArray<ConnectionPoint*>* 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 fcb51dc301..b6d4106f9c 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 daf6fe8dbf..7f9d269432 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<void*>(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 53c3044f1a..6cd93ad80a 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 f66931a360..c9715e1396 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 6f053ea267..94e61c63fd 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<NATIVEOVERLAPPED_AND_HANDLE> 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<OBJECTHANDLE>(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<size_t, EEPtrHashTableHelper<size_t>, 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<BYTE> pMTHolder(buffer);
- pNewMT->SetIsAsyncPinType();
- if (size >= 0)
- {
- pNewMT->SetBaseSize(static_cast<DWORD>(size));
- }
- else
- {
- pNewMT->SetBaseSize(ARRAYBASE_BASESIZE);
- pNewMT->SetComponentSize(static_cast<WORD>(-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 7f12f477e8..e1ffb886b6 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 5042e52b78..a354d54b0a 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 7f9953a0f9..644e2324c6 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
{