Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Platform networking #1719

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion packages/realm_dart/ffigen.yaml
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
# Regenerate bindings with `dart run ffigen --config ffigen.yaml`.
name: RealmLibrary
description: Realm native lib bindings.
output: "lib/src/native/realm_bindings.dart"
output: "lib/src/handles/native/realm_bindings.dart"
headers:
entry-points:
- 'src/realm-core/src/realm.h'
- 'src/realm_dart.h'
- 'src/realm_dart_logger.h'
- 'src/realm_dart_decimal128.h'
- 'src/realm_dart_scheduler.h'
- 'src/realm_dart_socket_provider.h'
- 'src/realm_dart_sync.h'
include-directives: # generate only for these headers
- 'src/realm-core/src/realm.h'
- 'src/realm_dart.h'
- 'src/realm_dart_logger.h'
- 'src/realm_dart_decimal128.h'
- 'src/realm_dart_scheduler.h'
- 'src/realm_dart_socket_provider.h'
- 'src/realm_dart_sync.h'
preamble: |
// ignore_for_file: always_specify_types
Expand Down
12 changes: 7 additions & 5 deletions packages/realm_dart/lib/src/app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ class AppConfiguration {
/// a more complex networking setup.
final Client httpClient;

final bool useManagedWebsockets;

/// Instantiates a new [AppConfiguration] with the specified appId.
AppConfiguration(
this.appId, {
Expand All @@ -71,6 +73,7 @@ class AppConfiguration {
this.metadataPersistenceMode = MetadataPersistenceMode.plaintext,
this.maxConnectionTimeout = const Duration(minutes: 2),
Client? httpClient,
this.useManagedWebsockets = true, // TODO: should be false
}) : baseUrl = baseUrl ?? Uri.parse(realmCore.getDefaultBaseUrl()),
baseFilePath = baseFilePath ?? path.dirname(Configuration.defaultRealmPath),
httpClient = httpClient ?? defaultClient {
Expand All @@ -95,7 +98,7 @@ class App {

/// Create an app with a particular [AppConfiguration]. This constructor should only be used on the main isolate and,
/// ideally, only once as soon as the app starts.
App(AppConfiguration configuration) : _handle = _createApp(configuration) {
static Future<App> create(AppConfiguration configuration) async {
// This is not foolproof, but could point people to errors they may have in their app. Realm apps are cached natively, so calling App(config)
// on a background isolate will not recreate the app. Instead, users should construct the app on the main isolate and then call getById on the
// background isolates. This check will log a warning if the isolate name is != 'main' and doesn't start with 'test/' since dart test will
Expand All @@ -104,6 +107,9 @@ class App {
Realm.logger.log(LogLevel.warn,
"App constructor called on Isolate ${Isolate.current.debugName} which doesn't appear to be the main isolate. If you need an app instance on a background isolate use App.getById after constructing the App on the main isolate.");
}

final appHandle = await AppHandle.from(configuration);
return App._(appHandle);
}

/// Obtain an [App] instance by id. The app must have first been created by calling the constructor that takes an [AppConfiguration]
Expand All @@ -116,10 +122,6 @@ class App {

App._(this._handle);

static AppHandle _createApp(AppConfiguration configuration) {
return AppHandle.from(configuration);
}

/// Logs in a user with the given credentials.
Future<User> logIn(Credentials credentials) async {
var userHandle = await handle.logIn(credentials.handle);
Expand Down
4 changes: 3 additions & 1 deletion packages/realm_dart/lib/src/handles/app_handle.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import 'user_handle.dart';
import 'native/app_handle.dart' if (dart.library.js_interop) 'web/app_handle.dart' as impl;

abstract interface class AppHandle {
factory AppHandle.from(AppConfiguration configuration) = impl.AppHandle.from;
static Future<AppHandle> from(AppConfiguration configuration) => impl.AppHandle.from(configuration);
static AppHandle? get(String id, String? baseUrl) => impl.AppHandle.get(id, baseUrl);

String get id;
Expand All @@ -36,4 +36,6 @@ abstract interface class AppHandle {
Future<void> deleteUser(UserHandle user);
bool resetRealm(String realmPath);
Future<String> callAppFunction(UserHandle user, String functionName, String? argsAsJSON);

void resetForTesting();
}
62 changes: 48 additions & 14 deletions packages/realm_dart/lib/src/handles/native/app_handle.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ import 'dart:ffi';
import 'dart:io';
import 'dart:isolate';

import 'package:realm_dart/realm.dart';

import '../../../realm.dart';
import 'convert.dart';
import 'convert_native.dart';
import 'credentials_handle.dart';
Expand All @@ -19,6 +18,7 @@ import 'realm_bindings.dart';
import 'realm_core.dart';
import 'realm_library.dart';
import 'scheduler_handle.dart';
import 'sync_socket_handle.dart';
import 'user_handle.dart';

import '../app_handle.dart' as intf;
Expand All @@ -27,18 +27,39 @@ class AppHandle extends HandleBase<realm_app> implements intf.AppHandle {
AppHandle(Pointer<realm_app> pointer) : super(pointer, 16);

static bool _firstTime = true;
factory AppHandle.from(AppConfiguration configuration) {
// to avoid caching apps across hot restarts we clear the cache on the first
// time the ctor is called in the root isolate.
if (_firstTime && _isRootIsolate) {
_firstTime = false;
realmLib.realm_clear_cached_apps();
static Future<AppHandle> from(AppConfiguration configuration) async {
HttpTransportHandle? httpTransportHandle;
_AppConfigHandle? appConfigHandle;
SyncSocketHandle? syncSocketHandle;
_SyncClientConfigHandle? syncClientConfigHandle;
try {
// to avoid caching apps across hot restarts we clear the cache on the first
// time the ctor is called in the root isolate.
if (_firstTime && _isRootIsolate) {
_firstTime = false;
realmLib.realm_clear_cached_apps();
}
Directory(configuration.baseFilePath).createSync(recursive: true);

httpTransportHandle = HttpTransportHandle.from(configuration.httpClient);
if (configuration.useManagedWebsockets) {
final websocketHandler = WebsocketHandler();
syncSocketHandle = await websocketHandler.start();

syncClientConfigHandle = _SyncClientConfigHandle(realmLib.realm_sync_client_config_new());
realmLib.realm_sync_client_config_set_sync_socket(syncClientConfigHandle.pointer, syncSocketHandle.pointer);
}

appConfigHandle = _createAppConfig(configuration, httpTransportHandle, syncClientConfigHandle);
final result = AppHandle(realmLib.realm_app_create_cached(appConfigHandle.pointer));

return result;
} finally {
httpTransportHandle?.release();
syncClientConfigHandle?.release();
syncSocketHandle?.release();
appConfigHandle?.release();
}
Directory(configuration.baseFilePath).createSync(recursive: true);

final httpTransportHandle = HttpTransportHandle.from(configuration.httpClient);
final appConfigHandle = _createAppConfig(configuration, httpTransportHandle);
return AppHandle(realmLib.realm_app_create_cached(appConfigHandle.pointer));
}

static AppHandle? get(String id, String? baseUrl) {
Expand Down Expand Up @@ -344,6 +365,11 @@ class AppHandle extends HandleBase<realm_app> implements intf.AppHandle {
return completer.future;
});
}

@override
void resetForTesting() {
realmLib.realm_dart_app_reset_for_testing(pointer).raiseLastErrorIfFalse();
}
}

Pointer<Void> createAsyncFunctionCallbackUserdata(Completer<String> completer) {
Expand Down Expand Up @@ -406,7 +432,11 @@ class _AppConfigHandle extends HandleBase<realm_app_config> {
_AppConfigHandle(Pointer<realm_app_config> pointer) : super(pointer, 8);
}

_AppConfigHandle _createAppConfig(AppConfiguration configuration, HttpTransportHandle httpTransport) {
class _SyncClientConfigHandle extends HandleBase<realm_sync_client_config> {
_SyncClientConfigHandle(Pointer<realm_sync_client_config> pointer) : super(pointer, 8);
}

_AppConfigHandle _createAppConfig(AppConfiguration configuration, HttpTransportHandle httpTransport, _SyncClientConfigHandle? syncClientConfig) {
return using((arena) {
final appId = configuration.appId.toCharPtr(arena);
final handle = _AppConfigHandle(realmLib.realm_app_config_new(appId, httpTransport.pointer));
Expand Down Expand Up @@ -438,6 +468,10 @@ _AppConfigHandle _createAppConfig(AppConfiguration configuration, HttpTransportH
realmLib.realm_app_config_set_metadata_encryption_key(handle.pointer, configuration.metadataEncryptionKey!.toUint8Ptr(arena));
}

if (syncClientConfig != null) {
realmLib.realm_app_config_set_sync_client_config(handle.pointer, syncClientConfig.pointer);
}

return handle;
});
}
Expand Down
125 changes: 106 additions & 19 deletions packages/realm_dart/lib/src/handles/native/realm_bindings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -175,25 +175,6 @@ class RealmLibrary {
ffi.Pointer<ffi.Void>,
realm_free_userdata_func_t)>();

ffi.Pointer<realm_sync_client_config_t>
realm_app_config_get_sync_client_config(
ffi.Pointer<realm_app_config_t> arg0,
) {
return _realm_app_config_get_sync_client_config(
arg0,
);
}

late final _realm_app_config_get_sync_client_configPtr = _lookup<
ffi.NativeFunction<
ffi.Pointer<realm_sync_client_config_t> Function(
ffi.Pointer<realm_app_config_t>)>>(
'realm_app_config_get_sync_client_config');
late final _realm_app_config_get_sync_client_config =
_realm_app_config_get_sync_client_configPtr.asFunction<
ffi.Pointer<realm_sync_client_config_t> Function(
ffi.Pointer<realm_app_config_t>)>();

/// Create a new app configuration.
///
/// @param app_id The MongoDB Realm app id.
Expand Down Expand Up @@ -483,6 +464,26 @@ class RealmLibrary {
void Function(
ffi.Pointer<realm_app_config_t>, ffi.Pointer<ffi.Char>)>();

void realm_app_config_set_sync_client_config(
ffi.Pointer<realm_app_config_t> config,
ffi.Pointer<realm_sync_client_config_t> sync_client_config,
) {
return _realm_app_config_set_sync_client_config(
config,
sync_client_config,
);
}

late final _realm_app_config_set_sync_client_configPtr = _lookup<
ffi.NativeFunction<
ffi.Void Function(ffi.Pointer<realm_app_config_t>,
ffi.Pointer<realm_sync_client_config_t>)>>(
'realm_app_config_set_sync_client_config');
late final _realm_app_config_set_sync_client_config =
_realm_app_config_set_sync_client_configPtr.asFunction<
void Function(ffi.Pointer<realm_app_config_t>,
ffi.Pointer<realm_sync_client_config_t>)>();

/// Create realm_app_t* instance given a valid realm app configuration.
///
/// @return A non-null pointer if no error occurred.
Expand Down Expand Up @@ -3410,6 +3411,21 @@ class RealmLibrary {
int,
ffi.Pointer<realm_app_error_t>)>();

bool realm_dart_app_reset_for_testing(
ffi.Pointer<realm_app_t> app,
) {
return _realm_dart_app_reset_for_testing(
app,
);
}

late final _realm_dart_app_reset_for_testingPtr =
_lookup<ffi.NativeFunction<ffi.Bool Function(ffi.Pointer<realm_app_t>)>>(
'realm_dart_app_reset_for_testing');
late final _realm_dart_app_reset_for_testing =
_realm_dart_app_reset_for_testingPtr
.asFunction<bool Function(ffi.Pointer<realm_app_t>)>();

void realm_dart_async_open_task_callback(
ffi.Pointer<ffi.Void> userdata,
ffi.Pointer<realm_thread_safe_reference_t> realm,
Expand Down Expand Up @@ -4126,6 +4142,60 @@ class RealmLibrary {
_realm_dart_sync_progress_callbackPtr
.asFunction<void Function(ffi.Pointer<ffi.Void>, int, int, double)>();

ffi.Pointer<realm_sync_socket_t> realm_dart_sync_socket_new(
ffi.Pointer<ffi.Void> userdata,
realm_free_userdata_func_t userdata_free,
ffi.Pointer<realm_scheduler_t> scheduler,
realm_sync_socket_post_func_t post_func,
realm_sync_socket_create_timer_func_t create_timer_func,
realm_sync_socket_timer_canceled_func_t cancel_timer_func,
realm_sync_socket_timer_free_func_t free_timer_func,
realm_sync_socket_connect_func_t websocket_connect_func,
realm_sync_socket_websocket_async_write_func_t websocket_write_func,
realm_sync_socket_websocket_free_func_t websocket_free_func,
) {
return _realm_dart_sync_socket_new(
userdata,
userdata_free,
scheduler,
post_func,
create_timer_func,
cancel_timer_func,
free_timer_func,
websocket_connect_func,
websocket_write_func,
websocket_free_func,
);
}

late final _realm_dart_sync_socket_newPtr = _lookup<
ffi.NativeFunction<
ffi.Pointer<realm_sync_socket_t> Function(
ffi.Pointer<ffi.Void>,
realm_free_userdata_func_t,
ffi.Pointer<realm_scheduler_t>,
realm_sync_socket_post_func_t,
realm_sync_socket_create_timer_func_t,
realm_sync_socket_timer_canceled_func_t,
realm_sync_socket_timer_free_func_t,
realm_sync_socket_connect_func_t,
realm_sync_socket_websocket_async_write_func_t,
realm_sync_socket_websocket_free_func_t)>>(
'realm_dart_sync_socket_new');
late final _realm_dart_sync_socket_new =
_realm_dart_sync_socket_newPtr.asFunction<
ffi.Pointer<realm_sync_socket_t> Function(
ffi.Pointer<ffi.Void>,
realm_free_userdata_func_t,
ffi.Pointer<realm_scheduler_t>,
realm_sync_socket_post_func_t,
realm_sync_socket_create_timer_func_t,
realm_sync_socket_timer_canceled_func_t,
realm_sync_socket_timer_free_func_t,
realm_sync_socket_connect_func_t,
realm_sync_socket_websocket_async_write_func_t,
realm_sync_socket_websocket_free_func_t)>();

void realm_dart_sync_wait_for_completion_callback(
ffi.Pointer<ffi.Void> userdata,
ffi.Pointer<realm_error_t> error,
Expand Down Expand Up @@ -11720,6 +11790,9 @@ class _SymbolAddresses {
ffi.Pointer<realm_app_error_t>)>>
get realm_dart_apikey_list_callback =>
_library._realm_dart_apikey_list_callbackPtr;
ffi.Pointer<ffi.NativeFunction<ffi.Bool Function(ffi.Pointer<realm_app_t>)>>
get realm_dart_app_reset_for_testing =>
_library._realm_dart_app_reset_for_testingPtr;
ffi.Pointer<
ffi.NativeFunction<
ffi.Void Function(
Expand Down Expand Up @@ -11900,6 +11973,20 @@ class _SymbolAddresses {
ffi.Pointer<ffi.Void>, ffi.Uint64, ffi.Uint64, ffi.Double)>>
get realm_dart_sync_progress_callback =>
_library._realm_dart_sync_progress_callbackPtr;
ffi.Pointer<
ffi.NativeFunction<
ffi.Pointer<realm_sync_socket_t> Function(
ffi.Pointer<ffi.Void>,
realm_free_userdata_func_t,
ffi.Pointer<realm_scheduler_t>,
realm_sync_socket_post_func_t,
realm_sync_socket_create_timer_func_t,
realm_sync_socket_timer_canceled_func_t,
realm_sync_socket_timer_free_func_t,
realm_sync_socket_connect_func_t,
realm_sync_socket_websocket_async_write_func_t,
realm_sync_socket_websocket_free_func_t)>>
get realm_dart_sync_socket_new => _library._realm_dart_sync_socket_newPtr;
ffi.Pointer<
ffi.NativeFunction<
ffi.Void Function(
Expand Down
Loading
Loading