2020-03-05 15:59:13 +00:00
|
|
|
// Copyright (C) 2018-2020 Sebastian Dröge <sebastian@centricular.com>
|
2021-11-29 15:05:33 +00:00
|
|
|
// Copyright (C) 2019-2021 François Laignel <fengalin@free.fr>
|
2018-03-15 18:52:38 +00:00
|
|
|
//
|
2021-12-14 18:40:27 +00:00
|
|
|
// Take a look at the license at the top of the repository in the LICENSE file.
|
2018-03-15 18:52:38 +00:00
|
|
|
|
2019-11-24 20:12:40 +00:00
|
|
|
use futures::prelude::*;
|
|
|
|
|
2021-12-14 18:40:27 +00:00
|
|
|
use gst::{gst_debug, gst_error, gst_trace, gst_warning};
|
2019-11-24 20:12:40 +00:00
|
|
|
|
2020-11-22 14:24:55 +00:00
|
|
|
use once_cell::sync::Lazy;
|
2019-11-24 20:12:40 +00:00
|
|
|
|
2021-11-29 18:45:24 +00:00
|
|
|
use std::collections::HashMap;
|
2018-03-16 18:24:36 +00:00
|
|
|
use std::io;
|
2021-12-14 18:40:27 +00:00
|
|
|
use std::pin::Pin;
|
2021-11-29 18:45:24 +00:00
|
|
|
use std::sync::{Arc, Mutex};
|
2021-12-14 18:40:27 +00:00
|
|
|
use std::task::{self, Poll};
|
2019-11-30 18:51:31 +00:00
|
|
|
use std::time::Duration;
|
2018-03-15 18:52:38 +00:00
|
|
|
|
2021-11-29 18:45:24 +00:00
|
|
|
use super::{Handle, HandleWeak, JoinHandle, Scheduler, SubTaskOutput, TaskId};
|
2021-11-29 15:05:33 +00:00
|
|
|
use crate::runtime::RUNTIME_CAT;
|
2019-12-02 09:30:07 +00:00
|
|
|
|
|
|
|
// We are bound to using `sync` for the `runtime` `Mutex`es. Attempts to use `async` `Mutex`es
|
|
|
|
// lead to the following issues:
|
|
|
|
//
|
|
|
|
// * `CONTEXTS`: can't `spawn` a `Future` when called from a `Context` thread via `ffi`.
|
|
|
|
// * `timers`: can't automatically `remove` the timer from `BinaryHeap` because `async drop`
|
|
|
|
// is not available.
|
|
|
|
// * `task_queues`: can't `add` a pending task when called from a `Context` thread via `ffi`.
|
|
|
|
//
|
|
|
|
// Also, we want to be able to `acquire` a `Context` outside of an `async` context.
|
|
|
|
// These `Mutex`es must be `lock`ed for a short period.
|
2021-11-29 18:45:24 +00:00
|
|
|
static CONTEXTS: Lazy<Mutex<HashMap<Arc<str>, ContextWeak>>> =
|
2020-11-22 14:24:55 +00:00
|
|
|
Lazy::new(|| Mutex::new(HashMap::new()));
|
2018-03-15 18:52:38 +00:00
|
|
|
|
2020-03-16 13:03:15 +00:00
|
|
|
/// Blocks on `future` in one way or another if possible.
|
|
|
|
///
|
|
|
|
/// IO & time related `Future`s must be handled within their own [`Context`].
|
|
|
|
/// Wait for the result using a [`JoinHandle`] or a `channel`.
|
|
|
|
///
|
|
|
|
/// If there's currently an active `Context` with a task, then the future is only queued up as a
|
|
|
|
/// pending sub task for that task.
|
|
|
|
///
|
|
|
|
/// Otherwise the current thread is blocking and the passed in future is executed.
|
|
|
|
///
|
|
|
|
/// Note that you must not pass any futures here that wait for the currently active task in one way
|
|
|
|
/// or another as this would deadlock!
|
2021-12-14 18:40:27 +00:00
|
|
|
#[track_caller]
|
|
|
|
pub fn block_on_or_add_sub_task<Fut>(future: Fut) -> Option<Fut::Output>
|
|
|
|
where
|
|
|
|
Fut: Future + Send + 'static,
|
|
|
|
Fut::Output: Send + 'static,
|
|
|
|
{
|
2020-03-16 13:03:15 +00:00
|
|
|
if let Some((cur_context, cur_task_id)) = Context::current_task() {
|
|
|
|
gst_debug!(
|
|
|
|
RUNTIME_CAT,
|
|
|
|
"Adding subtask to task {:?} on context {}",
|
|
|
|
cur_task_id,
|
|
|
|
cur_context.name()
|
|
|
|
);
|
|
|
|
let _ = Context::add_sub_task(async move {
|
|
|
|
future.await;
|
|
|
|
Ok(())
|
|
|
|
});
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Not running in a Context thread so we can block
|
|
|
|
Some(block_on(future))
|
|
|
|
}
|
|
|
|
|
2020-01-02 21:32:52 +00:00
|
|
|
/// Blocks on `future`.
|
|
|
|
///
|
2020-01-06 12:19:11 +00:00
|
|
|
/// IO & time related `Future`s must be handled within their own [`Context`].
|
|
|
|
/// Wait for the result using a [`JoinHandle`] or a `channel`.
|
2020-01-02 21:32:52 +00:00
|
|
|
///
|
2020-03-16 13:03:15 +00:00
|
|
|
/// The current thread is blocking and the passed in future is executed.
|
2020-01-02 21:32:52 +00:00
|
|
|
///
|
|
|
|
/// # Panics
|
|
|
|
///
|
|
|
|
/// This function panics if called within a [`Context`] thread.
|
2021-12-14 18:40:27 +00:00
|
|
|
#[track_caller]
|
|
|
|
pub fn block_on<Fut>(future: Fut) -> Fut::Output
|
|
|
|
where
|
|
|
|
Fut: Future + Send + 'static,
|
|
|
|
Fut::Output: Send + 'static,
|
|
|
|
{
|
|
|
|
if let Some(context) = Context::current() {
|
|
|
|
let msg = format!("Attempt to block within Context {}", context.name());
|
|
|
|
gst_error!(RUNTIME_CAT, "{}", msg);
|
|
|
|
panic!("{}", msg);
|
|
|
|
}
|
2020-01-02 21:32:52 +00:00
|
|
|
|
|
|
|
// Not running in a Context thread so we can block
|
2020-03-16 13:03:15 +00:00
|
|
|
gst_debug!(RUNTIME_CAT, "Blocking on new dummy context");
|
2021-11-29 18:45:24 +00:00
|
|
|
Scheduler::block_on(future)
|
2020-01-02 21:32:52 +00:00
|
|
|
}
|
|
|
|
|
2021-12-14 18:40:27 +00:00
|
|
|
/// Yields execution back to the runtime.
|
2020-03-19 18:34:51 +00:00
|
|
|
#[inline]
|
2021-12-14 18:40:27 +00:00
|
|
|
pub fn yield_now() -> YieldNow {
|
|
|
|
YieldNow::default()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Default)]
|
|
|
|
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
|
|
|
pub struct YieldNow(bool);
|
|
|
|
|
|
|
|
impl Future for YieldNow {
|
|
|
|
type Output = ();
|
|
|
|
|
|
|
|
fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
|
|
|
|
if !self.0 {
|
|
|
|
self.0 = true;
|
|
|
|
cx.waker().wake_by_ref();
|
|
|
|
Poll::Pending
|
|
|
|
} else {
|
|
|
|
Poll::Ready(())
|
|
|
|
}
|
|
|
|
}
|
2020-03-19 18:34:51 +00:00
|
|
|
}
|
|
|
|
|
2019-12-02 09:30:07 +00:00
|
|
|
#[derive(Clone, Debug)]
|
2021-11-29 18:45:24 +00:00
|
|
|
pub struct ContextWeak(HandleWeak);
|
2019-12-02 09:30:07 +00:00
|
|
|
|
|
|
|
impl ContextWeak {
|
|
|
|
pub fn upgrade(&self) -> Option<Context> {
|
|
|
|
self.0.upgrade().map(Context)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A `threadshare` `runtime` `Context`.
|
|
|
|
///
|
|
|
|
/// The `Context` provides low-level asynchronous processing features to
|
|
|
|
/// multiplex task execution on a single thread.
|
|
|
|
///
|
|
|
|
/// `Element` implementations should use [`PadSrc`] and [`PadSink`] which
|
|
|
|
/// provide high-level features.
|
|
|
|
///
|
2020-01-06 12:19:11 +00:00
|
|
|
/// [`PadSrc`]: ../pad/struct.PadSrc.html
|
|
|
|
/// [`PadSink`]: ../pad/struct.PadSink.html
|
2019-12-02 09:30:07 +00:00
|
|
|
#[derive(Clone, Debug)]
|
2021-11-29 18:45:24 +00:00
|
|
|
pub struct Context(Handle);
|
2019-12-02 09:30:07 +00:00
|
|
|
|
2020-03-05 15:59:13 +00:00
|
|
|
impl PartialEq for Context {
|
|
|
|
fn eq(&self, other: &Self) -> bool {
|
2021-11-29 18:45:24 +00:00
|
|
|
self.0.eq(&other.0)
|
2020-03-05 15:59:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Eq for Context {}
|
|
|
|
|
2019-12-02 09:30:07 +00:00
|
|
|
impl Context {
|
2021-05-26 09:54:34 +00:00
|
|
|
pub fn acquire(context_name: &str, wait: Duration) -> Result<Self, io::Error> {
|
2021-11-29 18:45:24 +00:00
|
|
|
assert_ne!(context_name, Scheduler::DUMMY_NAME);
|
2020-03-16 13:03:15 +00:00
|
|
|
|
2018-03-15 18:52:38 +00:00
|
|
|
let mut contexts = CONTEXTS.lock().unwrap();
|
2019-12-02 09:30:07 +00:00
|
|
|
|
2021-11-29 18:45:24 +00:00
|
|
|
if let Some(context_weak) = contexts.get(context_name) {
|
|
|
|
if let Some(context) = context_weak.upgrade() {
|
|
|
|
gst_debug!(RUNTIME_CAT, "Joining Context '{}'", context.name());
|
|
|
|
return Ok(context);
|
2018-03-15 18:52:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-29 18:45:24 +00:00
|
|
|
let context = Context(Scheduler::start(context_name, wait));
|
|
|
|
contexts.insert(context_name.into(), context.downgrade());
|
2018-03-15 18:52:38 +00:00
|
|
|
|
2021-11-29 18:45:24 +00:00
|
|
|
gst_debug!(RUNTIME_CAT, "New Context '{}'", context.name());
|
2019-12-02 09:30:07 +00:00
|
|
|
Ok(context)
|
2018-03-15 18:52:38 +00:00
|
|
|
}
|
|
|
|
|
2019-12-02 09:30:07 +00:00
|
|
|
pub fn downgrade(&self) -> ContextWeak {
|
2021-11-29 18:45:24 +00:00
|
|
|
ContextWeak(self.0.downgrade())
|
2019-12-02 09:30:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn name(&self) -> &str {
|
2021-11-29 18:45:24 +00:00
|
|
|
self.0.context_name()
|
2018-03-15 18:52:38 +00:00
|
|
|
}
|
2018-03-26 14:49:42 +00:00
|
|
|
|
2021-11-29 18:45:24 +00:00
|
|
|
// FIXME this could be renamed as max_throttling
|
|
|
|
// but then, all elements should also change their
|
|
|
|
// wait variables and properties to max_throttling.
|
2021-09-29 16:21:08 +00:00
|
|
|
pub fn wait_duration(&self) -> Duration {
|
2021-11-29 18:45:24 +00:00
|
|
|
self.0.max_throttling()
|
2021-09-29 16:21:08 +00:00
|
|
|
}
|
|
|
|
|
2020-01-02 21:32:52 +00:00
|
|
|
/// Returns `true` if a `Context` is running on current thread.
|
|
|
|
pub fn is_context_thread() -> bool {
|
2021-11-29 18:45:24 +00:00
|
|
|
Scheduler::is_scheduler_thread()
|
2020-01-02 21:32:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the `Context` running on current thread, if any.
|
|
|
|
pub fn current() -> Option<Context> {
|
2021-11-29 18:45:24 +00:00
|
|
|
Scheduler::current().map(Context)
|
2020-01-02 21:32:52 +00:00
|
|
|
}
|
|
|
|
|
2020-03-05 15:59:13 +00:00
|
|
|
/// Returns the `TaskId` running on current thread, if any.
|
|
|
|
pub fn current_task() -> Option<(Context, TaskId)> {
|
2021-11-29 18:45:24 +00:00
|
|
|
Scheduler::current().map(Context).zip(TaskId::current())
|
2020-03-05 15:59:13 +00:00
|
|
|
}
|
|
|
|
|
2021-12-14 18:40:27 +00:00
|
|
|
/// Executes the provided function relatively to this [`Context`].
|
|
|
|
///
|
|
|
|
/// Usefull to initialze i/o sources and timers from outside
|
|
|
|
/// of a [`Context`].
|
|
|
|
///
|
|
|
|
/// # Panic
|
|
|
|
///
|
|
|
|
/// This will block current thread and would panic if run
|
|
|
|
/// from the [`Context`].
|
|
|
|
#[track_caller]
|
2021-12-15 11:48:06 +00:00
|
|
|
pub fn enter<'a, F, O>(&'a self, f: F) -> O
|
2020-01-02 21:32:52 +00:00
|
|
|
where
|
2021-12-15 11:48:06 +00:00
|
|
|
F: FnOnce() -> O + Send + 'a,
|
|
|
|
O: Send + 'a,
|
2020-01-02 21:32:52 +00:00
|
|
|
{
|
2021-12-14 18:40:27 +00:00
|
|
|
if let Some(cur) = Context::current().as_ref() {
|
|
|
|
if cur == self {
|
|
|
|
panic!(
|
|
|
|
"Attempt to enter Context {} within itself, this would deadlock",
|
|
|
|
self.name()
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
gst_warning!(
|
|
|
|
RUNTIME_CAT,
|
|
|
|
"Entering Context {} within {}",
|
|
|
|
self.name(),
|
|
|
|
cur.name()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
gst_debug!(RUNTIME_CAT, "Entering Context {}", self.name());
|
|
|
|
}
|
|
|
|
|
2021-11-29 18:45:24 +00:00
|
|
|
self.0.enter(f)
|
2020-01-02 21:32:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn spawn<Fut>(&self, future: Fut) -> JoinHandle<Fut::Output>
|
2020-04-20 19:35:06 +00:00
|
|
|
where
|
|
|
|
Fut: Future + Send + 'static,
|
|
|
|
Fut::Output: Send + 'static,
|
|
|
|
{
|
2021-12-14 18:40:27 +00:00
|
|
|
self.0.spawn(future)
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
|
2021-12-14 18:40:27 +00:00
|
|
|
pub fn spawn_and_awake<Fut>(&self, future: Fut) -> JoinHandle<Fut::Output>
|
2020-04-20 19:35:06 +00:00
|
|
|
where
|
|
|
|
Fut: Future + Send + 'static,
|
|
|
|
Fut::Output: Send + 'static,
|
|
|
|
{
|
2021-12-14 18:40:27 +00:00
|
|
|
self.0.spawn_and_awake(future)
|
2018-03-26 14:49:42 +00:00
|
|
|
}
|
|
|
|
|
2020-03-05 15:59:13 +00:00
|
|
|
pub fn current_has_sub_tasks() -> bool {
|
|
|
|
let (ctx, task_id) = match Context::current_task() {
|
|
|
|
Some(task) => task,
|
|
|
|
None => {
|
|
|
|
gst_trace!(RUNTIME_CAT, "No current task");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-11-29 18:45:24 +00:00
|
|
|
ctx.0.has_sub_tasks(task_id)
|
2018-03-26 14:49:42 +00:00
|
|
|
}
|
|
|
|
|
2020-03-05 15:59:13 +00:00
|
|
|
pub fn add_sub_task<T>(sub_task: T) -> Result<(), T>
|
2018-03-26 14:49:42 +00:00
|
|
|
where
|
2020-03-05 15:59:13 +00:00
|
|
|
T: Future<Output = SubTaskOutput> + Send + 'static,
|
2018-03-26 14:49:42 +00:00
|
|
|
{
|
2020-03-05 15:59:13 +00:00
|
|
|
let (ctx, task_id) = match Context::current_task() {
|
|
|
|
Some(task) => task,
|
|
|
|
None => {
|
|
|
|
gst_trace!(RUNTIME_CAT, "No current task");
|
|
|
|
return Err(sub_task);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-11-29 18:45:24 +00:00
|
|
|
ctx.0.add_sub_task(task_id, sub_task)
|
2018-03-26 14:49:42 +00:00
|
|
|
}
|
|
|
|
|
2020-10-19 16:10:06 +00:00
|
|
|
pub async fn drain_sub_tasks() -> SubTaskOutput {
|
|
|
|
let (ctx, task_id) = match Context::current_task() {
|
|
|
|
Some(task) => task,
|
|
|
|
None => return Ok(()),
|
|
|
|
};
|
|
|
|
|
2021-11-29 18:45:24 +00:00
|
|
|
ctx.0.drain_sub_tasks(task_id).await
|
2019-12-02 09:30:07 +00:00
|
|
|
}
|
2021-11-29 18:45:24 +00:00
|
|
|
}
|
2019-12-02 09:30:07 +00:00
|
|
|
|
2021-11-29 18:45:24 +00:00
|
|
|
impl From<Handle> for Context {
|
|
|
|
fn from(handle: Handle) -> Self {
|
|
|
|
Context(handle)
|
2019-12-02 09:30:07 +00:00
|
|
|
}
|
2019-08-22 23:04:14 +00:00
|
|
|
}
|
2019-12-02 09:30:07 +00:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2019-11-30 18:51:31 +00:00
|
|
|
use futures::channel::mpsc;
|
2019-12-02 09:30:07 +00:00
|
|
|
use futures::lock::Mutex;
|
2020-01-02 21:32:52 +00:00
|
|
|
use futures::prelude::*;
|
2019-12-02 09:30:07 +00:00
|
|
|
|
2020-01-02 21:32:52 +00:00
|
|
|
use std::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket};
|
2019-12-02 09:30:07 +00:00
|
|
|
use std::sync::Arc;
|
2020-01-02 21:32:52 +00:00
|
|
|
use std::time::{Duration, Instant};
|
2019-12-02 09:30:07 +00:00
|
|
|
|
2021-11-29 18:45:24 +00:00
|
|
|
use super::super::Scheduler;
|
2020-01-02 21:32:52 +00:00
|
|
|
use super::Context;
|
2021-12-14 18:40:27 +00:00
|
|
|
use crate::runtime::Async;
|
2019-12-02 09:30:07 +00:00
|
|
|
|
|
|
|
type Item = i32;
|
|
|
|
|
2021-05-26 09:54:34 +00:00
|
|
|
const SLEEP_DURATION_MS: u64 = 2;
|
|
|
|
const SLEEP_DURATION: Duration = Duration::from_millis(SLEEP_DURATION_MS);
|
|
|
|
const DELAY: Duration = Duration::from_millis(SLEEP_DURATION_MS * 10);
|
2019-12-02 09:30:07 +00:00
|
|
|
|
2021-11-25 18:26:26 +00:00
|
|
|
#[test]
|
|
|
|
fn block_on_task_id() {
|
|
|
|
gst::init().unwrap();
|
|
|
|
|
2021-11-29 18:45:24 +00:00
|
|
|
assert!(!Context::is_context_thread());
|
|
|
|
|
2021-11-25 18:26:26 +00:00
|
|
|
crate::runtime::executor::block_on(async {
|
2021-11-29 18:45:24 +00:00
|
|
|
let (ctx, task_id) = Context::current_task().unwrap();
|
|
|
|
assert_eq!(ctx.name(), Scheduler::DUMMY_NAME);
|
2021-11-25 18:26:26 +00:00
|
|
|
assert_eq!(task_id, super::TaskId(0));
|
|
|
|
|
|
|
|
let res = Context::add_sub_task(async move {
|
|
|
|
let (_ctx, task_id) = Context::current_task().unwrap();
|
|
|
|
assert_eq!(task_id, super::TaskId(0));
|
|
|
|
Ok(())
|
|
|
|
});
|
|
|
|
assert!(res.is_ok());
|
2021-11-29 18:45:24 +00:00
|
|
|
assert!(Context::is_context_thread());
|
2021-11-25 18:26:26 +00:00
|
|
|
});
|
2021-11-29 18:45:24 +00:00
|
|
|
|
|
|
|
assert!(!Context::is_context_thread());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn block_on_timer() {
|
|
|
|
gst::init().unwrap();
|
|
|
|
|
|
|
|
let elapsed = crate::runtime::executor::block_on(async {
|
|
|
|
let now = Instant::now();
|
|
|
|
crate::runtime::time::delay_for(DELAY).await;
|
|
|
|
now.elapsed()
|
|
|
|
});
|
|
|
|
|
|
|
|
assert!(elapsed >= DELAY);
|
2021-11-25 18:26:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn context_task_id() {
|
2021-12-14 18:40:27 +00:00
|
|
|
use super::TaskId;
|
|
|
|
|
2021-11-25 18:26:26 +00:00
|
|
|
gst::init().unwrap();
|
|
|
|
|
|
|
|
let context = Context::acquire("context_task_id", SLEEP_DURATION).unwrap();
|
|
|
|
let join_handle = context.spawn(async {
|
2021-11-29 18:45:24 +00:00
|
|
|
let (ctx, task_id) = Context::current_task().unwrap();
|
|
|
|
assert_eq!(ctx.name(), "context_task_id");
|
2021-12-14 18:40:27 +00:00
|
|
|
assert_eq!(task_id, TaskId(0));
|
2021-11-25 18:26:26 +00:00
|
|
|
});
|
|
|
|
futures::executor::block_on(join_handle).unwrap();
|
2021-12-14 18:40:27 +00:00
|
|
|
// TaskId(0) is vacant again
|
2021-11-25 18:26:26 +00:00
|
|
|
|
|
|
|
let ctx_weak = context.downgrade();
|
|
|
|
let join_handle = context.spawn(async move {
|
|
|
|
let (_ctx, task_id) = Context::current_task().unwrap();
|
2021-12-14 18:40:27 +00:00
|
|
|
assert_eq!(task_id, TaskId(0));
|
2021-11-25 18:26:26 +00:00
|
|
|
|
|
|
|
let res = Context::add_sub_task(async move {
|
|
|
|
let (_ctx, task_id) = Context::current_task().unwrap();
|
2021-12-14 18:40:27 +00:00
|
|
|
assert_eq!(task_id, TaskId(0));
|
2021-11-25 18:26:26 +00:00
|
|
|
Ok(())
|
|
|
|
});
|
|
|
|
assert!(res.is_ok());
|
|
|
|
|
|
|
|
ctx_weak
|
|
|
|
.upgrade()
|
|
|
|
.unwrap()
|
|
|
|
.spawn(async {
|
|
|
|
let (_ctx, task_id) = Context::current_task().unwrap();
|
2021-12-14 18:40:27 +00:00
|
|
|
assert_eq!(task_id, TaskId(1));
|
2021-11-25 18:26:26 +00:00
|
|
|
|
|
|
|
let res = Context::add_sub_task(async move {
|
|
|
|
let (_ctx, task_id) = Context::current_task().unwrap();
|
2021-12-14 18:40:27 +00:00
|
|
|
assert_eq!(task_id, TaskId(1));
|
2021-11-25 18:26:26 +00:00
|
|
|
Ok(())
|
|
|
|
});
|
|
|
|
assert!(res.is_ok());
|
|
|
|
assert!(Context::drain_sub_tasks().await.is_ok());
|
|
|
|
|
|
|
|
let (_ctx, task_id) = Context::current_task().unwrap();
|
2021-12-14 18:40:27 +00:00
|
|
|
assert_eq!(task_id, TaskId(1));
|
2021-11-25 18:26:26 +00:00
|
|
|
})
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
assert!(Context::drain_sub_tasks().await.is_ok());
|
|
|
|
|
|
|
|
let (_ctx, task_id) = Context::current_task().unwrap();
|
2021-12-14 18:40:27 +00:00
|
|
|
assert_eq!(task_id, TaskId(0));
|
2021-11-25 18:26:26 +00:00
|
|
|
});
|
|
|
|
futures::executor::block_on(join_handle).unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn drain_sub_tasks() {
|
2019-12-02 09:30:07 +00:00
|
|
|
// Setup
|
|
|
|
gst::init().unwrap();
|
|
|
|
|
2021-05-26 09:54:34 +00:00
|
|
|
let context = Context::acquire("drain_sub_tasks", SLEEP_DURATION).unwrap();
|
2020-03-05 15:59:13 +00:00
|
|
|
|
2021-11-25 18:26:26 +00:00
|
|
|
let join_handle = context.spawn(async {
|
2020-03-05 15:59:13 +00:00
|
|
|
let (sender, mut receiver) = mpsc::channel(1);
|
|
|
|
let sender: Arc<Mutex<mpsc::Sender<Item>>> = Arc::new(Mutex::new(sender));
|
|
|
|
|
|
|
|
let add_sub_task = move |item| {
|
|
|
|
let sender = sender.clone();
|
|
|
|
Context::add_sub_task(async move {
|
|
|
|
sender
|
|
|
|
.lock()
|
|
|
|
.await
|
|
|
|
.send(item)
|
|
|
|
.await
|
|
|
|
.map_err(|_| gst::FlowError::Error)
|
|
|
|
})
|
|
|
|
};
|
2019-12-02 09:30:07 +00:00
|
|
|
|
2020-03-05 15:59:13 +00:00
|
|
|
// Tests
|
2019-12-02 09:30:07 +00:00
|
|
|
|
2020-03-05 15:59:13 +00:00
|
|
|
// Drain empty queue
|
|
|
|
let drain_fut = Context::drain_sub_tasks();
|
|
|
|
drain_fut.await.unwrap();
|
2019-12-02 09:30:07 +00:00
|
|
|
|
2020-03-05 15:59:13 +00:00
|
|
|
// Add a subtask
|
|
|
|
add_sub_task(0).map_err(drop).unwrap();
|
2019-12-02 09:30:07 +00:00
|
|
|
|
2020-03-05 15:59:13 +00:00
|
|
|
// Check that it was not executed yet
|
|
|
|
receiver.try_next().unwrap_err();
|
|
|
|
|
|
|
|
// Drain it now and check that it was executed
|
|
|
|
let drain_fut = Context::drain_sub_tasks();
|
|
|
|
drain_fut.await.unwrap();
|
|
|
|
assert_eq!(receiver.try_next().unwrap(), Some(0));
|
|
|
|
|
|
|
|
// Add another task and check that it's not executed yet
|
|
|
|
add_sub_task(1).map_err(drop).unwrap();
|
|
|
|
receiver.try_next().unwrap_err();
|
|
|
|
|
|
|
|
// Return the receiver
|
|
|
|
receiver
|
|
|
|
});
|
2019-12-02 09:30:07 +00:00
|
|
|
|
2021-11-25 18:26:26 +00:00
|
|
|
let mut receiver = futures::executor::block_on(join_handle).unwrap();
|
2019-12-02 09:30:07 +00:00
|
|
|
|
2020-03-05 15:59:13 +00:00
|
|
|
// The last sub task should be simply dropped at this point
|
2021-11-29 18:45:24 +00:00
|
|
|
match receiver.try_next() {
|
|
|
|
Ok(None) | Err(_) => (),
|
|
|
|
other => panic!("Unexpected {:?}", other),
|
|
|
|
}
|
2019-12-02 09:30:07 +00:00
|
|
|
}
|
|
|
|
|
2020-01-02 21:32:52 +00:00
|
|
|
#[test]
|
|
|
|
fn block_on_from_sync() {
|
2020-04-13 11:44:38 +00:00
|
|
|
gst::init().unwrap();
|
|
|
|
|
2021-05-26 09:54:34 +00:00
|
|
|
let context = Context::acquire("block_on_from_sync", SLEEP_DURATION).unwrap();
|
2020-01-02 21:32:52 +00:00
|
|
|
|
|
|
|
let bytes_sent = crate::runtime::executor::block_on(context.spawn(async {
|
|
|
|
let saddr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 5001);
|
2021-12-14 18:40:27 +00:00
|
|
|
let socket = Async::<UdpSocket>::bind(saddr).unwrap();
|
|
|
|
let saddr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 4001);
|
2020-01-02 21:32:52 +00:00
|
|
|
socket.send_to(&[0; 10], saddr).await.unwrap()
|
|
|
|
}))
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(bytes_sent, 10);
|
|
|
|
|
|
|
|
let elapsed = crate::runtime::executor::block_on(context.spawn(async {
|
2021-12-14 18:40:27 +00:00
|
|
|
let start = Instant::now();
|
2020-01-02 21:32:52 +00:00
|
|
|
crate::runtime::time::delay_for(DELAY).await;
|
2021-12-14 18:40:27 +00:00
|
|
|
start.elapsed()
|
2020-01-02 21:32:52 +00:00
|
|
|
}))
|
|
|
|
.unwrap();
|
|
|
|
// Due to throttling, `Delay` may be fired earlier
|
|
|
|
assert!(elapsed + SLEEP_DURATION / 2 >= DELAY);
|
|
|
|
}
|
2019-12-02 09:30:07 +00:00
|
|
|
|
2020-01-02 21:32:52 +00:00
|
|
|
#[test]
|
2021-12-14 18:40:27 +00:00
|
|
|
#[should_panic]
|
2020-01-02 21:32:52 +00:00
|
|
|
fn block_on_from_context() {
|
|
|
|
gst::init().unwrap();
|
2019-12-02 09:30:07 +00:00
|
|
|
|
2021-05-26 09:54:34 +00:00
|
|
|
let context = Context::acquire("block_on_from_context", SLEEP_DURATION).unwrap();
|
2021-12-14 18:40:27 +00:00
|
|
|
|
|
|
|
// Panic: attempt to `runtime::executor::block_on` within a `Context` thread
|
2020-01-02 21:32:52 +00:00
|
|
|
let join_handle = context.spawn(async {
|
2021-12-14 18:40:27 +00:00
|
|
|
crate::runtime::executor::block_on(crate::runtime::time::delay_for(DELAY));
|
2019-12-02 09:30:07 +00:00
|
|
|
});
|
2021-12-14 18:40:27 +00:00
|
|
|
|
|
|
|
// Panic: task has failed
|
|
|
|
// (enforced by `async-task`, see comment in `Future` impl for `JoinHanlde`).
|
2020-01-02 21:32:52 +00:00
|
|
|
futures::executor::block_on(join_handle).unwrap_err();
|
2019-12-02 09:30:07 +00:00
|
|
|
}
|
|
|
|
|
2021-11-29 18:45:24 +00:00
|
|
|
#[test]
|
|
|
|
fn enter_context_from_scheduler() {
|
2019-12-02 09:30:07 +00:00
|
|
|
gst::init().unwrap();
|
|
|
|
|
2021-11-29 18:45:24 +00:00
|
|
|
let elapsed = crate::runtime::executor::block_on(async {
|
2021-12-14 18:40:27 +00:00
|
|
|
let context = Context::acquire("enter_context_from_executor", SLEEP_DURATION).unwrap();
|
|
|
|
let socket = context
|
2021-11-29 18:45:24 +00:00
|
|
|
.enter(|| {
|
|
|
|
let saddr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 5002);
|
2021-12-14 18:40:27 +00:00
|
|
|
Async::<UdpSocket>::bind(saddr)
|
2021-11-29 18:45:24 +00:00
|
|
|
})
|
|
|
|
.unwrap();
|
2019-11-30 18:51:31 +00:00
|
|
|
|
2021-12-14 18:40:27 +00:00
|
|
|
let saddr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 4002);
|
2021-11-29 18:45:24 +00:00
|
|
|
let bytes_sent = socket.send_to(&[0; 10], saddr).await.unwrap();
|
|
|
|
assert_eq!(bytes_sent, 10);
|
|
|
|
|
2021-12-14 18:40:27 +00:00
|
|
|
let (start, timer) =
|
|
|
|
context.enter(|| (Instant::now(), crate::runtime::time::delay_for(DELAY)));
|
|
|
|
timer.await;
|
|
|
|
start.elapsed()
|
2020-01-02 21:32:52 +00:00
|
|
|
});
|
2021-11-29 18:45:24 +00:00
|
|
|
|
2020-01-02 21:32:52 +00:00
|
|
|
// Due to throttling, `Delay` may be fired earlier
|
|
|
|
assert!(elapsed + SLEEP_DURATION / 2 >= DELAY);
|
2019-12-02 09:30:07 +00:00
|
|
|
}
|
|
|
|
|
2020-01-02 21:32:52 +00:00
|
|
|
#[test]
|
|
|
|
fn enter_context_from_sync() {
|
2019-12-02 09:30:07 +00:00
|
|
|
gst::init().unwrap();
|
|
|
|
|
2021-05-26 09:54:34 +00:00
|
|
|
let context = Context::acquire("enter_context_from_sync", SLEEP_DURATION).unwrap();
|
2021-12-14 18:40:27 +00:00
|
|
|
let socket = context
|
2020-01-02 21:32:52 +00:00
|
|
|
.enter(|| {
|
|
|
|
let saddr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 5003);
|
2021-12-14 18:40:27 +00:00
|
|
|
Async::<UdpSocket>::bind(saddr)
|
2020-01-02 21:32:52 +00:00
|
|
|
})
|
|
|
|
.unwrap();
|
2019-12-02 09:30:07 +00:00
|
|
|
|
2021-12-14 18:40:27 +00:00
|
|
|
let saddr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 4003);
|
2020-01-02 21:32:52 +00:00
|
|
|
let bytes_sent = futures::executor::block_on(socket.send_to(&[0; 10], saddr)).unwrap();
|
|
|
|
assert_eq!(bytes_sent, 10);
|
2019-12-02 09:30:07 +00:00
|
|
|
|
2021-12-14 18:40:27 +00:00
|
|
|
let (start, timer) =
|
|
|
|
context.enter(|| (Instant::now(), crate::runtime::time::delay_for(DELAY)));
|
|
|
|
let elapsed = crate::runtime::executor::block_on(async move {
|
|
|
|
timer.await;
|
|
|
|
start.elapsed()
|
2020-01-02 21:32:52 +00:00
|
|
|
});
|
|
|
|
// Due to throttling, `Delay` may be fired earlier
|
|
|
|
assert!(elapsed + SLEEP_DURATION / 2 >= DELAY);
|
2019-12-02 09:30:07 +00:00
|
|
|
}
|
|
|
|
}
|