rework stream threading model

Fixes #206. 307a388 switched to creating a single-threaded runtime for
each stream, then destroying prior to waiting for TEARDOWN on shutdown.
This meant that the shutdown process could panic with this error:

```
panic at '/home/slamb/git/retina/src/client/mod.rs:219:22': teardown Sender shouldn't be dropped: RecvError(())
```

Let's switch back to expecting a multithreaded runtime context.
Create one for the config subcommand, too.

Don't go all the way back to the old code with its channels, though.
That had the downside that the underlying retina::Session might outlive
the caller, so there could still be an active session when we start
the next one. I haven't seen this cause problems in practice but it
still doesn't seem right.
This commit is contained in:
Scott Lamb
2022-04-13 11:39:38 -07:00
parent 5e7d558f99
commit 7b0a489541
6 changed files with 46 additions and 43 deletions

View File

@@ -204,18 +204,14 @@ fn press_edit(siv: &mut Cursive, db: &Arc<db::Database>, id: Option<i32>) {
}
fn press_test_inner(
handle: tokio::runtime::Handle,
url: Url,
username: String,
password: String,
transport: retina::client::Transport,
) -> Result<String, Error> {
let rt = tokio::runtime::Builder::new_current_thread()
.enable_time()
.enable_io()
.build()?;
let _guard = rt.enter();
let _enter = handle.enter();
let (extra_data, stream) = stream::OPENER.open(
&rt,
"test stream".to_owned(),
url,
retina::client::SessionOptions::default()
@@ -262,8 +258,12 @@ fn press_test(siv: &mut Cursive, t: db::StreamType) {
// siv.cb_sink doesn't actually wake up the event loop. Tell siv to poll, as a workaround.
siv.set_fps(5);
let sink = siv.cb_sink().clone();
// Note: this expects to be called within a tokio runtime. Currently this
// is set up by the config subcommand's run().
let handle = tokio::runtime::Handle::current();
::std::thread::spawn(move || {
let r = press_test_inner(url.clone(), username, password, transport);
let r = press_test_inner(handle, url.clone(), username, password, transport);
sink.send(Box::new(move |siv: &mut Cursive| {
// Polling is no longer necessary.
siv.set_fps(0);

View File

@@ -36,6 +36,13 @@ pub fn run(args: Args) -> Result<i32, Error> {
let clocks = clock::RealClocks {};
let db = Arc::new(db::Database::new(clocks, conn, true)?);
// This runtime is needed by the "Test" button in the camera config.
let rt = tokio::runtime::Builder::new_multi_thread()
.enable_io()
.enable_time()
.build()?;
let _enter = rt.enter();
let mut siv = cursive::default();
//siv.add_global_callback('q', |s| s.quit());

View File

@@ -303,6 +303,7 @@ async fn inner(
}
// Then start up streams.
let handle = tokio::runtime::Handle::current();
let l = db.lock();
for (i, (id, stream)) in l.streams_by_id().iter().enumerate() {
if stream.config.mode != db::json::STREAM_MODE_RECORD {
@@ -342,10 +343,14 @@ async fn inner(
)?;
info!("Starting streamer for {}", streamer.short_name());
let name = format!("s-{}", streamer.short_name());
let handle = handle.clone();
streamers.push(
thread::Builder::new()
.name(name)
.spawn(move || streamer.run())
.spawn(move || {
let _enter = handle.enter();
streamer.run();
})
.expect("can't create thread"),
);
}