Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Threadpool autorelease #47592

Merged
merged 23 commits into from
Feb 24, 2021
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
aee6785
Add test for ThreadPool autorelease without enable.
jkoritzinsky Jan 26, 2021
a6b59df
Fix prototypes.
jkoritzinsky Feb 2, 2021
b0938eb
Always include Interop.AutoreleasePool.cs for build simplicity.
jkoritzinsky Feb 2, 2021
ed44c1e
PR Feedback.
jkoritzinsky Feb 2, 2021
d15f47b
Comment why Interop.AutoreleasePool.cs is included unconditionally an…
jkoritzinsky Feb 2, 2021
284a865
Update Interop.AutoreleasePool.cs
jkoritzinsky Feb 2, 2021
43bd530
Refactor out the AutoreleasePool impl to only exist on osx-like platf…
jkoritzinsky Feb 2, 2021
c4ab7e7
Disable -Wmissing-noreturn on Clang and GCC.
jkoritzinsky Feb 2, 2021
5671fa8
Make feature flag a property.
jkoritzinsky Feb 2, 2021
c7e2934
Fix typo
jkoritzinsky Feb 2, 2021
eb2e39d
Fix missing using
jkoritzinsky Feb 2, 2021
d6bab12
Reorganize to try to get WASM building.
jkoritzinsky Feb 3, 2021
9bd2af5
Use get-only property with initializer.
jkoritzinsky Feb 3, 2021
be9d6df
Add API to wasm threadpool impl.
jkoritzinsky Feb 3, 2021
1c85528
Use feature flag for all platforms, not just macos.
jkoritzinsky Feb 3, 2021
b6053fd
Merge branch 'master' of github.com:dotnet/runtime into threadpool-au…
jkoritzinsky Feb 5, 2021
c9eea84
Make EnableDispatchAutoreleasePool a property on all targets.
jkoritzinsky Feb 5, 2021
c803877
Use a preprocessor if instead of a runtime if with the OS APIs.
jkoritzinsky Feb 8, 2021
483db22
Merge branch 'master' of github.com:dotnet/runtime into threadpool-au…
jkoritzinsky Feb 8, 2021
f6c9313
Merge branch 'master' into threadpool-autorelease
jkoritzinsky Feb 9, 2021
bb19d85
Merge branch 'master' into threadpool-autorelease
jkoritzinsky Feb 12, 2021
a46dd70
Move EnableDispatchAutoreleasePool definition to be local to the Disp…
jkoritzinsky Feb 19, 2021
0fe7547
Merge branch 'threadpool-autorelease' of github.com:jkoritzinsky/runt…
jkoritzinsky Feb 19, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/workflow/trimming/feature-switches.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ configurations but their defaults might vary as any SDK can set the defaults dif
| UseSystemResourceKeys | System.Resources.UseSystemResourceKeys | Any localizable resources for system assemblies is trimmed when set to true |
| HttpActivityPropagationSupport | System.Net.Http.EnableActivityPropagation | Any dependency related to diagnostics support for System.Net.Http is trimmed when set to false |
| StartupHookSupport | System.StartupHookProvider.IsSupported | Startup hooks are disabled when set to false. Startup hook related functionality can be trimmed. |
| TBD | System.Threading.ThreadPool.EnableDispatchAutoreleasePool | When set to true, creates an NSAutoreleasePool around each thread pool work item on applicable platforms. |

Any feature-switch which defines property can be set in csproj file or
on the command line as any other MSBuild property. Those without predefined property name
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,9 @@ private static bool GetEnableWorkerTracking() =>
? AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.EnableWorkerTracking", false)
: GetEnableWorkerTrackingNative();

internal static bool EnableDispatchAutoreleasePool { get; } =
AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.EnableDispatchAutoreleasePool", false);
jkoritzinsky marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we expect these two AppContext switches to always default to false? The current naming pattern we've used is to use the pattern EnableXXX when XXX is off by default (or will be off by default in the future - like with BinaryFormatter).

My naive understanding from looking at the code below is this switch only affects MacOS. If you are on any platform other than Apple, this switch doesn't do anything. If you are on an Apple OS that isn't MacOS, the behavior will be "on".

Can you also update https://github.com/dotnet/runtime/blob/master/docs/workflow/trimming/feature-switches.md with your new feature switch?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My naive understanding from looking at the code below is this switch only affects MacOS.

Yep, there is a bit of efficiency that can be gained if we put the reading of the switch under old-fashined ifdef. As it is written, we will read the switch even on platforms where it does nothing.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm questioning if we need a full-blown "feature switch" for this. Not all AppContext switches need to follow the feature switch design (added ILLink.Substitutions.xml entry, and MSBuild entry). If this only affects MacOS, we aren't really concerned about trimming out every bit of code on MacOS.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lambdageek specifically requested that we add a linker substitution for it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess I don't fully understand the value. Adding and maintaining these feature switches isn't "free". They are kind of like public API that we need to document and maintain compatibility.

The only thing I'm seeing this enable is if you wanted to trim an osx self contained application, you could set this switch to trim this code. Is it a lot of code being trimmed? Size of osx apps aren't really a major concern. I guess I just don't see the value outweighing the cost here.

cc @stephentoub @vitek-karas @marek-safar as well

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the existing default is true /cc @rolfbjarne

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The existing default is true (and that's what it should be too in .NET 6).

Copy link
Member

@jkotas jkotas Feb 4, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The existing default on CoreCLR on macOS is false.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does the following sound to everyone? @rolfbjarne @jkotas

The default for in the runtime for when the flag is not specified is false. So, for the net6.0 TFM without a target platform, the default is false.

The MSBuild SDK tooling for net6.0-macos, net6.0-ios, etc. sets the feature flag default to true in MSBuild. That way people migrating to net6.0-* from the Xamarin world to the TFMs with target platform APIs will continue to have the same default.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds reasonable to me.


[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern bool CanSetMinIOCompletionThreads(int ioCompletionThreads);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#nullable enable
using System;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;

internal static partial class Interop
{
internal static partial class Sys
{
[SupportedOSPlatform("macos")]
[SupportedOSPlatform("ios")]
[SupportedOSPlatform("tvos")]
[SupportedOSPlatform("watchos")]
[DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_CreateAutoreleasePool")]
internal static extern IntPtr CreateAutoreleasePool();

[SupportedOSPlatform("macos")]
[SupportedOSPlatform("ios")]
[SupportedOSPlatform("tvos")]
[SupportedOSPlatform("watchos")]
[DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_DrainAutoreleasePool")]
internal static extern void DrainAutoreleasePool(IntPtr ptr);
}
}
13 changes: 10 additions & 3 deletions src/libraries/Native/Unix/System.Native/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,26 @@ set(NATIVE_SOURCES
pal_sysctl.c
)

if (CLR_CMAKE_TARGET_OSX OR CLR_CMAKE_TARGET_IOS OR CLR_CMAKE_TARGET_TVOS)
list (APPEND NATIVE_SOURCES pal_autoreleasepool.m)
set_source_files_properties(pal_autoreleasepool.m PROPERTIES COMPILE_FLAGS -fno-objc-arc)
else()
list (APPEND NATIVE_SOURCES pal_autoreleasepool.c)
endif()

if (CLR_CMAKE_TARGET_IOS OR CLR_CMAKE_TARGET_TVOS)
set(NATIVE_SOURCES ${NATIVE_SOURCES}
list (APPEND NATIVE_SOURCES
pal_log.m
pal_searchpath.m)
else ()
set(NATIVE_SOURCES ${NATIVE_SOURCES}
list (APPEND NATIVE_SOURCES
pal_searchpath.c
pal_console.c
pal_log.c)
endif ()

if (NOT CLR_CMAKE_TARGET_BROWSER)
set(NATIVE_SOURCES ${NATIVE_SOURCES} pal_networkchange.c)
list (APPEND NATIVE_SOURCES pal_networkchange.c)
endif ()

include(${CMAKE_CURRENT_LIST_DIR}/extra_libs.cmake)
Expand Down
3 changes: 3 additions & 0 deletions src/libraries/Native/Unix/System.Native/entrypoints.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "../../AnyOS/entrypoints.h"

// Include System.Native headers
#include "pal_autoreleasepool.h"
#include "pal_console.h"
#include "pal_datetime.h"
#include "pal_errno.h"
Expand Down Expand Up @@ -235,6 +236,8 @@ static const Entry s_sysNative[] =
DllImportEntry(SystemNative_GetGroupList)
DllImportEntry(SystemNative_GetUid)
DllImportEntry(SystemNative_LowLevelMonitor_Create)
DllImportEntry(SystemNative_CreateAutoreleasePool)
DllImportEntry(SystemNative_DrainAutoreleasePool)
};

EXTERN_C const void* SystemResolveDllImport(const char* name);
Expand Down
6 changes: 4 additions & 2 deletions src/libraries/Native/Unix/System.Native/extra_libs.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ macro(append_extra_system_libs NativeLibsExtra)
list(APPEND ${NativeLibsExtra} socket)
endif ()

if (CLR_CMAKE_TARGET_IOS OR CLR_CMAKE_TARGET_TVOS)
list(APPEND ${NativeLibsExtra} "-framework Foundation")
if (CLR_CMAKE_TARGET_OSX OR CLR_CMAKE_TARGET_IOS OR CLR_CMAKE_TARGET_TVOS)
include(CMakeFindFrameworks)
find_library(FOUNDATION Foundation REQUIRED)
list(APPEND ${NativeLibsExtra} ${FOUNDATION})
endif ()

if (CLR_CMAKE_TARGET_LINUX AND HAVE_GETADDRINFO_A)
Expand Down
26 changes: 26 additions & 0 deletions src/libraries/Native/Unix/System.Native/pal_autoreleasepool.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#include "pal_autoreleasepool.h"
#include <stdlib.h>
#include "pal_utilities.h"

#ifndef _MSC_VER
// Don't warning about not declaring a function with [[noreturn]] since it's only true in Debug mode.
#pragma GCC diagnostic ignored "-Wmissing-noreturn"
#endif

// These functions should not be used, but they need to be defined
// to satisfy the tooling we used to enable redirecting P/Invokes
// for the single file scenario.
void* SystemNative_CreateAutoreleasePool(void)
{
assert_err(false, "Autorelease pools not supported on this platform.", EINVAL);
return NULL;
jkoritzinsky marked this conversation as resolved.
Show resolved Hide resolved
}

void SystemNative_DrainAutoreleasePool(void* pool)
{
(void)pool;
assert_err(false, "Autorelease pools not supported on this platform.", EINVAL);
}
17 changes: 17 additions & 0 deletions src/libraries/Native/Unix/System.Native/pal_autoreleasepool.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#pragma once

#include "pal_compiler.h"
#include "pal_types.h"

/**
* Creates an pool to automatically release applicable ref-counted resources.
*/
PALEXPORT void* SystemNative_CreateAutoreleasePool(void);

/**
* Drains and releases a pool created by SystemNative_CreateAutoreleasePool.
*/
PALEXPORT void SystemNative_DrainAutoreleasePool(void* pool);
40 changes: 40 additions & 0 deletions src/libraries/Native/Unix/System.Native/pal_autoreleasepool.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#include "pal_autoreleasepool.h"
#include <Foundation/Foundation.h>

@interface PlaceholderObject : NSObject
- (void)noop:(id)_;
@end

@implementation PlaceholderObject : NSObject
- (void)noop:(id)_
{
[self release];
}
@end

void* SystemNative_CreateAutoreleasePool(void)
{
if (![NSThread isMultiThreaded])
{
// Start another no-op thread with the NSThread APIs to get NSThread into multithreaded mode.
// The NSAutoReleasePool APIs can't be used on secondary threads until NSThread is in multithreaded mode.
jkoritzinsky marked this conversation as resolved.
Show resolved Hide resolved
// See https://developer.apple.com/documentation/foundation/nsautoreleasepool for more information.
PlaceholderObject* placeholderObject = [[PlaceholderObject alloc] init];

// We need to use detachNewThreadSelector to put NSThread into multithreaded mode.
// We can't use detachNewThreadWithBlock since it doesn't change NSThread into multithreaded mode for some reason.
// See https://developer.apple.com/documentation/foundation/nswillbecomemultithreadednotification for more information.
[NSThread detachNewThreadSelector:@selector(noop:) toTarget:placeholderObject withObject:nil];
}
assert([NSThread isMultiThreaded]);

return [[NSAutoreleasePool alloc] init];
}

void SystemNative_DrainAutoreleasePool(void* pool)
{
[((NSAutoreleasePool*)pool) drain];
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,8 @@
<type fullname="System.StartupHookProvider" feature="System.StartupHookProvider.IsSupported" featurevalue="false">
<method signature="System.Boolean get_IsSupported()" body="stub" value="false" />
</type>
<type fullname="System.Threading.ThreadPool" feature="System.Threading.ThreadPool.EnableDispatchAutoreleasePool" featurevalue="false">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this needs to be the other way. false is the default in the code. The feature switch is usually the opposite of the default. See GlobalizationMode.Invariant above. The default in the code is false and setting the feature switch to true trims away ICU dependencies.

Which value for this setting trims code? true or false?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Setting this switch to false (which is the default in code) should trim out the call to DispatchItemWithAutoreleasePool.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. Just know that the code won't be trimmed unless someone explicitly sets the switch.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the feature switch is now set up correctly.

I'm wondering how much code is actually going to be trimmed. It looks like only the ThreadPoolWorkQueue.DispatchItemWithAutoreleasePool static method, and 2 extern methods will be able to be trimmed. I still question if this is worth the effort of defining and maintaining a feature switch here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The thread pool's performance is very sensitive to inlining and branching decisions. By enabling trimming here, we can make some of these branches disappear and eek out a little more performance. This one isn't particularly focused on size reduction.

<method signature="System.Boolean get_EnableDispatchAutoreleasePool()" body="stub" value="false" />
jkoritzinsky marked this conversation as resolved.
Show resolved Hide resolved
</type>
</assembly>
</linker>
Original file line number Diff line number Diff line change
Expand Up @@ -1012,7 +1012,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\ThreadInt64PersistentCounter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\ThreadInterruptedException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\ThreadLocal.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\ThreadPool.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\ThreadPoolWorkQueue.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\ThreadPriority.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\ThreadStart.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\ThreadStartException.cs" />
Expand Down Expand Up @@ -1635,6 +1635,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Security\SecureString.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\LowLevelMonitor.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\TimerQueue.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\ThreadPoolWorkQueue.AutoreleasePool.Unsupported.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\TimeZoneInfo.Win32.cs" />
</ItemGroup>
<ItemGroup Condition="'$(FeatureComWrappers)' != 'true'">
Expand Down Expand Up @@ -1869,6 +1870,8 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Environment.OSX.cs" Condition="'$(IsOSXLike)' == 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\Environment.OSVersion.Unix.cs" Condition="'$(IsOSXLike)' != 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\Environment.SunOS.cs" Condition="'$(Targetsillumos)' == 'true' or '$(TargetsSolaris)' == 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\ThreadPoolWorkQueue.AutoreleasePool.OSX.cs" Condition="'$(IsOSXLike)' == 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\ThreadPoolWorkQueue.AutoreleasePool.Unsupported.cs" Condition="'$(IsOSXLike)' != 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\TimeZoneInfo.GetDisplayName.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\DriveInfoInternal.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\PersistedFiles.Unix.cs" />
Expand All @@ -1879,6 +1882,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\IO\DriveInfoInternal.Browser.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\PersistedFiles.Browser.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\TimeZoneInfo.GetDisplayName.Invariant.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\ThreadPoolWorkQueue.AutoreleasePool.Unsupported.cs" />
</ItemGroup>
<ItemGroup Condition="'$(IsOSXLike)' == 'true'">
<Compile Include="$(CommonPath)Interop\OSX\Interop.libobjc.cs">
Expand All @@ -1887,6 +1891,8 @@
<Compile Include="$(CommonPath)Interop\OSX\Interop.Libraries.cs">
<Link>Common\Interop\OSX\Interop.Libraries.cs</Link>
</Compile>
<Compile Include="$(CommonPath)Interop\OSX\System.Native\Interop.AutoreleasePool.cs"
Link="Common\Interop\OSX\System.Native\Interop.AutoreleasePool.cs" />
</ItemGroup>
<ItemGroup Condition="'$(SupportsX86Intrinsics)' == 'true'">
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\Intrinsics\X86\Aes.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ public static partial class ThreadPool
AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.EnableWorkerTracking", false);
#endif

internal static bool EnableDispatchAutoreleasePool { get; } =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a partial class. Isn't there a file where you can put this property so it doesn't need to be duplicated?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There actually isn't. ThreadPool.cs didn't actually have the ThreadPool type and I don't want to make a new ThreadPool.cs in the same PR where I'm renaming the old one because once it's squashed, the git history will get all messed up.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we put it in ThreadPoolWorkQueue.AutoreleasePool.OSX.cs?

public static partial class ThreadPool
{
    internal static bool EnableDispatchAutoreleasePool { get; } = AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.EnableDispatchAutoreleasePool", false);
}

I understand that it is a minor violation of coding conventions, but I think it is acceptable.

Also, we won't be unnecessarily reading the property on non-OSX platforms anymore.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.EnableDispatchAutoreleasePool", false);

// Threadpool specific initialization of a new thread. Used by OS-provided threadpools. No-op for portable threadpool.
internal static void InitializeForThreadPoolThread() { }

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Licensed to the .NET Foundation under one or more agreements.
jkoritzinsky marked this conversation as resolved.
Show resolved Hide resolved
// The .NET Foundation licenses this file to you under the MIT license.

using System.Runtime.CompilerServices;
using System.Runtime.Versioning;

namespace System.Threading
{
internal sealed partial class ThreadPoolWorkQueue
{
[MethodImpl(MethodImplOptions.NoInlining)]
[SupportedOSPlatform("macos")]
[SupportedOSPlatform("ios")]
[SupportedOSPlatform("tvos")]
[SupportedOSPlatform("watchos")]
private static void DispatchItemWithAutoreleasePool(object workItem, Thread currentThread)
{
IntPtr autoreleasePool = Interop.Sys.CreateAutoreleasePool();
try
{
#pragma warning disable CS0162 // Unreachable code detected. EnableWorkerTracking may be a constant in some runtimes.
if (ThreadPool.EnableWorkerTracking)
jkoritzinsky marked this conversation as resolved.
Show resolved Hide resolved
{
DispatchWorkItemWithWorkerTracking(workItem, currentThread);
}
else
{
DispatchWorkItem(workItem, currentThread);
}
#pragma warning restore CS0162
}
finally
{
Interop.Sys.DrainAutoreleasePool(autoreleasePool);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;

namespace System.Threading
{
internal sealed partial class ThreadPoolWorkQueue
{
[SupportedOSPlatform("macos")]
[SupportedOSPlatform("ios")]
[SupportedOSPlatform("tvos")]
[SupportedOSPlatform("watchos")]
private static void DispatchItemWithAutoreleasePool(object workItem, Thread currentThread)
jkoritzinsky marked this conversation as resolved.
Show resolved Hide resolved
{
Debug.Fail("DispatchItemWithAutoreleasePool should only be called on macOS-like platforms.");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

namespace System.Threading
{
internal sealed class ThreadPoolWorkQueue
internal sealed partial class ThreadPoolWorkQueue
{
internal static class WorkStealingQueueList
{
Expand Down Expand Up @@ -691,23 +691,19 @@ internal static bool Dispatch()
//
// Execute the workitem outside of any finally blocks, so that it can be aborted if needed.
//
#pragma warning disable CS0162 // Unreachable code detected. EnableWorkerTracking may be a constant in some runtimes.
if (ThreadPool.EnableWorkerTracking)
if (OperatingSystem.IsIOS() || OperatingSystem.IsTvOS() || OperatingSystem.IsWatchOS() ||
(OperatingSystem.IsMacOS() && ThreadPool.EnableDispatchAutoreleasePool))
{
DispatchWorkItemWithWorkerTracking(workItem, currentThread);
DispatchItemWithAutoreleasePool(workItem, currentThread);
}
else if (workItem is Task task)
#pragma warning disable CS0162 // Unreachable code detected. EnableWorkerTracking may be a constant in some runtimes.
else if (ThreadPool.EnableWorkerTracking)
{
// Check for Task first as it's currently faster to type check
// for Task and then Unsafe.As for the interface, rather than
// vice versa, in particular when the object implements a bunch
// of interfaces.
task.ExecuteFromThreadPool(currentThread);
DispatchWorkItemWithWorkerTracking(workItem, currentThread);
}
else
{
Debug.Assert(workItem is IThreadPoolWorkItem);
Unsafe.As<IThreadPoolWorkItem>(workItem).Execute();
DispatchWorkItem(workItem, currentThread);
}
#pragma warning restore CS0162

Expand Down Expand Up @@ -781,22 +777,28 @@ private static void DispatchWorkItemWithWorkerTracking(object workItem, Thread c
{
ThreadPool.ReportThreadStatus(isWorking: true);
reportedStatus = true;
if (workItem is Task task)
{
task.ExecuteFromThreadPool(currentThread);
}
else
{
Debug.Assert(workItem is IThreadPoolWorkItem);
Unsafe.As<IThreadPoolWorkItem>(workItem).Execute();
}
DispatchWorkItem(workItem, currentThread);
}
finally
{
if (reportedStatus)
ThreadPool.ReportThreadStatus(isWorking: false);
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void DispatchWorkItem(object workItem, Thread currentThread)
{
if (workItem is Task task)
{
task.ExecuteFromThreadPool(currentThread);
}
else
{
Debug.Assert(workItem is IThreadPoolWorkItem);
Unsafe.As<IThreadPoolWorkItem>(workItem).Execute();
}
}
}

// Holds a WorkStealingQueue, and removes it from the list when this object is no longer referenced.
Expand Down
Loading