Skip to content

Commit

Permalink
chore: ignore engine start if runtime is already up
Browse files Browse the repository at this point in the history
Thanks to the engine overhaul, we shut down the runtime static at the
end of every session. We should check and make sure that's the case
before letting the user start the engine again. Ideally the UX would
handle this with not allowing the button to be active but eh.
  • Loading branch information
qdot committed Jan 22, 2024
1 parent d0c168a commit 089853f
Show file tree
Hide file tree
Showing 11 changed files with 101 additions and 13 deletions.
22 changes: 13 additions & 9 deletions intiface-engine-flutter-bridge/src/api.rs
Expand Up @@ -77,6 +77,10 @@ pub struct _EngineOptionsExternal {
pub repeater_remote_address: Option<String>,
}

pub fn runtime_started() -> bool {
RUNTIME.lock().unwrap().is_some()
}

pub fn run_engine(sink: StreamSink<String>, args: EngineOptionsExternal) -> Result<()> {

if RUN_STATUS.load(Ordering::Relaxed) {
Expand Down Expand Up @@ -214,21 +218,21 @@ pub fn stop_engine() {
}
// Need to park ourselves real quick to let the other runtime threads finish out.
//
// HACK The android JNI drop calls are slow and need quite a while to get everything disconnected.
// If they don't run to completion, the runtime won't shutdown properly and everything will stall.
// Waiting on this is not the optimal way to do it, but I also don't have a good way to know when
// shutdown is finished right now. So waiting it is.
#[cfg(target_os = "android")]
thread::sleep(Duration::from_millis(1000));
#[cfg(not(target_os = "android"))]
thread::sleep(Duration::from_millis(1));
// HACK The android JNI drop calls (and sometimes windows UWP calls) are slow (100ms+) and need
// quite a while to get everything disconnected if there are currently connected devices. If they
// don't run to completion, the runtime won't shutdown properly and everything will stall. Running
// runtime_shutdown() doesn't work here because these are all tasks that may be stalled at the OS
// level so we don't have enough info. Waiting on this is not the optimal way to do it, but I also
// don't have a good way to know when shutdown is finished right now. So waiting it is. 1s isn't
// super noticable from an UX standpoint.
thread::sleep(Duration::from_millis(500));
let runtime;
{
runtime = RUNTIME.lock().unwrap().take();
}
if let Some(rt) = runtime {
info!("Shutting down runtime");
rt.shutdown_timeout(Duration::from_secs(5));
rt.shutdown_timeout(Duration::from_secs(1));
info!("Runtime shutdown complete");
}
RUN_STATUS.store(false, Ordering::Relaxed);
Expand Down
5 changes: 5 additions & 0 deletions intiface-engine-flutter-bridge/src/bridge_generated.io.rs
@@ -1,6 +1,11 @@
use super::*;
// Section: wire functions

#[no_mangle]
pub extern "C" fn wire_runtime_started(port_: i64) {
wire_runtime_started_impl(port_)
}

#[no_mangle]
pub extern "C" fn wire_run_engine(port_: i64, args: *mut wire_EngineOptionsExternal) {
wire_run_engine_impl(port_, args)
Expand Down
10 changes: 10 additions & 0 deletions intiface-engine-flutter-bridge/src/bridge_generated.rs
Expand Up @@ -22,6 +22,16 @@ use std::sync::Arc;

// Section: wire functions

fn wire_runtime_started_impl(port_: MessagePort) {
FLUTTER_RUST_BRIDGE_HANDLER.wrap::<_, _, _, bool, _>(
WrapInfo {
debug_name: "runtime_started",
port: Some(port_),
mode: FfiCallMode::Normal,
},
move || move |task_callback| Result::<_, ()>::Ok(runtime_started()),
)
}
fn wire_run_engine_impl(
port_: MessagePort,
args: impl Wire2Api<EngineOptionsExternal> + UnwindSafe,
Expand Down
5 changes: 3 additions & 2 deletions ios/Runner/bridge_generated.h
Expand Up @@ -103,6 +103,8 @@ uintptr_t new_dart_opaque(Dart_Handle handle);

intptr_t init_frb_dart_api_dl(void *obj);

void wire_runtime_started(int64_t port_);

void wire_run_engine(int64_t port_, struct wire_EngineOptionsExternal *args);

void wire_send(int64_t port_, struct wire_uint_8_list *msg_json);
Expand Down Expand Up @@ -148,10 +150,9 @@ struct wire_uint_8_list *new_uint_8_list_0(int32_t len);

void free_WireSyncReturn(WireSyncReturn ptr);

jint JNI_OnLoad(JavaVM vm, const void *_res);

static int64_t dummy_method_to_enforce_bundling(void) {
int64_t dummy_var = 0;
dummy_var ^= ((int64_t) (void*) wire_runtime_started);
dummy_var ^= ((int64_t) (void*) wire_run_engine);
dummy_var ^= ((int64_t) (void*) wire_send);
dummy_var ^= ((int64_t) (void*) wire_stop_engine);
Expand Down
4 changes: 4 additions & 0 deletions lib/bloc/engine/engine_control_bloc.dart
Expand Up @@ -88,6 +88,10 @@ class EngineControlBloc extends Bloc<EngineControlEvent, EngineControlState> {

EngineControlBloc(this._repo) : super(EngineStoppedState()) {
on<EngineControlEventStart>((event, emit) async {
if (await _repo.runtimeStarted()) {
logWarning("Runtime already started, ignoring restart request.");
return;
}
logInfo("Trying to start engine...");
await _repo.start(options: event.options);
_isRunning = true;
Expand Down
1 change: 1 addition & 0 deletions lib/bloc/engine/engine_provider.dart
Expand Up @@ -9,6 +9,7 @@ abstract class EngineProcessMessage {}
abstract class EngineProvider {
Future<void> start({required EngineOptionsExternal options});
Future<void> stop();
Future<bool> runtimeStarted();
void cycleStream();

void onEngineStart();
Expand Down
4 changes: 4 additions & 0 deletions lib/bloc/engine/engine_repository.dart
Expand Up @@ -62,6 +62,10 @@ class EngineRepository {
await _provider.stop();
}

Future<bool> runtimeStarted() async {
return await _provider.runtimeStarted();
}

void send(String msg) {
_provider.send(msg);
}
Expand Down
12 changes: 11 additions & 1 deletion lib/bloc/engine/foreground_task_library_engine_provider.dart
Expand Up @@ -77,7 +77,12 @@ class IntifaceEngineTaskHandler extends TaskHandler {
_sendProviderLog("INFO", "Starting engine");

_sendProviderLog("INFO", "Starting library internal engine with the following arguments: $engineOptions");
_stream = api!.runEngine(args: engineOptions);
try {
_stream = api!.runEngine(args: engineOptions);
} catch (e) {
_sendProviderLog("ERROR", "Engine start failed!");
return;
}
_sendProviderLog("INFO", "Engine started");
_stream!.listen((element) {
try {
Expand Down Expand Up @@ -147,6 +152,11 @@ class ForegroundTaskLibraryEngineProvider implements EngineProvider {
await _startForegroundTask();
}

@override
Future<bool> runtimeStarted() async {
return await api!.runtimeStarted();
}

@override
void cycleStream() {
_processMessageStream.close();
Expand Down
13 changes: 12 additions & 1 deletion lib/bloc/engine/library_engine_provider.dart
Expand Up @@ -10,7 +10,13 @@ class LibraryEngineProvider implements EngineProvider {
@override
Future<void> start({required EngineOptionsExternal options}) async {
logInfo("Starting library internal engine with the following arguments: $options");
_stream = api!.runEngine(args: options);
try {
_stream = api!.runEngine(args: options);
} catch (e) {
logError("Engine start failed!");
stop();
return;
}
logInfo("Engine started");
_stream!.listen((element) {
try {
Expand All @@ -22,6 +28,11 @@ class LibraryEngineProvider implements EngineProvider {
}).onError((e) => logError(e.anyhow));
}

@override
Future<bool> runtimeStarted() async {
return await api!.runtimeStarted();
}

@override
void cycleStream() {
_processMessageStream.close();
Expand Down
35 changes: 35 additions & 0 deletions lib/bridge_generated.dart
Expand Up @@ -11,6 +11,10 @@ import 'package:uuid/uuid.dart';
import 'dart:ffi' as ffi;

abstract class IntifaceEngineFlutterBridge {
Future<bool> runtimeStarted({dynamic hint});

FlutterRustBridgeTaskConstMeta get kRuntimeStartedConstMeta;

Stream<String> runEngine({required EngineOptionsExternal args, dynamic hint});

FlutterRustBridgeTaskConstMeta get kRunEngineConstMeta;
Expand Down Expand Up @@ -180,6 +184,23 @@ class IntifaceEngineFlutterBridgeImpl implements IntifaceEngineFlutterBridge {
factory IntifaceEngineFlutterBridgeImpl.wasm(FutureOr<WasmModule> module) =>
IntifaceEngineFlutterBridgeImpl(module as ExternalLibrary);
IntifaceEngineFlutterBridgeImpl.raw(this._platform);
Future<bool> runtimeStarted({dynamic hint}) {
return _platform.executeNormal(FlutterRustBridgeTask(
callFfi: (port_) => _platform.inner.wire_runtime_started(port_),
parseSuccessData: _wire2api_bool,
parseErrorData: null,
constMeta: kRuntimeStartedConstMeta,
argValues: [],
hint: hint,
));
}

FlutterRustBridgeTaskConstMeta get kRuntimeStartedConstMeta =>
const FlutterRustBridgeTaskConstMeta(
debugName: "runtime_started",
argNames: [],
);

Stream<String> runEngine(
{required EngineOptionsExternal args, dynamic hint}) {
var arg0 = _platform.api2wire_box_autoadd_engine_options_external(args);
Expand Down Expand Up @@ -870,6 +891,20 @@ class IntifaceEngineFlutterBridgeWire implements FlutterRustBridgeWireBase {
late final _init_frb_dart_api_dl = _init_frb_dart_api_dlPtr
.asFunction<int Function(ffi.Pointer<ffi.Void>)>();

void wire_runtime_started(
int port_,
) {
return _wire_runtime_started(
port_,
);
}

late final _wire_runtime_startedPtr =
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Int64)>>(
'wire_runtime_started');
late final _wire_runtime_started =
_wire_runtime_startedPtr.asFunction<void Function(int)>();

void wire_run_engine(
int port_,
ffi.Pointer<wire_EngineOptionsExternal> args,
Expand Down
3 changes: 3 additions & 0 deletions macos/Runner/bridge_generated.h
Expand Up @@ -103,6 +103,8 @@ uintptr_t new_dart_opaque(Dart_Handle handle);

intptr_t init_frb_dart_api_dl(void *obj);

void wire_runtime_started(int64_t port_);

void wire_run_engine(int64_t port_, struct wire_EngineOptionsExternal *args);

void wire_send(int64_t port_, struct wire_uint_8_list *msg_json);
Expand Down Expand Up @@ -150,6 +152,7 @@ void free_WireSyncReturn(WireSyncReturn ptr);

static int64_t dummy_method_to_enforce_bundling(void) {
int64_t dummy_var = 0;
dummy_var ^= ((int64_t) (void*) wire_runtime_started);
dummy_var ^= ((int64_t) (void*) wire_run_engine);
dummy_var ^= ((int64_t) (void*) wire_send);
dummy_var ^= ((int64_t) (void*) wire_stop_engine);
Expand Down

0 comments on commit 089853f

Please sign in to comment.