From 5a65985bf88dcbfdd3310caff677ccc8fccc6332 Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Mon, 11 Mar 2019 18:36:01 +0100 Subject: [PATCH] add NativeCallbackDispatcher --- .../Internal/NativeCallbackDispatcher.cs | 120 ++++++++++++++++++ .../Grpc.Core/Internal/NativeExtension.cs | 3 + .../NativeMetadataCredentialsPlugin.cs | 21 ++- .../Internal/NativeMethods.Generated.cs | 17 ++- src/csharp/ext/grpc_csharp_ext.c | 32 ++--- .../runtimes/grpc_csharp_ext_dummy_stubs.c | 4 + .../Grpc.Core/Internal/native_methods.include | 3 +- 7 files changed, 170 insertions(+), 30 deletions(-) create mode 100644 src/csharp/Grpc.Core/Internal/NativeCallbackDispatcher.cs diff --git a/src/csharp/Grpc.Core/Internal/NativeCallbackDispatcher.cs b/src/csharp/Grpc.Core/Internal/NativeCallbackDispatcher.cs new file mode 100644 index 00000000000..53f8b7c6489 --- /dev/null +++ b/src/csharp/Grpc.Core/Internal/NativeCallbackDispatcher.cs @@ -0,0 +1,120 @@ +#region Copyright notice and license + +// Copyright 2019 The gRPC Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#endregion + +using System; +using System.Collections.Concurrent; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Threading; +using System.Collections.Generic; +using Grpc.Core.Logging; + +namespace Grpc.Core.Internal +{ + internal delegate void UniversalNativeCallback(IntPtr arg0, IntPtr arg1, IntPtr arg2, IntPtr arg3, IntPtr arg4, IntPtr arg5); + + internal delegate void NativeCallbackDispatcherCallback(IntPtr tag, IntPtr arg0, IntPtr arg1, IntPtr arg2, IntPtr arg3, IntPtr arg4, IntPtr arg5); + + internal class NativeCallbackDispatcher + { + static readonly ILogger Logger = GrpcEnvironment.Logger.ForType(); + static readonly object staticLock = new object(); + static readonly AtomicCounter atomicCounter = new AtomicCounter(); + static readonly ConcurrentDictionary registry = new ConcurrentDictionary(); + + static NativeCallbackDispatcherCallback dispatcherCallback; + + public static void Init(NativeMethods native) + { + lock (staticLock) + { + if (dispatcherCallback == null) + { + dispatcherCallback = new NativeCallbackDispatcherCallback(HandleDispatcherCallback); + native.grpcsharp_native_callback_dispatcher_init(dispatcherCallback); + } + } + } + + public static NativeCallbackRegistration RegisterCallback(UniversalNativeCallback callback) + { + while (true) + { + // TODO: retries might not work well on 32-bit + var tag = NextTag(); + if (registry.TryAdd(tag, callback)) + { + return new NativeCallbackRegistration(tag); + } + } + } + + public static void UnregisterCallback(IntPtr tag) + { + registry.TryRemove(tag, out UniversalNativeCallback callback); + } + + private static bool TryGetCallback(IntPtr tag, out UniversalNativeCallback callback) + { + return registry.TryGetValue(tag, out callback); + } + + private static IntPtr NextTag() + { + return (IntPtr) atomicCounter.Increment(); + } + + [MonoPInvokeCallback(typeof(NativeCallbackDispatcherCallback))] + private static void HandleDispatcherCallback(IntPtr tag, IntPtr arg0, IntPtr arg1, IntPtr arg2, IntPtr arg3, IntPtr arg4, IntPtr arg5) + { + try + { + UniversalNativeCallback callback; + if (!TryGetCallback(tag, out callback)) + { + Logger.Error("No native callback handler registered for tag {0}.", tag); + } + callback(arg0, arg1, arg2, arg3, arg4, arg5); + } + catch (Exception e) + { + // eat the exception, we must not throw when inside callback from native code. + Logger.Error(e, "Caught exception inside callback from native callback."); + } + } + } + + internal class NativeCallbackRegistration : IDisposable + { + readonly IntPtr tag; + readonly Action disposeAction; + + public NativeCallbackRegistration(IntPtr tag, Action disposeAction) + { + this.tag = tag; + } + + public IntPtr Tag => tag; + + public void Dispose() + { + NativeCallbackDispatcher.UnregisterCallback(tag); + } + } +} diff --git a/src/csharp/Grpc.Core/Internal/NativeExtension.cs b/src/csharp/Grpc.Core/Internal/NativeExtension.cs index 5177b69fd90..6d9cbaf97f0 100644 --- a/src/csharp/Grpc.Core/Internal/NativeExtension.cs +++ b/src/csharp/Grpc.Core/Internal/NativeExtension.cs @@ -43,6 +43,9 @@ namespace Grpc.Core.Internal // to make sure we don't lose any logs. NativeLogRedirector.Redirect(this.nativeMethods); + // Initialize + NativeCallbackDispatcher.Init(this.nativeMethods); + DefaultSslRootsOverride.Override(this.nativeMethods); Logger.Debug("gRPC native library loaded successfully."); diff --git a/src/csharp/Grpc.Core/Internal/NativeMetadataCredentialsPlugin.cs b/src/csharp/Grpc.Core/Internal/NativeMetadataCredentialsPlugin.cs index faeb51e6f7a..d01d4ef287a 100644 --- a/src/csharp/Grpc.Core/Internal/NativeMetadataCredentialsPlugin.cs +++ b/src/csharp/Grpc.Core/Internal/NativeMetadataCredentialsPlugin.cs @@ -23,8 +23,6 @@ using Grpc.Core.Utils; namespace Grpc.Core.Internal { - internal delegate void NativeMetadataInterceptor(IntPtr statePtr, IntPtr serviceUrlPtr, IntPtr methodNamePtr, IntPtr callbackPtr, IntPtr userDataPtr, bool isDestroy); - internal class NativeMetadataCredentialsPlugin { const string GetMetadataExceptionStatusMsg = "Exception occurred in metadata credentials plugin."; @@ -33,18 +31,14 @@ namespace Grpc.Core.Internal static readonly NativeMethods Native = NativeMethods.Get(); AsyncAuthInterceptor interceptor; - GCHandle gcHandle; - NativeMetadataInterceptor nativeInterceptor; CallCredentialsSafeHandle credentials; + NativeCallbackRegistration callbackRegistration; public NativeMetadataCredentialsPlugin(AsyncAuthInterceptor interceptor) { this.interceptor = GrpcPreconditions.CheckNotNull(interceptor, "interceptor"); - this.nativeInterceptor = NativeMetadataInterceptorHandler; - - // Make sure the callback doesn't get garbage collected until it is destroyed. - this.gcHandle = GCHandle.Alloc(this.nativeInterceptor, GCHandleType.Normal); - this.credentials = Native.grpcsharp_metadata_credentials_create_from_plugin(nativeInterceptor); + this.callbackRegistration = NativeCallbackDispatcher.RegisterCallback(HandleUniversalCallback); + this.credentials = Native.grpcsharp_metadata_credentials_create_from_plugin(this.callbackRegistration.Tag); } public CallCredentialsSafeHandle Credentials @@ -52,11 +46,16 @@ namespace Grpc.Core.Internal get { return credentials; } } - private void NativeMetadataInterceptorHandler(IntPtr statePtr, IntPtr serviceUrlPtr, IntPtr methodNamePtr, IntPtr callbackPtr, IntPtr userDataPtr, bool isDestroy) + private void HandleUniversalCallback(IntPtr arg0, IntPtr arg1, IntPtr arg2, IntPtr arg3, IntPtr arg4, IntPtr arg5) + { + NativeMetadataInterceptorHandler(arg0, arg1, arg2, arg3, arg4 != IntPtr.Zero); + } + + private void NativeMetadataInterceptorHandler(IntPtr serviceUrlPtr, IntPtr methodNamePtr, IntPtr callbackPtr, IntPtr userDataPtr, bool isDestroy) { if (isDestroy) { - gcHandle.Free(); + this.callbackRegistration.Dispose(); return; } diff --git a/src/csharp/Grpc.Core/Internal/NativeMethods.Generated.cs b/src/csharp/Grpc.Core/Internal/NativeMethods.Generated.cs index a45cbe4107d..b7b9a12d8a3 100644 --- a/src/csharp/Grpc.Core/Internal/NativeMethods.Generated.cs +++ b/src/csharp/Grpc.Core/Internal/NativeMethods.Generated.cs @@ -103,6 +103,7 @@ namespace Grpc.Core.Internal public readonly Delegates.grpcsharp_metadata_array_get_value_delegate grpcsharp_metadata_array_get_value; public readonly Delegates.grpcsharp_metadata_array_destroy_full_delegate grpcsharp_metadata_array_destroy_full; public readonly Delegates.grpcsharp_redirect_log_delegate grpcsharp_redirect_log; + public readonly Delegates.grpcsharp_native_callback_dispatcher_init_delegate grpcsharp_native_callback_dispatcher_init; public readonly Delegates.grpcsharp_metadata_credentials_create_from_plugin_delegate grpcsharp_metadata_credentials_create_from_plugin; public readonly Delegates.grpcsharp_metadata_credentials_notify_from_plugin_delegate grpcsharp_metadata_credentials_notify_from_plugin; public readonly Delegates.grpcsharp_ssl_server_credentials_create_delegate grpcsharp_ssl_server_credentials_create; @@ -203,6 +204,7 @@ namespace Grpc.Core.Internal this.grpcsharp_metadata_array_get_value = GetMethodDelegate(library); this.grpcsharp_metadata_array_destroy_full = GetMethodDelegate(library); this.grpcsharp_redirect_log = GetMethodDelegate(library); + this.grpcsharp_native_callback_dispatcher_init = GetMethodDelegate(library); this.grpcsharp_metadata_credentials_create_from_plugin = GetMethodDelegate(library); this.grpcsharp_metadata_credentials_notify_from_plugin = GetMethodDelegate(library); this.grpcsharp_ssl_server_credentials_create = GetMethodDelegate(library); @@ -302,6 +304,7 @@ namespace Grpc.Core.Internal this.grpcsharp_metadata_array_get_value = DllImportsFromStaticLib.grpcsharp_metadata_array_get_value; this.grpcsharp_metadata_array_destroy_full = DllImportsFromStaticLib.grpcsharp_metadata_array_destroy_full; this.grpcsharp_redirect_log = DllImportsFromStaticLib.grpcsharp_redirect_log; + this.grpcsharp_native_callback_dispatcher_init = DllImportsFromStaticLib.grpcsharp_native_callback_dispatcher_init; this.grpcsharp_metadata_credentials_create_from_plugin = DllImportsFromStaticLib.grpcsharp_metadata_credentials_create_from_plugin; this.grpcsharp_metadata_credentials_notify_from_plugin = DllImportsFromStaticLib.grpcsharp_metadata_credentials_notify_from_plugin; this.grpcsharp_ssl_server_credentials_create = DllImportsFromStaticLib.grpcsharp_ssl_server_credentials_create; @@ -401,6 +404,7 @@ namespace Grpc.Core.Internal this.grpcsharp_metadata_array_get_value = DllImportsFromSharedLib.grpcsharp_metadata_array_get_value; this.grpcsharp_metadata_array_destroy_full = DllImportsFromSharedLib.grpcsharp_metadata_array_destroy_full; this.grpcsharp_redirect_log = DllImportsFromSharedLib.grpcsharp_redirect_log; + this.grpcsharp_native_callback_dispatcher_init = DllImportsFromSharedLib.grpcsharp_native_callback_dispatcher_init; this.grpcsharp_metadata_credentials_create_from_plugin = DllImportsFromSharedLib.grpcsharp_metadata_credentials_create_from_plugin; this.grpcsharp_metadata_credentials_notify_from_plugin = DllImportsFromSharedLib.grpcsharp_metadata_credentials_notify_from_plugin; this.grpcsharp_ssl_server_credentials_create = DllImportsFromSharedLib.grpcsharp_ssl_server_credentials_create; @@ -503,7 +507,8 @@ namespace Grpc.Core.Internal public delegate IntPtr grpcsharp_metadata_array_get_value_delegate(IntPtr metadataArray, UIntPtr index, out UIntPtr valueLength); public delegate void grpcsharp_metadata_array_destroy_full_delegate(IntPtr array); public delegate void grpcsharp_redirect_log_delegate(GprLogDelegate callback); - public delegate CallCredentialsSafeHandle grpcsharp_metadata_credentials_create_from_plugin_delegate(NativeMetadataInterceptor interceptor); + public delegate void grpcsharp_native_callback_dispatcher_init_delegate(NativeCallbackDispatcherCallback dispatcher); + public delegate CallCredentialsSafeHandle grpcsharp_metadata_credentials_create_from_plugin_delegate(IntPtr nativeCallbackTag); public delegate void grpcsharp_metadata_credentials_notify_from_plugin_delegate(IntPtr callbackPtr, IntPtr userData, MetadataArraySafeHandle metadataArray, StatusCode statusCode, string errorDetails); public delegate ServerCredentialsSafeHandle grpcsharp_ssl_server_credentials_create_delegate(string pemRootCerts, string[] keyCertPairCertChainArray, string[] keyCertPairPrivateKeyArray, UIntPtr numKeyCertPairs, SslClientCertificateRequestType clientCertificateRequest); public delegate void grpcsharp_server_credentials_release_delegate(IntPtr credentials); @@ -746,7 +751,10 @@ namespace Grpc.Core.Internal public static extern void grpcsharp_redirect_log(GprLogDelegate callback); [DllImport(ImportName)] - public static extern CallCredentialsSafeHandle grpcsharp_metadata_credentials_create_from_plugin(NativeMetadataInterceptor interceptor); + public static extern void grpcsharp_native_callback_dispatcher_init(NativeCallbackDispatcherCallback dispatcher); + + [DllImport(ImportName)] + public static extern CallCredentialsSafeHandle grpcsharp_metadata_credentials_create_from_plugin(IntPtr nativeCallbackTag); [DllImport(ImportName)] public static extern void grpcsharp_metadata_credentials_notify_from_plugin(IntPtr callbackPtr, IntPtr userData, MetadataArraySafeHandle metadataArray, StatusCode statusCode, string errorDetails); @@ -1039,7 +1047,10 @@ namespace Grpc.Core.Internal public static extern void grpcsharp_redirect_log(GprLogDelegate callback); [DllImport(ImportName)] - public static extern CallCredentialsSafeHandle grpcsharp_metadata_credentials_create_from_plugin(NativeMetadataInterceptor interceptor); + public static extern void grpcsharp_native_callback_dispatcher_init(NativeCallbackDispatcherCallback dispatcher); + + [DllImport(ImportName)] + public static extern CallCredentialsSafeHandle grpcsharp_metadata_credentials_create_from_plugin(IntPtr nativeCallbackTag); [DllImport(ImportName)] public static extern void grpcsharp_metadata_credentials_notify_from_plugin(IntPtr callbackPtr, IntPtr userData, MetadataArraySafeHandle metadataArray, StatusCode statusCode, string errorDetails); diff --git a/src/csharp/ext/grpc_csharp_ext.c b/src/csharp/ext/grpc_csharp_ext.c index ed002ae1fff..b034bafe86c 100644 --- a/src/csharp/ext/grpc_csharp_ext.c +++ b/src/csharp/ext/grpc_csharp_ext.c @@ -1010,6 +1010,18 @@ grpcsharp_composite_call_credentials_create(grpc_call_credentials* creds1, return grpc_composite_call_credentials_create(creds1, creds2, NULL); } +/* Native callback dispatcher */ + +typedef void(GPR_CALLTYPE* grpcsharp_native_callback_dispatcher_func)( + void* tag, void* arg0, void* arg1, void* arg2, void* arg3, void* arg4, void *arg5); + +static grpcsharp_native_callback_dispatcher_func native_callback_dispatcher = NULL; + +GPR_EXPORT void GPR_CALLTYPE grpcsharp_native_callback_dispatcher_init(grpcsharp_native_callback_dispatcher_func func) { + GPR_ASSERT(func); + native_callback_dispatcher = func; +} + /* Metadata credentials plugin */ GPR_EXPORT void GPR_CALLTYPE grpcsharp_metadata_credentials_notify_from_plugin( @@ -1023,37 +1035,27 @@ GPR_EXPORT void GPR_CALLTYPE grpcsharp_metadata_credentials_notify_from_plugin( } } -typedef void(GPR_CALLTYPE* grpcsharp_metadata_interceptor_func)( - void* state, const char* service_url, const char* method_name, - grpc_credentials_plugin_metadata_cb cb, void* user_data, - int32_t is_destroy); - static int grpcsharp_get_metadata_handler( void* state, grpc_auth_metadata_context context, grpc_credentials_plugin_metadata_cb cb, void* user_data, grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX], size_t* num_creds_md, grpc_status_code* status, const char** error_details) { - grpcsharp_metadata_interceptor_func interceptor = - (grpcsharp_metadata_interceptor_func)(intptr_t)state; - interceptor(state, context.service_url, context.method_name, cb, user_data, - 0); + native_callback_dispatcher(state, context.service_url, context.method_name, cb, user_data, + 0, NULL); return 0; /* Asynchronous return. */ } static void grpcsharp_metadata_credentials_destroy_handler(void* state) { - grpcsharp_metadata_interceptor_func interceptor = - (grpcsharp_metadata_interceptor_func)(intptr_t)state; - interceptor(state, NULL, NULL, NULL, NULL, 1); + native_callback_dispatcher(state, NULL, NULL, NULL, NULL, 1, NULL); } GPR_EXPORT grpc_call_credentials* GPR_CALLTYPE -grpcsharp_metadata_credentials_create_from_plugin( - grpcsharp_metadata_interceptor_func metadata_interceptor) { +grpcsharp_metadata_credentials_create_from_plugin(void *callback_tag) { grpc_metadata_credentials_plugin plugin; plugin.get_metadata = grpcsharp_get_metadata_handler; plugin.destroy = grpcsharp_metadata_credentials_destroy_handler; - plugin.state = (void*)(intptr_t)metadata_interceptor; + plugin.state = callback_tag; plugin.type = ""; return grpc_metadata_credentials_create_from_plugin(plugin, NULL); } diff --git a/src/csharp/unitypackage/unitypackage_skeleton/Plugins/Grpc.Core/runtimes/grpc_csharp_ext_dummy_stubs.c b/src/csharp/unitypackage/unitypackage_skeleton/Plugins/Grpc.Core/runtimes/grpc_csharp_ext_dummy_stubs.c index 200dd022bf8..0e9d56f5bdf 100644 --- a/src/csharp/unitypackage/unitypackage_skeleton/Plugins/Grpc.Core/runtimes/grpc_csharp_ext_dummy_stubs.c +++ b/src/csharp/unitypackage/unitypackage_skeleton/Plugins/Grpc.Core/runtimes/grpc_csharp_ext_dummy_stubs.c @@ -298,6 +298,10 @@ void grpcsharp_redirect_log() { fprintf(stderr, "Should never reach here"); abort(); } +void grpcsharp_native_callback_dispatcher_init() { + fprintf(stderr, "Should never reach here"); + abort(); +} void grpcsharp_metadata_credentials_create_from_plugin() { fprintf(stderr, "Should never reach here"); abort(); diff --git a/templates/src/csharp/Grpc.Core/Internal/native_methods.include b/templates/src/csharp/Grpc.Core/Internal/native_methods.include index 2afffd03720..b7a8e285488 100644 --- a/templates/src/csharp/Grpc.Core/Internal/native_methods.include +++ b/templates/src/csharp/Grpc.Core/Internal/native_methods.include @@ -69,7 +69,8 @@ native_method_signatures = [ 'IntPtr grpcsharp_metadata_array_get_value(IntPtr metadataArray, UIntPtr index, out UIntPtr valueLength)', 'void grpcsharp_metadata_array_destroy_full(IntPtr array)', 'void grpcsharp_redirect_log(GprLogDelegate callback)', - 'CallCredentialsSafeHandle grpcsharp_metadata_credentials_create_from_plugin(NativeMetadataInterceptor interceptor)', + 'void grpcsharp_native_callback_dispatcher_init(NativeCallbackDispatcherCallback dispatcher)', + 'CallCredentialsSafeHandle grpcsharp_metadata_credentials_create_from_plugin(IntPtr nativeCallbackTag)', 'void grpcsharp_metadata_credentials_notify_from_plugin(IntPtr callbackPtr, IntPtr userData, MetadataArraySafeHandle metadataArray, StatusCode statusCode, string errorDetails)', 'ServerCredentialsSafeHandle grpcsharp_ssl_server_credentials_create(string pemRootCerts, string[] keyCertPairCertChainArray, string[] keyCertPairPrivateKeyArray, UIntPtr numKeyCertPairs, SslClientCertificateRequestType clientCertificateRequest)', 'void grpcsharp_server_credentials_release(IntPtr credentials)',