From cfb988822d647ba3b6e2532cd1a01aaa03da8d39 Mon Sep 17 00:00:00 2001 From: Nikola Irinchev Date: Wed, 28 Apr 2021 17:16:10 +0200 Subject: [PATCH] Ensure that all native callbacks are setup always (#2331) * Ensure that all native callbacks are setup always * Make ShouldCompactOnLaunch a static callback + clear app cache on reset * Update to new core * Rework how gchandles are freed in native callbacks * New Core * Rename the unity package * Make Migration non-disposable * Update to latest Core * Use Core v11. * Use Core with the snapshot fix * Try to enable core dumps * Use new Core * Revert to a less buggy Core version * Add a link for a TODO entry. --- Jenkinsfile | 6 +- .../Realm.Unity/Runtime/Realm.dll.config.meta | 85 ------- Realm/Realm.Unity/package.json | 6 +- .../Configurations/RealmConfiguration.cs | 31 +-- .../Realm/Configurations/SyncConfiguration.cs | 5 +- Realm/Realm/Handles/AppHandle.cs | 121 ++++++---- Realm/Realm/Handles/ObjectHandle.cs | 14 +- Realm/Realm/Handles/SessionHandle.cs | 44 ++-- Realm/Realm/Handles/SharedRealmHandle.cs | 70 ++++-- Realm/Realm/Handles/SyncUserHandle.cs | 212 +++++++++++------- Realm/Realm/Migration.cs | 18 +- Realm/Realm/Native/Configuration.cs | 9 - Realm/Realm/Native/NativeCommon.cs | 8 + Realm/Realm/Native/NativeException.cs | 13 +- Realm/Realm/Sync/App.cs | 12 +- Realm/Realm/Sync/Session.cs | 14 +- Realm/Realm/Sync/User.cs | 75 +++---- .../SetupUnityPackage/Program.cs | 5 +- wrappers/realm-core | 2 +- wrappers/src/app_cs.cpp | 8 +- wrappers/src/app_cs.hpp | 62 +++++ wrappers/src/shared_realm_cs.cpp | 11 +- wrappers/src/shared_realm_cs.hpp | 1 - wrappers/src/sync_user_cs.cpp | 66 ------ 24 files changed, 435 insertions(+), 463 deletions(-) delete mode 100644 Realm/Realm.Unity/Runtime/Realm.dll.config.meta diff --git a/Jenkinsfile b/Jenkinsfile index 438245176e..bc228336de 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -208,8 +208,8 @@ stage('Unity Package') { bat "dotnet run --project Tools/SetupUnityPackage/SetupUnityPackage/ -- --path ${packagePath} --utils-path ${utilsPackagePath} --weaver-path ${weaverPackagePath} --pack" dir('Realm/Realm.Unity') { - archiveArtifacts "realm.unity-${packageVersion}.tgz" - bat "del realm.unity-${packageVersion}.tgz" + archiveArtifacts "io.realm.unity-${packageVersion}.tgz" + bat "del io.realm.unity-${packageVersion}.tgz" } bat "dotnet run --project Tools/SetupUnityPackage/SetupUnityPackage/ -- --path ${packagePath} --utils-path ${utilsPackagePath} --weaver-path ${weaverPackagePath} --include-dependencies --pack" @@ -387,7 +387,7 @@ def NetCoreTest(String nodeName, String targetFramework) { "objectid-partition-key": "${env.WORKSPACE}/Tests/TestApps/objectid-partition-key", "uuid-partition-key": "${env.WORKSPACE}/Tests/TestApps/uuid-partition-key" ]) { networkName -> - test_runner_image.inside("--network=${networkName}") { + test_runner_image.inside("--network=${networkName} --ulimit core=-1") { script += " --baasurl http://mongodb-realm:9090" // see https://stackoverflow.com/a/53782505 sh """ diff --git a/Realm/Realm.Unity/Runtime/Realm.dll.config.meta b/Realm/Realm.Unity/Runtime/Realm.dll.config.meta deleted file mode 100644 index 91ab5b1040..0000000000 --- a/Realm/Realm.Unity/Runtime/Realm.dll.config.meta +++ /dev/null @@ -1,85 +0,0 @@ -fileFormatVersion: 2 -guid: a3af9323b72904ed0a863bfc6a7f2569 -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 1 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Android: 1 - Exclude Editor: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude Win: 1 - Exclude Win64: 1 - Exclude iOS: 0 - - first: - Android: Android - second: - enabled: 0 - settings: - CPU: ARMv7 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - CPU: AnyCPU - DefaultValueInitialized: true - OS: AnyOS - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: x86_64 - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: x86_64 - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: x86 - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: x86_64 - - first: - Windows Store Apps: WindowsStoreApps - second: - enabled: 1 - settings: {} - - first: - iPhone: iOS - second: - enabled: 1 - settings: - AddToEmbeddedBinaries: false - CPU: AnyCPU - CompileFlags: - FrameworkDependencies: - userData: - assetBundleName: - assetBundleVariant: diff --git a/Realm/Realm.Unity/package.json b/Realm/Realm.Unity/package.json index b18831827f..e9541e3689 100644 --- a/Realm/Realm.Unity/package.json +++ b/Realm/Realm.Unity/package.json @@ -1,10 +1,10 @@ { - "name": "realm.unity", + "name": "io.realm.unity", "version": "10.2.0-beta.2", "displayName": "Realm", "description": "Realm is a mobile database: a replacement for SQLite", - "unity": "2019.1", - "unityRelease": "0b5", + "unity": "2021.1", + "unityRelease": "3f1", "dependencies": { }, "keywords": [ diff --git a/Realm/Realm/Configurations/RealmConfiguration.cs b/Realm/Realm/Configurations/RealmConfiguration.cs index b65f0c31a7..3c3bc7ab2f 100644 --- a/Realm/Realm/Configurations/RealmConfiguration.cs +++ b/Realm/Realm/Configurations/RealmConfiguration.cs @@ -23,7 +23,6 @@ using System.Threading.Tasks; using Realms.Exceptions; using Realms.Helpers; -using Realms.Native; using Realms.Schema; namespace Realms @@ -133,14 +132,14 @@ internal override Realm CreateRealm(RealmSchema schema) if (MigrationCallback != null) { migration = new Migration(this, schema); - migration.PopulateConfiguration(ref configuration); + configuration.managed_migration_handle = GCHandle.ToIntPtr(migration.MigrationHandle); } + GCHandle? shouldCompactHandle = null; if (ShouldCompactOnLaunch != null) { - var handle = GCHandle.Alloc(ShouldCompactOnLaunch); - configuration.should_compact_callback = ShouldCompactOnLaunchCallback; - configuration.managed_should_compact_delegate = GCHandle.ToIntPtr(handle); + shouldCompactHandle = GCHandle.Alloc(ShouldCompactOnLaunch); + configuration.managed_should_compact_delegate = GCHandle.ToIntPtr(shouldCompactHandle.Value); } var srPtr = IntPtr.Zero; @@ -152,11 +151,16 @@ internal override Realm CreateRealm(RealmSchema schema) { throw new AggregateException("Exception occurred in a Realm migration callback. See inner exception for more details.", migration?.MigrationException); } + finally + { + migration?.ReleaseHandle(); + shouldCompactHandle?.Free(); + } var srHandle = new SharedRealmHandle(srPtr); if (IsDynamic && !schema.Any()) { - srHandle.GetSchema(nativeSchema => schema = RealmSchema.CreateFromObjectStoreSchema(nativeSchema)); + schema = srHandle.GetSchema(); } return new Realm(srHandle, this, schema); @@ -178,20 +182,5 @@ internal override Task CreateRealmAsync(RealmSchema schema, CancellationT return Task.FromResult(CreateRealm(schema)); } - - [MonoPInvokeCallback(typeof(ShouldCompactCallback))] - private static bool ShouldCompactOnLaunchCallback(IntPtr delegatePtr, ulong totalSize, ulong dataSize) - { - var handle = GCHandle.FromIntPtr(delegatePtr); - var compactDelegate = (ShouldCompactDelegate)handle.Target; - try - { - return compactDelegate(totalSize, dataSize); - } - finally - { - handle.Free(); - } - } } } diff --git a/Realm/Realm/Configurations/SyncConfiguration.cs b/Realm/Realm/Configurations/SyncConfiguration.cs index 257d168d37..c45cb3c6bc 100644 --- a/Realm/Realm/Configurations/SyncConfiguration.cs +++ b/Realm/Realm/Configurations/SyncConfiguration.cs @@ -146,13 +146,12 @@ internal override Realm CreateRealm(RealmSchema schema) var srHandle = SharedRealmHandle.OpenWithSync(configuration, syncConfiguration, schema, EncryptionKey); if (IsDynamic && !schema.Any()) { - srHandle.GetSchema(nativeSchema => schema = RealmSchema.CreateFromObjectStoreSchema(nativeSchema)); + schema = srHandle.GetSchema(); } return new Realm(srHandle, this, schema); } - [SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope", Justification = "The Realm instance will own its handle")] internal override async Task CreateRealmAsync(RealmSchema schema, CancellationToken cancellationToken) { var configuration = CreateNativeConfiguration(); @@ -195,7 +194,7 @@ internal override async Task CreateRealmAsync(RealmSchema schema, Cancell var sharedRealmHandle = new SharedRealmHandle(realmPtr); if (IsDynamic && !schema.Any()) { - sharedRealmHandle.GetSchema(nativeSchema => schema = RealmSchema.CreateFromObjectStoreSchema(nativeSchema)); + schema = sharedRealmHandle.GetSchema(); } return new Realm(sharedRealmHandle, this, schema); diff --git a/Realm/Realm/Handles/AppHandle.cs b/Realm/Realm/Handles/AppHandle.cs index 69908b0686..7e9e3da9b3 100644 --- a/Realm/Realm/Handles/AppHandle.cs +++ b/Realm/Realm/Handles/AppHandle.cs @@ -47,12 +47,15 @@ private static class NativeMethods [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public unsafe delegate void BsonCallback(IntPtr tcs_ptr, BsonPayload response, AppError error); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void ApiKeysCallback(IntPtr tcs_ptr, /* UserApiKey[] */ IntPtr api_keys, int api_keys_len, AppError error); + [DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_app_initialize", CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr initialize( [MarshalAs(UnmanagedType.LPWStr)] string platform, IntPtr platform_len, [MarshalAs(UnmanagedType.LPWStr)] string platform_version, IntPtr platform_version_len, [MarshalAs(UnmanagedType.LPWStr)] string sdk_version, IntPtr sdk_version_len, - UserCallback user_callback, VoidTaskCallback void_callback, BsonCallback bson_callback, LogMessageCallback log_message_callback); + UserCallback user_callback, VoidTaskCallback void_callback, BsonCallback bson_callback, LogMessageCallback log_message_callback, ApiKeysCallback api_keys_callback); [DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_app_create", CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr create_app(Native.AppConfiguration app_config, byte[] encryptionKey, out NativeException ex); @@ -143,17 +146,21 @@ public static extern void call_reset_password_function(AppHandle app, static unsafe AppHandle() { NativeCommon.Initialize(); - SessionHandle.InstallCallbacks(); + } + public static void Initialize() + { NativeMethods.LogMessageCallback logMessage = HandleLogMessage; NativeMethods.UserCallback userLogin = HandleUserCallback; NativeMethods.VoidTaskCallback taskCallback = HandleTaskCompletion; NativeMethods.BsonCallback bsonCallback = HandleBsonCallback; + NativeMethods.ApiKeysCallback apiKeysCallback = HandleApiKeysCallback; GCHandle.Alloc(logMessage); GCHandle.Alloc(userLogin); GCHandle.Alloc(taskCallback); GCHandle.Alloc(bsonCallback); + GCHandle.Alloc(apiKeysCallback); //// This is a hack due to a mixup of what OS uses as platform/SDK and what is displayed in the UI. //// The original code is below: @@ -176,16 +183,14 @@ static unsafe AppHandle() var platform = InteropConfig.Platform; var platformVersion = RuntimeInformation.OSDescription; - // TODO: temp - remove that once we're out of beta + // TODO: https://github.com/realm/realm-dotnet/issues/2218 remove that once we're out of beta or fix the issue. var sdkVersion = "10.2.0-beta.2"; // InteropConfig.SDKVersion.ToString(3); NativeMethods.initialize( platform, platform.IntPtrLength(), platformVersion, platformVersion.IntPtrLength(), sdkVersion, sdkVersion.IntPtrLength(), - userLogin, taskCallback, bsonCallback, logMessage); - - HttpClientTransport.Install(); + userLogin, taskCallback, bsonCallback, logMessage, apiKeysCallback); } internal AppHandle(IntPtr handle) : base(null, handle) @@ -256,18 +261,38 @@ public void SwitchUser(SyncUserHandle user) ex.ThrowIfNecessary(); } - public void LogIn(Native.Credentials credentials, TaskCompletionSource tcs) + public async Task LogInAsync(Native.Credentials credentials) { + var tcs = new TaskCompletionSource(); var tcsHandle = GCHandle.Alloc(tcs); - NativeMethods.login_user(this, credentials, GCHandle.ToIntPtr(tcsHandle), out var ex); - ex.ThrowIfNecessary(tcsHandle); + + try + { + NativeMethods.login_user(this, credentials, GCHandle.ToIntPtr(tcsHandle), out var ex); + ex.ThrowIfNecessary(); + + return await tcs.Task; + } + finally + { + tcsHandle.Free(); + } } - public void Remove(SyncUserHandle user, TaskCompletionSource tcs) + public async Task RemoveAsync(SyncUserHandle user) { + var tcs = new TaskCompletionSource(); var tcsHandle = GCHandle.Alloc(tcs); - NativeMethods.remove_user(this, user, GCHandle.ToIntPtr(tcsHandle), out var ex); - ex.ThrowIfNecessary(tcsHandle); + try + { + NativeMethods.remove_user(this, user, GCHandle.ToIntPtr(tcsHandle), out var ex); + ex.ThrowIfNecessary(); + await tcs.Task; + } + finally + { + tcsHandle.Free(); + } } public void ResetForTesting() @@ -304,7 +329,7 @@ private static unsafe void HandleLogMessage(IntPtr managedHandler, PrimitiveValu catch (Exception ex) { var errorMessage = $"An error occurred while trying to log a message: {ex}"; - Logger.Console.Log(LogLevel.Error, errorMessage); + Logger.LogDefault(LogLevel.Error, errorMessage); } } @@ -313,22 +338,15 @@ private static unsafe void HandleLogMessage(IntPtr managedHandler, PrimitiveValu private static unsafe void HandleUserCallback(IntPtr tcs_ptr, IntPtr user_ptr, AppError error) { var tcsHandle = GCHandle.FromIntPtr(tcs_ptr); - try + var tcs = (TaskCompletionSource)tcsHandle.Target; + if (error.is_null) { - var tcs = (TaskCompletionSource)tcsHandle.Target; - if (error.is_null) - { - var userHandle = new SyncUserHandle(user_ptr); - tcs.TrySetResult(userHandle); - } - else - { - tcs.TrySetException(new AppException(error)); - } + var userHandle = new SyncUserHandle(user_ptr); + tcs.TrySetResult(userHandle); } - finally + else { - tcsHandle.Free(); + tcs.TrySetException(new AppException(error)); } } @@ -336,21 +354,14 @@ private static unsafe void HandleUserCallback(IntPtr tcs_ptr, IntPtr user_ptr, A private static unsafe void HandleTaskCompletion(IntPtr tcs_ptr, AppError error) { var tcsHandle = GCHandle.FromIntPtr(tcs_ptr); - try + var tcs = (TaskCompletionSource)tcsHandle.Target; + if (error.is_null) { - var tcs = (TaskCompletionSource)tcsHandle.Target; - if (error.is_null) - { - tcs.TrySetResult(null); - } - else - { - tcs.TrySetException(new AppException(error)); - } + tcs.TrySetResult(null); } - finally + else { - tcsHandle.Free(); + tcs.TrySetException(new AppException(error)); } } @@ -358,21 +369,35 @@ private static unsafe void HandleTaskCompletion(IntPtr tcs_ptr, AppError error) private static unsafe void HandleBsonCallback(IntPtr tcs_ptr, BsonPayload response, AppError error) { var tcsHandle = GCHandle.FromIntPtr(tcs_ptr); - try + var tcs = (TaskCompletionSource)tcsHandle.Target; + if (error.is_null) { - var tcs = (TaskCompletionSource)tcsHandle.Target; - if (error.is_null) - { - tcs.TrySetResult(response); - } - else + tcs.TrySetResult(response); + } + else + { + tcs.TrySetException(new AppException(error)); + } + } + + [MonoPInvokeCallback(typeof(NativeMethods.ApiKeysCallback))] + private static unsafe void HandleApiKeysCallback(IntPtr tcs_ptr, IntPtr api_keys, int api_keys_len, AppError error) + { + var tcsHandle = GCHandle.FromIntPtr(tcs_ptr); + var tcs = (TaskCompletionSource)tcsHandle.Target; + if (error.is_null) + { + var result = new UserApiKey[api_keys_len]; + for (var i = 0; i < api_keys_len; i++) { - tcs.TrySetException(new AppException(error)); + result[i] = Marshal.PtrToStructure(IntPtr.Add(api_keys, i * UserApiKey.Size)); } + + tcs.TrySetResult(result); } - finally + else { - tcsHandle.Free(); + tcs.TrySetException(new AppException(error)); } } } diff --git a/Realm/Realm/Handles/ObjectHandle.cs b/Realm/Realm/Handles/ObjectHandle.cs index bc2853b59b..232ac31b4b 100644 --- a/Realm/Realm/Handles/ObjectHandle.cs +++ b/Realm/Realm/Handles/ObjectHandle.cs @@ -174,11 +174,17 @@ public RealmSchema GetSchema() { RealmSchema result = null; Action callback = (nativeSmallSchema) => result = RealmSchema.CreateFromObjectStoreSchema(nativeSmallSchema); - - // The callbackHandle will get freed in SharedRealmHandle.GetNativeSchema. var callbackHandle = GCHandle.Alloc(callback); - NativeMethods.get_schema(this, GCHandle.ToIntPtr(callbackHandle), out var nativeException); - nativeException.ThrowIfNecessary(); + + try + { + NativeMethods.get_schema(this, GCHandle.ToIntPtr(callbackHandle), out var nativeException); + nativeException.ThrowIfNecessary(); + } + finally + { + callbackHandle.Free(); + } return result; } diff --git a/Realm/Realm/Handles/SessionHandle.cs b/Realm/Realm/Handles/SessionHandle.cs index 1f414c280a..9798f3e62b 100644 --- a/Realm/Realm/Handles/SessionHandle.cs +++ b/Realm/Realm/Handles/SessionHandle.cs @@ -32,7 +32,7 @@ internal class SessionHandle : RealmHandle private static class NativeMethods { #pragma warning disable IDE1006 // Naming Styles -#pragma warning disable SA1121 // Use built-in type alias + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public unsafe delegate void SessionErrorCallback(IntPtr session_handle_ptr, ErrorCode error_code, byte* message_buf, IntPtr message_len, IntPtr user_info_pairs, int user_info_pairs_len, [MarshalAs(UnmanagedType.U1)] bool is_client_reset); @@ -87,12 +87,6 @@ public static extern ulong register_progress_notifier(SessionHandle session, public static extern void start(SessionHandle session, out NativeException ex); #pragma warning restore IDE1006 // Naming Styles -#pragma warning restore SA1121 // Use built-in type alias - } - - static SessionHandle() - { - NativeCommon.Initialize(); } [Preserve] @@ -156,11 +150,22 @@ public void UnregisterProgressNotifier(ulong token) ex.ThrowIfNecessary(); } - public void Wait(TaskCompletionSource tcs, ProgressDirection direction) + public async Task WaitAsync(ProgressDirection direction) { + var tcs = new TaskCompletionSource(); var tcsHandle = GCHandle.Alloc(tcs); - NativeMethods.wait(this, GCHandle.ToIntPtr(tcsHandle), direction, out var ex); - ex.ThrowIfNecessary(); + + try + { + NativeMethods.wait(this, GCHandle.ToIntPtr(tcsHandle), direction, out var ex); + ex.ThrowIfNecessary(); + + await tcs.Task; + } + finally + { + tcsHandle.Free(); + } } public IntPtr GetRawPointer() @@ -246,22 +251,15 @@ private static unsafe void HandleSessionWaitCallback(IntPtr taskCompletionSource var handle = GCHandle.FromIntPtr(taskCompletionSource); var tcs = (TaskCompletionSource)handle.Target; - try + if (error_code == 0) { - if (error_code == 0) - { - tcs.TrySetResult(null); - } - else - { - var inner = new SessionException(Encoding.UTF8.GetString(messageBuffer, (int)messageLength), (ErrorCode)error_code); - const string OuterMessage = "A system error occurred while waiting for completion. See InnerException for more details"; - tcs.TrySetException(new RealmException(OuterMessage, inner)); - } + tcs.TrySetResult(null); } - finally + else { - handle.Free(); + var inner = new SessionException(Encoding.UTF8.GetString(messageBuffer, (int)messageLength), (ErrorCode)error_code); + const string OuterMessage = "A system error occurred while waiting for completion. See InnerException for more details"; + tcs.TrySetException(new RealmException(OuterMessage, inner)); } } } diff --git a/Realm/Realm/Handles/SharedRealmHandle.cs b/Realm/Realm/Handles/SharedRealmHandle.cs index ad98c416ee..5d3a5b5054 100644 --- a/Realm/Realm/Handles/SharedRealmHandle.cs +++ b/Realm/Realm/Handles/SharedRealmHandle.cs @@ -27,6 +27,7 @@ using Realms.Logging; using Realms.Native; using Realms.Schema; +using static Realms.RealmConfiguration; namespace Realms { @@ -51,6 +52,14 @@ private static class NativeMethods [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void LogMessageCallback(PrimitiveValue message, LogLevel level); + [return: MarshalAs(UnmanagedType.U1)] + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate bool MigrationCallback(IntPtr oldRealm, IntPtr newRealm, Native.Schema oldSchema, ulong schemaVersion, IntPtr managedMigrationHandle); + + [return: MarshalAs(UnmanagedType.U1)] + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate bool ShouldCompactCallback(IntPtr config, ulong totalSize, ulong dataSize); + [DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_realm_open", CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr open(Configuration configuration, [MarshalAs(UnmanagedType.LPArray), In] SchemaObject[] objects, int objects_length, @@ -141,14 +150,15 @@ public static extern IntPtr create_object_unique(SharedRealmHandle sharedRealm, [DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_realm_install_callbacks", CallingConvention = CallingConvention.Cdecl)] public static extern void install_callbacks( - NotifyRealmCallback notifyRealmCallback, - GetNativeSchemaCallback nativeSchemaCallback, - OpenRealmCallback openCallback, - OnBindingContextDestructedCallback contextDestructedCallback, - LogMessageCallback logMessageCallback, - NotifiableObjectHandleBase.NotificationCallback notifyObject, - DictionaryHandle.KeyNotificationCallback notifyDictionary, - MigrationCallback onMigration); + NotifyRealmCallback notify_realm_callback, + GetNativeSchemaCallback native_schema_callback, + OpenRealmCallback open_callback, + OnBindingContextDestructedCallback context_destructed_callback, + LogMessageCallback log_mssage_callback, + NotifiableObjectHandleBase.NotificationCallback notify_object, + DictionaryHandle.KeyNotificationCallback notify_dictionary, + MigrationCallback migration_callback, + ShouldCompactCallback should_compact_callback); [DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_realm_has_changed", CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.U1)] @@ -173,9 +183,10 @@ public static extern void install_callbacks( static unsafe SharedRealmHandle() { NativeCommon.Initialize(); + } - SynchronizationContextScheduler.Install(); - + public static void Initialize() + { NativeMethods.NotifyRealmCallback notifyRealm = NotifyRealmChanged; NativeMethods.GetNativeSchemaCallback getNativeSchema = GetNativeSchema; NativeMethods.OpenRealmCallback openRealm = HandleOpenRealmCallback; @@ -183,7 +194,8 @@ static unsafe SharedRealmHandle() NativeMethods.LogMessageCallback logMessage = LogMessage; NotifiableObjectHandleBase.NotificationCallback notifyObject = NotifiableObjectHandleBase.NotifyObjectChanged; DictionaryHandle.KeyNotificationCallback notifyDictionary = DictionaryHandle.NotifyDictionaryChanged; - MigrationCallback onMigration = OnMigration; + NativeMethods.MigrationCallback onMigration = OnMigration; + NativeMethods.ShouldCompactCallback shouldCompact = ShouldCompactOnLaunchCallback; GCHandle.Alloc(notifyRealm); GCHandle.Alloc(getNativeSchema); @@ -193,8 +205,9 @@ static unsafe SharedRealmHandle() GCHandle.Alloc(notifyObject); GCHandle.Alloc(notifyDictionary); GCHandle.Alloc(onMigration); + GCHandle.Alloc(shouldCompact); - NativeMethods.install_callbacks(notifyRealm, getNativeSchema, openRealm, onBindingContextDestructed, logMessage, notifyObject, notifyDictionary, onMigration); + NativeMethods.install_callbacks(notifyRealm, getNativeSchema, openRealm, onBindingContextDestructed, logMessage, notifyObject, notifyDictionary, onMigration, shouldCompact); } [Preserve] @@ -234,8 +247,7 @@ public static AsyncOpenTaskHandle OpenWithSyncAsync(Configuration configuration, var asyncTaskPtr = NativeMethods.open_with_sync_async(configuration, syncConfiguration, marshaledSchema.Objects, marshaledSchema.Objects.Length, marshaledSchema.Properties, encryptionKey, GCHandle.ToIntPtr(tcsHandle), out var nativeException); nativeException.ThrowIfNecessary(); - var asyncTaskHandle = new AsyncOpenTaskHandle(asyncTaskPtr); - return asyncTaskHandle; + return new AsyncOpenTaskHandle(asyncTaskPtr); } public static IntPtr ResolveFromReference(ThreadSafeReferenceHandle referenceHandle) @@ -361,11 +373,22 @@ public void WriteCopy(string path, byte[] encryptionKey) nativeException.ThrowIfNecessary(); } - public void GetSchema(Action callback) + public RealmSchema GetSchema() { + RealmSchema result = null; + Action callback = schema => result = RealmSchema.CreateFromObjectStoreSchema(schema); var handle = GCHandle.Alloc(callback); - NativeMethods.get_schema(this, GCHandle.ToIntPtr(handle), out var nativeException); - nativeException.ThrowIfNecessary(); + try + { + NativeMethods.get_schema(this, GCHandle.ToIntPtr(handle), out var nativeException); + nativeException.ThrowIfNecessary(); + } + finally + { + handle.Free(); + } + + return result; } public ObjectHandle CreateObject(TableKey tableKey) @@ -440,7 +463,6 @@ private static void GetNativeSchema(Native.Schema schema, IntPtr managedCallback var handle = GCHandle.FromIntPtr(managedCallbackPtr); var callback = (Action)handle.Target; callback(schema); - handle.Free(); } [MonoPInvokeCallback(typeof(NativeMethods.NotifyRealmCallback))] @@ -485,7 +507,7 @@ private static void LogMessage(PrimitiveValue message, LogLevel level) Logger.LogDefault(level, message.AsString()); } - [MonoPInvokeCallback(typeof(MigrationCallback))] + [MonoPInvokeCallback(typeof(NativeMethods.MigrationCallback))] private static bool OnMigration(IntPtr oldRealmPtr, IntPtr newRealmPtr, Native.Schema oldSchema, ulong schemaVersion, IntPtr managedMigrationHandle) { var migrationHandle = GCHandle.FromIntPtr(managedMigrationHandle); @@ -501,15 +523,21 @@ private static bool OnMigration(IntPtr oldRealmPtr, IntPtr newRealmPtr, Native.S var oldRealm = new Realm(oldRealmHandle, oldConfiguration, RealmSchema.CreateFromObjectStoreSchema(oldSchema)); var newRealmHandle = new UnownedRealmHandle(newRealmPtr); - var newConfiguration = migration.Configuration.Clone(); var newRealm = new Realm(newRealmHandle, migration.Configuration, migration.Schema); var result = migration.Execute(oldRealm, newRealm); - migrationHandle.Free(); return result; } + [MonoPInvokeCallback(typeof(NativeMethods.ShouldCompactCallback))] + private static bool ShouldCompactOnLaunchCallback(IntPtr delegatePtr, ulong totalSize, ulong dataSize) + { + var handle = GCHandle.FromIntPtr(delegatePtr); + var compactDelegate = (ShouldCompactDelegate)handle.Target; + return compactDelegate(totalSize, dataSize); + } + public class SchemaMarshaler { public readonly SchemaObject[] Objects; diff --git a/Realm/Realm/Handles/SyncUserHandle.cs b/Realm/Realm/Handles/SyncUserHandle.cs index 4f86b4adf9..8bfdba7ede 100644 --- a/Realm/Realm/Handles/SyncUserHandle.cs +++ b/Realm/Realm/Handles/SyncUserHandle.cs @@ -17,12 +17,12 @@ //////////////////////////////////////////////////////////////////////////// using System; +using System.Diagnostics; +using System.Linq; using System.Runtime.InteropServices; using System.Threading.Tasks; using MongoDB.Bson; using Realms.Native; -using Realms.Sync.Exceptions; -using Realms.Sync.Native; namespace Realms.Sync { @@ -31,10 +31,6 @@ internal class SyncUserHandle : RealmHandle private static class NativeMethods { #pragma warning disable IDE1006 // Naming Styles -#pragma warning disable SA1121 // Use built-in type alias - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void ApiKeysCallback(IntPtr tcs_ptr, /* UserApiKey[] */ IntPtr api_keys, int api_keys_len, AppError error); [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncuser_get_id", CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr get_user_id(SyncUserHandle user, IntPtr buffer, IntPtr buffer_length, out NativeException ex); @@ -75,9 +71,6 @@ public static extern IntPtr get_custom_data(SyncUserHandle user, IntPtr buffer, [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncuser_destroy", CallingConvention = CallingConvention.Cdecl)] public static extern void destroy(IntPtr syncuserHandle); - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_sync_user_initialize", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr initialize(ApiKeysCallback api_keys_callback); - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncuser_call_function", CallingConvention = CallingConvention.Cdecl)] public static extern void call_function(SyncUserHandle handle, AppHandle app, [MarshalAs(UnmanagedType.LPWStr)] string function_name, IntPtr function_name_len, @@ -130,18 +123,6 @@ public static extern void create_api_key(SyncUserHandle handle, AppHandle app, #endregion #pragma warning restore IDE1006 // Naming Styles -#pragma warning restore SA1121 // Use built-in type alias - } - - static unsafe SyncUserHandle() - { - NativeCommon.Initialize(); - - NativeMethods.ApiKeysCallback apiKeysCallback = HandleApiKeysCallback; - - GCHandle.Alloc(apiKeysCallback); - - NativeMethods.initialize(apiKeysCallback); } [Preserve] @@ -220,11 +201,21 @@ public void LogOut() ex.ThrowIfNecessary(); } - public void RefreshCustomData(TaskCompletionSource tcs) + public async Task RefreshCustomDataAsync() { + var tcs = new TaskCompletionSource(); var tcsHandle = GCHandle.Alloc(tcs); - NativeMethods.refresh_custom_data(this, GCHandle.ToIntPtr(tcsHandle), out var ex); - ex.ThrowIfNecessary(tcsHandle); + try + { + NativeMethods.refresh_custom_data(this, GCHandle.ToIntPtr(tcsHandle), out var ex); + ex.ThrowIfNecessary(); + + await tcs.Task; + } + finally + { + tcsHandle.Free(); + } } public string GetProfileData(UserProfileField field) @@ -243,19 +234,39 @@ public string GetCustomData() }); } - public void CallFunction(AppHandle app, string name, string args, TaskCompletionSource tcs) + public async Task CallFunctionAsync(AppHandle app, string name, string args) { + var tcs = new TaskCompletionSource(); var tcsHandle = GCHandle.Alloc(tcs); - NativeMethods.call_function(this, app, name, (IntPtr)name.Length, args, (IntPtr)args.Length, GCHandle.ToIntPtr(tcsHandle), out var ex); - ex.ThrowIfNecessary(); + try + { + NativeMethods.call_function(this, app, name, (IntPtr)name.Length, args, (IntPtr)args.Length, GCHandle.ToIntPtr(tcsHandle), out var ex); + ex.ThrowIfNecessary(); + + return await tcs.Task; + } + finally + { + tcsHandle.Free(); + } } - public void LinkCredentials(AppHandle app, Native.Credentials credentials, TaskCompletionSource tcs) + public async Task LinkCredentialsAsync(AppHandle app, Native.Credentials credentials) { + var tcs = new TaskCompletionSource(); var tcsHandle = GCHandle.Alloc(tcs); - NativeMethods.link_credentials(this, app, credentials, GCHandle.ToIntPtr(tcsHandle), out var ex); - ex.ThrowIfNecessary(tcsHandle); + try + { + NativeMethods.link_credentials(this, app, credentials, GCHandle.ToIntPtr(tcsHandle), out var ex); + ex.ThrowIfNecessary(); + + return await tcs.Task; + } + finally + { + tcsHandle.Free(); + } } public string GetIdentities() @@ -289,85 +300,130 @@ public void DeregisterPushToken(AppHandle app, string service, TaskCompletionSou #region Api Keys - public void CreateApiKey(AppHandle app, string name, TaskCompletionSource tcs) + public async Task CreateApiKeyAsync(AppHandle app, string name) { + var tcs = new TaskCompletionSource(); var tcsHandle = GCHandle.Alloc(tcs); - NativeMethods.create_api_key(this, app, name, (IntPtr)name.Length, GCHandle.ToIntPtr(tcsHandle), out var ex); - ex.ThrowIfNecessary(tcsHandle); - } - public unsafe void FetchApiKey(AppHandle app, ObjectId id, TaskCompletionSource tcs) - { - var tcsHandle = GCHandle.Alloc(tcs); - var primitiveId = PrimitiveValue.ObjectId(id); - NativeMethods.fetch_api_key(this, app, primitiveId, GCHandle.ToIntPtr(tcsHandle), out var ex); - ex.ThrowIfNecessary(tcsHandle); + try + { + NativeMethods.create_api_key(this, app, name, (IntPtr)name.Length, GCHandle.ToIntPtr(tcsHandle), out var ex); + ex.ThrowIfNecessary(); + + var result = await tcs.Task; + + Debug.Assert(result.Length == 1, "The result of Create should be exactly 1 ApiKey."); + + return result.Single(); + } + finally + { + tcsHandle.Free(); + } } - public void FetchAllApiKeys(AppHandle app, TaskCompletionSource tcs) + public async Task FetchApiKeyAsync(AppHandle app, ObjectId id) { + var tcs = new TaskCompletionSource(); var tcsHandle = GCHandle.Alloc(tcs); - NativeMethods.fetch_api_keys(this, app, GCHandle.ToIntPtr(tcsHandle), out var ex); - ex.ThrowIfNecessary(tcsHandle); + + try + { + var primitiveId = PrimitiveValue.ObjectId(id); + NativeMethods.fetch_api_key(this, app, primitiveId, GCHandle.ToIntPtr(tcsHandle), out var ex); + ex.ThrowIfNecessary(); + + var result = await tcs.Task; + + Debug.Assert(result == null || result.Length <= 1, "The result of the fetch operation should be either null, or an array of 0 or 1 elements."); + + return result == null || result.Length == 0 ? (UserApiKey?)null : result.Single(); + } + finally + { + tcsHandle.Free(); + } } - public unsafe void DeleteApiKey(AppHandle app, ObjectId id, TaskCompletionSource tcs) + public async Task FetchAllApiKeysAsync(AppHandle app) { + var tcs = new TaskCompletionSource(); var tcsHandle = GCHandle.Alloc(tcs); - var primitiveId = PrimitiveValue.ObjectId(id); - NativeMethods.delete_api_key(this, app, primitiveId, GCHandle.ToIntPtr(tcsHandle), out var ex); - ex.ThrowIfNecessary(tcsHandle); + + try + { + NativeMethods.fetch_api_keys(this, app, GCHandle.ToIntPtr(tcsHandle), out var ex); + ex.ThrowIfNecessary(); + + return await tcs.Task; + } + finally + { + tcsHandle.Free(); + } } - public unsafe void DisableApiKey(AppHandle app, ObjectId id, TaskCompletionSource tcs) + public async Task DeleteApiKeyAsync(AppHandle app, ObjectId id) { + var tcs = new TaskCompletionSource(); var tcsHandle = GCHandle.Alloc(tcs); - var primitiveId = PrimitiveValue.ObjectId(id); - NativeMethods.disable_api_key(this, app, primitiveId, GCHandle.ToIntPtr(tcsHandle), out var ex); - ex.ThrowIfNecessary(tcsHandle); + try + { + var primitiveId = PrimitiveValue.ObjectId(id); + NativeMethods.delete_api_key(this, app, primitiveId, GCHandle.ToIntPtr(tcsHandle), out var ex); + ex.ThrowIfNecessary(); + + await tcs.Task; + } + finally + { + tcsHandle.Free(); + } } - public unsafe void EnableApiKey(AppHandle app, ObjectId id, TaskCompletionSource tcs) + public async Task DisableApiKeyAsync(AppHandle app, ObjectId id) { + var tcs = new TaskCompletionSource(); var tcsHandle = GCHandle.Alloc(tcs); - var primitiveId = PrimitiveValue.ObjectId(id); - NativeMethods.enable_api_key(this, app, primitiveId, GCHandle.ToIntPtr(tcsHandle), out var ex); - ex.ThrowIfNecessary(tcsHandle); - } - #endregion + try + { + var primitiveId = PrimitiveValue.ObjectId(id); + NativeMethods.disable_api_key(this, app, primitiveId, GCHandle.ToIntPtr(tcsHandle), out var ex); + ex.ThrowIfNecessary(); - protected override void Unbind() - { - NativeMethods.destroy(handle); + await tcs.Task; + } + finally + { + tcsHandle.Free(); + } } - [MonoPInvokeCallback(typeof(NativeMethods.ApiKeysCallback))] - private static unsafe void HandleApiKeysCallback(IntPtr tcs_ptr, IntPtr api_keys, int api_keys_len, AppError error) + public async Task EnableApiKeyAsync(AppHandle app, ObjectId id) { - var tcsHandle = GCHandle.FromIntPtr(tcs_ptr); + var tcs = new TaskCompletionSource(); + var tcsHandle = GCHandle.Alloc(tcs); + try { - var tcs = (TaskCompletionSource)tcsHandle.Target; - if (error.is_null) - { - var result = new UserApiKey[api_keys_len]; - for (var i = 0; i < api_keys_len; i++) - { - result[i] = Marshal.PtrToStructure(IntPtr.Add(api_keys, i * UserApiKey.Size)); - } - - tcs.TrySetResult(result); - } - else - { - tcs.TrySetException(new AppException(error)); - } + var primitiveId = PrimitiveValue.ObjectId(id); + NativeMethods.enable_api_key(this, app, primitiveId, GCHandle.ToIntPtr(tcsHandle), out var ex); + ex.ThrowIfNecessary(); + + await tcs.Task; } finally { tcsHandle.Free(); } } + + #endregion + + protected override void Unbind() + { + NativeMethods.destroy(handle); + } } } diff --git a/Realm/Realm/Migration.cs b/Realm/Realm/Migration.cs index 1709e0ae60..1950e681b3 100644 --- a/Realm/Realm/Migration.cs +++ b/Realm/Realm/Migration.cs @@ -18,7 +18,6 @@ using System; using System.Runtime.InteropServices; -using Realms.Native; using Realms.Schema; namespace Realms @@ -34,6 +33,10 @@ namespace Realms /// See more in the migrations section in the documentation. public class Migration { + private GCHandle? _handle; + + internal GCHandle MigrationHandle => _handle ?? throw new ObjectDisposedException(nameof(Migration)); + internal RealmConfiguration Configuration { get; } internal RealmSchema Schema { get; } @@ -56,12 +59,7 @@ internal Migration(RealmConfiguration configuration, RealmSchema schema) { Configuration = configuration; Schema = schema; - } - - internal void PopulateConfiguration(ref Configuration configuration) - { - var migrationHandle = GCHandle.Alloc(this); - configuration.managed_migration_handle = GCHandle.ToIntPtr(migrationHandle); + _handle = GCHandle.Alloc(this); } internal bool Execute(Realm oldRealm, Realm newRealm) @@ -89,5 +87,11 @@ internal bool Execute(Realm oldRealm, Realm newRealm) return true; } + + internal void ReleaseHandle() + { + _handle?.Free(); + _handle = null; + } } } diff --git a/Realm/Realm/Native/Configuration.cs b/Realm/Realm/Native/Configuration.cs index 6909193efa..fb3a28700c 100644 --- a/Realm/Realm/Native/Configuration.cs +++ b/Realm/Realm/Native/Configuration.cs @@ -21,14 +21,6 @@ namespace Realms.Native { - [return: MarshalAs(UnmanagedType.U1)] - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate bool MigrationCallback(IntPtr oldRealm, IntPtr newRealm, Schema oldSchema, ulong schemaVersion, IntPtr managedMigrationHandle); - - [return: MarshalAs(UnmanagedType.U1)] - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate bool ShouldCompactCallback(IntPtr config, ulong totalSize, ulong dataSize); - [StructLayout(LayoutKind.Sequential)] internal struct Configuration { @@ -57,7 +49,6 @@ internal string Path internal IntPtr managed_migration_handle; - internal ShouldCompactCallback should_compact_callback; internal IntPtr managed_should_compact_delegate; [MarshalAs(UnmanagedType.U1)] diff --git a/Realm/Realm/Native/NativeCommon.cs b/Realm/Realm/Native/NativeCommon.cs index cc1af309ac..96219868b2 100644 --- a/Realm/Realm/Native/NativeCommon.cs +++ b/Realm/Realm/Native/NativeCommon.cs @@ -22,6 +22,8 @@ using System.Reflection; using System.Runtime.InteropServices; using System.Threading; +using Realms.Native; +using Realms.Sync; namespace Realms { @@ -49,6 +51,12 @@ internal static unsafe void Initialize() // This is the path in the Unity package - it is what the Editor uses. AddWindowsWrappersToPath("Windows", isUnityTarget: true); } + + SynchronizationContextScheduler.Install(); + SharedRealmHandle.Initialize(); + SessionHandle.InstallCallbacks(); + HttpClientTransport.Install(); + AppHandle.Initialize(); } } diff --git a/Realm/Realm/Native/NativeException.cs b/Realm/Realm/Native/NativeException.cs index b8c497d65b..183b5875e6 100644 --- a/Realm/Realm/Native/NativeException.cs +++ b/Realm/Realm/Native/NativeException.cs @@ -54,16 +54,5 @@ internal void ThrowIfNecessary(Func overrider = throw Convert(overrider); } - - internal void ThrowIfNecessary(GCHandle handleToFree) - { - if (type == RealmExceptionCodes.NoError) - { - return; - } - - handleToFree.Free(); - throw Convert(); - } } -} \ No newline at end of file +} diff --git a/Realm/Realm/Sync/App.cs b/Realm/Realm/Sync/App.cs index 3a22824826..c929ea65a2 100644 --- a/Realm/Realm/Sync/App.cs +++ b/Realm/Realm/Sync/App.cs @@ -17,7 +17,6 @@ //////////////////////////////////////////////////////////////////////////// using System; -using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Runtime.InteropServices; using System.Threading.Tasks; @@ -111,7 +110,6 @@ public class App /// Gets the currently user. If none exists, null is returned. /// /// Valid user or null to indicate nobody logged in. - [SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope", Justification = "The User instance will own its handle.")] public User CurrentUser => Handle.TryGetCurrentUser(out var userHandle) ? new User(userHandle, this) : null; /// @@ -206,9 +204,7 @@ public async Task LogInAsync(Credentials credentials) { Argument.NotNull(credentials, nameof(credentials)); - var tcs = new TaskCompletionSource(); - Handle.LogIn(credentials.ToNative(), tcs); - var handle = await tcs.Task; + var handle = await Handle.LogInAsync(credentials.ToNative()); return new User(handle, this); } @@ -235,13 +231,11 @@ public void SwitchUser(User user) /// An awaitable that represents the asynchronous RemoveUser operation. Successful completion indicates that the user has been logged out, /// their local data - removed, and the user's - revoked on the server. /// - public async Task RemoveUserAsync(User user) + public Task RemoveUserAsync(User user) { Argument.NotNull(user, nameof(user)); - var tcs = new TaskCompletionSource(); - Handle.Remove(user.Handle, tcs); - await tcs.Task; + return Handle.RemoveAsync(user.Handle); } /// diff --git a/Realm/Realm/Sync/Session.cs b/Realm/Realm/Sync/Session.cs index 2fc757bad9..c2e8d268a1 100644 --- a/Realm/Realm/Sync/Session.cs +++ b/Realm/Realm/Sync/Session.cs @@ -106,24 +106,14 @@ public IObservable GetProgressObservable(ProgressDirection directi /// /// An awaitable that will be completed when all pending uploads for this are completed. /// Thrown when a faulted session is waited on. - public Task WaitForUploadAsync() - { - var tcs = new TaskCompletionSource(); - Handle.Wait(tcs, ProgressDirection.Upload); - return tcs.Task; - } + public Task WaitForUploadAsync() => Handle.WaitAsync(ProgressDirection.Upload); /// /// Waits for the to finish all pending downloads. /// /// An awaitable that will be completed when all pending downloads for this are completed. /// Thrown when a faulted session is waited on. - public Task WaitForDownloadAsync() - { - var tcs = new TaskCompletionSource(); - Handle.Wait(tcs, ProgressDirection.Download); - return tcs.Task; - } + public Task WaitForDownloadAsync() => Handle.WaitAsync(ProgressDirection.Download); /// /// Stops any synchronization with the server until the Realm is re-opened again diff --git a/Realm/Realm/Sync/User.cs b/Realm/Realm/Sync/User.cs index 31d0afb8bc..c79d39918a 100644 --- a/Realm/Realm/Sync/User.cs +++ b/Realm/Realm/Sync/User.cs @@ -18,7 +18,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Net; @@ -26,7 +25,6 @@ using MongoDB.Bson; using MongoDB.Bson.Serialization; using Realms.Helpers; -using Realms.Native; using Realms.Sync.Exceptions; namespace Realms.Sync @@ -194,9 +192,7 @@ internal User(SyncUserHandle handle, App app = null) /// public async Task RefreshCustomDataAsync() { - var tcs = new TaskCompletionSource(); - Handle.RefreshCustomData(tcs); - await tcs.Task; + await Handle.RefreshCustomDataAsync(); return GetCustomData(); } @@ -274,10 +270,7 @@ public async Task LinkCredentialsAsync(Credentials credentials) { Argument.NotNull(credentials, nameof(credentials)); - var tcs = new TaskCompletionSource(); - Handle.LinkCredentials(App.Handle, credentials.ToNative(), tcs); - var handle = await tcs.Task; - + var handle = await Handle.LinkCredentialsAsync(App.Handle, credentials.ToNative()); return new User(handle, App); } @@ -334,13 +327,8 @@ public async Task CreateAsync(string name) { Argument.NotNullOrEmpty(name, nameof(name)); - var tcs = new TaskCompletionSource(); - _user.Handle.CreateApiKey(_user.App.Handle, name, tcs); - var apiKeys = await tcs.Task; - - Debug.Assert(apiKeys.Length == 1, "The result of Create should be exactly 1 ApiKey."); - - return new ApiKey(apiKeys.Single()); + var apiKey = await _user.Handle.CreateApiKeyAsync(_user.App.Handle, name); + return new ApiKey(apiKey); } /// @@ -352,13 +340,8 @@ public async Task CreateAsync(string name) /// public async Task FetchAsync(ObjectId id) { - var tcs = new TaskCompletionSource(); - _user.Handle.FetchApiKey(_user.App.Handle, id, tcs); - var apiKeys = await Handle404(tcs); - - Debug.Assert(apiKeys == null || apiKeys.Length <= 1, "The result of the fetch operation should be either null, or an array of 0 or 1 elements."); - - return apiKeys == null || apiKeys.Length == 0 ? null : new ApiKey(apiKeys.Single()); + var apiKey = await Handle404(_user.Handle.FetchApiKeyAsync(_user.App.Handle, id)); + return apiKey.HasValue ? new ApiKey(apiKey.Value) : null; } /// @@ -370,10 +353,7 @@ public async Task FetchAsync(ObjectId id) /// public async Task> FetchAllAsync() { - var tcs = new TaskCompletionSource(); - _user.Handle.FetchAllApiKeys(_user.App.Handle, tcs); - var apiKeys = await tcs.Task; - + var apiKeys = await _user.Handle.FetchAllApiKeysAsync(_user.App.Handle); return apiKeys.Select(k => new ApiKey(k)).ToArray(); } @@ -384,10 +364,7 @@ public async Task> FetchAllAsync() /// An awaitable representing the asynchronous delete operation. public Task DeleteAsync(ObjectId id) { - var tcs = new TaskCompletionSource(); - _user.Handle.DeleteApiKey(_user.App.Handle, id, tcs); - - return Handle404(tcs); + return Handle404(_user.Handle.DeleteApiKeyAsync(_user.App.Handle, id)); } /// @@ -398,10 +375,7 @@ public Task DeleteAsync(ObjectId id) /// public Task DisableAsync(ObjectId id) { - var tcs = new TaskCompletionSource(); - _user.Handle.DisableApiKey(_user.App.Handle, id, tcs); - - return Handle404(tcs, id, shouldThrow: true); + return Handle404(_user.Handle.DisableApiKeyAsync(_user.App.Handle, id), id); } /// @@ -412,26 +386,33 @@ public Task DisableAsync(ObjectId id) /// public Task EnableAsync(ObjectId id) { - var tcs = new TaskCompletionSource(); - _user.Handle.EnableApiKey(_user.App.Handle, id, tcs); + return Handle404(_user.Handle.EnableApiKeyAsync(_user.App.Handle, id), id); + } - return Handle404(tcs, id, shouldThrow: true); + private static async Task Handle404(Task task) + { + try + { + return await task; + } + catch (AppException ex) when (ex.StatusCode == HttpStatusCode.NotFound) + { + return default; + } } - private static async Task Handle404(TaskCompletionSource tcs, ObjectId? id = null, bool shouldThrow = false) + private static async Task Handle404(Task task, ObjectId? id = null) { try { - return await tcs.Task; + await task; } catch (AppException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { - if (shouldThrow) + if (id.HasValue) { throw new AppException($"Failed to execute operation because ApiKey with Id: {id} doesn't exist.", ex.HelpLink, 404); } - - return default; } } } @@ -485,11 +466,7 @@ public async Task CallAsync(string name, params object[] args) { Argument.NotNullOrEmpty(name, nameof(name)); - var tcs = new TaskCompletionSource(); - - _user.Handle.CallFunction(_user.App.Handle, name, args.ToNativeJson(), tcs); - - var response = await tcs.Task; + var response = await _user.Handle.CallFunctionAsync(_user.App.Handle, name, args.ToNativeJson()); return response.GetValue(); } @@ -542,4 +519,4 @@ public Task DeregisterDeviceAsync() } } } -} \ No newline at end of file +} diff --git a/Tools/SetupUnityPackage/SetupUnityPackage/Program.cs b/Tools/SetupUnityPackage/SetupUnityPackage/Program.cs index c13d13e754..f1d36fe2bd 100644 --- a/Tools/SetupUnityPackage/SetupUnityPackage/Program.cs +++ b/Tools/SetupUnityPackage/SetupUnityPackage/Program.cs @@ -42,8 +42,8 @@ public static class Program private const string RealmPackageId = "Realm"; private const string UnityUtilsPackageId = "Realm.UnityUtils"; private const string UnityWeaverPackageId = "Realm.UnityWeaver"; - private const string RealmPackagaName = "realm.unity"; - private const string RealmBundlePackageName = "realm.unity.bundle"; + private const string RealmPackagaName = "io.realm.unity"; + private const string RealmBundlePackageName = "io.realm.unity-bundled"; private static readonly string _buildFolder = Path.GetDirectoryName(typeof(Program).Assembly.Location); @@ -52,7 +52,6 @@ public static class Program [RealmPackageId] = new Dictionary { { "lib/netstandard2.0/Realm.dll", "Realm.dll" }, - { "native/ios/Realm.dll.config", "Realm.dll.config" }, { "native/ios/universal/realm-wrappers.framework/realm-wrappers", "iOS/realm-wrappers.framework/realm-wrappers" }, { "native/ios/universal/realm-wrappers.framework/Info.plist", "iOS/realm-wrappers.framework/Info.plist" }, { "runtimes/osx-x64/native/librealm-wrappers.dylib", "macOS/librealm-wrappers.dylib" }, diff --git a/wrappers/realm-core b/wrappers/realm-core index 19f3314cf8..62c7b6bf50 160000 --- a/wrappers/realm-core +++ b/wrappers/realm-core @@ -1 +1 @@ -Subproject commit 19f3314cf8496151163b21c4575834728efc8c02 +Subproject commit 62c7b6bf502f77cf73140d5b7b6414dd2a1dbd20 diff --git a/wrappers/src/app_cs.cpp b/wrappers/src/app_cs.cpp index 3393a36210..7c117d88f3 100644 --- a/wrappers/src/app_cs.cpp +++ b/wrappers/src/app_cs.cpp @@ -44,6 +44,7 @@ using LogMessageCallbackT = void(void* managed_handler, realm_value_t message, u using UserCallbackT = void(void* tcs_ptr, SharedSyncUser* user, MarshaledAppError err); using VoidCallbackT = void(void* tcs_ptr, MarshaledAppError err); using BsonCallbackT = void(void* tcs_ptr, BsonPayload response, MarshaledAppError err); +using ApiKeysCallbackT = void(void* tcs_ptr, UserApiKey* api_keys, int api_keys_len, MarshaledAppError err); namespace realm { namespace binding { @@ -55,6 +56,7 @@ namespace realm { std::function s_user_callback; std::function s_void_callback; std::function s_bson_callback; + std::function s_api_keys_callback; struct AppConfiguration { @@ -124,7 +126,8 @@ extern "C" { UserCallbackT* user_callback, VoidCallbackT* void_callback, BsonCallbackT* bson_callback, - LogMessageCallbackT* log_message_callback) + LogMessageCallbackT* log_message_callback, + ApiKeysCallbackT* api_keys_callback) { s_platform = Utf16StringAccessor(platform, platform_len); s_platform_version = Utf16StringAccessor(platform_version, platform_version_len); @@ -134,6 +137,7 @@ extern "C" { s_void_callback = wrap_managed_callback(void_callback); s_bson_callback = wrap_managed_callback(bson_callback); s_log_message_callback = wrap_managed_callback(log_message_callback); + s_api_keys_callback = wrap_managed_callback(api_keys_callback); realm::binding::s_can_call_managed = true; } @@ -200,7 +204,7 @@ extern "C" { return nullptr; } - return new SharedSyncUser(std::move(ptr)); + return new SharedSyncUser(ptr); }); } diff --git a/wrappers/src/app_cs.hpp b/wrappers/src/app_cs.hpp index 84dc6d7712..0ce42debe8 100644 --- a/wrappers/src/app_cs.hpp +++ b/wrappers/src/app_cs.hpp @@ -124,13 +124,28 @@ namespace binding { size_t serialized_len; }; + struct UserApiKey { + const char* id; + size_t id_len; + + const char* key; + size_t key_len; + + const char* name; + size_t name_len; + + bool disabled; + }; + using UserCallbackT = void(void* tcs_ptr, SharedSyncUser* user, MarshaledAppError err); using VoidCallbackT = void(void* tcs_ptr, MarshaledAppError err); using BsonCallbackT = void(void* tcs_ptr, BsonPayload response, MarshaledAppError err); + using ApiKeysCallbackT = void(void* tcs_ptr, UserApiKey* api_keys, int api_keys_len, MarshaledAppError err); extern std::function s_void_callback; extern std::function s_user_callback; extern std::function s_bson_callback; + extern std::function s_api_keys_callback; inline std::function user, util::Optional)> get_user_callback_handler(void* tcs_ptr) { return [tcs_ptr](std::shared_ptr user, util::Optional err) { @@ -180,6 +195,53 @@ namespace binding { }; } + inline void invoke_api_key_callback(void* tcs_ptr, std::vector keys, util::Optional err) { + if (err) { + std::string error_category = err->error_code.message(); + MarshaledAppError app_error(err->message, error_category, err->link_to_server_logs, err->http_status_code); + + s_api_keys_callback(tcs_ptr, nullptr, 0, app_error); + } + else { + std::vector marshalled_keys(keys.size()); + std::vector id_storage(keys.size()); + + for (auto i = 0; i < keys.size(); i++) { + auto& api_key = keys[i]; + UserApiKey marshaled_key; + + id_storage[i] = api_key.id.to_string(); + marshaled_key.id = id_storage[i].c_str(); + marshaled_key.id_len = id_storage[i].size(); + + if (api_key.key) { + marshaled_key.key = api_key.key->c_str(); + marshaled_key.key_len = api_key.key->size(); + } + else { + marshaled_key.key = nullptr; + marshaled_key.key_len = 0; + } + + marshaled_key.name = api_key.name.c_str(); + marshaled_key.name_len = api_key.name.size(); + marshaled_key.disabled = api_key.disabled; + + marshalled_keys[i] = marshaled_key; + } + + s_api_keys_callback(tcs_ptr, marshalled_keys.data(), static_cast(marshalled_keys.size()), MarshaledAppError()); + } + } + + inline void invoke_api_key_callback(void* tcs_ptr, App::UserAPIKey key, util::Optional err) { + std::vector api_keys; + api_keys.push_back(key); + + invoke_api_key_callback(tcs_ptr, api_keys, err); + } + + inline bson::BsonDocument to_document(uint16_t* buf, size_t len) { if (buf == nullptr) { return bson::BsonDocument(); diff --git a/wrappers/src/shared_realm_cs.cpp b/wrappers/src/shared_realm_cs.cpp index e0628f9566..bd5726c748 100644 --- a/wrappers/src/shared_realm_cs.cpp +++ b/wrappers/src/shared_realm_cs.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -46,7 +47,7 @@ using GetNativeSchemaT = void(SchemaForMarshaling schema, void* managed_callback using OnBindingContextDestructedT = void(void* managed_handle); using LogMessageT = void(realm_value_t message, util::Logger::Level level); using MigrationCallbackT = bool(realm::SharedRealm* old_realm, realm::SharedRealm* new_realm, SchemaForMarshaling, uint64_t schema_version, void* managed_migration_handle); - +using ShouldCompactCallbackT = bool(void* managed_config_handle, uint64_t total_size, uint64_t data_size); namespace realm { std::function s_object_notification_callback; std::function s_dictionary_notification_callback; @@ -58,6 +59,7 @@ namespace binding { std::function s_on_binding_context_destructed; std::function s_log_message; std::function s_on_migration; + std::function s_should_compact; std::atomic s_can_call_managed; @@ -136,7 +138,8 @@ REALM_EXPORT void shared_realm_install_callbacks( LogMessageT* log_message, ObjectNotificationCallbackT* notify_object, DictionaryNotificationCallbackT* notify_dictionary, - MigrationCallbackT* on_migration) + MigrationCallbackT* on_migration, + ShouldCompactCallbackT* should_compact) { s_realm_changed = wrap_managed_callback(realm_changed); s_get_native_schema = wrap_managed_callback(get_schema); @@ -146,6 +149,7 @@ REALM_EXPORT void shared_realm_install_callbacks( realm::s_object_notification_callback = wrap_managed_callback(notify_object); realm::s_dictionary_notification_callback = wrap_managed_callback(notify_dictionary); s_on_migration = wrap_managed_callback(on_migration); + s_should_compact = wrap_managed_callback(should_compact); realm::binding::s_can_call_managed = true; } @@ -200,7 +204,7 @@ REALM_EXPORT SharedRealm* shared_realm_open(Configuration configuration, SchemaO if (configuration.managed_should_compact_delegate) { config.should_compact_on_launch_function = [&configuration](uint64_t total_bytes, uint64_t used_bytes) { - return configuration.should_compact_callback(configuration.managed_should_compact_delegate, total_bytes, used_bytes); + return s_should_compact(configuration.managed_should_compact_delegate, total_bytes, used_bytes); }; } @@ -286,6 +290,7 @@ REALM_EXPORT void shared_realm_close_all_realms(NativeException::Marshallable& e handle_errors(ex, [&]() { realm::_impl::RealmCoordinator::clear_all_caches(); + app::App::clear_cached_apps(); }); } diff --git a/wrappers/src/shared_realm_cs.hpp b/wrappers/src/shared_realm_cs.hpp index 4b3fe3022b..b8c38565c1 100644 --- a/wrappers/src/shared_realm_cs.hpp +++ b/wrappers/src/shared_realm_cs.hpp @@ -56,7 +56,6 @@ struct Configuration void* managed_migration_handle; - bool (*should_compact_callback)(void* managed_config_handle, uint64_t total_size, uint64_t data_size); void* managed_should_compact_delegate; bool enable_cache; diff --git a/wrappers/src/sync_user_cs.cpp b/wrappers/src/sync_user_cs.cpp index 1761f04e79..c91491f1f3 100644 --- a/wrappers/src/sync_user_cs.cpp +++ b/wrappers/src/sync_user_cs.cpp @@ -33,69 +33,8 @@ using namespace app; using SharedSyncUser = std::shared_ptr; using SharedSyncSession = std::shared_ptr; -struct UserApiKey { - const char* id; - size_t id_len; - - const char* key; - size_t key_len; - - const char* name; - size_t name_len; - - bool disabled; -}; - namespace realm { namespace binding { - void (*s_api_key_callback)(void* tcs_ptr, UserApiKey* api_keys, int api_keys_len, MarshaledAppError err); - - inline void invoke_api_key_callback(void* tcs_ptr, std::vector keys, util::Optional err) { - if (err) { - std::string error_category = err->error_code.message(); - MarshaledAppError app_error(err->message, error_category, err->link_to_server_logs, err->http_status_code); - - s_api_key_callback(tcs_ptr, nullptr, 0, app_error); - } - else { - std::vector marshalled_keys(keys.size()); - std::vector id_storage(keys.size()); - - for (auto i = 0; i < keys.size(); i++) { - auto& api_key = keys[i]; - UserApiKey marshaled_key; - - id_storage[i] = api_key.id.to_string(); - marshaled_key.id = id_storage[i].c_str(); - marshaled_key.id_len = id_storage[i].size(); - - if (api_key.key) { - marshaled_key.key = api_key.key->c_str(); - marshaled_key.key_len = api_key.key->size(); - } - else { - marshaled_key.key = nullptr; - marshaled_key.key_len = 0; - } - - marshaled_key.name = api_key.name.c_str(); - marshaled_key.name_len = api_key.name.size(); - marshaled_key.disabled = api_key.disabled; - - marshalled_keys[i] = marshaled_key; - } - - s_api_key_callback(tcs_ptr, marshalled_keys.data(), static_cast(marshalled_keys.size()), MarshaledAppError()); - } - } - - inline void invoke_api_key_callback(void* tcs_ptr, App::UserAPIKey key, util::Optional err) { - std::vector api_keys; - api_keys.push_back(key); - - invoke_api_key_callback(tcs_ptr, api_keys, err); - } - inline AuthProvider to_auth_provider(const std::string& provider) { if (provider == IdentityProviderAnonymous) { return AuthProvider::ANONYMOUS; @@ -147,11 +86,6 @@ namespace realm { } extern "C" { - REALM_EXPORT void realm_sync_user_initialize(decltype(s_api_key_callback) api_key_callback) - { - s_api_key_callback = api_key_callback; - } - REALM_EXPORT void realm_syncuser_log_out(SharedSyncUser& user, NativeException::Marshallable& ex) { handle_errors(ex, [&] {