Merge pull request #16308 from jtattermusch/csharp_ios_crash_workaround

C#: avoid shutdown crash on iOS
pull/16340/head
Jan Tattermusch 7 years ago committed by GitHub
commit 46f8637bf7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 24
      src/csharp/Grpc.Core/GrpcEnvironment.cs
  2. 29
      src/csharp/Grpc.Core/Internal/PlatformApis.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();

@ -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;
@ -97,6 +116,14 @@ namespace Grpc.Core.Internal
get { return isUnity; }
}
/// <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.

Loading…
Cancel
Save