Skip to content

Commit

Permalink
Threadpool autorelease (#47592)
Browse files Browse the repository at this point in the history
  • Loading branch information
jkoritzinsky authored Feb 24, 2021
1 parent 1b9e50d commit 86eacff
Show file tree
Hide file tree
Showing 18 changed files with 331 additions and 27 deletions.
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. |
| CustomResourceTypesSupport | System.Resources.ResourceManager.AllowCustomResourceTypes | Use of custom resource types is disabled when set to false. ResourceManager code paths that use reflection for custom types can be trimmed. |

Any feature-switch which defines property can be set in csproj file or
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// 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
{
[DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_CreateAutoreleasePool")]
internal static extern IntPtr CreateAutoreleasePool();

[DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_DrainAutoreleasePool")]
internal static extern void DrainAutoreleasePool(IntPtr ptr);
}
}
11 changes: 9 additions & 2 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_MACCATALYST 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_MACCATALYST OR CLR_CMAKE_TARGET_IOS OR CLR_CMAKE_TARGET_TVOS)
set(NATIVE_SOURCES ${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 @@ -236,6 +237,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
10 changes: 6 additions & 4 deletions src/libraries/Native/Unix/System.Native/extra_libs.cmake
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

macro(append_extra_system_libs NativeLibsExtra)
macro(append_extra_system_libs NativeLibsExtra)
if (CLR_CMAKE_TARGET_LINUX AND NOT CLR_CMAKE_TARGET_ANDROID)
list(APPEND ${NativeLibsExtra} rt)
elseif (CLR_CMAKE_TARGET_FREEBSD)
Expand All @@ -9,9 +9,11 @@ macro(append_extra_system_libs NativeLibsExtra)
elseif (CLR_CMAKE_TARGET_SUNOS)
list(APPEND ${NativeLibsExtra} socket)
endif ()

if (CLR_CMAKE_TARGET_MACCATALYST OR CLR_CMAKE_TARGET_IOS OR CLR_CMAKE_TARGET_TVOS)
list(APPEND ${NativeLibsExtra} "-framework Foundation")

if (CLR_CMAKE_TARGET_OSX OR CLR_CMAKE_TARGET_MACCATALYST 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;
}

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.
// 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
@@ -0,0 +1,7 @@
<linker>
<assembly fullname="System.Private.CoreLib">
<type fullname="System.Threading.ThreadPool" feature="System.Threading.ThreadPool.EnableDispatchAutoreleasePool" featurevalue="false">
<method signature="System.Boolean get_EnableDispatchAutoreleasePool()" body="stub" value="false" />
</type>
</assembly>
</linker>
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
<ILLinkSubstitutionsXmls Include="$(ILLinkSharedDirectory)ILLink.Substitutions.64bit.xml" Condition="'$(Is64Bit)' == 'true'" />
<ILLinkSubstitutionsXmls Include="$(ILLinkSharedDirectory)ILLink.Substitutions.NoArmIntrinsics.xml" Condition="'$(SupportsArmIntrinsics)' != 'true'" />
<ILLinkSubstitutionsXmls Include="$(ILLinkSharedDirectory)ILLink.Substitutions.NoX86Intrinsics.xml" Condition="'$(SupportsX86Intrinsics)' != 'true'" />
<ILLinkSubstitutionsXmls Include="$(ILLinkSharedDirectory)ILLink.Substitutions.OSX.xml" Condition="'$(IsOSXLike)' == 'true'" />
<ILLinkLinkAttributesXmls Include="$(ILLinkSharedDirectory)ILLink.LinkAttributes.Shared.xml" />
</ItemGroup>
<PropertyGroup>
Expand Down Expand Up @@ -1020,7 +1021,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 @@ -1880,6 +1881,7 @@
<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\TimeZoneInfo.GetDisplayName.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\DriveInfoInternal.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\PersistedFiles.Unix.cs" />
Expand All @@ -1898,6 +1900,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
@@ -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.

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

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

internal sealed partial class ThreadPoolWorkQueue
{
[MethodImpl(MethodImplOptions.NoInlining)]
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)
{
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
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,21 @@ internal static bool Dispatch()
//
// Execute the workitem outside of any finally blocks, so that it can be aborted if needed.
//
#if TARGET_OSX || TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
if (ThreadPool.EnableDispatchAutoreleasePool)
{
DispatchItemWithAutoreleasePool(workItem, currentThread);
}
else
#endif
#pragma warning disable CS0162 // Unreachable code detected. EnableWorkerTracking may be a constant in some runtimes.
if (ThreadPool.EnableWorkerTracking)
{
DispatchWorkItemWithWorkerTracking(workItem, currentThread);
}
else if (workItem is Task task)
{
// 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);
}
else
{
Debug.Assert(workItem is IThreadPoolWorkItem);
Unsafe.As<IThreadPoolWorkItem>(workItem).Execute();
DispatchWorkItem(workItem, currentThread);
}
#pragma warning restore CS0162

Expand Down Expand Up @@ -781,22 +779,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
7 changes: 7 additions & 0 deletions src/tests/Common/Platform/platformdefines.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,14 @@ typedef HANDLE THREAD_ID;

typedef char16_t WCHAR;
typedef unsigned int DWORD;

#ifdef OBJC_TESTS
// The Objective-C headers define the BOOL type to be unsigned char or bool.
// As a result, we can't redefine it here. So instead, define WINBOOL to be int-sized.
typedef int WINBOOL;
#else
typedef int BOOL;
#endif
typedef WCHAR *LPWSTR, *PWSTR;
typedef const WCHAR *LPCWSTR, *PCWSTR;

Expand Down
4 changes: 4 additions & 0 deletions src/tests/Interop/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,7 @@ if(CLR_CMAKE_TARGET_WIN32)
add_subdirectory(IJW/NativeVarargs)
endif()
endif(CLR_CMAKE_TARGET_WIN32)

if(CLR_CMAKE_TARGET_OSX OR CLR_CMAKE_TARGET_IOS OR CLR_CMAKE_TARGET_TVOS)
add_subdirectory(ObjectiveC)
endif()
Loading

0 comments on commit 86eacff

Please sign in to comment.