Skip to content

Commit

Permalink
Merge pull request #604 from yamadapc/issue/better-error-handling
Browse files Browse the repository at this point in the history
Implement better error handling on metronome
  • Loading branch information
yamadapc authored Jul 24, 2023
2 parents e29cdb5 + c5f77e5 commit d341785
Show file tree
Hide file tree
Showing 15 changed files with 230 additions and 38 deletions.
3 changes: 3 additions & 0 deletions crates/apps/metronome/ios/Runner/bridge_generated.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ void wire_set_sound(int64_t port_, int32_t value);

void wire_get_playhead(int64_t port_);

void wire_stream_errors(int64_t port_);

struct wire_InitializeOptions *new_box_autoadd_initialize_options_0(void);

struct wire_uint_8_list *new_uint_8_list_0(int32_t len);
Expand All @@ -65,6 +67,7 @@ static int64_t dummy_method_to_enforce_bundling(void) {
dummy_var ^= ((int64_t) (void*) wire_set_beats_per_bar);
dummy_var ^= ((int64_t) (void*) wire_set_sound);
dummy_var ^= ((int64_t) (void*) wire_get_playhead);
dummy_var ^= ((int64_t) (void*) wire_stream_errors);
dummy_var ^= ((int64_t) (void*) new_box_autoadd_initialize_options_0);
dummy_var ^= ((int64_t) (void*) new_uint_8_list_0);
dummy_var ^= ((int64_t) (void*) free_WireSyncReturn);
Expand Down
63 changes: 63 additions & 0 deletions crates/apps/metronome/lib/bridge_generated.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,18 @@ abstract class Metronome {
Future<double> getPlayhead({dynamic hint});

FlutterRustBridgeTaskConstMeta get kGetPlayheadConstMeta;

Stream<EngineError> streamErrors({dynamic hint});

FlutterRustBridgeTaskConstMeta get kStreamErrorsConstMeta;
}

class EngineError {
final String message;

const EngineError({
required this.message,
});
}

class InitializeOptions {
Expand Down Expand Up @@ -202,18 +214,55 @@ class MetronomeImpl implements Metronome {
argNames: [],
);

Stream<EngineError> streamErrors({dynamic hint}) {
return _platform.executeStream(FlutterRustBridgeTask(
callFfi: (port_) => _platform.inner.wire_stream_errors(port_),
parseSuccessData: _wire2api_engine_error,
constMeta: kStreamErrorsConstMeta,
argValues: [],
hint: hint,
));
}

FlutterRustBridgeTaskConstMeta get kStreamErrorsConstMeta =>
const FlutterRustBridgeTaskConstMeta(
debugName: "stream_errors",
argNames: [],
);

void dispose() {
_platform.dispose();
}
// Section: wire2api

String _wire2api_String(dynamic raw) {
return raw as String;
}

EngineError _wire2api_engine_error(dynamic raw) {
final arr = raw as List<dynamic>;
if (arr.length != 1)
throw Exception('unexpected arr length: expect 1 but see ${arr.length}');
return EngineError(
message: _wire2api_String(arr[0]),
);
}

double _wire2api_f32(dynamic raw) {
return raw as double;
}

int _wire2api_i32(dynamic raw) {
return raw as int;
}

int _wire2api_u8(dynamic raw) {
return raw as int;
}

Uint8List _wire2api_uint_8_list(dynamic raw) {
return raw as Uint8List;
}
}

// Section: api2wire
Expand Down Expand Up @@ -520,6 +569,20 @@ class MetronomeWire implements FlutterRustBridgeWireBase {
late final _wire_get_playhead =
_wire_get_playheadPtr.asFunction<void Function(int)>();

void wire_stream_errors(
int port_,
) {
return _wire_stream_errors(
port_,
);
}

late final _wire_stream_errorsPtr =
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Int64)>>(
'wire_stream_errors');
late final _wire_stream_errors =
_wire_stream_errorsPtr.asFunction<void Function(int)>();

ffi.Pointer<wire_InitializeOptions> new_box_autoadd_initialize_options_0() {
return _new_box_autoadd_initialize_options_0();
}
Expand Down
15 changes: 14 additions & 1 deletion crates/apps/metronome/lib/ui/home_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
import 'package:macos_ui/macos_ui.dart';
import 'package:metronome/bridge_generated.dart';
import 'package:metronome/logger.dart';
import 'package:metronome/modules/context/app_context.dart';
Expand Down Expand Up @@ -54,6 +55,14 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
MetronomeStateController? metronomeStateController;
HistoryStateController? historyStateController;

void onError(dynamic err) {
logger.e("ERROR: $err");
showMacosAlertDialog(
context: context,
builder: (context) => Center(child: Text("ERROR: $err")),
);
}

@override
void initState() {
final performance = getMetrics();
Expand Down Expand Up @@ -88,10 +97,14 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {

trace.stop();
});

metronome.streamErrors().listen((error) {
onError(error);
});
}

runInitSequence().catchError((err) {
logger.e("ERROR: $err");
onError(err);
});

super.initState();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 51;
objectVersion = 54;
objects = {

/* Begin PBXAggregateTarget section */
Expand Down
3 changes: 3 additions & 0 deletions crates/apps/metronome/macos/Runner/bridge_generated.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ void wire_set_sound(int64_t port_, int32_t value);

void wire_get_playhead(int64_t port_);

void wire_stream_errors(int64_t port_);

struct wire_InitializeOptions *new_box_autoadd_initialize_options_0(void);

struct wire_uint_8_list *new_uint_8_list_0(int32_t len);
Expand All @@ -65,6 +67,7 @@ static int64_t dummy_method_to_enforce_bundling(void) {
dummy_var ^= ((int64_t) (void*) wire_set_beats_per_bar);
dummy_var ^= ((int64_t) (void*) wire_set_sound);
dummy_var ^= ((int64_t) (void*) wire_get_playhead);
dummy_var ^= ((int64_t) (void*) wire_stream_errors);
dummy_var ^= ((int64_t) (void*) new_box_autoadd_initialize_options_0);
dummy_var ^= ((int64_t) (void*) new_uint_8_list_0);
dummy_var ^= ((int64_t) (void*) free_WireSyncReturn);
Expand Down
41 changes: 41 additions & 0 deletions crates/apps/metronome/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@
// = /copyright ===================================================================
//! This module contains the public API exposed to flutter
use std::sync::mpsc::TryRecvError;

use anyhow::Result;
use flutter_rust_bridge::StreamSink;

pub use crate::api::state::InitializeOptions;

Expand Down Expand Up @@ -83,23 +86,59 @@ pub fn get_playhead() -> Result<f32> {
})
}

#[derive(Debug)]
#[repr(C)]
pub struct EngineError {
pub message: String,
}

pub fn stream_errors(stream: StreamSink<EngineError>) {
loop {
let rx = with_state(|state| Ok(state.handles.errors_rx().try_recv()));

match rx {
Ok(Ok(error)) => {
if !stream.add(EngineError {
message: error.to_string(),
}) {
break;
}
}
Ok(Err(TryRecvError::Empty)) => {
std::thread::sleep(std::time::Duration::from_millis(100))
}
Ok(Err(TryRecvError::Disconnected)) => break,
_ => break,
}
}
}

#[cfg(test)]
mod test {
use lazy_static::lazy_static;
use std::sync::Mutex;

use super::*;

lazy_static! {
static ref TEST_LOCK: Mutex<()> = Mutex::new(());
}

#[test]
fn test_initialize() {
initialize(Default::default()).unwrap();
}

#[test]
fn test_deinitialize() {
let _lock = TEST_LOCK.lock().unwrap();
initialize(Default::default()).unwrap();
deinitialize().unwrap();
}

#[test]
fn test_set_is_playing() {
let _lock = TEST_LOCK.lock().unwrap();
initialize(Default::default()).unwrap();
let handle = with_state(|state| Ok(state.processor_handle.clone())).unwrap();
set_is_playing(true).unwrap();
Expand All @@ -110,6 +149,7 @@ mod test {

#[test]
fn test_set_beats_per_bar() {
let _lock = TEST_LOCK.lock().unwrap();
initialize(Default::default()).unwrap();
let handle = with_state(|state| Ok(state.processor_handle.clone())).unwrap();
set_beats_per_bar(5).unwrap();
Expand All @@ -120,6 +160,7 @@ mod test {

#[test]
fn test_set_volume() {
let _lock = TEST_LOCK.lock().unwrap();
initialize(Default::default()).unwrap();
let handle = with_state(|state| Ok(state.processor_handle.clone())).unwrap();
set_volume(0.44).unwrap();
Expand Down
4 changes: 2 additions & 2 deletions crates/apps/metronome/src/api/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ pub struct InitializeOptions {
}

pub struct State {
_handles: StandaloneMetronomeHandle,
pub handles: StandaloneMetronomeHandle,
pub app_processor_messages: ringbuf::Producer<AppAudioThreadMessage>,
pub processor_handle: Shared<MetronomeProcessorHandle>,
}
Expand Down Expand Up @@ -70,7 +70,7 @@ impl State {
Self {
app_processor_messages,
processor_handle,
_handles: handles,
handles: handles,
}
}
}
Expand Down
5 changes: 5 additions & 0 deletions crates/apps/metronome/src/bridge_generated.io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ pub extern "C" fn wire_get_playhead(port_: i64) {
wire_get_playhead_impl(port_)
}

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

// Section: allocate functions

#[no_mangle]
Expand Down
17 changes: 17 additions & 0 deletions crates/apps/metronome/src/bridge_generated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,16 @@ fn wire_get_playhead_impl(port_: MessagePort) {
move || move |task_callback| get_playhead(),
)
}
fn wire_stream_errors_impl(port_: MessagePort) {
FLUTTER_RUST_BRIDGE_HANDLER.wrap(
WrapInfo {
debug_name: "stream_errors",
port: Some(port_),
mode: FfiCallMode::Stream,
},
move || move |task_callback| Ok(stream_errors(task_callback.stream_sink())),
)
}
// Section: wrapper structs

// Section: static checks
Expand Down Expand Up @@ -185,6 +195,13 @@ impl Wire2Api<u8> for u8 {

// Section: impl IntoDart

impl support::IntoDart for EngineError {
fn into_dart(self) -> support::DartAbi {
vec![self.message.into_dart()].into_dart()
}
}
impl support::IntoDartExceptPrimitive for EngineError {}

// Section: executor

support::lazy_static! {
Expand Down
11 changes: 11 additions & 0 deletions crates/apps/metronome/test/mock_metronome.dart
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,17 @@ class MockMetronomeLib implements Metronome {
Future<int> setSound({required MetronomeSoundTypeTag value, dynamic hint}) {
return Future.value(0);
}

@override
Stream<EngineError> streamErrors({hint}) {
// TODO: implement streamErrors
throw UnimplementedError();
}

@override
// TODO: implement kStreamErrorsConstMeta
FlutterRustBridgeTaskConstMeta get kStreamErrorsConstMeta =>
throw UnimplementedError();
}

Future<MockEnvironment> buildTestEnvironment() async {
Expand Down
Loading

0 comments on commit d341785

Please sign in to comment.