aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/csharp/Grpc.Core
diff options
context:
space:
mode:
authorGravatar Jan Tattermusch <jtattermusch@users.noreply.github.com>2018-08-11 00:44:47 +0200
committerGravatar GitHub <noreply@github.com>2018-08-11 00:44:47 +0200
commit46f8637bf76eabf362f9edf10ea7600c2d911b00 (patch)
tree2dbeb315d2c63329ad78bbc85ff300964188d653 /src/csharp/Grpc.Core
parent9bde41b8e8a940bfa172d2220a34ced0b272ed48 (diff)
parent1b59cc8606026b231394ef648885ef3650d165fe (diff)
Merge pull request #16308 from jtattermusch/csharp_ios_crash_workaround
C#: avoid shutdown crash on iOS
Diffstat (limited to 'src/csharp/Grpc.Core')
-rw-r--r--src/csharp/Grpc.Core/GrpcEnvironment.cs24
-rw-r--r--src/csharp/Grpc.Core/Internal/PlatformApis.cs29
2 files changed, 51 insertions, 2 deletions
diff --git a/src/csharp/Grpc.Core/GrpcEnvironment.cs b/src/csharp/Grpc.Core/GrpcEnvironment.cs
index a6a1d8af50..6ca694e0e4 100644
--- a/src/csharp/Grpc.Core/GrpcEnvironment.cs
+++ b/src/csharp/Grpc.Core/GrpcEnvironment.cs
@@ -50,6 +50,7 @@ namespace Grpc.Core
static int requestCallContextPoolThreadLocalCapacity = DefaultRequestCallContextPoolThreadLocalCapacity;
static readonly HashSet<Channel> registeredChannels = new HashSet<Channel>();
static readonly HashSet<Server> registeredServers = new HashSet<Server>();
+ static readonly AtomicCounter nativeInitCounter = new AtomicCounter();
static ILogger logger = new LogLevelFilterLogger(new ConsoleLogger(), LogLevel.Off, true);
@@ -360,12 +361,25 @@ namespace Grpc.Core
internal static void GrpcNativeInit()
{
+ if (!IsNativeShutdownAllowed && nativeInitCounter.Count > 0)
+ {
+ // Normally grpc_init and grpc_shutdown calls should come in pairs (C core does reference counting),
+ // but in case we avoid grpc_shutdown calls altogether, calling grpc_init has no effect
+ // besides incrementing an internal C core counter that could theoretically overflow.
+ // To avoid this theoretical possibility we guard repeated calls to grpc_init()
+ // with a 64-bit atomic counter (that can't realistically overflow).
+ return;
+ }
NativeMethods.Get().grpcsharp_init();
+ nativeInitCounter.Increment();
}
internal static void GrpcNativeShutdown()
{
- NativeMethods.Get().grpcsharp_shutdown();
+ if (IsNativeShutdownAllowed)
+ {
+ NativeMethods.Get().grpcsharp_shutdown();
+ }
}
/// <summary>
@@ -411,6 +425,14 @@ namespace Grpc.Core
return GetThreadPoolSizeOrDefault();
}
+ // On some platforms (specifically iOS), thread local variables in native code
+ // require initialization/destruction. By skipping the grpc_shutdown() call,
+ // we avoid a potential crash where grpc_shutdown() has already destroyed
+ // the thread local variables, but some C core's *_destroy() methods still
+ // need to run (e.g. they may be run by finalizer thread which is out of our control)
+ // For more context, see https://github.com/grpc/grpc/issues/16294
+ private static bool IsNativeShutdownAllowed => !PlatformApis.IsXamarinIOS && !PlatformApis.IsUnityIOS;
+
private static class ShutdownHooks
{
static object staticLock = new object();
diff --git a/src/csharp/Grpc.Core/Internal/PlatformApis.cs b/src/csharp/Grpc.Core/Internal/PlatformApis.cs
index c501aa89fb..a8f147545b 100644
--- a/src/csharp/Grpc.Core/Internal/PlatformApis.cs
+++ b/src/csharp/Grpc.Core/Internal/PlatformApis.cs
@@ -42,6 +42,7 @@ namespace Grpc.Core.Internal
static readonly bool isMono;
static readonly bool isNetCore;
static readonly bool isUnity;
+ static readonly bool isUnityIOS;
static readonly bool isXamarin;
static readonly bool isXamarinIOS;
static readonly bool isXamarinAndroid;
@@ -63,7 +64,25 @@ namespace Grpc.Core.Internal
isNetCore = false;
#endif
isMono = Type.GetType("Mono.Runtime") != null;
- isUnity = Type.GetType(UnityEngineApplicationClassName) != null;
+
+ // Unity
+ var unityApplicationClass = Type.GetType(UnityEngineApplicationClassName);
+ if (unityApplicationClass != null)
+ {
+ isUnity = true;
+ // Consult value of Application.platform via reflection
+ // https://docs.unity3d.com/ScriptReference/Application-platform.html
+ var platformProperty = unityApplicationClass.GetTypeInfo().GetProperty("platform");
+ var unityRuntimePlatform = platformProperty?.GetValue(null)?.ToString();
+ isUnityIOS = (unityRuntimePlatform == "IPhonePlayer");
+ }
+ else
+ {
+ isUnity = false;
+ isUnityIOS = false;
+ }
+
+ // Xamarin
isXamarinIOS = Type.GetType(XamarinIOSObjectClassName) != null;
isXamarinAndroid = Type.GetType(XamarinAndroidObjectClassName) != null;
isXamarin = isXamarinIOS || isXamarinAndroid;
@@ -98,6 +117,14 @@ namespace Grpc.Core.Internal
}
/// <summary>
+ /// true if running on Unity iOS, false otherwise.
+ /// </summary>
+ public static bool IsUnityIOS
+ {
+ get { return isUnityIOS; }
+ }
+
+ /// <summary>
/// true if running on a Xamarin platform (either Xamarin.Android or Xamarin.iOS),
/// false otherwise.
/// </summary>