2020-03-19 18:34:51 +00:00
|
|
|
// Copyright (C) 2019-2020 François Laignel <fengalin@free.fr>
|
2020-03-05 15:59:13 +00:00
|
|
|
// Copyright (C) 2020 Sebastian Dröge <sebastian@centricular.com>
|
2019-12-02 09:30:07 +00:00
|
|
|
//
|
|
|
|
// This library is free software; you can redistribute it and/or
|
|
|
|
// modify it under the terms of the GNU Library General Public
|
|
|
|
// License as published by the Free Software Foundation; either
|
|
|
|
// version 2 of the License, or (at your option) any later version.
|
|
|
|
//
|
|
|
|
// This library is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
// Library General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU Library General Public
|
|
|
|
// License along with this library; if not, write to the
|
|
|
|
// Free Software Foundation, Inc., 51 Franklin Street, Suite 500,
|
|
|
|
// Boston, MA 02110-1335, USA.
|
2022-01-15 19:18:47 +00:00
|
|
|
//
|
|
|
|
// SPDX-License-Identifier: LGPL-2.1-or-later
|
2019-12-02 09:30:07 +00:00
|
|
|
|
2020-01-06 12:19:11 +00:00
|
|
|
//! An execution loop to run asynchronous processing.
|
2019-12-02 09:30:07 +00:00
|
|
|
|
2020-04-20 19:35:06 +00:00
|
|
|
use futures::channel::mpsc as async_mpsc;
|
2020-03-19 18:34:51 +00:00
|
|
|
use futures::channel::oneshot;
|
2020-05-15 21:37:56 +00:00
|
|
|
use futures::future::{self, abortable, AbortHandle, Aborted, BoxFuture, RemoteHandle};
|
2019-12-02 09:30:07 +00:00
|
|
|
use futures::prelude::*;
|
2020-04-20 19:35:06 +00:00
|
|
|
use futures::stream::StreamExt;
|
2019-12-02 09:30:07 +00:00
|
|
|
|
|
|
|
use std::fmt;
|
2020-04-20 19:35:06 +00:00
|
|
|
use std::ops::Deref;
|
2020-05-15 17:38:54 +00:00
|
|
|
use std::stringify;
|
2020-04-20 19:35:06 +00:00
|
|
|
use std::sync::{Arc, Mutex, MutexGuard};
|
2019-12-02 09:30:07 +00:00
|
|
|
|
2022-03-22 16:18:18 +00:00
|
|
|
use super::executor::TaskId;
|
2020-05-15 21:37:56 +00:00
|
|
|
use super::{Context, RUNTIME_CAT};
|
2019-12-02 09:30:07 +00:00
|
|
|
|
2020-03-19 18:34:51 +00:00
|
|
|
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
|
|
|
|
pub enum TaskState {
|
2020-05-15 17:38:54 +00:00
|
|
|
Error,
|
2020-04-20 19:35:06 +00:00
|
|
|
Flushing,
|
2020-03-19 18:34:51 +00:00
|
|
|
Paused,
|
2020-04-20 19:35:06 +00:00
|
|
|
PausedFlushing,
|
|
|
|
Prepared,
|
2020-03-19 18:34:51 +00:00
|
|
|
Preparing,
|
2020-04-20 19:35:06 +00:00
|
|
|
Started,
|
|
|
|
Stopped,
|
2020-03-19 18:34:51 +00:00
|
|
|
Unprepared,
|
2020-04-20 19:35:06 +00:00
|
|
|
Unpreparing,
|
2020-03-19 18:34:51 +00:00
|
|
|
}
|
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
2020-05-25 15:18:31 +00:00
|
|
|
pub enum Trigger {
|
2020-05-15 17:38:54 +00:00
|
|
|
Error,
|
|
|
|
FlushStart,
|
|
|
|
FlushStop,
|
|
|
|
Pause,
|
|
|
|
Prepare,
|
|
|
|
Start,
|
|
|
|
Stop,
|
|
|
|
Unprepare,
|
|
|
|
}
|
|
|
|
|
2020-05-25 15:18:31 +00:00
|
|
|
/// TriggeringEvent error details.
|
2019-12-02 09:30:07 +00:00
|
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
2020-05-15 17:38:54 +00:00
|
|
|
pub struct TransitionError {
|
2020-05-25 15:18:31 +00:00
|
|
|
pub trigger: Trigger,
|
2020-05-15 17:38:54 +00:00
|
|
|
pub state: TaskState,
|
|
|
|
pub err_msg: gst::ErrorMessage,
|
2019-12-02 09:30:07 +00:00
|
|
|
}
|
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
impl fmt::Display for TransitionError {
|
2019-12-02 09:30:07 +00:00
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
2020-05-15 17:38:54 +00:00
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
"{:?} from state {:?}: {:?}",
|
2020-05-25 15:18:31 +00:00
|
|
|
self.trigger, self.state, self.err_msg
|
2020-05-15 17:38:54 +00:00
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl std::error::Error for TransitionError {}
|
|
|
|
|
|
|
|
impl From<TransitionError> for gst::ErrorMessage {
|
|
|
|
fn from(err: TransitionError) -> Self {
|
|
|
|
err.err_msg
|
2019-12-02 09:30:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-25 15:18:31 +00:00
|
|
|
/// Transition details.
|
|
|
|
///
|
|
|
|
/// A state transition occurs as a result of a triggering event.
|
2020-05-15 17:38:54 +00:00
|
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
|
|
pub enum TransitionStatus {
|
|
|
|
/// Transition completed successfully.
|
|
|
|
Complete {
|
|
|
|
origin: TaskState,
|
|
|
|
target: TaskState,
|
|
|
|
},
|
2020-05-25 15:18:31 +00:00
|
|
|
/// Asynchronously awaiting for transition completion in a subtask.
|
2020-05-15 17:38:54 +00:00
|
|
|
///
|
2020-05-25 15:18:31 +00:00
|
|
|
/// This occurs when the event is triggered from a `Context`.
|
|
|
|
Async { trigger: Trigger, origin: TaskState },
|
2020-05-15 17:38:54 +00:00
|
|
|
/// Not waiting for transition completion.
|
|
|
|
///
|
|
|
|
/// This is to prevent:
|
2020-05-25 15:18:31 +00:00
|
|
|
/// - A deadlock when executing a transition action.
|
2020-05-15 17:38:54 +00:00
|
|
|
/// - A potential infinite wait when pausing a running loop
|
|
|
|
/// which could be awaiting for an `iterate` to complete.
|
2020-05-25 15:18:31 +00:00
|
|
|
NotWaiting { trigger: Trigger, origin: TaskState },
|
|
|
|
/// Skipping triggering event due to current state.
|
|
|
|
Skipped { trigger: Trigger, state: TaskState },
|
2020-05-15 17:38:54 +00:00
|
|
|
}
|
2019-12-02 09:30:07 +00:00
|
|
|
|
2020-04-20 19:35:06 +00:00
|
|
|
/// Implementation trait for `Task`s.
|
|
|
|
///
|
2020-05-25 15:18:31 +00:00
|
|
|
/// Defines implementations for state transition actions and error handlers.
|
2020-04-20 19:35:06 +00:00
|
|
|
pub trait TaskImpl: Send + 'static {
|
|
|
|
fn prepare(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
|
|
|
|
future::ok(()).boxed()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn unprepare(&mut self) -> BoxFuture<'_, ()> {
|
|
|
|
future::ready(()).boxed()
|
|
|
|
}
|
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
fn start(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
|
|
|
|
future::ok(()).boxed()
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Executes an iteration in `TaskState::Started`.
|
|
|
|
fn iterate(&mut self) -> BoxFuture<'_, Result<(), gst::FlowError>>;
|
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
fn pause(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
|
|
|
|
future::ok(()).boxed()
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
fn flush_start(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
|
|
|
|
future::ok(()).boxed()
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
fn flush_stop(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
|
|
|
|
future::ok(()).boxed()
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
fn stop(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
|
|
|
|
future::ok(()).boxed()
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
/// Handles an error occuring during the execution of an iteration.
|
|
|
|
///
|
|
|
|
/// This handler also catches errors returned by subtasks spawned by the iteration.
|
|
|
|
///
|
|
|
|
/// If the error is unrecoverable, implementations might use `gst::Element::post_error_message`
|
2020-05-25 15:18:31 +00:00
|
|
|
/// and return `Trigger::Error`.
|
2020-05-15 17:38:54 +00:00
|
|
|
///
|
|
|
|
/// Otherwise, handle the error and return the requested `Transition` to recover.
|
|
|
|
///
|
|
|
|
/// Default behaviour depends on the `err`:
|
|
|
|
///
|
2020-05-25 15:18:31 +00:00
|
|
|
/// - `FlowError::Flushing` -> `Trigger::FlushStart`.
|
|
|
|
/// - `FlowError::Eos` -> `Trigger::Stop`.
|
|
|
|
/// - Other `FlowError` -> `Trigger::Error`.
|
|
|
|
fn handle_iterate_error(&mut self, err: gst::FlowError) -> BoxFuture<'_, Trigger> {
|
2020-05-15 17:38:54 +00:00
|
|
|
async move {
|
|
|
|
match err {
|
|
|
|
gst::FlowError::Flushing => {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(
|
2020-05-15 17:38:54 +00:00
|
|
|
RUNTIME_CAT,
|
|
|
|
"TaskImpl iterate returned Flushing. Posting FlushStart"
|
|
|
|
);
|
2020-05-25 15:18:31 +00:00
|
|
|
Trigger::FlushStart
|
2020-05-15 17:38:54 +00:00
|
|
|
}
|
|
|
|
gst::FlowError::Eos => {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "TaskImpl iterate returned Eos. Posting Stop");
|
2020-05-25 15:18:31 +00:00
|
|
|
Trigger::Stop
|
2020-05-15 17:38:54 +00:00
|
|
|
}
|
|
|
|
other => {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::error!(
|
2020-05-15 17:38:54 +00:00
|
|
|
RUNTIME_CAT,
|
|
|
|
"TaskImpl iterate returned {:?}. Posting Error",
|
|
|
|
other
|
|
|
|
);
|
2020-05-25 15:18:31 +00:00
|
|
|
Trigger::Error
|
2020-05-15 17:38:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.boxed()
|
|
|
|
}
|
|
|
|
|
2020-05-25 15:18:31 +00:00
|
|
|
/// Handles an error occuring during the execution of a transition action.
|
2020-05-15 17:38:54 +00:00
|
|
|
///
|
2020-05-25 15:18:31 +00:00
|
|
|
/// This handler also catches errors returned by subtasks spawned by the transition action.
|
2020-05-15 17:38:54 +00:00
|
|
|
///
|
|
|
|
/// If the error is unrecoverable, implementations might use `gst::Element::post_error_message`
|
2020-05-25 15:18:31 +00:00
|
|
|
/// and return `Trigger::Error`.
|
2020-05-15 17:38:54 +00:00
|
|
|
///
|
2020-05-25 15:18:31 +00:00
|
|
|
/// Otherwise, handle the error and return the recovering `Trigger`.
|
2020-05-15 17:38:54 +00:00
|
|
|
///
|
2022-02-21 17:43:46 +00:00
|
|
|
/// Default is to `gst::error` log and return `Trigger::Error`.
|
2020-05-25 15:18:31 +00:00
|
|
|
fn handle_action_error(
|
2020-05-15 17:38:54 +00:00
|
|
|
&mut self,
|
2020-05-25 15:18:31 +00:00
|
|
|
trigger: Trigger,
|
2020-05-15 17:38:54 +00:00
|
|
|
state: TaskState,
|
|
|
|
err: gst::ErrorMessage,
|
2020-05-25 15:18:31 +00:00
|
|
|
) -> BoxFuture<'_, Trigger> {
|
2020-05-15 17:38:54 +00:00
|
|
|
async move {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::error!(
|
2020-05-15 17:38:54 +00:00
|
|
|
RUNTIME_CAT,
|
2020-05-25 15:18:31 +00:00
|
|
|
"TaskImpl transition action error during {:?} from {:?}: {:?}. Posting Trigger::Error",
|
|
|
|
trigger,
|
2020-05-15 17:38:54 +00:00
|
|
|
state,
|
|
|
|
err,
|
|
|
|
);
|
|
|
|
|
2020-05-25 15:18:31 +00:00
|
|
|
Trigger::Error
|
2020-05-15 17:38:54 +00:00
|
|
|
}
|
|
|
|
.boxed()
|
|
|
|
}
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
|
2020-05-25 15:18:31 +00:00
|
|
|
struct TriggeringEvent {
|
|
|
|
trigger: Trigger,
|
2020-05-15 17:38:54 +00:00
|
|
|
ack_tx: oneshot::Sender<Result<TransitionStatus, TransitionError>>,
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
|
2020-05-25 15:18:31 +00:00
|
|
|
impl TriggeringEvent {
|
2020-05-15 17:38:54 +00:00
|
|
|
fn new(
|
2020-05-25 15:18:31 +00:00
|
|
|
trigger: Trigger,
|
2020-05-15 17:38:54 +00:00
|
|
|
) -> (
|
|
|
|
Self,
|
|
|
|
oneshot::Receiver<Result<TransitionStatus, TransitionError>>,
|
|
|
|
) {
|
2020-04-20 19:35:06 +00:00
|
|
|
let (ack_tx, ack_rx) = oneshot::channel();
|
2020-05-25 15:18:31 +00:00
|
|
|
let req = TriggeringEvent { trigger, ack_tx };
|
2020-04-20 19:35:06 +00:00
|
|
|
|
|
|
|
(req, ack_rx)
|
|
|
|
}
|
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
fn send_ack(self, res: Result<TransitionStatus, TransitionError>) {
|
|
|
|
let _ = self.ack_tx.send(res);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn send_err_ack(self) {
|
|
|
|
let res = Err(TransitionError {
|
2020-05-25 15:18:31 +00:00
|
|
|
trigger: self.trigger,
|
2020-05-15 17:38:54 +00:00
|
|
|
state: TaskState::Error,
|
2020-12-20 18:43:45 +00:00
|
|
|
err_msg: gst::error_msg!(
|
2020-05-15 17:38:54 +00:00
|
|
|
gst::CoreError::StateChange,
|
|
|
|
[
|
2020-05-25 15:18:31 +00:00
|
|
|
"Triggering Event {:?} rejected due to a previous unrecoverable error",
|
|
|
|
self.trigger,
|
2020-05-15 17:38:54 +00:00
|
|
|
]
|
|
|
|
),
|
|
|
|
});
|
|
|
|
|
|
|
|
self.send_ack(res);
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-25 15:18:31 +00:00
|
|
|
impl fmt::Debug for TriggeringEvent {
|
2020-04-20 19:35:06 +00:00
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
2020-05-25 15:18:31 +00:00
|
|
|
f.debug_struct("TriggeringEvent")
|
|
|
|
.field("trigger", &self.trigger)
|
2020-04-20 19:35:06 +00:00
|
|
|
.finish()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-02 09:30:07 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
struct TaskInner {
|
|
|
|
context: Option<Context>,
|
|
|
|
state: TaskState,
|
2020-05-15 21:37:56 +00:00
|
|
|
state_machine_handle: Option<RemoteHandle<()>>,
|
2020-05-25 15:18:31 +00:00
|
|
|
triggering_evt_tx: Option<async_mpsc::Sender<TriggeringEvent>>,
|
2020-03-05 15:59:13 +00:00
|
|
|
prepare_abort_handle: Option<AbortHandle>,
|
2020-04-20 19:35:06 +00:00
|
|
|
loop_abort_handle: Option<AbortHandle>,
|
|
|
|
spawned_task_id: Option<TaskId>,
|
2019-12-02 09:30:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for TaskInner {
|
|
|
|
fn default() -> Self {
|
|
|
|
TaskInner {
|
|
|
|
context: None,
|
2020-03-19 18:34:51 +00:00
|
|
|
state: TaskState::Unprepared,
|
2020-04-20 19:35:06 +00:00
|
|
|
state_machine_handle: None,
|
2020-05-25 15:18:31 +00:00
|
|
|
triggering_evt_tx: None,
|
2020-03-05 15:59:13 +00:00
|
|
|
prepare_abort_handle: None,
|
2020-04-20 19:35:06 +00:00
|
|
|
loop_abort_handle: None,
|
|
|
|
spawned_task_id: None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TaskInner {
|
2020-05-25 15:18:31 +00:00
|
|
|
fn switch_to_state(&mut self, target_state: TaskState, triggering_evt: TriggeringEvent) {
|
2020-05-15 17:38:54 +00:00
|
|
|
let res = Ok(TransitionStatus::Complete {
|
|
|
|
origin: self.state,
|
|
|
|
target: target_state,
|
|
|
|
});
|
|
|
|
|
|
|
|
self.state = target_state;
|
2020-05-25 15:18:31 +00:00
|
|
|
triggering_evt.send_ack(res);
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
|
2020-05-25 15:18:31 +00:00
|
|
|
fn switch_to_err(&mut self, triggering_evt: TriggeringEvent) {
|
2020-05-15 17:38:54 +00:00
|
|
|
let res = Err(TransitionError {
|
2020-05-25 15:18:31 +00:00
|
|
|
trigger: triggering_evt.trigger,
|
2020-05-15 17:38:54 +00:00
|
|
|
state: self.state,
|
2020-12-20 18:43:45 +00:00
|
|
|
err_msg: gst::error_msg!(
|
2020-05-15 17:38:54 +00:00
|
|
|
gst::CoreError::StateChange,
|
|
|
|
[
|
|
|
|
"Unrecoverable error for {:?} from state {:?}",
|
2020-05-25 15:18:31 +00:00
|
|
|
triggering_evt,
|
2020-05-15 17:38:54 +00:00
|
|
|
self.state,
|
|
|
|
]
|
|
|
|
),
|
|
|
|
});
|
|
|
|
|
|
|
|
self.state = TaskState::Error;
|
2020-05-25 15:18:31 +00:00
|
|
|
triggering_evt.send_ack(res);
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
|
2020-05-25 15:18:31 +00:00
|
|
|
fn skip_triggering_evt(&mut self, triggering_evt: TriggeringEvent) {
|
2020-05-15 17:38:54 +00:00
|
|
|
let res = Ok(TransitionStatus::Skipped {
|
2020-05-25 15:18:31 +00:00
|
|
|
trigger: triggering_evt.trigger,
|
2020-05-15 17:38:54 +00:00
|
|
|
state: self.state,
|
|
|
|
});
|
|
|
|
|
2020-05-25 15:18:31 +00:00
|
|
|
triggering_evt.send_ack(res);
|
2020-05-15 17:38:54 +00:00
|
|
|
}
|
|
|
|
|
2020-05-25 15:18:31 +00:00
|
|
|
fn trigger(
|
2020-04-20 19:35:06 +00:00
|
|
|
&mut self,
|
2020-05-25 15:18:31 +00:00
|
|
|
trigger: Trigger,
|
2020-05-15 17:38:54 +00:00
|
|
|
) -> Result<oneshot::Receiver<Result<TransitionStatus, TransitionError>>, TransitionError> {
|
2020-05-25 15:18:31 +00:00
|
|
|
let triggering_evt_tx = self.triggering_evt_tx.as_mut().unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2020-05-25 15:18:31 +00:00
|
|
|
let (triggering_evt, ack_rx) = TriggeringEvent::new(trigger);
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::log!(RUNTIME_CAT, "Pushing {:?}", triggering_evt);
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2020-07-28 14:00:48 +00:00
|
|
|
triggering_evt_tx.try_send(triggering_evt).map_err(|err| {
|
2020-05-15 17:38:54 +00:00
|
|
|
let resource_err = if err.is_full() {
|
|
|
|
gst::ResourceError::NoSpaceLeft
|
|
|
|
} else {
|
|
|
|
gst::ResourceError::Close
|
|
|
|
};
|
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::warning!(RUNTIME_CAT, "Unable to send {:?}: {:?}", trigger, err);
|
2020-07-28 14:00:48 +00:00
|
|
|
TransitionError {
|
2020-05-25 15:18:31 +00:00
|
|
|
trigger,
|
2020-05-15 17:38:54 +00:00
|
|
|
state: self.state,
|
2020-12-20 18:43:45 +00:00
|
|
|
err_msg: gst::error_msg!(resource_err, ["Unable to send {:?}: {:?}", trigger, err]),
|
2020-07-28 14:00:48 +00:00
|
|
|
}
|
2020-05-15 17:38:54 +00:00
|
|
|
})?;
|
2020-04-20 19:35:06 +00:00
|
|
|
|
|
|
|
Ok(ack_rx)
|
2019-12-02 09:30:07 +00:00
|
|
|
}
|
2022-03-21 11:40:28 +00:00
|
|
|
|
|
|
|
/// Aborts the task iteration loop ASAP.
|
|
|
|
///
|
|
|
|
/// When the iteration loop is throttling, the call to `abort`
|
|
|
|
/// on the `loop_abort_handle` returns immediately, but the
|
|
|
|
/// actual `Future` for the iteration loop is aborted only when
|
|
|
|
/// the scheduler throttling completes.
|
|
|
|
///
|
|
|
|
/// This function aborts the task iteration loop and awakes the
|
|
|
|
/// iteration scheduler.
|
|
|
|
fn abort_task_loop(&mut self) {
|
|
|
|
if let Some(loop_abort_handle) = self.loop_abort_handle.take() {
|
|
|
|
loop_abort_handle.abort();
|
|
|
|
|
|
|
|
if let Some(context) = self.context.as_ref() {
|
|
|
|
context.wake_up();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-12-02 09:30:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Drop for TaskInner {
|
|
|
|
fn drop(&mut self) {
|
2020-03-19 18:34:51 +00:00
|
|
|
if self.state != TaskState::Unprepared {
|
2020-04-20 19:35:06 +00:00
|
|
|
// Don't panic here: in case another panic occurs, we would get
|
|
|
|
// "panicked while panicking" which would prevents developers
|
|
|
|
// from getting the initial panic message.
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::fixme!(RUNTIME_CAT, "Missing call to `Task::unprepare`");
|
2019-12-02 09:30:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-20 19:35:06 +00:00
|
|
|
/// An RAII implementation of scoped locked `TaskState`.
|
|
|
|
pub struct TaskStateGuard<'guard>(MutexGuard<'guard, TaskInner>);
|
|
|
|
|
|
|
|
impl Deref for TaskStateGuard<'_> {
|
|
|
|
type Target = TaskState;
|
|
|
|
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
|
|
&(self.0).state
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-02 09:30:07 +00:00
|
|
|
/// A `Task` operating on a `threadshare` [`Context`].
|
|
|
|
///
|
2020-01-06 12:19:11 +00:00
|
|
|
/// [`Context`]: ../executor/struct.Context.html
|
2020-04-20 19:35:06 +00:00
|
|
|
#[derive(Debug, Clone)]
|
2019-12-02 09:30:07 +00:00
|
|
|
pub struct Task(Arc<Mutex<TaskInner>>);
|
|
|
|
|
|
|
|
impl Default for Task {
|
|
|
|
fn default() -> Self {
|
|
|
|
Task(Arc::new(Mutex::new(TaskInner::default())))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Task {
|
2020-04-20 19:35:06 +00:00
|
|
|
pub fn state(&self) -> TaskState {
|
|
|
|
self.0.lock().unwrap().state
|
|
|
|
}
|
2019-12-02 09:30:07 +00:00
|
|
|
|
2020-04-20 19:35:06 +00:00
|
|
|
pub fn lock_state(&self) -> TaskStateGuard<'_> {
|
|
|
|
TaskStateGuard(self.0.lock().unwrap())
|
|
|
|
}
|
2020-03-05 15:59:13 +00:00
|
|
|
|
2020-04-20 19:35:06 +00:00
|
|
|
pub fn context(&self) -> Option<Context> {
|
|
|
|
self.0.lock().unwrap().context.as_ref().cloned()
|
|
|
|
}
|
2020-03-05 15:59:13 +00:00
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
pub fn prepare(
|
|
|
|
&self,
|
|
|
|
task_impl: impl TaskImpl,
|
|
|
|
context: Context,
|
|
|
|
) -> Result<TransitionStatus, TransitionError> {
|
2020-04-20 19:35:06 +00:00
|
|
|
let mut inner = self.0.lock().unwrap();
|
2020-03-05 15:59:13 +00:00
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
let origin = inner.state;
|
|
|
|
match origin {
|
2020-04-20 19:35:06 +00:00
|
|
|
TaskState::Unprepared => (),
|
2020-05-15 17:38:54 +00:00
|
|
|
TaskState::Prepared | TaskState::Preparing => {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "Task already {:?}", origin);
|
2020-05-15 17:38:54 +00:00
|
|
|
return Ok(TransitionStatus::Skipped {
|
2020-05-25 15:18:31 +00:00
|
|
|
trigger: Trigger::Prepare,
|
2020-05-15 17:38:54 +00:00
|
|
|
state: origin,
|
|
|
|
});
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
state => {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::warning!(RUNTIME_CAT, "Attempt to prepare Task in state {:?}", state);
|
2020-05-15 17:38:54 +00:00
|
|
|
return Err(TransitionError {
|
2020-05-25 15:18:31 +00:00
|
|
|
trigger: Trigger::Prepare,
|
2020-05-15 17:38:54 +00:00
|
|
|
state: inner.state,
|
2020-12-20 18:43:45 +00:00
|
|
|
err_msg: gst::error_msg!(
|
2020-05-15 17:38:54 +00:00
|
|
|
gst::CoreError::StateChange,
|
|
|
|
["Attempt to prepare Task in state {:?}", state]
|
|
|
|
),
|
|
|
|
});
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
}
|
2020-03-05 15:59:13 +00:00
|
|
|
|
2020-04-20 19:35:06 +00:00
|
|
|
assert!(inner.state_machine_handle.is_none());
|
2020-03-05 15:59:13 +00:00
|
|
|
|
2020-03-19 18:34:51 +00:00
|
|
|
inner.state = TaskState::Preparing;
|
2019-12-02 09:30:07 +00:00
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::log!(RUNTIME_CAT, "Spawning task state machine");
|
2020-03-05 15:59:13 +00:00
|
|
|
|
2020-04-20 19:35:06 +00:00
|
|
|
// FIXME allow configuration of the channel buffer size,
|
|
|
|
// this determines the contention on the Task.
|
2020-05-25 15:18:31 +00:00
|
|
|
let (triggering_evt_tx, triggering_evt_rx) = async_mpsc::channel(4);
|
|
|
|
let state_machine = StateMachine::new(Box::new(task_impl), triggering_evt_rx);
|
2020-05-15 21:37:56 +00:00
|
|
|
inner.state_machine_handle = Some(self.spawn_state_machine(state_machine, &context));
|
2020-03-05 15:59:13 +00:00
|
|
|
|
2020-05-25 15:18:31 +00:00
|
|
|
inner.triggering_evt_tx = Some(triggering_evt_tx);
|
2020-03-05 15:59:13 +00:00
|
|
|
inner.context = Some(context);
|
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
Ok(TransitionStatus::Async {
|
2020-05-25 15:18:31 +00:00
|
|
|
trigger: Trigger::Prepare,
|
2020-05-15 17:38:54 +00:00
|
|
|
origin,
|
|
|
|
})
|
2019-12-02 09:30:07 +00:00
|
|
|
}
|
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
pub fn unprepare(&self) -> Result<TransitionStatus, TransitionError> {
|
2020-03-05 15:59:13 +00:00
|
|
|
let mut inner = self.0.lock().unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
let origin = inner.state;
|
|
|
|
match origin {
|
|
|
|
TaskState::Stopped | TaskState::Error | TaskState::Prepared | TaskState::Preparing => {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "Unpreparing task");
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
2020-05-15 17:38:54 +00:00
|
|
|
TaskState::Unprepared | TaskState::Unpreparing => {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "Task already {:?}", origin);
|
2020-05-15 17:38:54 +00:00
|
|
|
return Ok(TransitionStatus::Skipped {
|
2020-05-25 15:18:31 +00:00
|
|
|
trigger: Trigger::Unprepare,
|
2020-05-15 17:38:54 +00:00
|
|
|
state: origin,
|
|
|
|
});
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
state => {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::warning!(
|
2020-04-20 19:35:06 +00:00
|
|
|
RUNTIME_CAT,
|
2020-05-15 17:38:54 +00:00
|
|
|
"Attempt to unprepare Task in state {:?}",
|
2020-04-20 19:35:06 +00:00
|
|
|
state
|
|
|
|
);
|
2020-05-15 17:38:54 +00:00
|
|
|
return Err(TransitionError {
|
2020-05-25 15:18:31 +00:00
|
|
|
trigger: Trigger::Unprepare,
|
2020-05-15 17:38:54 +00:00
|
|
|
state: inner.state,
|
2020-12-20 18:43:45 +00:00
|
|
|
err_msg: gst::error_msg!(
|
2020-05-15 17:38:54 +00:00
|
|
|
gst::CoreError::StateChange,
|
|
|
|
["Attempt to unprepare Task in state {:?}", state]
|
|
|
|
),
|
|
|
|
});
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
2020-03-05 15:59:13 +00:00
|
|
|
}
|
|
|
|
|
2020-04-20 19:35:06 +00:00
|
|
|
inner.state = TaskState::Unpreparing;
|
|
|
|
|
2022-03-21 11:40:28 +00:00
|
|
|
inner.abort_task_loop();
|
2020-03-05 15:59:13 +00:00
|
|
|
|
2020-05-25 15:18:31 +00:00
|
|
|
let _ = inner.trigger(Trigger::Unprepare).unwrap();
|
|
|
|
let triggering_evt_tx = inner.triggering_evt_tx.take().unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
|
|
|
let state_machine_handle = inner.state_machine_handle.take();
|
2020-03-05 15:59:13 +00:00
|
|
|
let context = inner.context.take().unwrap();
|
2020-03-19 18:34:51 +00:00
|
|
|
|
2020-04-20 19:35:06 +00:00
|
|
|
if let Some(prepare_abort_handle) = inner.prepare_abort_handle.take() {
|
|
|
|
prepare_abort_handle.abort();
|
|
|
|
}
|
2020-03-19 18:34:51 +00:00
|
|
|
|
2020-03-05 15:59:13 +00:00
|
|
|
drop(inner);
|
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
match state_machine_handle {
|
|
|
|
Some(state_machine_handle) => {
|
2022-03-22 16:18:18 +00:00
|
|
|
let state_machine_end_fut = async {
|
2020-05-15 21:37:56 +00:00
|
|
|
state_machine_handle.await;
|
2020-05-15 17:38:54 +00:00
|
|
|
|
2020-05-25 15:18:31 +00:00
|
|
|
drop(triggering_evt_tx);
|
2020-05-15 17:38:54 +00:00
|
|
|
drop(context);
|
2020-03-05 15:59:13 +00:00
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "Task unprepared");
|
2022-03-22 16:18:18 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
if let Some((cur_context, cur_task_id)) = Context::current_task() {
|
|
|
|
gst::log!(
|
|
|
|
RUNTIME_CAT,
|
|
|
|
"Will wait for state machine termination completion in subtask to task {:?} on context {}",
|
|
|
|
cur_task_id,
|
|
|
|
cur_context.name()
|
|
|
|
);
|
|
|
|
let _ = Context::add_sub_task(state_machine_end_fut.map(|_| Ok(())));
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
return Ok(TransitionStatus::Async {
|
2020-05-25 15:18:31 +00:00
|
|
|
trigger: Trigger::Unprepare,
|
2020-05-15 17:38:54 +00:00
|
|
|
origin,
|
|
|
|
});
|
2022-03-22 16:18:18 +00:00
|
|
|
} else {
|
|
|
|
gst::log!(
|
|
|
|
RUNTIME_CAT,
|
|
|
|
"Waiting for state machine termination on current thread"
|
|
|
|
);
|
|
|
|
// Use a light-weight executor, no timer nor async io.
|
|
|
|
futures::executor::block_on(state_machine_end_fut)
|
2020-05-15 17:38:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
None => {
|
2020-05-25 15:18:31 +00:00
|
|
|
drop(triggering_evt_tx);
|
2020-05-15 17:38:54 +00:00
|
|
|
drop(context);
|
|
|
|
}
|
|
|
|
}
|
2020-03-05 15:59:13 +00:00
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
Ok(TransitionStatus::Complete {
|
|
|
|
origin,
|
|
|
|
target: TaskState::Unprepared,
|
|
|
|
})
|
2020-03-05 15:59:13 +00:00
|
|
|
}
|
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
/// Starts the `Task`.
|
2019-12-02 09:30:07 +00:00
|
|
|
///
|
2020-04-20 19:35:06 +00:00
|
|
|
/// The execution occurs on the `Task` context.
|
2020-05-15 17:38:54 +00:00
|
|
|
pub fn start(&self) -> Result<TransitionStatus, TransitionError> {
|
2020-03-05 15:59:13 +00:00
|
|
|
let mut inner = self.0.lock().unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2020-05-25 15:18:31 +00:00
|
|
|
let ack_rx = inner.trigger(Trigger::Start)?;
|
2020-03-19 18:34:51 +00:00
|
|
|
|
2020-04-20 19:35:06 +00:00
|
|
|
if let TaskState::Started = inner.state {
|
2020-05-15 17:38:54 +00:00
|
|
|
return Ok(TransitionStatus::NotWaiting {
|
2020-05-25 15:18:31 +00:00
|
|
|
trigger: Trigger::Start,
|
2020-05-15 17:38:54 +00:00
|
|
|
origin: TaskState::Started,
|
|
|
|
});
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
2020-03-19 18:34:51 +00:00
|
|
|
|
2020-05-25 15:18:31 +00:00
|
|
|
Self::await_ack(inner, ack_rx, Trigger::Start)
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
2020-03-19 18:34:51 +00:00
|
|
|
|
2020-04-20 19:35:06 +00:00
|
|
|
/// Requests the `Task` loop to pause.
|
|
|
|
///
|
|
|
|
/// If an iteration is in progress, it will run to completion,
|
|
|
|
/// then no more iteration will be executed before `start` is called again.
|
|
|
|
/// Therefore, it is not guaranteed that `Paused` is reached when `pause` returns.
|
2020-05-15 17:38:54 +00:00
|
|
|
pub fn pause(&self) -> Result<TransitionStatus, TransitionError> {
|
2020-04-20 19:35:06 +00:00
|
|
|
let mut inner = self.0.lock().unwrap();
|
|
|
|
|
2020-05-25 15:18:31 +00:00
|
|
|
let ack_rx = inner.trigger(Trigger::Pause)?;
|
2020-05-15 17:38:54 +00:00
|
|
|
|
|
|
|
if let TaskState::Started = inner.state {
|
|
|
|
return Ok(TransitionStatus::NotWaiting {
|
2020-05-25 15:18:31 +00:00
|
|
|
trigger: Trigger::Pause,
|
2020-05-15 17:38:54 +00:00
|
|
|
origin: TaskState::Started,
|
|
|
|
});
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
2020-05-15 17:38:54 +00:00
|
|
|
|
2020-05-25 15:18:31 +00:00
|
|
|
Self::await_ack(inner, ack_rx, Trigger::Pause)
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
pub fn flush_start(&self) -> Result<TransitionStatus, TransitionError> {
|
2022-03-21 11:40:28 +00:00
|
|
|
self.abort_push_wakeup_await(Trigger::FlushStart)
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
pub fn flush_stop(&self) -> Result<TransitionStatus, TransitionError> {
|
2022-03-21 11:40:28 +00:00
|
|
|
self.abort_push_wakeup_await(Trigger::FlushStop)
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
2019-12-02 09:30:07 +00:00
|
|
|
|
2020-04-20 19:35:06 +00:00
|
|
|
/// Stops the `Started` `Task` and wait for it to finish.
|
2020-05-15 17:38:54 +00:00
|
|
|
pub fn stop(&self) -> Result<TransitionStatus, TransitionError> {
|
2022-03-21 11:40:28 +00:00
|
|
|
self.abort_push_wakeup_await(Trigger::Stop)
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
2020-03-05 15:59:13 +00:00
|
|
|
|
2022-03-21 11:40:28 +00:00
|
|
|
/// Pushes a [`Trigger`] which requires the iteration loop to abort ASAP.
|
|
|
|
///
|
|
|
|
/// This function:
|
|
|
|
/// - Makes sure the iteration loop aborts as soon as possible.
|
|
|
|
/// - Pushes the provided [`Trigger`].
|
|
|
|
/// - Awaits for the expected transition as usual.
|
|
|
|
fn abort_push_wakeup_await(
|
|
|
|
&self,
|
2020-05-25 15:18:31 +00:00
|
|
|
trigger: Trigger,
|
2020-05-15 17:38:54 +00:00
|
|
|
) -> Result<TransitionStatus, TransitionError> {
|
2022-03-21 11:40:28 +00:00
|
|
|
let mut inner = self.0.lock().unwrap();
|
2020-05-15 17:38:54 +00:00
|
|
|
|
2022-03-21 11:40:28 +00:00
|
|
|
inner.abort_task_loop();
|
|
|
|
let ack_rx = inner.trigger(trigger)?;
|
2020-05-25 15:18:31 +00:00
|
|
|
Self::await_ack(inner, ack_rx, trigger)
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
2020-03-05 15:59:13 +00:00
|
|
|
|
2020-04-20 19:35:06 +00:00
|
|
|
fn await_ack(
|
|
|
|
inner: MutexGuard<TaskInner>,
|
2020-05-15 17:38:54 +00:00
|
|
|
ack_rx: oneshot::Receiver<Result<TransitionStatus, TransitionError>>,
|
2020-05-25 15:18:31 +00:00
|
|
|
trigger: Trigger,
|
2020-05-15 17:38:54 +00:00
|
|
|
) -> Result<TransitionStatus, TransitionError> {
|
|
|
|
let origin = inner.state;
|
|
|
|
|
2020-05-25 15:18:31 +00:00
|
|
|
// Since triggering events handling is serialized by the state machine and
|
|
|
|
// we hold a lock on TaskInner, we can verify if current spawned loop / tansition action
|
2020-04-20 19:35:06 +00:00
|
|
|
// task_id matches the task_id of current subtask, if any.
|
|
|
|
if let Some(spawned_task_id) = inner.spawned_task_id {
|
|
|
|
if let Some((cur_context, cur_task_id)) = Context::current_task() {
|
|
|
|
if cur_task_id == spawned_task_id && &cur_context == inner.context.as_ref().unwrap()
|
|
|
|
{
|
|
|
|
// Don't block as this would deadlock
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::log!(
|
2020-04-20 19:35:06 +00:00
|
|
|
RUNTIME_CAT,
|
2020-05-25 15:18:31 +00:00
|
|
|
"Requested {:?} from loop or transition action, not waiting",
|
|
|
|
trigger,
|
2020-04-20 19:35:06 +00:00
|
|
|
);
|
2020-05-25 15:18:31 +00:00
|
|
|
return Ok(TransitionStatus::NotWaiting { trigger, origin });
|
2020-03-05 15:59:13 +00:00
|
|
|
}
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
drop(inner);
|
2020-03-19 18:34:51 +00:00
|
|
|
|
2022-03-22 16:18:18 +00:00
|
|
|
let ack_await_fut = async move {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::trace!(RUNTIME_CAT, "Awaiting ack for {:?}", trigger);
|
2020-05-15 17:38:54 +00:00
|
|
|
|
|
|
|
let res = ack_rx.await.unwrap();
|
|
|
|
if res.is_ok() {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::log!(RUNTIME_CAT, "Received ack {:?} for {:?}", res, trigger);
|
2020-04-20 19:35:06 +00:00
|
|
|
} else {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::error!(RUNTIME_CAT, "Received ack {:?} for {:?}", res, trigger);
|
2020-03-05 15:59:13 +00:00
|
|
|
}
|
2020-05-15 17:38:54 +00:00
|
|
|
|
|
|
|
res
|
2022-03-22 16:18:18 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
if let Some((cur_context, cur_task_id)) = Context::current_task() {
|
|
|
|
gst::log!(
|
|
|
|
RUNTIME_CAT,
|
|
|
|
"Will await ack for {:?} in subtask to task {:?} on context {}",
|
|
|
|
trigger,
|
|
|
|
cur_task_id,
|
|
|
|
cur_context.name()
|
|
|
|
);
|
|
|
|
let _ = Context::add_sub_task(ack_await_fut.map(|_| Ok(())));
|
|
|
|
|
2020-05-25 15:18:31 +00:00
|
|
|
Ok(TransitionStatus::Async { trigger, origin })
|
2022-03-22 16:18:18 +00:00
|
|
|
} else {
|
|
|
|
gst::log!(
|
|
|
|
RUNTIME_CAT,
|
|
|
|
"Awaiting ack for {:?} on current thread",
|
|
|
|
trigger
|
|
|
|
);
|
|
|
|
// Use a light-weight executor, no timer nor async io.
|
|
|
|
futures::executor::block_on(ack_await_fut)
|
|
|
|
}
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
2020-05-15 21:37:56 +00:00
|
|
|
|
|
|
|
fn spawn_state_machine(
|
|
|
|
&self,
|
|
|
|
state_machine: StateMachine,
|
|
|
|
context: &Context,
|
|
|
|
) -> RemoteHandle<()> {
|
|
|
|
use futures::executor::ThreadPool;
|
|
|
|
use futures::task::SpawnExt;
|
|
|
|
use once_cell::sync::OnceCell;
|
|
|
|
|
|
|
|
static EXECUTOR: OnceCell<ThreadPool> = OnceCell::new();
|
|
|
|
EXECUTOR
|
|
|
|
.get_or_init(|| ThreadPool::builder().pool_size(1).create().unwrap())
|
|
|
|
.spawn_with_handle(state_machine.run(Arc::clone(&self.0), context.clone()))
|
|
|
|
.unwrap()
|
|
|
|
}
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
2019-12-02 09:30:07 +00:00
|
|
|
|
2020-04-30 16:49:32 +00:00
|
|
|
struct StateMachine {
|
|
|
|
task_impl: Box<dyn TaskImpl>,
|
2020-05-25 15:18:31 +00:00
|
|
|
triggering_evt_rx: async_mpsc::Receiver<TriggeringEvent>,
|
|
|
|
pending_triggering_evt: Option<TriggeringEvent>,
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
2020-03-05 15:59:13 +00:00
|
|
|
|
2020-05-25 15:18:31 +00:00
|
|
|
// Make sure the Context doesn't throttle otherwise we end up with long delays executing
|
|
|
|
// transition actions in a pipeline with many elements. This is because pipeline serializes
|
|
|
|
// the transition actions and the Context's scheduler gets a chance to reach its throttling
|
|
|
|
// state between 2 elements.
|
2020-03-19 18:34:51 +00:00
|
|
|
|
2020-05-25 15:18:31 +00:00
|
|
|
macro_rules! exec_action {
|
|
|
|
($self:ident, $action:ident, $triggering_evt:expr, $origin:expr, $task_inner:expr, $context:expr) => {{
|
|
|
|
let trigger = $triggering_evt.trigger;
|
2020-05-15 17:38:54 +00:00
|
|
|
|
2020-05-25 15:18:31 +00:00
|
|
|
let action_fut = async move {
|
|
|
|
let mut res = $self.task_impl.$action().await;
|
2020-05-15 17:38:54 +00:00
|
|
|
|
|
|
|
if res.is_ok() {
|
|
|
|
while Context::current_has_sub_tasks() {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::trace!(RUNTIME_CAT, "Draining subtasks for {}", stringify!($action));
|
2020-05-15 17:38:54 +00:00
|
|
|
res = Context::drain_sub_tasks().await.map_err(|err| {
|
2020-05-25 15:18:31 +00:00
|
|
|
let msg = format!("{} subtask returned {:?}", stringify!($action), err);
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::log!(RUNTIME_CAT, "{}", &msg);
|
2020-12-20 18:43:45 +00:00
|
|
|
gst::error_msg!(gst::CoreError::StateChange, ["{}", &msg])
|
2020-05-15 17:38:54 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
if res.is_err() {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let res = match res {
|
|
|
|
Ok(()) => Ok(()),
|
|
|
|
Err(err) => {
|
2020-05-25 15:18:31 +00:00
|
|
|
let next_triggering_evt = $self
|
2020-05-15 17:38:54 +00:00
|
|
|
.task_impl
|
2020-05-25 15:18:31 +00:00
|
|
|
.handle_action_error(trigger, $origin, err)
|
2020-05-15 17:38:54 +00:00
|
|
|
.await;
|
2020-05-25 15:18:31 +00:00
|
|
|
Err(next_triggering_evt)
|
2020-05-15 17:38:54 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
($self, res)
|
2020-04-20 19:35:06 +00:00
|
|
|
};
|
2020-03-19 18:34:51 +00:00
|
|
|
|
2020-04-20 19:35:06 +00:00
|
|
|
let join_handle = {
|
|
|
|
let mut task_inner = $task_inner.lock().unwrap();
|
2021-12-14 18:40:27 +00:00
|
|
|
let join_handle = $context.spawn_and_awake(action_fut);
|
2020-04-20 19:35:06 +00:00
|
|
|
task_inner.spawned_task_id = Some(join_handle.task_id());
|
2020-03-19 18:34:51 +00:00
|
|
|
|
2020-04-20 19:35:06 +00:00
|
|
|
join_handle
|
|
|
|
};
|
2020-03-19 18:34:51 +00:00
|
|
|
|
2021-12-14 18:40:27 +00:00
|
|
|
let (this, res) = join_handle.await.unwrap();
|
2020-05-15 17:38:54 +00:00
|
|
|
$self = this;
|
|
|
|
|
|
|
|
match res {
|
2020-05-25 15:18:31 +00:00
|
|
|
Ok(()) => Ok($triggering_evt),
|
|
|
|
Err(next_trigger) => {
|
|
|
|
// Convert triggering event according to the error handler's decision
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::trace!(
|
2020-05-15 17:38:54 +00:00
|
|
|
RUNTIME_CAT,
|
2020-05-25 15:18:31 +00:00
|
|
|
"TaskImpl transition action error: converting {:?} to {:?}",
|
|
|
|
$triggering_evt.trigger,
|
|
|
|
next_trigger,
|
2020-05-15 17:38:54 +00:00
|
|
|
);
|
|
|
|
|
2020-05-25 15:18:31 +00:00
|
|
|
$triggering_evt.trigger = next_trigger;
|
|
|
|
$self.pending_triggering_evt = Some($triggering_evt);
|
2020-05-15 17:38:54 +00:00
|
|
|
|
|
|
|
Err(())
|
|
|
|
}
|
|
|
|
}
|
2020-04-20 19:35:06 +00:00
|
|
|
}};
|
|
|
|
}
|
|
|
|
|
2020-04-30 16:49:32 +00:00
|
|
|
impl StateMachine {
|
|
|
|
// Use dynamic dispatch for TaskImpl as it reduces memory usage compared to monomorphization
|
|
|
|
// without inducing any significant performance penalties.
|
2020-05-15 17:38:54 +00:00
|
|
|
fn new(
|
|
|
|
task_impl: Box<dyn TaskImpl>,
|
2020-05-25 15:18:31 +00:00
|
|
|
triggering_evt_rx: async_mpsc::Receiver<TriggeringEvent>,
|
2020-05-15 17:38:54 +00:00
|
|
|
) -> Self {
|
2020-04-20 19:35:06 +00:00
|
|
|
StateMachine {
|
|
|
|
task_impl,
|
2020-05-25 15:18:31 +00:00
|
|
|
triggering_evt_rx,
|
|
|
|
pending_triggering_evt: None,
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-15 21:37:56 +00:00
|
|
|
async fn run(mut self, task_inner: Arc<Mutex<TaskInner>>, context: Context) {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::trace!(RUNTIME_CAT, "Preparing task");
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
{
|
2020-05-25 15:18:31 +00:00
|
|
|
let (mut triggering_evt, _) = TriggeringEvent::new(Trigger::Prepare);
|
|
|
|
let res = exec_action!(
|
2020-05-15 17:38:54 +00:00
|
|
|
self,
|
|
|
|
prepare,
|
2020-05-25 15:18:31 +00:00
|
|
|
triggering_evt,
|
2020-05-15 17:38:54 +00:00
|
|
|
TaskState::Preparing,
|
|
|
|
&task_inner,
|
|
|
|
&context
|
|
|
|
);
|
2020-05-25 15:18:31 +00:00
|
|
|
if let Ok(triggering_evt) = res {
|
2020-05-15 17:38:54 +00:00
|
|
|
task_inner
|
|
|
|
.lock()
|
|
|
|
.unwrap()
|
2020-05-25 15:18:31 +00:00
|
|
|
.switch_to_state(TaskState::Prepared, triggering_evt);
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::trace!(RUNTIME_CAT, "Task Prepared");
|
2020-05-15 17:38:54 +00:00
|
|
|
}
|
|
|
|
}
|
2020-04-20 19:35:06 +00:00
|
|
|
|
|
|
|
loop {
|
2020-05-25 15:18:31 +00:00
|
|
|
let mut triggering_evt = match self.pending_triggering_evt.take() {
|
|
|
|
Some(pending_triggering_evt) => pending_triggering_evt,
|
2020-04-20 19:35:06 +00:00
|
|
|
None => self
|
2020-05-25 15:18:31 +00:00
|
|
|
.triggering_evt_rx
|
2020-04-20 19:35:06 +00:00
|
|
|
.next()
|
|
|
|
.await
|
2020-05-25 15:18:31 +00:00
|
|
|
.expect("triggering_evt_rx dropped"),
|
2020-04-20 19:35:06 +00:00
|
|
|
};
|
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::trace!(RUNTIME_CAT, "State machine popped {:?}", triggering_evt);
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2020-05-25 15:18:31 +00:00
|
|
|
match triggering_evt.trigger {
|
|
|
|
Trigger::Error => {
|
2020-05-15 17:38:54 +00:00
|
|
|
let mut task_inner = task_inner.lock().unwrap();
|
2020-05-25 15:18:31 +00:00
|
|
|
task_inner.switch_to_err(triggering_evt);
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::trace!(RUNTIME_CAT, "Switched to Error");
|
2020-05-15 17:38:54 +00:00
|
|
|
}
|
2020-05-25 15:18:31 +00:00
|
|
|
Trigger::Start => {
|
2020-05-15 17:38:54 +00:00
|
|
|
let origin = {
|
2020-04-20 19:35:06 +00:00
|
|
|
let mut task_inner = task_inner.lock().unwrap();
|
2020-05-15 17:38:54 +00:00
|
|
|
let origin = task_inner.state;
|
|
|
|
match origin {
|
2020-04-20 19:35:06 +00:00
|
|
|
TaskState::Stopped | TaskState::Paused | TaskState::Prepared => (),
|
|
|
|
TaskState::PausedFlushing => {
|
2020-05-25 15:18:31 +00:00
|
|
|
task_inner.switch_to_state(TaskState::Flushing, triggering_evt);
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::trace!(
|
|
|
|
RUNTIME_CAT,
|
|
|
|
"Switched from PausedFlushing to Flushing"
|
|
|
|
);
|
2020-04-20 19:35:06 +00:00
|
|
|
continue;
|
|
|
|
}
|
2020-05-15 17:38:54 +00:00
|
|
|
TaskState::Error => {
|
2020-05-25 15:18:31 +00:00
|
|
|
triggering_evt.send_err_ack();
|
2020-05-15 17:38:54 +00:00
|
|
|
continue;
|
|
|
|
}
|
2020-04-20 19:35:06 +00:00
|
|
|
state => {
|
2020-05-25 15:18:31 +00:00
|
|
|
task_inner.skip_triggering_evt(triggering_evt);
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::trace!(RUNTIME_CAT, "Skipped Start in state {:?}", state);
|
2020-04-20 19:35:06 +00:00
|
|
|
continue;
|
|
|
|
}
|
2020-04-03 14:31:44 +00:00
|
|
|
}
|
2020-03-05 15:59:13 +00:00
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
origin
|
|
|
|
};
|
|
|
|
|
|
|
|
self =
|
2020-05-25 15:18:31 +00:00
|
|
|
Self::spawn_loop(self, triggering_evt, origin, &task_inner, &context).await;
|
|
|
|
// next/pending triggering event handled in next iteration
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
2020-05-25 15:18:31 +00:00
|
|
|
Trigger::Pause => {
|
2020-05-15 17:38:54 +00:00
|
|
|
let (origin, target) = {
|
2020-04-20 19:35:06 +00:00
|
|
|
let mut task_inner = task_inner.lock().unwrap();
|
2020-05-15 17:38:54 +00:00
|
|
|
let origin = task_inner.state;
|
|
|
|
match origin {
|
2020-04-20 19:35:06 +00:00
|
|
|
TaskState::Started | TaskState::Stopped | TaskState::Prepared => {
|
2020-05-15 17:38:54 +00:00
|
|
|
(origin, TaskState::Paused)
|
|
|
|
}
|
|
|
|
TaskState::Flushing => (origin, TaskState::PausedFlushing),
|
|
|
|
TaskState::Error => {
|
2020-05-25 15:18:31 +00:00
|
|
|
triggering_evt.send_err_ack();
|
2020-05-15 17:38:54 +00:00
|
|
|
continue;
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
state => {
|
2020-05-25 15:18:31 +00:00
|
|
|
task_inner.skip_triggering_evt(triggering_evt);
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::trace!(RUNTIME_CAT, "Skipped Pause in state {:?}", state);
|
2020-04-20 19:35:06 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2020-03-05 15:59:13 +00:00
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
let res =
|
2020-05-25 15:18:31 +00:00
|
|
|
exec_action!(self, pause, triggering_evt, origin, &task_inner, &context);
|
|
|
|
if let Ok(triggering_evt) = res {
|
2020-05-15 17:38:54 +00:00
|
|
|
task_inner
|
|
|
|
.lock()
|
|
|
|
.unwrap()
|
2020-05-25 15:18:31 +00:00
|
|
|
.switch_to_state(target, triggering_evt);
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::trace!(RUNTIME_CAT, "Task loop {:?}", target);
|
2020-05-15 17:38:54 +00:00
|
|
|
}
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
2020-05-25 15:18:31 +00:00
|
|
|
Trigger::Stop => {
|
2020-05-15 17:38:54 +00:00
|
|
|
let origin = {
|
2020-04-20 19:35:06 +00:00
|
|
|
let mut task_inner = task_inner.lock().unwrap();
|
2020-05-15 17:38:54 +00:00
|
|
|
let origin = task_inner.state;
|
|
|
|
match origin {
|
2020-04-20 19:35:06 +00:00
|
|
|
TaskState::Started
|
|
|
|
| TaskState::Paused
|
|
|
|
| TaskState::PausedFlushing
|
2020-05-15 17:38:54 +00:00
|
|
|
| TaskState::Flushing => origin,
|
|
|
|
TaskState::Error => {
|
2020-05-25 15:18:31 +00:00
|
|
|
triggering_evt.send_err_ack();
|
2020-05-15 17:38:54 +00:00
|
|
|
continue;
|
|
|
|
}
|
2020-04-20 19:35:06 +00:00
|
|
|
state => {
|
2020-05-25 15:18:31 +00:00
|
|
|
task_inner.skip_triggering_evt(triggering_evt);
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::trace!(RUNTIME_CAT, "Skipped Stop in state {:?}", state);
|
2020-04-20 19:35:06 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
2020-05-15 17:38:54 +00:00
|
|
|
};
|
2020-03-05 15:59:13 +00:00
|
|
|
|
2020-05-25 15:18:31 +00:00
|
|
|
let res =
|
|
|
|
exec_action!(self, stop, triggering_evt, origin, &task_inner, &context);
|
|
|
|
if let Ok(triggering_evt) = res {
|
2020-05-15 17:38:54 +00:00
|
|
|
task_inner
|
|
|
|
.lock()
|
|
|
|
.unwrap()
|
2020-05-25 15:18:31 +00:00
|
|
|
.switch_to_state(TaskState::Stopped, triggering_evt);
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::trace!(RUNTIME_CAT, "Task loop Stopped");
|
2020-05-15 17:38:54 +00:00
|
|
|
}
|
2020-03-05 15:59:13 +00:00
|
|
|
}
|
2020-05-25 15:18:31 +00:00
|
|
|
Trigger::FlushStart => {
|
2020-05-15 17:38:54 +00:00
|
|
|
let (origin, target) = {
|
2020-04-20 19:35:06 +00:00
|
|
|
let mut task_inner = task_inner.lock().unwrap();
|
2020-05-15 17:38:54 +00:00
|
|
|
let origin = task_inner.state;
|
|
|
|
match origin {
|
|
|
|
TaskState::Started => (origin, TaskState::Flushing),
|
|
|
|
TaskState::Paused => (origin, TaskState::PausedFlushing),
|
|
|
|
TaskState::Error => {
|
2020-05-25 15:18:31 +00:00
|
|
|
triggering_evt.send_err_ack();
|
2020-05-15 17:38:54 +00:00
|
|
|
continue;
|
|
|
|
}
|
2020-04-20 19:35:06 +00:00
|
|
|
state => {
|
2020-05-25 15:18:31 +00:00
|
|
|
task_inner.skip_triggering_evt(triggering_evt);
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::trace!(RUNTIME_CAT, "Skipped FlushStart in state {:?}", state);
|
2020-04-20 19:35:06 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-05-25 15:18:31 +00:00
|
|
|
let res = exec_action!(
|
2020-05-15 17:38:54 +00:00
|
|
|
self,
|
|
|
|
flush_start,
|
2020-05-25 15:18:31 +00:00
|
|
|
triggering_evt,
|
2020-05-15 17:38:54 +00:00
|
|
|
origin,
|
|
|
|
&task_inner,
|
|
|
|
&context
|
|
|
|
);
|
2020-05-25 15:18:31 +00:00
|
|
|
if let Ok(triggering_evt) = res {
|
2020-05-15 17:38:54 +00:00
|
|
|
task_inner
|
|
|
|
.lock()
|
|
|
|
.unwrap()
|
2020-05-25 15:18:31 +00:00
|
|
|
.switch_to_state(target, triggering_evt);
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::trace!(RUNTIME_CAT, "Task {:?}", target);
|
2020-05-15 17:38:54 +00:00
|
|
|
}
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
2020-05-25 15:18:31 +00:00
|
|
|
Trigger::FlushStop => {
|
2020-05-15 17:38:54 +00:00
|
|
|
let origin = task_inner.lock().unwrap().state;
|
|
|
|
let is_paused = match origin {
|
|
|
|
TaskState::Flushing => false,
|
|
|
|
TaskState::PausedFlushing => true,
|
|
|
|
TaskState::Error => {
|
2020-05-25 15:18:31 +00:00
|
|
|
triggering_evt.send_err_ack();
|
2020-04-20 19:35:06 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
state => {
|
2020-05-25 15:18:31 +00:00
|
|
|
task_inner
|
|
|
|
.lock()
|
|
|
|
.unwrap()
|
|
|
|
.skip_triggering_evt(triggering_evt);
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::trace!(RUNTIME_CAT, "Skipped FlushStop in state {:?}", state);
|
2020-04-20 19:35:06 +00:00
|
|
|
continue;
|
|
|
|
}
|
2020-05-15 17:38:54 +00:00
|
|
|
};
|
2020-03-19 18:34:51 +00:00
|
|
|
|
2020-05-25 15:18:31 +00:00
|
|
|
let res = exec_action!(
|
2020-05-15 17:38:54 +00:00
|
|
|
self,
|
|
|
|
flush_stop,
|
2020-05-25 15:18:31 +00:00
|
|
|
triggering_evt,
|
2020-05-15 17:38:54 +00:00
|
|
|
origin,
|
|
|
|
&task_inner,
|
|
|
|
&context
|
|
|
|
);
|
2020-05-25 15:18:31 +00:00
|
|
|
if let Ok(triggering_evt) = res {
|
2020-05-15 17:38:54 +00:00
|
|
|
if is_paused {
|
|
|
|
task_inner
|
|
|
|
.lock()
|
|
|
|
.unwrap()
|
2020-05-25 15:18:31 +00:00
|
|
|
.switch_to_state(TaskState::Paused, triggering_evt);
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::trace!(RUNTIME_CAT, "Switched from PausedFlushing to Paused");
|
2020-05-15 17:38:54 +00:00
|
|
|
} else {
|
|
|
|
self = Self::spawn_loop(
|
|
|
|
self,
|
2020-05-25 15:18:31 +00:00
|
|
|
triggering_evt,
|
2020-05-15 17:38:54 +00:00
|
|
|
origin,
|
|
|
|
&task_inner,
|
|
|
|
&context,
|
|
|
|
)
|
|
|
|
.await;
|
2020-05-25 15:18:31 +00:00
|
|
|
// next/pending triggering event handled in next iteration
|
2020-05-15 17:38:54 +00:00
|
|
|
}
|
|
|
|
}
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
2020-05-25 15:18:31 +00:00
|
|
|
Trigger::Unprepare => {
|
2020-05-15 17:38:54 +00:00
|
|
|
// Unprepare is not joined by an ack_rx but by joining the state machine
|
|
|
|
// handle, so we don't need to keep track of the spwaned_task_id
|
|
|
|
context
|
2021-12-14 18:40:27 +00:00
|
|
|
.spawn_and_awake(async move {
|
2020-05-15 17:38:54 +00:00
|
|
|
self.task_impl.unprepare().await;
|
|
|
|
|
|
|
|
while Context::current_has_sub_tasks() {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::trace!(RUNTIME_CAT, "Draining subtasks for unprepare");
|
2020-05-15 17:38:54 +00:00
|
|
|
let res = Context::drain_sub_tasks().await.map_err(|err| {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::log!(RUNTIME_CAT, "unprepare subtask returned {:?}", err);
|
2020-05-15 17:38:54 +00:00
|
|
|
err
|
|
|
|
});
|
|
|
|
if res.is_err() {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
2020-04-20 19:35:06 +00:00
|
|
|
task_inner
|
|
|
|
.lock()
|
|
|
|
.unwrap()
|
2020-05-25 15:18:31 +00:00
|
|
|
.switch_to_state(TaskState::Unprepared, triggering_evt);
|
2020-03-05 15:59:13 +00:00
|
|
|
|
2020-04-20 19:35:06 +00:00
|
|
|
break;
|
2020-03-05 15:59:13 +00:00
|
|
|
}
|
2020-05-25 15:18:31 +00:00
|
|
|
_ => unreachable!("State machine handler {:?}", triggering_evt),
|
2019-12-02 09:30:07 +00:00
|
|
|
}
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
2020-03-05 15:59:13 +00:00
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::trace!(RUNTIME_CAT, "Task state machine terminated");
|
2019-12-02 09:30:07 +00:00
|
|
|
}
|
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
async fn spawn_loop(
|
2020-04-20 19:35:06 +00:00
|
|
|
mut self,
|
2020-05-25 15:18:31 +00:00
|
|
|
mut triggering_evt: TriggeringEvent,
|
2020-05-15 17:38:54 +00:00
|
|
|
origin: TaskState,
|
2020-04-20 19:35:06 +00:00
|
|
|
task_inner: &Arc<Mutex<TaskInner>>,
|
|
|
|
context: &Context,
|
|
|
|
) -> Self {
|
2021-07-30 10:53:35 +00:00
|
|
|
let task_inner_clone = Arc::clone(task_inner);
|
2020-05-15 17:38:54 +00:00
|
|
|
let loop_fut = async move {
|
|
|
|
let mut res = self.task_impl.start().await;
|
|
|
|
if res.is_ok() {
|
|
|
|
while Context::current_has_sub_tasks() {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::trace!(RUNTIME_CAT, "Draining subtasks for start");
|
2020-05-15 17:38:54 +00:00
|
|
|
res = Context::drain_sub_tasks().await.map_err(|err| {
|
|
|
|
let msg = format!("start subtask returned {:?}", err);
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::log!(RUNTIME_CAT, "{}", &msg);
|
2020-12-20 18:43:45 +00:00
|
|
|
gst::error_msg!(gst::CoreError::StateChange, ["{}", &msg])
|
2020-05-15 17:38:54 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
if res.is_err() {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-04-20 19:35:06 +00:00
|
|
|
|
|
|
|
match res {
|
|
|
|
Ok(()) => {
|
2020-05-15 17:38:54 +00:00
|
|
|
let abortable_task_loop = {
|
|
|
|
let (abortable_task_loop, loop_abort_handle) =
|
|
|
|
abortable(self.run_loop(Arc::clone(&task_inner_clone)));
|
|
|
|
|
2020-04-20 19:35:06 +00:00
|
|
|
let mut task_inner = task_inner_clone.lock().unwrap();
|
2020-05-15 17:38:54 +00:00
|
|
|
task_inner.loop_abort_handle = Some(loop_abort_handle);
|
2020-05-25 15:18:31 +00:00
|
|
|
task_inner.switch_to_state(TaskState::Started, triggering_evt);
|
2019-12-02 09:30:07 +00:00
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
abortable_task_loop
|
|
|
|
};
|
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::trace!(RUNTIME_CAT, "Starting task loop");
|
2020-05-15 17:38:54 +00:00
|
|
|
match abortable_task_loop.await {
|
|
|
|
Ok(Ok(())) => (),
|
|
|
|
Ok(Err(err)) => {
|
2020-05-25 15:18:31 +00:00
|
|
|
let next_trigger = self.task_impl.handle_iterate_error(err).await;
|
|
|
|
let (triggering_evt, _) = TriggeringEvent::new(next_trigger);
|
|
|
|
self.pending_triggering_evt = Some(triggering_evt);
|
2020-05-15 17:38:54 +00:00
|
|
|
}
|
2022-02-21 17:43:46 +00:00
|
|
|
Err(Aborted) => gst::trace!(RUNTIME_CAT, "Task loop aborted"),
|
2020-05-15 17:38:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Err(err) => {
|
2020-05-25 15:18:31 +00:00
|
|
|
// Error while executing start transition action
|
|
|
|
let next_trigger = self
|
2020-05-15 17:38:54 +00:00
|
|
|
.task_impl
|
2020-05-25 15:18:31 +00:00
|
|
|
.handle_action_error(triggering_evt.trigger, origin, err)
|
2020-05-15 17:38:54 +00:00
|
|
|
.await;
|
2019-12-02 09:30:07 +00:00
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::log!(
|
2020-04-20 19:35:06 +00:00
|
|
|
RUNTIME_CAT,
|
2020-05-25 15:18:31 +00:00
|
|
|
"TaskImpl transition action error: converting Start to {:?}",
|
|
|
|
next_trigger,
|
2020-04-20 19:35:06 +00:00
|
|
|
);
|
|
|
|
|
2020-05-25 15:18:31 +00:00
|
|
|
triggering_evt.trigger = next_trigger;
|
|
|
|
self.pending_triggering_evt = Some(triggering_evt);
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
}
|
2020-03-05 15:59:13 +00:00
|
|
|
|
2020-05-25 15:18:31 +00:00
|
|
|
// next/pending triggering event handled in state machine loop
|
2020-05-15 17:38:54 +00:00
|
|
|
|
2020-04-20 19:35:06 +00:00
|
|
|
self
|
|
|
|
};
|
2019-11-30 18:51:31 +00:00
|
|
|
|
2020-04-20 19:35:06 +00:00
|
|
|
let join_handle = {
|
|
|
|
let mut task_inner = task_inner.lock().unwrap();
|
2021-12-14 18:40:27 +00:00
|
|
|
let join_handle = context.spawn_and_awake(loop_fut);
|
2020-04-20 19:35:06 +00:00
|
|
|
task_inner.spawned_task_id = Some(join_handle.task_id());
|
2020-03-19 18:34:51 +00:00
|
|
|
|
2020-04-20 19:35:06 +00:00
|
|
|
join_handle
|
|
|
|
};
|
2020-03-19 18:34:51 +00:00
|
|
|
|
2021-12-14 18:40:27 +00:00
|
|
|
join_handle.await.unwrap()
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
2020-03-05 15:59:13 +00:00
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
async fn run_loop(&mut self, task_inner: Arc<Mutex<TaskInner>>) -> Result<(), gst::FlowError> {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::trace!(RUNTIME_CAT, "Task loop started");
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
loop {
|
2020-05-25 15:18:31 +00:00
|
|
|
while let Ok(Some(triggering_evt)) = self.triggering_evt_rx.try_next() {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::trace!(RUNTIME_CAT, "Task loop popped {:?}", triggering_evt);
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2020-05-25 15:18:31 +00:00
|
|
|
match triggering_evt.trigger {
|
|
|
|
Trigger::Start => {
|
|
|
|
task_inner
|
|
|
|
.lock()
|
|
|
|
.unwrap()
|
|
|
|
.skip_triggering_evt(triggering_evt);
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::trace!(RUNTIME_CAT, "Skipped Start in state Started");
|
2020-05-15 17:38:54 +00:00
|
|
|
}
|
|
|
|
_ => {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::trace!(
|
2020-05-15 17:38:54 +00:00
|
|
|
RUNTIME_CAT,
|
|
|
|
"Task loop handing {:?} to state machine",
|
2020-05-25 15:18:31 +00:00
|
|
|
triggering_evt,
|
2020-05-15 17:38:54 +00:00
|
|
|
);
|
2020-05-25 15:18:31 +00:00
|
|
|
self.pending_triggering_evt = Some(triggering_evt);
|
2020-05-15 17:38:54 +00:00
|
|
|
return Ok(());
|
|
|
|
}
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
2020-03-05 15:59:13 +00:00
|
|
|
}
|
2019-12-02 09:30:07 +00:00
|
|
|
|
2020-05-25 15:18:31 +00:00
|
|
|
// Run the iteration function
|
2020-05-15 17:38:54 +00:00
|
|
|
self.task_impl.iterate().await.map_err(|err| {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::log!(RUNTIME_CAT, "Task loop iterate impl returned {:?}", err);
|
2020-05-15 17:38:54 +00:00
|
|
|
err
|
|
|
|
})?;
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
while Context::current_has_sub_tasks() {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::trace!(RUNTIME_CAT, "Draining subtasks for {}", stringify!($action));
|
2020-05-15 17:38:54 +00:00
|
|
|
Context::drain_sub_tasks().await.map_err(|err| {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::log!(RUNTIME_CAT, "Task loop iterate subtask returned {:?}", err);
|
2020-05-15 17:38:54 +00:00
|
|
|
err
|
|
|
|
})?;
|
|
|
|
}
|
|
|
|
}
|
2019-12-02 09:30:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2020-03-05 15:59:13 +00:00
|
|
|
use futures::channel::{mpsc, oneshot};
|
2021-11-25 18:26:26 +00:00
|
|
|
use futures::executor::block_on;
|
2020-04-20 19:35:06 +00:00
|
|
|
use std::time::Duration;
|
2019-12-02 09:30:07 +00:00
|
|
|
|
|
|
|
use crate::runtime::Context;
|
|
|
|
|
|
|
|
use super::*;
|
|
|
|
|
2021-11-25 18:26:26 +00:00
|
|
|
#[test]
|
|
|
|
fn iterate() {
|
2019-12-02 09:30:07 +00:00
|
|
|
gst::init().unwrap();
|
|
|
|
|
2020-04-20 19:35:06 +00:00
|
|
|
struct TaskTest {
|
|
|
|
prepared_sender: mpsc::Sender<()>,
|
|
|
|
started_sender: mpsc::Sender<()>,
|
|
|
|
iterate_sender: mpsc::Sender<()>,
|
|
|
|
complete_iterate_receiver: mpsc::Receiver<Result<(), gst::FlowError>>,
|
|
|
|
paused_sender: mpsc::Sender<()>,
|
|
|
|
stopped_sender: mpsc::Sender<()>,
|
|
|
|
flush_start_sender: mpsc::Sender<()>,
|
|
|
|
unprepared_sender: mpsc::Sender<()>,
|
|
|
|
}
|
2019-12-02 09:30:07 +00:00
|
|
|
|
2020-04-20 19:35:06 +00:00
|
|
|
impl TaskImpl for TaskTest {
|
|
|
|
fn prepare(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
|
|
|
|
async move {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "task_iterate: prepared");
|
2020-04-20 19:35:06 +00:00
|
|
|
self.prepared_sender.send(()).await.unwrap();
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
.boxed()
|
|
|
|
}
|
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
fn start(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
|
2020-04-20 19:35:06 +00:00
|
|
|
async move {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "task_iterate: started");
|
2020-04-20 19:35:06 +00:00
|
|
|
self.started_sender.send(()).await.unwrap();
|
2020-05-15 17:38:54 +00:00
|
|
|
Ok(())
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
.boxed()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn iterate(&mut self) -> BoxFuture<'_, Result<(), gst::FlowError>> {
|
|
|
|
async move {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "task_iterate: entering iterate");
|
2020-04-20 19:35:06 +00:00
|
|
|
self.iterate_sender.send(()).await.unwrap();
|
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(
|
2020-04-20 19:35:06 +00:00
|
|
|
RUNTIME_CAT,
|
|
|
|
"task_iterate: awaiting complete_iterate_receiver"
|
|
|
|
);
|
|
|
|
|
|
|
|
let res = self.complete_iterate_receiver.next().await.unwrap();
|
|
|
|
if res.is_ok() {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "task_iterate: received Ok => keep looping");
|
2020-04-20 19:35:06 +00:00
|
|
|
} else {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(
|
2020-04-20 19:35:06 +00:00
|
|
|
RUNTIME_CAT,
|
2020-05-15 17:38:54 +00:00
|
|
|
"task_iterate: received {:?} => cancelling loop",
|
|
|
|
res
|
2020-04-20 19:35:06 +00:00
|
|
|
);
|
2020-03-05 15:59:13 +00:00
|
|
|
}
|
2020-04-20 19:35:06 +00:00
|
|
|
|
|
|
|
res
|
2019-12-02 09:30:07 +00:00
|
|
|
}
|
2020-04-20 19:35:06 +00:00
|
|
|
.boxed()
|
2019-12-02 09:30:07 +00:00
|
|
|
}
|
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
fn pause(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
|
2020-04-20 19:35:06 +00:00
|
|
|
async move {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "task_iterate: paused");
|
2020-04-20 19:35:06 +00:00
|
|
|
self.paused_sender.send(()).await.unwrap();
|
2020-05-15 17:38:54 +00:00
|
|
|
Ok(())
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
.boxed()
|
|
|
|
}
|
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
fn stop(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
|
2020-04-20 19:35:06 +00:00
|
|
|
async move {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "task_iterate: stopped");
|
2020-04-20 19:35:06 +00:00
|
|
|
self.stopped_sender.send(()).await.unwrap();
|
2020-05-15 17:38:54 +00:00
|
|
|
Ok(())
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
.boxed()
|
|
|
|
}
|
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
fn flush_start(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
|
2020-04-20 19:35:06 +00:00
|
|
|
async move {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "task_iterate: stopped");
|
2020-04-20 19:35:06 +00:00
|
|
|
self.flush_start_sender.send(()).await.unwrap();
|
2020-05-15 17:38:54 +00:00
|
|
|
Ok(())
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
.boxed()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn unprepare(&mut self) -> BoxFuture<'_, ()> {
|
|
|
|
async move {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "task_iterate: unprepared");
|
2020-04-20 19:35:06 +00:00
|
|
|
self.unprepared_sender.send(()).await.unwrap();
|
|
|
|
}
|
|
|
|
.boxed()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-26 09:54:34 +00:00
|
|
|
let context = Context::acquire("task_iterate", Duration::from_millis(2)).unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
|
|
|
let task = Task::default();
|
|
|
|
|
|
|
|
assert_eq!(task.state(), TaskState::Unprepared);
|
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "task_iterate: preparing");
|
2020-04-20 19:35:06 +00:00
|
|
|
|
|
|
|
let (prepared_sender, mut prepared_receiver) = mpsc::channel(1);
|
|
|
|
let (started_sender, mut started_receiver) = mpsc::channel(1);
|
|
|
|
let (iterate_sender, mut iterate_receiver) = mpsc::channel(1);
|
|
|
|
let (mut complete_iterate_sender, complete_iterate_receiver) = mpsc::channel(1);
|
|
|
|
let (paused_sender, mut paused_receiver) = mpsc::channel(1);
|
|
|
|
let (stopped_sender, mut stopped_receiver) = mpsc::channel(1);
|
|
|
|
let (flush_start_sender, mut flush_start_receiver) = mpsc::channel(1);
|
|
|
|
let (unprepared_sender, mut unprepared_receiver) = mpsc::channel(1);
|
2020-05-15 17:38:54 +00:00
|
|
|
let res = task
|
|
|
|
.prepare(
|
|
|
|
TaskTest {
|
|
|
|
prepared_sender,
|
|
|
|
started_sender,
|
|
|
|
iterate_sender,
|
|
|
|
complete_iterate_receiver,
|
|
|
|
paused_sender,
|
|
|
|
stopped_sender,
|
|
|
|
flush_start_sender,
|
|
|
|
unprepared_sender,
|
|
|
|
},
|
|
|
|
context,
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
res,
|
|
|
|
TransitionStatus::Async {
|
2020-05-25 15:18:31 +00:00
|
|
|
trigger: Trigger::Prepare,
|
2020-05-15 17:38:54 +00:00
|
|
|
origin: TaskState::Unprepared,
|
|
|
|
}
|
|
|
|
);
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "task_iterate: starting (initial)");
|
2020-05-15 17:38:54 +00:00
|
|
|
assert_eq!(
|
|
|
|
task.start().unwrap(),
|
|
|
|
TransitionStatus::Complete {
|
|
|
|
origin: TaskState::Prepared,
|
|
|
|
target: TaskState::Started
|
|
|
|
},
|
|
|
|
);
|
2020-04-20 19:35:06 +00:00
|
|
|
|
|
|
|
assert_eq!(task.state(), TaskState::Started);
|
|
|
|
// At this point, prepared must be completed
|
2021-11-25 18:26:26 +00:00
|
|
|
block_on(prepared_receiver.next()).unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
// ... and start executed
|
2021-11-25 18:26:26 +00:00
|
|
|
block_on(started_receiver.next()).unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
|
|
|
assert_eq!(task.state(), TaskState::Started);
|
|
|
|
|
|
|
|
// unlock task loop and keep looping
|
2021-11-25 18:26:26 +00:00
|
|
|
block_on(iterate_receiver.next()).unwrap();
|
|
|
|
block_on(complete_iterate_sender.send(Ok(()))).unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "task_iterate: starting (redundant)");
|
2020-04-20 19:35:06 +00:00
|
|
|
// start will return immediately
|
2020-05-15 17:38:54 +00:00
|
|
|
assert_eq!(
|
|
|
|
task.start().unwrap(),
|
|
|
|
TransitionStatus::NotWaiting {
|
2020-05-25 15:18:31 +00:00
|
|
|
trigger: Trigger::Start,
|
2020-05-15 17:38:54 +00:00
|
|
|
origin: TaskState::Started,
|
|
|
|
},
|
|
|
|
);
|
2020-04-20 19:35:06 +00:00
|
|
|
assert_eq!(task.state(), TaskState::Started);
|
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "task_iterate: pause (initial)");
|
2020-05-15 17:38:54 +00:00
|
|
|
assert_eq!(
|
|
|
|
task.pause().unwrap(),
|
|
|
|
TransitionStatus::NotWaiting {
|
2020-05-25 15:18:31 +00:00
|
|
|
trigger: Trigger::Pause,
|
2020-05-15 17:38:54 +00:00
|
|
|
origin: TaskState::Started,
|
|
|
|
},
|
|
|
|
);
|
2020-04-20 19:35:06 +00:00
|
|
|
|
|
|
|
// Pause transition is asynchronous
|
|
|
|
while TaskState::Paused != task.state() {
|
2021-11-25 18:26:26 +00:00
|
|
|
std::thread::sleep(Duration::from_millis(2));
|
2020-04-20 19:35:06 +00:00
|
|
|
|
|
|
|
if let Ok(Some(())) = iterate_receiver.try_next() {
|
|
|
|
// unlock iteration
|
2021-11-25 18:26:26 +00:00
|
|
|
block_on(complete_iterate_sender.send(Ok(()))).unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
}
|
2019-12-02 09:30:07 +00:00
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "task_iterate: awaiting pause ack");
|
2021-11-25 18:26:26 +00:00
|
|
|
block_on(paused_receiver.next()).unwrap();
|
2019-12-02 09:30:07 +00:00
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "task_iterate: starting (after pause)");
|
2020-05-15 17:38:54 +00:00
|
|
|
assert_eq!(
|
|
|
|
task.start().unwrap(),
|
|
|
|
TransitionStatus::Complete {
|
|
|
|
origin: TaskState::Paused,
|
|
|
|
target: TaskState::Started
|
|
|
|
},
|
|
|
|
);
|
2020-04-20 19:35:06 +00:00
|
|
|
|
|
|
|
assert_eq!(task.state(), TaskState::Started);
|
|
|
|
// Paused -> Started
|
2021-11-25 18:26:26 +00:00
|
|
|
let _ = block_on(started_receiver.next());
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "task_iterate: stopping");
|
2020-05-15 17:38:54 +00:00
|
|
|
assert_eq!(
|
|
|
|
task.stop().unwrap(),
|
|
|
|
TransitionStatus::Complete {
|
|
|
|
origin: TaskState::Started,
|
|
|
|
target: TaskState::Stopped
|
|
|
|
},
|
|
|
|
);
|
2020-04-20 19:35:06 +00:00
|
|
|
|
|
|
|
assert_eq!(task.state(), TaskState::Stopped);
|
2021-11-25 18:26:26 +00:00
|
|
|
let _ = block_on(stopped_receiver.next());
|
2020-04-20 19:35:06 +00:00
|
|
|
|
|
|
|
// purge remaining iteration received before stop if any
|
|
|
|
let _ = iterate_receiver.try_next();
|
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "task_iterate: starting (after stop)");
|
2020-05-15 17:38:54 +00:00
|
|
|
assert_eq!(
|
|
|
|
task.start().unwrap(),
|
|
|
|
TransitionStatus::Complete {
|
|
|
|
origin: TaskState::Stopped,
|
|
|
|
target: TaskState::Started
|
|
|
|
},
|
|
|
|
);
|
2021-11-25 18:26:26 +00:00
|
|
|
let _ = block_on(started_receiver.next());
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "task_iterate: req. iterate to return Eos");
|
2021-11-25 18:26:26 +00:00
|
|
|
block_on(iterate_receiver.next()).unwrap();
|
|
|
|
block_on(complete_iterate_sender.send(Err(gst::FlowError::Eos))).unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "task_iterate: awaiting stop ack");
|
2021-11-25 18:26:26 +00:00
|
|
|
block_on(stopped_receiver.next()).unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
|
|
|
// Wait for state machine to reach Stopped
|
|
|
|
while TaskState::Stopped != task.state() {
|
2021-11-25 18:26:26 +00:00
|
|
|
std::thread::sleep(Duration::from_millis(2));
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "task_iterate: starting (after stop)");
|
2020-05-15 17:38:54 +00:00
|
|
|
assert_eq!(
|
|
|
|
task.start().unwrap(),
|
|
|
|
TransitionStatus::Complete {
|
|
|
|
origin: TaskState::Stopped,
|
|
|
|
target: TaskState::Started
|
|
|
|
},
|
|
|
|
);
|
2021-11-25 18:26:26 +00:00
|
|
|
let _ = block_on(started_receiver.next());
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "task_iterate: req. iterate to return Flushing");
|
2021-11-25 18:26:26 +00:00
|
|
|
block_on(iterate_receiver.next()).unwrap();
|
|
|
|
block_on(complete_iterate_sender.send(Err(gst::FlowError::Flushing))).unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "task_iterate: awaiting flush_start ack");
|
2021-11-25 18:26:26 +00:00
|
|
|
block_on(flush_start_receiver.next()).unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
// Wait for state machine to reach Flushing
|
|
|
|
while TaskState::Flushing != task.state() {
|
2021-11-25 18:26:26 +00:00
|
|
|
std::thread::sleep(Duration::from_millis(2));
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "task_iterate: stop flushing");
|
2020-05-15 17:38:54 +00:00
|
|
|
assert_eq!(
|
|
|
|
task.flush_stop().unwrap(),
|
|
|
|
TransitionStatus::Complete {
|
|
|
|
origin: TaskState::Flushing,
|
|
|
|
target: TaskState::Started
|
|
|
|
},
|
|
|
|
);
|
2021-11-25 18:26:26 +00:00
|
|
|
let _ = block_on(started_receiver.next());
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "task_iterate: req. iterate to return Error");
|
2021-11-25 18:26:26 +00:00
|
|
|
block_on(iterate_receiver.next()).unwrap();
|
|
|
|
block_on(complete_iterate_sender.send(Err(gst::FlowError::Error))).unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
// Wait for state machine to reach Error
|
|
|
|
while TaskState::Error != task.state() {
|
2021-11-25 18:26:26 +00:00
|
|
|
std::thread::sleep(Duration::from_millis(2));
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(
|
2020-05-15 17:38:54 +00:00
|
|
|
RUNTIME_CAT,
|
|
|
|
"task_iterate: attempting to start (after Error)"
|
|
|
|
);
|
|
|
|
let err = task.start().unwrap_err();
|
|
|
|
match err {
|
|
|
|
TransitionError {
|
2020-05-25 15:18:31 +00:00
|
|
|
trigger: Trigger::Start,
|
2020-05-15 17:38:54 +00:00
|
|
|
state: TaskState::Error,
|
|
|
|
..
|
|
|
|
} => (),
|
2022-02-21 23:18:28 +00:00
|
|
|
_ => unreachable!(),
|
2020-05-15 17:38:54 +00:00
|
|
|
}
|
2020-03-05 15:59:13 +00:00
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
assert_eq!(
|
|
|
|
task.unprepare().unwrap(),
|
|
|
|
TransitionStatus::Complete {
|
|
|
|
origin: TaskState::Error,
|
|
|
|
target: TaskState::Unprepared
|
|
|
|
},
|
|
|
|
);
|
2020-04-20 19:35:06 +00:00
|
|
|
|
|
|
|
assert_eq!(task.state(), TaskState::Unprepared);
|
2021-11-25 18:26:26 +00:00
|
|
|
let _ = block_on(unprepared_receiver.next());
|
2020-03-05 15:59:13 +00:00
|
|
|
}
|
|
|
|
|
2021-11-25 18:26:26 +00:00
|
|
|
#[test]
|
|
|
|
fn prepare_error() {
|
2020-03-05 15:59:13 +00:00
|
|
|
gst::init().unwrap();
|
|
|
|
|
2020-04-20 19:35:06 +00:00
|
|
|
struct TaskPrepareTest {
|
|
|
|
prepare_error_sender: mpsc::Sender<()>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TaskImpl for TaskPrepareTest {
|
|
|
|
fn prepare(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
|
|
|
|
async move {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "prepare_error: prepare returning an error");
|
2020-12-20 18:43:45 +00:00
|
|
|
Err(gst::error_msg!(
|
2020-05-15 17:38:54 +00:00
|
|
|
gst::ResourceError::Failed,
|
|
|
|
["prepare_error: intentional error"]
|
2020-04-20 19:35:06 +00:00
|
|
|
))
|
|
|
|
}
|
|
|
|
.boxed()
|
|
|
|
}
|
|
|
|
|
2020-05-25 15:18:31 +00:00
|
|
|
fn handle_action_error(
|
2020-05-15 17:38:54 +00:00
|
|
|
&mut self,
|
2020-05-25 15:18:31 +00:00
|
|
|
trigger: Trigger,
|
2020-05-15 17:38:54 +00:00
|
|
|
state: TaskState,
|
|
|
|
err: gst::ErrorMessage,
|
2020-05-25 15:18:31 +00:00
|
|
|
) -> BoxFuture<'_, Trigger> {
|
2020-04-20 19:35:06 +00:00
|
|
|
async move {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(
|
2020-05-15 17:38:54 +00:00
|
|
|
RUNTIME_CAT,
|
|
|
|
"prepare_error: handling prepare error {:?}",
|
|
|
|
err
|
|
|
|
);
|
2020-05-25 15:18:31 +00:00
|
|
|
match (trigger, state) {
|
|
|
|
(Trigger::Prepare, TaskState::Preparing) => {
|
2020-05-15 17:38:54 +00:00
|
|
|
self.prepare_error_sender.send(()).await.unwrap();
|
|
|
|
}
|
|
|
|
other => unreachable!("{:?}", other),
|
|
|
|
}
|
2020-05-25 15:18:31 +00:00
|
|
|
Trigger::Error
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
.boxed()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn iterate(&mut self) -> BoxFuture<'_, Result<(), gst::FlowError>> {
|
|
|
|
future::ok(()).boxed()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-26 09:54:34 +00:00
|
|
|
let context = Context::acquire("prepare_error", Duration::from_millis(2)).unwrap();
|
2020-03-05 15:59:13 +00:00
|
|
|
|
|
|
|
let task = Task::default();
|
|
|
|
|
2020-04-20 19:35:06 +00:00
|
|
|
assert_eq!(task.state(), TaskState::Unprepared);
|
|
|
|
|
|
|
|
let (prepare_error_sender, mut prepare_error_receiver) = mpsc::channel(1);
|
|
|
|
task.prepare(
|
|
|
|
TaskPrepareTest {
|
|
|
|
prepare_error_sender,
|
|
|
|
},
|
|
|
|
context,
|
|
|
|
)
|
2020-03-05 15:59:13 +00:00
|
|
|
.unwrap();
|
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(
|
2020-05-25 15:18:31 +00:00
|
|
|
RUNTIME_CAT,
|
|
|
|
"prepare_error: await action error notification"
|
|
|
|
);
|
2021-11-25 18:26:26 +00:00
|
|
|
block_on(prepare_error_receiver.next()).unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
// Wait for state machine to reach Error
|
|
|
|
while TaskState::Error != task.state() {
|
2021-11-25 18:26:26 +00:00
|
|
|
std::thread::sleep(Duration::from_millis(2));
|
2020-05-15 17:38:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let res = task.start().unwrap_err();
|
|
|
|
match res {
|
|
|
|
TransitionError {
|
2020-05-25 15:18:31 +00:00
|
|
|
trigger: Trigger::Start,
|
2020-05-15 17:38:54 +00:00
|
|
|
state: TaskState::Error,
|
|
|
|
..
|
|
|
|
} => (),
|
|
|
|
other => unreachable!("{:?}", other),
|
|
|
|
}
|
|
|
|
|
2020-04-20 19:35:06 +00:00
|
|
|
task.unprepare().unwrap();
|
|
|
|
}
|
|
|
|
|
2021-11-25 18:26:26 +00:00
|
|
|
#[test]
|
|
|
|
fn prepare_start_ok() {
|
2020-04-20 19:35:06 +00:00
|
|
|
// Hold the preparation function so that it completes after the start request is engaged
|
2020-03-05 15:59:13 +00:00
|
|
|
|
2020-04-20 19:35:06 +00:00
|
|
|
gst::init().unwrap();
|
|
|
|
|
|
|
|
struct TaskPrepareTest {
|
|
|
|
prepare_receiver: mpsc::Receiver<()>,
|
|
|
|
}
|
2020-03-05 15:59:13 +00:00
|
|
|
|
2020-04-20 19:35:06 +00:00
|
|
|
impl TaskImpl for TaskPrepareTest {
|
|
|
|
fn prepare(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
|
|
|
|
async move {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(
|
2020-04-20 19:35:06 +00:00
|
|
|
RUNTIME_CAT,
|
|
|
|
"prepare_start_ok: preparation awaiting trigger"
|
|
|
|
);
|
|
|
|
self.prepare_receiver.next().await.unwrap();
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "prepare_start_ok: preparation complete Ok");
|
2020-04-20 19:35:06 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
.boxed()
|
2020-03-05 15:59:13 +00:00
|
|
|
}
|
|
|
|
|
2020-05-25 15:18:31 +00:00
|
|
|
fn handle_action_error(
|
2020-05-15 17:38:54 +00:00
|
|
|
&mut self,
|
2020-05-25 15:18:31 +00:00
|
|
|
_trigger: Trigger,
|
2020-05-15 17:38:54 +00:00
|
|
|
_state: TaskState,
|
|
|
|
_err: gst::ErrorMessage,
|
2020-05-25 15:18:31 +00:00
|
|
|
) -> BoxFuture<'_, Trigger> {
|
2020-04-20 19:35:06 +00:00
|
|
|
unreachable!("prepare_start_ok: handle_prepare_error");
|
|
|
|
}
|
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
fn start(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
|
2020-04-20 19:35:06 +00:00
|
|
|
async move {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "prepare_start_ok: started");
|
2020-05-15 17:38:54 +00:00
|
|
|
Ok(())
|
2020-03-05 15:59:13 +00:00
|
|
|
}
|
2020-04-20 19:35:06 +00:00
|
|
|
.boxed()
|
2020-03-05 15:59:13 +00:00
|
|
|
}
|
2020-04-20 19:35:06 +00:00
|
|
|
|
|
|
|
fn iterate(&mut self) -> BoxFuture<'_, Result<(), gst::FlowError>> {
|
|
|
|
future::pending::<Result<(), gst::FlowError>>().boxed()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-26 09:54:34 +00:00
|
|
|
let context = Context::acquire("prepare_start_ok", Duration::from_millis(2)).unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
|
|
|
let task = Task::default();
|
|
|
|
|
|
|
|
let (mut prepare_sender, prepare_receiver) = mpsc::channel(1);
|
2021-12-14 18:40:27 +00:00
|
|
|
task.prepare(TaskPrepareTest { prepare_receiver }, context)
|
2020-04-20 19:35:06 +00:00
|
|
|
.unwrap();
|
|
|
|
|
2021-10-09 10:17:05 +00:00
|
|
|
let start_ctx = Context::acquire("prepare_start_ok_requester", Duration::ZERO).unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
let task_clone = task.clone();
|
|
|
|
let (ready_sender, ready_receiver) = oneshot::channel();
|
|
|
|
let start_handle = start_ctx.spawn(async move {
|
|
|
|
assert_eq!(task_clone.state(), TaskState::Preparing);
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "prepare_start_ok: starting");
|
2020-05-15 17:38:54 +00:00
|
|
|
assert_eq!(
|
|
|
|
task_clone.start().unwrap(),
|
|
|
|
TransitionStatus::Async {
|
2020-05-25 15:18:31 +00:00
|
|
|
trigger: Trigger::Start,
|
2020-05-15 17:38:54 +00:00
|
|
|
origin: TaskState::Preparing,
|
|
|
|
}
|
|
|
|
);
|
2020-04-20 19:35:06 +00:00
|
|
|
ready_sender.send(()).unwrap();
|
|
|
|
Context::drain_sub_tasks().await.unwrap();
|
|
|
|
assert_eq!(task_clone.state(), TaskState::Started);
|
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
assert_eq!(
|
|
|
|
task.stop().unwrap(),
|
|
|
|
TransitionStatus::Async {
|
2020-05-25 15:18:31 +00:00
|
|
|
trigger: Trigger::Stop,
|
2020-05-15 17:38:54 +00:00
|
|
|
origin: TaskState::Started,
|
|
|
|
},
|
|
|
|
);
|
2020-04-20 19:35:06 +00:00
|
|
|
Context::drain_sub_tasks().await.unwrap();
|
2020-05-15 17:38:54 +00:00
|
|
|
assert_eq!(task_clone.state(), TaskState::Stopped);
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
task.unprepare().unwrap(),
|
|
|
|
TransitionStatus::Async {
|
2020-05-25 15:18:31 +00:00
|
|
|
trigger: Trigger::Unprepare,
|
2020-05-15 17:38:54 +00:00
|
|
|
origin: TaskState::Stopped,
|
|
|
|
},
|
|
|
|
);
|
2020-04-20 19:35:06 +00:00
|
|
|
Context::drain_sub_tasks().await.unwrap();
|
2020-05-15 17:38:54 +00:00
|
|
|
assert_eq!(task_clone.state(), TaskState::Unprepared);
|
2020-03-05 15:59:13 +00:00
|
|
|
});
|
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "prepare_start_ok: awaiting for start_ctx");
|
2021-11-25 18:26:26 +00:00
|
|
|
block_on(ready_receiver).unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "prepare_start_ok: triggering preparation");
|
2021-11-25 18:26:26 +00:00
|
|
|
block_on(prepare_sender.send(())).unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2021-11-25 18:26:26 +00:00
|
|
|
block_on(start_handle).unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
|
2021-11-25 18:26:26 +00:00
|
|
|
#[test]
|
|
|
|
fn prepare_start_error() {
|
2020-04-20 19:35:06 +00:00
|
|
|
// Hold the preparation function so that it completes after the start request is engaged
|
|
|
|
|
|
|
|
gst::init().unwrap();
|
|
|
|
|
|
|
|
struct TaskPrepareTest {
|
|
|
|
prepare_receiver: mpsc::Receiver<()>,
|
|
|
|
prepare_error_sender: mpsc::Sender<()>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TaskImpl for TaskPrepareTest {
|
|
|
|
fn prepare(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
|
|
|
|
async move {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(
|
2020-04-20 19:35:06 +00:00
|
|
|
RUNTIME_CAT,
|
|
|
|
"prepare_start_error: preparation awaiting trigger"
|
|
|
|
);
|
|
|
|
self.prepare_receiver.next().await.unwrap();
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "prepare_start_error: preparation complete Err");
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2020-12-20 18:43:45 +00:00
|
|
|
Err(gst::error_msg!(
|
2020-05-15 17:38:54 +00:00
|
|
|
gst::ResourceError::Failed,
|
|
|
|
["prepare_start_error: intentional error"]
|
2020-04-20 19:35:06 +00:00
|
|
|
))
|
|
|
|
}
|
|
|
|
.boxed()
|
|
|
|
}
|
|
|
|
|
2020-05-25 15:18:31 +00:00
|
|
|
fn handle_action_error(
|
2020-05-15 17:38:54 +00:00
|
|
|
&mut self,
|
2020-05-25 15:18:31 +00:00
|
|
|
trigger: Trigger,
|
2020-05-15 17:38:54 +00:00
|
|
|
state: TaskState,
|
|
|
|
err: gst::ErrorMessage,
|
2020-05-25 15:18:31 +00:00
|
|
|
) -> BoxFuture<'_, Trigger> {
|
2020-04-20 19:35:06 +00:00
|
|
|
async move {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(
|
2020-05-15 17:38:54 +00:00
|
|
|
RUNTIME_CAT,
|
|
|
|
"prepare_start_error: handling prepare error {:?}",
|
|
|
|
err
|
|
|
|
);
|
2020-05-25 15:18:31 +00:00
|
|
|
match (trigger, state) {
|
|
|
|
(Trigger::Prepare, TaskState::Preparing) => {
|
2020-05-15 17:38:54 +00:00
|
|
|
self.prepare_error_sender.send(()).await.unwrap();
|
|
|
|
}
|
2020-05-25 15:18:31 +00:00
|
|
|
other => unreachable!("action error for {:?}", other),
|
2020-05-15 17:38:54 +00:00
|
|
|
}
|
2020-05-25 15:18:31 +00:00
|
|
|
Trigger::Error
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
.boxed()
|
|
|
|
}
|
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
fn start(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
|
2020-04-20 19:35:06 +00:00
|
|
|
unreachable!("prepare_start_error: start");
|
|
|
|
}
|
|
|
|
|
|
|
|
fn iterate(&mut self) -> BoxFuture<'_, Result<(), gst::FlowError>> {
|
|
|
|
unreachable!("prepare_start_error: iterate");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-26 09:54:34 +00:00
|
|
|
let context = Context::acquire("prepare_start_error", Duration::from_millis(2)).unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
|
|
|
let task = Task::default();
|
|
|
|
|
|
|
|
let (mut prepare_sender, prepare_receiver) = mpsc::channel(1);
|
|
|
|
let (prepare_error_sender, mut prepare_error_receiver) = mpsc::channel(1);
|
|
|
|
task.prepare(
|
|
|
|
TaskPrepareTest {
|
|
|
|
prepare_receiver,
|
|
|
|
prepare_error_sender,
|
|
|
|
},
|
|
|
|
context,
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
2021-10-09 10:17:05 +00:00
|
|
|
let start_ctx = Context::acquire("prepare_start_error_requester", Duration::ZERO).unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
let task_clone = task.clone();
|
|
|
|
let (ready_sender, ready_receiver) = oneshot::channel();
|
|
|
|
let start_handle = start_ctx.spawn(async move {
|
|
|
|
assert_eq!(task_clone.state(), TaskState::Preparing);
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "prepare_start_error: starting (Err)");
|
2020-05-15 17:38:54 +00:00
|
|
|
task_clone.start().unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
ready_sender.send(()).unwrap();
|
|
|
|
Context::drain_sub_tasks().await.unwrap();
|
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
assert_eq!(
|
|
|
|
task.unprepare().unwrap(),
|
|
|
|
TransitionStatus::Async {
|
2020-05-25 15:18:31 +00:00
|
|
|
trigger: Trigger::Unprepare,
|
2020-05-15 17:38:54 +00:00
|
|
|
origin: TaskState::Error,
|
|
|
|
},
|
|
|
|
);
|
2020-04-20 19:35:06 +00:00
|
|
|
Context::drain_sub_tasks().await.unwrap();
|
|
|
|
});
|
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "prepare_start_error: awaiting for start_ctx");
|
2021-11-25 18:26:26 +00:00
|
|
|
block_on(ready_receiver).unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(
|
2020-04-20 19:35:06 +00:00
|
|
|
RUNTIME_CAT,
|
|
|
|
"prepare_start_error: triggering preparation (failure)"
|
|
|
|
);
|
2021-11-25 18:26:26 +00:00
|
|
|
block_on(prepare_sender.send(())).unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(
|
2020-04-20 19:35:06 +00:00
|
|
|
RUNTIME_CAT,
|
|
|
|
"prepare_start_error: await prepare error notification"
|
|
|
|
);
|
2021-11-25 18:26:26 +00:00
|
|
|
block_on(prepare_error_receiver.next()).unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2021-11-25 18:26:26 +00:00
|
|
|
block_on(start_handle).unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
|
2021-11-25 18:26:26 +00:00
|
|
|
#[test]
|
|
|
|
fn pause_start() {
|
2020-04-20 19:35:06 +00:00
|
|
|
gst::init().unwrap();
|
|
|
|
|
|
|
|
struct TaskPauseStartTest {
|
|
|
|
iterate_sender: mpsc::Sender<()>,
|
|
|
|
complete_receiver: mpsc::Receiver<()>,
|
|
|
|
paused_sender: mpsc::Sender<()>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TaskImpl for TaskPauseStartTest {
|
|
|
|
fn iterate(&mut self) -> BoxFuture<'_, Result<(), gst::FlowError>> {
|
|
|
|
async move {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "pause_start: entering iteration");
|
2020-04-20 19:35:06 +00:00
|
|
|
self.iterate_sender.send(()).await.unwrap();
|
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "pause_start: iteration awaiting completion");
|
2020-04-20 19:35:06 +00:00
|
|
|
self.complete_receiver.next().await.unwrap();
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "pause_start: iteration complete");
|
2020-04-20 19:35:06 +00:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
.boxed()
|
|
|
|
}
|
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
fn pause(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
|
2020-04-20 19:35:06 +00:00
|
|
|
async move {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "pause_start: paused");
|
2020-04-20 19:35:06 +00:00
|
|
|
self.paused_sender.send(()).await.unwrap();
|
2020-05-15 17:38:54 +00:00
|
|
|
Ok(())
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
.boxed()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-26 09:54:34 +00:00
|
|
|
let context = Context::acquire("pause_start", Duration::from_millis(2)).unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
|
|
|
let task = Task::default();
|
|
|
|
|
|
|
|
let (iterate_sender, mut iterate_receiver) = mpsc::channel(1);
|
|
|
|
let (mut complete_sender, complete_receiver) = mpsc::channel(0);
|
|
|
|
let (paused_sender, mut paused_receiver) = mpsc::channel(1);
|
|
|
|
task.prepare(
|
|
|
|
TaskPauseStartTest {
|
|
|
|
iterate_sender,
|
|
|
|
complete_receiver,
|
|
|
|
paused_sender,
|
|
|
|
},
|
|
|
|
context,
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "pause_start: starting");
|
2020-05-15 17:38:54 +00:00
|
|
|
assert_eq!(
|
|
|
|
task.start().unwrap(),
|
|
|
|
TransitionStatus::Complete {
|
|
|
|
origin: TaskState::Prepared,
|
|
|
|
target: TaskState::Started,
|
|
|
|
},
|
|
|
|
);
|
2020-04-20 19:35:06 +00:00
|
|
|
assert_eq!(task.state(), TaskState::Started);
|
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "pause_start: awaiting 1st iteration");
|
2021-11-25 18:26:26 +00:00
|
|
|
block_on(iterate_receiver.next()).unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "pause_start: pausing (1)");
|
2020-05-15 17:38:54 +00:00
|
|
|
assert_eq!(
|
|
|
|
task.pause().unwrap(),
|
|
|
|
TransitionStatus::NotWaiting {
|
2020-05-25 15:18:31 +00:00
|
|
|
trigger: Trigger::Pause,
|
2020-05-15 17:38:54 +00:00
|
|
|
origin: TaskState::Started,
|
|
|
|
},
|
|
|
|
);
|
2020-03-05 15:59:13 +00:00
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "pause_start: sending 1st iteration completion");
|
2020-04-20 19:35:06 +00:00
|
|
|
complete_sender.try_send(()).unwrap();
|
|
|
|
|
|
|
|
// Pause transition is asynchronous
|
|
|
|
while TaskState::Paused != task.state() {
|
2021-11-25 18:26:26 +00:00
|
|
|
std::thread::sleep(Duration::from_millis(5));
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "pause_start: awaiting paused");
|
2021-11-25 18:26:26 +00:00
|
|
|
let _ = block_on(paused_receiver.next());
|
2020-04-20 19:35:06 +00:00
|
|
|
|
|
|
|
// Loop held on due to Pause
|
|
|
|
iterate_receiver.try_next().unwrap_err();
|
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
assert_eq!(
|
|
|
|
task.start().unwrap(),
|
|
|
|
TransitionStatus::Complete {
|
|
|
|
origin: TaskState::Paused,
|
|
|
|
target: TaskState::Started,
|
|
|
|
},
|
|
|
|
);
|
2020-04-20 19:35:06 +00:00
|
|
|
assert_eq!(task.state(), TaskState::Started);
|
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "pause_start: awaiting 2d iteration");
|
2021-11-25 18:26:26 +00:00
|
|
|
block_on(iterate_receiver.next()).unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "pause_start: sending 2d iteration completion");
|
2020-04-20 19:35:06 +00:00
|
|
|
complete_sender.try_send(()).unwrap();
|
2019-12-02 09:30:07 +00:00
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
task.stop().unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
task.unprepare().unwrap();
|
|
|
|
}
|
|
|
|
|
2021-11-25 18:26:26 +00:00
|
|
|
#[test]
|
|
|
|
fn successive_pause_start() {
|
2020-04-20 19:35:06 +00:00
|
|
|
// Purpose: check pause cancellation.
|
|
|
|
gst::init().unwrap();
|
|
|
|
|
|
|
|
struct TaskPauseStartTest {
|
|
|
|
iterate_sender: mpsc::Sender<()>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TaskImpl for TaskPauseStartTest {
|
|
|
|
fn iterate(&mut self) -> BoxFuture<'_, Result<(), gst::FlowError>> {
|
|
|
|
async move {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "successive_pause_start: iteration");
|
2020-04-20 19:35:06 +00:00
|
|
|
self.iterate_sender.send(()).await.unwrap();
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
.boxed()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-26 09:54:34 +00:00
|
|
|
let context = Context::acquire("successive_pause_start", Duration::from_millis(2)).unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
|
|
|
let task = Task::default();
|
|
|
|
|
|
|
|
let (iterate_sender, mut iterate_receiver) = mpsc::channel(1);
|
|
|
|
task.prepare(TaskPauseStartTest { iterate_sender }, context)
|
|
|
|
.unwrap();
|
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "successive_pause_start: starting");
|
2020-05-15 17:38:54 +00:00
|
|
|
task.start().unwrap();
|
2020-03-05 15:59:13 +00:00
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "successive_pause_start: awaiting iteration 1");
|
2021-11-25 18:26:26 +00:00
|
|
|
block_on(iterate_receiver.next()).unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "successive_pause_start: pause and start");
|
2020-05-15 17:38:54 +00:00
|
|
|
task.pause().unwrap();
|
|
|
|
task.start().unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
|
|
|
assert_eq!(task.state(), TaskState::Started);
|
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "successive_pause_start: awaiting iteration 2");
|
2021-11-25 18:26:26 +00:00
|
|
|
block_on(iterate_receiver.next()).unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "successive_pause_start: stopping");
|
2020-05-15 17:38:54 +00:00
|
|
|
task.stop().unwrap();
|
2020-03-05 15:59:13 +00:00
|
|
|
task.unprepare().unwrap();
|
2019-12-02 09:30:07 +00:00
|
|
|
}
|
2020-03-19 18:34:51 +00:00
|
|
|
|
2021-11-25 18:26:26 +00:00
|
|
|
#[test]
|
|
|
|
fn flush_regular_sync() {
|
2020-04-20 19:35:06 +00:00
|
|
|
gst::init().unwrap();
|
2020-03-19 18:34:51 +00:00
|
|
|
|
2020-04-20 19:35:06 +00:00
|
|
|
struct TaskFlushTest {
|
|
|
|
flush_start_sender: mpsc::Sender<()>,
|
|
|
|
flush_stop_sender: mpsc::Sender<()>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TaskImpl for TaskFlushTest {
|
|
|
|
fn iterate(&mut self) -> BoxFuture<'_, Result<(), gst::FlowError>> {
|
|
|
|
future::pending::<Result<(), gst::FlowError>>().boxed()
|
|
|
|
}
|
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
fn flush_start(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
|
2020-04-20 19:35:06 +00:00
|
|
|
async move {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "flush_regular_sync: started flushing");
|
2020-04-20 19:35:06 +00:00
|
|
|
self.flush_start_sender.send(()).await.unwrap();
|
2020-05-15 17:38:54 +00:00
|
|
|
Ok(())
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
.boxed()
|
|
|
|
}
|
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
fn flush_stop(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
|
2020-04-20 19:35:06 +00:00
|
|
|
async move {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "flush_regular_sync: stopped flushing");
|
2020-04-20 19:35:06 +00:00
|
|
|
self.flush_stop_sender.send(()).await.unwrap();
|
2020-05-15 17:38:54 +00:00
|
|
|
Ok(())
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
.boxed()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-26 09:54:34 +00:00
|
|
|
let context = Context::acquire("flush_regular_sync", Duration::from_millis(2)).unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
|
|
|
let task = Task::default();
|
|
|
|
|
|
|
|
let (flush_start_sender, mut flush_start_receiver) = mpsc::channel(1);
|
|
|
|
let (flush_stop_sender, mut flush_stop_receiver) = mpsc::channel(1);
|
|
|
|
task.prepare(
|
|
|
|
TaskFlushTest {
|
|
|
|
flush_start_sender,
|
|
|
|
flush_stop_sender,
|
|
|
|
},
|
|
|
|
context,
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "flush_regular_sync: start");
|
2020-05-15 17:38:54 +00:00
|
|
|
task.start().unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "flush_regular_sync: starting flush");
|
2020-05-15 17:38:54 +00:00
|
|
|
assert_eq!(
|
|
|
|
task.flush_start().unwrap(),
|
|
|
|
TransitionStatus::Complete {
|
|
|
|
origin: TaskState::Started,
|
|
|
|
target: TaskState::Flushing,
|
|
|
|
},
|
|
|
|
);
|
2020-04-20 19:35:06 +00:00
|
|
|
assert_eq!(task.state(), TaskState::Flushing);
|
|
|
|
|
2021-11-25 18:26:26 +00:00
|
|
|
block_on(flush_start_receiver.next()).unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "flush_regular_sync: stopping flush");
|
2020-05-15 17:38:54 +00:00
|
|
|
assert_eq!(
|
|
|
|
task.flush_stop().unwrap(),
|
|
|
|
TransitionStatus::Complete {
|
|
|
|
origin: TaskState::Flushing,
|
|
|
|
target: TaskState::Started,
|
|
|
|
},
|
|
|
|
);
|
2020-04-20 19:35:06 +00:00
|
|
|
assert_eq!(task.state(), TaskState::Started);
|
|
|
|
|
2021-11-25 18:26:26 +00:00
|
|
|
block_on(flush_stop_receiver.next()).unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
task.pause().unwrap();
|
|
|
|
task.stop().unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
task.unprepare().unwrap();
|
|
|
|
}
|
|
|
|
|
2021-11-25 18:26:26 +00:00
|
|
|
#[test]
|
|
|
|
fn flush_regular_different_context() {
|
2020-04-20 19:35:06 +00:00
|
|
|
// Purpose: make sure a flush sequence triggered from a Context doesn't block.
|
2020-03-19 18:34:51 +00:00
|
|
|
gst::init().unwrap();
|
|
|
|
|
2020-04-20 19:35:06 +00:00
|
|
|
struct TaskFlushTest {
|
|
|
|
flush_start_sender: mpsc::Sender<()>,
|
|
|
|
flush_stop_sender: mpsc::Sender<()>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TaskImpl for TaskFlushTest {
|
|
|
|
fn iterate(&mut self) -> BoxFuture<'_, Result<(), gst::FlowError>> {
|
|
|
|
future::pending::<Result<(), gst::FlowError>>().boxed()
|
|
|
|
}
|
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
fn flush_start(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
|
2020-04-20 19:35:06 +00:00
|
|
|
async move {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(
|
2020-04-20 19:35:06 +00:00
|
|
|
RUNTIME_CAT,
|
|
|
|
"flush_regular_different_context: started flushing"
|
|
|
|
);
|
|
|
|
self.flush_start_sender.send(()).await.unwrap();
|
2020-05-15 17:38:54 +00:00
|
|
|
Ok(())
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
.boxed()
|
|
|
|
}
|
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
fn flush_stop(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
|
2020-04-20 19:35:06 +00:00
|
|
|
async move {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(
|
2020-04-20 19:35:06 +00:00
|
|
|
RUNTIME_CAT,
|
|
|
|
"flush_regular_different_context: stopped flushing"
|
|
|
|
);
|
|
|
|
self.flush_stop_sender.send(()).await.unwrap();
|
2020-05-15 17:38:54 +00:00
|
|
|
Ok(())
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
.boxed()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-26 09:54:34 +00:00
|
|
|
let context =
|
|
|
|
Context::acquire("flush_regular_different_context", Duration::from_millis(2)).unwrap();
|
2020-03-19 18:34:51 +00:00
|
|
|
|
|
|
|
let task = Task::default();
|
|
|
|
|
2020-04-20 19:35:06 +00:00
|
|
|
let (flush_start_sender, mut flush_start_receiver) = mpsc::channel(1);
|
|
|
|
let (flush_stop_sender, mut flush_stop_receiver) = mpsc::channel(1);
|
|
|
|
task.prepare(
|
|
|
|
TaskFlushTest {
|
|
|
|
flush_start_sender,
|
|
|
|
flush_stop_sender,
|
|
|
|
},
|
|
|
|
context,
|
|
|
|
)
|
|
|
|
.unwrap();
|
2020-03-19 18:34:51 +00:00
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "flush_regular_different_context: start");
|
2020-05-15 17:38:54 +00:00
|
|
|
task.start().unwrap();
|
2020-03-19 18:34:51 +00:00
|
|
|
|
2021-05-26 09:54:34 +00:00
|
|
|
let oob_context = Context::acquire(
|
|
|
|
"flush_regular_different_context_oob",
|
|
|
|
Duration::from_millis(2),
|
|
|
|
)
|
|
|
|
.unwrap();
|
2020-03-19 18:34:51 +00:00
|
|
|
|
2020-04-20 19:35:06 +00:00
|
|
|
let task_clone = task.clone();
|
|
|
|
let flush_handle = oob_context.spawn(async move {
|
2020-05-15 17:38:54 +00:00
|
|
|
assert_eq!(
|
|
|
|
task_clone.flush_start().unwrap(),
|
|
|
|
TransitionStatus::Async {
|
2020-05-25 15:18:31 +00:00
|
|
|
trigger: Trigger::FlushStart,
|
2020-05-15 17:38:54 +00:00
|
|
|
origin: TaskState::Started,
|
|
|
|
},
|
|
|
|
);
|
2020-04-20 19:35:06 +00:00
|
|
|
Context::drain_sub_tasks().await.unwrap();
|
|
|
|
assert_eq!(task_clone.state(), TaskState::Flushing);
|
|
|
|
flush_start_receiver.next().await.unwrap();
|
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
assert_eq!(
|
|
|
|
task_clone.flush_stop().unwrap(),
|
|
|
|
TransitionStatus::Async {
|
2020-05-25 15:18:31 +00:00
|
|
|
trigger: Trigger::FlushStop,
|
2020-05-15 17:38:54 +00:00
|
|
|
origin: TaskState::Flushing,
|
|
|
|
},
|
|
|
|
);
|
2020-04-20 19:35:06 +00:00
|
|
|
Context::drain_sub_tasks().await.unwrap();
|
|
|
|
assert_eq!(task_clone.state(), TaskState::Started);
|
|
|
|
});
|
|
|
|
|
2021-11-25 18:26:26 +00:00
|
|
|
block_on(flush_handle).unwrap();
|
|
|
|
block_on(flush_stop_receiver.next()).unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
task.stop().unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
task.unprepare().unwrap();
|
|
|
|
}
|
|
|
|
|
2021-11-25 18:26:26 +00:00
|
|
|
#[test]
|
|
|
|
fn flush_regular_same_context() {
|
2020-04-20 19:35:06 +00:00
|
|
|
// Purpose: make sure a flush sequence triggered from the same Context doesn't block.
|
|
|
|
gst::init().unwrap();
|
|
|
|
|
|
|
|
struct TaskFlushTest {
|
|
|
|
flush_start_sender: mpsc::Sender<()>,
|
|
|
|
flush_stop_sender: mpsc::Sender<()>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TaskImpl for TaskFlushTest {
|
|
|
|
fn iterate(&mut self) -> BoxFuture<'_, Result<(), gst::FlowError>> {
|
|
|
|
future::pending::<Result<(), gst::FlowError>>().boxed()
|
|
|
|
}
|
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
fn flush_start(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
|
2020-04-20 19:35:06 +00:00
|
|
|
async move {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "flush_regular_same_context: started flushing");
|
2020-04-20 19:35:06 +00:00
|
|
|
self.flush_start_sender.send(()).await.unwrap();
|
2020-05-15 17:38:54 +00:00
|
|
|
Ok(())
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
.boxed()
|
|
|
|
}
|
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
fn flush_stop(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
|
2020-04-20 19:35:06 +00:00
|
|
|
async move {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "flush_regular_same_context: stopped flushing");
|
2020-04-20 19:35:06 +00:00
|
|
|
self.flush_stop_sender.send(()).await.unwrap();
|
2020-05-15 17:38:54 +00:00
|
|
|
Ok(())
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
.boxed()
|
2020-03-19 18:34:51 +00:00
|
|
|
}
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
|
2021-05-26 09:54:34 +00:00
|
|
|
let context =
|
|
|
|
Context::acquire("flush_regular_same_context", Duration::from_millis(2)).unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
|
|
|
let task = Task::default();
|
|
|
|
|
|
|
|
let (flush_start_sender, mut flush_start_receiver) = mpsc::channel(1);
|
|
|
|
let (flush_stop_sender, mut flush_stop_receiver) = mpsc::channel(1);
|
|
|
|
task.prepare(
|
|
|
|
TaskFlushTest {
|
|
|
|
flush_start_sender,
|
|
|
|
flush_stop_sender,
|
|
|
|
},
|
|
|
|
context,
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
task.start().unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
|
|
|
let task_clone = task.clone();
|
|
|
|
let flush_handle = task.context().as_ref().unwrap().spawn(async move {
|
2020-05-15 17:38:54 +00:00
|
|
|
assert_eq!(
|
|
|
|
task_clone.flush_start().unwrap(),
|
|
|
|
TransitionStatus::Async {
|
2020-05-25 15:18:31 +00:00
|
|
|
trigger: Trigger::FlushStart,
|
2020-05-15 17:38:54 +00:00
|
|
|
origin: TaskState::Started,
|
|
|
|
},
|
|
|
|
);
|
2020-04-20 19:35:06 +00:00
|
|
|
Context::drain_sub_tasks().await.unwrap();
|
|
|
|
assert_eq!(task_clone.state(), TaskState::Flushing);
|
|
|
|
flush_start_receiver.next().await.unwrap();
|
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
assert_eq!(
|
|
|
|
task_clone.flush_stop().unwrap(),
|
|
|
|
TransitionStatus::Async {
|
2020-05-25 15:18:31 +00:00
|
|
|
trigger: Trigger::FlushStop,
|
2020-05-15 17:38:54 +00:00
|
|
|
origin: TaskState::Flushing,
|
|
|
|
},
|
|
|
|
);
|
2020-04-20 19:35:06 +00:00
|
|
|
Context::drain_sub_tasks().await.unwrap();
|
|
|
|
assert_eq!(task_clone.state(), TaskState::Started);
|
2020-03-19 18:34:51 +00:00
|
|
|
});
|
|
|
|
|
2021-11-25 18:26:26 +00:00
|
|
|
block_on(flush_handle).unwrap();
|
|
|
|
block_on(flush_stop_receiver.next()).unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
task.stop().unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
task.unprepare().unwrap();
|
|
|
|
}
|
|
|
|
|
2021-11-25 18:26:26 +00:00
|
|
|
#[test]
|
|
|
|
fn flush_from_loop() {
|
2020-05-25 15:18:31 +00:00
|
|
|
// Purpose: make sure a flush_start triggered from an iteration doesn't block.
|
2020-04-20 19:35:06 +00:00
|
|
|
gst::init().unwrap();
|
|
|
|
|
|
|
|
struct TaskFlushTest {
|
|
|
|
task: Task,
|
|
|
|
flush_start_sender: mpsc::Sender<()>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TaskImpl for TaskFlushTest {
|
|
|
|
fn iterate(&mut self) -> BoxFuture<'_, Result<(), gst::FlowError>> {
|
|
|
|
async move {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "flush_from_loop: flush_start from iteration");
|
2020-05-15 17:38:54 +00:00
|
|
|
assert_eq!(
|
|
|
|
self.task.flush_start().unwrap(),
|
|
|
|
TransitionStatus::NotWaiting {
|
2020-05-25 15:18:31 +00:00
|
|
|
trigger: Trigger::FlushStart,
|
2020-05-15 17:38:54 +00:00
|
|
|
origin: TaskState::Started,
|
|
|
|
},
|
|
|
|
);
|
|
|
|
Ok(())
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
.boxed()
|
|
|
|
}
|
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
fn flush_start(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
|
2020-04-20 19:35:06 +00:00
|
|
|
async move {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "flush_from_loop: started flushing");
|
2020-04-20 19:35:06 +00:00
|
|
|
self.flush_start_sender.send(()).await.unwrap();
|
2020-05-15 17:38:54 +00:00
|
|
|
Ok(())
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
.boxed()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-26 09:54:34 +00:00
|
|
|
let context = Context::acquire("flush_from_loop", Duration::from_millis(2)).unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
|
|
|
let task = Task::default();
|
|
|
|
|
|
|
|
let (flush_start_sender, mut flush_start_receiver) = mpsc::channel(1);
|
|
|
|
task.prepare(
|
|
|
|
TaskFlushTest {
|
|
|
|
task: task.clone(),
|
|
|
|
flush_start_sender,
|
|
|
|
},
|
|
|
|
context,
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
task.start().unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(
|
2020-04-20 19:35:06 +00:00
|
|
|
RUNTIME_CAT,
|
|
|
|
"flush_from_loop: awaiting flush_start notification"
|
|
|
|
);
|
2021-11-25 18:26:26 +00:00
|
|
|
block_on(flush_start_receiver.next()).unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
assert_eq!(
|
|
|
|
task.stop().unwrap(),
|
|
|
|
TransitionStatus::Complete {
|
|
|
|
origin: TaskState::Flushing,
|
|
|
|
target: TaskState::Stopped,
|
|
|
|
},
|
|
|
|
);
|
2020-04-20 19:35:06 +00:00
|
|
|
task.unprepare().unwrap();
|
|
|
|
}
|
|
|
|
|
2021-11-25 18:26:26 +00:00
|
|
|
#[test]
|
|
|
|
fn pause_from_loop() {
|
2020-05-25 15:18:31 +00:00
|
|
|
// Purpose: make sure a start triggered from an iteration doesn't block.
|
2020-04-20 19:35:06 +00:00
|
|
|
// E.g. an auto pause cancellation after a delay.
|
|
|
|
gst::init().unwrap();
|
|
|
|
|
|
|
|
struct TaskStartTest {
|
|
|
|
task: Task,
|
2020-05-15 17:38:54 +00:00
|
|
|
pause_sender: mpsc::Sender<()>,
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl TaskImpl for TaskStartTest {
|
|
|
|
fn iterate(&mut self) -> BoxFuture<'_, Result<(), gst::FlowError>> {
|
|
|
|
async move {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "pause_from_loop: entering iteration");
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2021-12-14 18:40:27 +00:00
|
|
|
crate::runtime::time::delay_for(Duration::from_millis(50)).await;
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "pause_from_loop: pause from iteration");
|
2020-05-15 17:38:54 +00:00
|
|
|
assert_eq!(
|
|
|
|
self.task.pause().unwrap(),
|
|
|
|
TransitionStatus::NotWaiting {
|
2020-05-25 15:18:31 +00:00
|
|
|
trigger: Trigger::Pause,
|
2020-05-15 17:38:54 +00:00
|
|
|
origin: TaskState::Started,
|
|
|
|
},
|
|
|
|
);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
.boxed()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn pause(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
|
|
|
|
async move {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "pause_from_loop: entering pause action");
|
2020-05-15 17:38:54 +00:00
|
|
|
self.pause_sender.send(()).await.unwrap();
|
|
|
|
Ok(())
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
.boxed()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-26 09:54:34 +00:00
|
|
|
let context = Context::acquire("pause_from_loop", Duration::from_millis(2)).unwrap();
|
2020-03-19 18:34:51 +00:00
|
|
|
|
2020-04-20 19:35:06 +00:00
|
|
|
let task = Task::default();
|
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
let (pause_sender, mut pause_receiver) = mpsc::channel(1);
|
2020-04-20 19:35:06 +00:00
|
|
|
task.prepare(
|
|
|
|
TaskStartTest {
|
|
|
|
task: task.clone(),
|
2020-05-15 17:38:54 +00:00
|
|
|
pause_sender,
|
2020-04-20 19:35:06 +00:00
|
|
|
},
|
|
|
|
context,
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
task.start().unwrap();
|
2020-03-19 18:34:51 +00:00
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "pause_from_loop: awaiting pause notification");
|
2021-11-25 18:26:26 +00:00
|
|
|
block_on(pause_receiver.next()).unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
task.stop().unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
task.unprepare().unwrap();
|
|
|
|
}
|
|
|
|
|
2021-11-25 18:26:26 +00:00
|
|
|
#[test]
|
|
|
|
fn trigger_from_action() {
|
2020-05-25 15:18:31 +00:00
|
|
|
// Purpose: make sure an event triggered from a transition action doesn't block.
|
2020-04-20 19:35:06 +00:00
|
|
|
gst::init().unwrap();
|
|
|
|
|
|
|
|
struct TaskFlushTest {
|
|
|
|
task: Task,
|
|
|
|
flush_stop_sender: mpsc::Sender<()>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TaskImpl for TaskFlushTest {
|
|
|
|
fn iterate(&mut self) -> BoxFuture<'_, Result<(), gst::FlowError>> {
|
|
|
|
future::pending::<Result<(), gst::FlowError>>().boxed()
|
|
|
|
}
|
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
fn flush_start(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
|
2020-04-20 19:35:06 +00:00
|
|
|
async move {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(
|
2020-04-20 19:35:06 +00:00
|
|
|
RUNTIME_CAT,
|
2020-05-25 15:18:31 +00:00
|
|
|
"trigger_from_action: flush_start triggering flush_stop"
|
2020-04-20 19:35:06 +00:00
|
|
|
);
|
2020-05-15 17:38:54 +00:00
|
|
|
assert_eq!(
|
|
|
|
self.task.flush_stop().unwrap(),
|
|
|
|
TransitionStatus::NotWaiting {
|
2020-05-25 15:18:31 +00:00
|
|
|
trigger: Trigger::FlushStop,
|
2020-05-15 17:38:54 +00:00
|
|
|
origin: TaskState::Started,
|
|
|
|
},
|
|
|
|
);
|
|
|
|
Ok(())
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
.boxed()
|
|
|
|
}
|
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
fn flush_stop(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
|
2020-04-20 19:35:06 +00:00
|
|
|
async move {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "trigger_from_action: stopped flushing");
|
2020-04-20 19:35:06 +00:00
|
|
|
self.flush_stop_sender.send(()).await.unwrap();
|
2020-05-15 17:38:54 +00:00
|
|
|
Ok(())
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
.boxed()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-26 09:54:34 +00:00
|
|
|
let context = Context::acquire("trigger_from_action", Duration::from_millis(2)).unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
|
|
|
let task = Task::default();
|
|
|
|
|
|
|
|
let (flush_stop_sender, mut flush_stop_receiver) = mpsc::channel(1);
|
|
|
|
task.prepare(
|
|
|
|
TaskFlushTest {
|
|
|
|
task: task.clone(),
|
|
|
|
flush_stop_sender,
|
|
|
|
},
|
|
|
|
context,
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
task.start().unwrap();
|
|
|
|
task.flush_start().unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(
|
2020-03-19 18:34:51 +00:00
|
|
|
RUNTIME_CAT,
|
2020-05-25 15:18:31 +00:00
|
|
|
"trigger_from_action: awaiting flush_stop notification"
|
2020-03-19 18:34:51 +00:00
|
|
|
);
|
2021-11-25 18:26:26 +00:00
|
|
|
block_on(flush_stop_receiver.next()).unwrap();
|
2020-03-19 18:34:51 +00:00
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
task.stop().unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
task.unprepare().unwrap();
|
|
|
|
}
|
2020-03-19 18:34:51 +00:00
|
|
|
|
2021-11-25 18:26:26 +00:00
|
|
|
#[test]
|
|
|
|
fn pause_flush_start() {
|
2020-04-20 19:35:06 +00:00
|
|
|
gst::init().unwrap();
|
|
|
|
|
|
|
|
struct TaskFlushTest {
|
|
|
|
started_sender: mpsc::Sender<()>,
|
|
|
|
flush_start_sender: mpsc::Sender<()>,
|
|
|
|
flush_stop_sender: mpsc::Sender<()>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TaskImpl for TaskFlushTest {
|
2020-05-15 17:38:54 +00:00
|
|
|
fn start(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
|
2020-04-20 19:35:06 +00:00
|
|
|
async move {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "pause_flush_start: started");
|
2020-04-20 19:35:06 +00:00
|
|
|
self.started_sender.send(()).await.unwrap();
|
2020-05-15 17:38:54 +00:00
|
|
|
Ok(())
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
.boxed()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn iterate(&mut self) -> BoxFuture<'_, Result<(), gst::FlowError>> {
|
|
|
|
future::pending::<Result<(), gst::FlowError>>().boxed()
|
|
|
|
}
|
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
fn flush_start(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
|
2020-04-20 19:35:06 +00:00
|
|
|
async move {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "pause_flush_start: started flushing");
|
2020-04-20 19:35:06 +00:00
|
|
|
self.flush_start_sender.send(()).await.unwrap();
|
2020-05-15 17:38:54 +00:00
|
|
|
Ok(())
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
.boxed()
|
|
|
|
}
|
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
fn flush_stop(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
|
2020-04-20 19:35:06 +00:00
|
|
|
async move {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "pause_flush_start: stopped flushing");
|
2020-04-20 19:35:06 +00:00
|
|
|
self.flush_stop_sender.send(()).await.unwrap();
|
2020-05-15 17:38:54 +00:00
|
|
|
Ok(())
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
.boxed()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-26 09:54:34 +00:00
|
|
|
let context = Context::acquire("pause_flush_start", Duration::from_millis(2)).unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
|
|
|
let task = Task::default();
|
|
|
|
|
|
|
|
let (started_sender, mut started_receiver) = mpsc::channel(1);
|
|
|
|
let (flush_start_sender, mut flush_start_receiver) = mpsc::channel(1);
|
|
|
|
let (flush_stop_sender, mut flush_stop_receiver) = mpsc::channel(1);
|
|
|
|
task.prepare(
|
|
|
|
TaskFlushTest {
|
|
|
|
started_sender,
|
|
|
|
flush_start_sender,
|
|
|
|
flush_stop_sender,
|
|
|
|
},
|
|
|
|
context,
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
// Pause, FlushStart, FlushStop, Start
|
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "pause_flush_start: pausing");
|
2020-05-15 17:38:54 +00:00
|
|
|
assert_eq!(
|
|
|
|
task.pause().unwrap(),
|
|
|
|
TransitionStatus::Complete {
|
|
|
|
origin: TaskState::Prepared,
|
|
|
|
target: TaskState::Paused,
|
|
|
|
},
|
|
|
|
);
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "pause_flush_start: starting flush");
|
2020-05-15 17:38:54 +00:00
|
|
|
assert_eq!(
|
|
|
|
task.flush_start().unwrap(),
|
|
|
|
TransitionStatus::Complete {
|
|
|
|
origin: TaskState::Paused,
|
|
|
|
target: TaskState::PausedFlushing,
|
|
|
|
},
|
|
|
|
);
|
2020-04-20 19:35:06 +00:00
|
|
|
assert_eq!(task.state(), TaskState::PausedFlushing);
|
2021-11-25 18:26:26 +00:00
|
|
|
block_on(flush_start_receiver.next());
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "pause_flush_start: stopping flush");
|
2020-05-15 17:38:54 +00:00
|
|
|
assert_eq!(
|
|
|
|
task.flush_stop().unwrap(),
|
|
|
|
TransitionStatus::Complete {
|
|
|
|
origin: TaskState::PausedFlushing,
|
|
|
|
target: TaskState::Paused,
|
|
|
|
},
|
|
|
|
);
|
2020-04-20 19:35:06 +00:00
|
|
|
assert_eq!(task.state(), TaskState::Paused);
|
2021-11-25 18:26:26 +00:00
|
|
|
block_on(flush_stop_receiver.next());
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2020-05-25 15:18:31 +00:00
|
|
|
// start action not executed
|
2020-04-20 19:35:06 +00:00
|
|
|
started_receiver.try_next().unwrap_err();
|
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "pause_flush_start: starting after flushing");
|
2020-05-15 17:38:54 +00:00
|
|
|
assert_eq!(
|
|
|
|
task.start().unwrap(),
|
|
|
|
TransitionStatus::Complete {
|
|
|
|
origin: TaskState::Paused,
|
|
|
|
target: TaskState::Started,
|
|
|
|
},
|
|
|
|
);
|
2020-04-20 19:35:06 +00:00
|
|
|
assert_eq!(task.state(), TaskState::Started);
|
2021-11-25 18:26:26 +00:00
|
|
|
block_on(started_receiver.next());
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
task.stop().unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
task.unprepare().unwrap();
|
|
|
|
}
|
|
|
|
|
2021-11-25 18:26:26 +00:00
|
|
|
#[test]
|
|
|
|
fn pause_flushing_start() {
|
2020-04-20 19:35:06 +00:00
|
|
|
gst::init().unwrap();
|
|
|
|
|
|
|
|
struct TaskFlushTest {
|
|
|
|
started_sender: mpsc::Sender<()>,
|
|
|
|
flush_start_sender: mpsc::Sender<()>,
|
|
|
|
flush_stop_sender: mpsc::Sender<()>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TaskImpl for TaskFlushTest {
|
2020-05-15 17:38:54 +00:00
|
|
|
fn start(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
|
2020-04-20 19:35:06 +00:00
|
|
|
async move {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "pause_flushing_start: started");
|
2020-04-20 19:35:06 +00:00
|
|
|
self.started_sender.send(()).await.unwrap();
|
2020-05-15 17:38:54 +00:00
|
|
|
Ok(())
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
.boxed()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn iterate(&mut self) -> BoxFuture<'_, Result<(), gst::FlowError>> {
|
|
|
|
future::pending::<Result<(), gst::FlowError>>().boxed()
|
|
|
|
}
|
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
fn flush_start(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
|
2020-04-20 19:35:06 +00:00
|
|
|
async move {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "pause_flushing_start: started flushing");
|
2020-04-20 19:35:06 +00:00
|
|
|
self.flush_start_sender.send(()).await.unwrap();
|
2020-05-15 17:38:54 +00:00
|
|
|
Ok(())
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
.boxed()
|
|
|
|
}
|
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
fn flush_stop(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
|
2020-04-20 19:35:06 +00:00
|
|
|
async move {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "pause_flushing_start: stopped flushing");
|
2020-04-20 19:35:06 +00:00
|
|
|
self.flush_stop_sender.send(()).await.unwrap();
|
2020-05-15 17:38:54 +00:00
|
|
|
Ok(())
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
.boxed()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-26 09:54:34 +00:00
|
|
|
let context = Context::acquire("pause_flushing_start", Duration::from_millis(2)).unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
|
|
|
let task = Task::default();
|
|
|
|
|
|
|
|
let (started_sender, mut started_receiver) = mpsc::channel(1);
|
|
|
|
let (flush_start_sender, mut flush_start_receiver) = mpsc::channel(1);
|
|
|
|
let (flush_stop_sender, mut flush_stop_receiver) = mpsc::channel(1);
|
|
|
|
task.prepare(
|
|
|
|
TaskFlushTest {
|
|
|
|
started_sender,
|
|
|
|
flush_start_sender,
|
|
|
|
flush_stop_sender,
|
|
|
|
},
|
|
|
|
context,
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
// Pause, FlushStart, Start, FlushStop
|
2020-03-19 18:34:51 +00:00
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "pause_flushing_start: pausing");
|
2020-05-15 17:38:54 +00:00
|
|
|
task.pause().unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "pause_flushing_start: starting flush");
|
2020-05-15 17:38:54 +00:00
|
|
|
task.flush_start().unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
assert_eq!(task.state(), TaskState::PausedFlushing);
|
2021-11-25 18:26:26 +00:00
|
|
|
block_on(flush_start_receiver.next());
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "pause_flushing_start: starting while flushing");
|
2020-05-15 17:38:54 +00:00
|
|
|
assert_eq!(
|
|
|
|
task.start().unwrap(),
|
|
|
|
TransitionStatus::Complete {
|
|
|
|
origin: TaskState::PausedFlushing,
|
|
|
|
target: TaskState::Flushing,
|
|
|
|
},
|
|
|
|
);
|
2020-04-20 19:35:06 +00:00
|
|
|
assert_eq!(task.state(), TaskState::Flushing);
|
|
|
|
|
2020-05-25 15:18:31 +00:00
|
|
|
// start action not executed
|
2020-04-20 19:35:06 +00:00
|
|
|
started_receiver.try_next().unwrap_err();
|
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "pause_flushing_start: stopping flush");
|
2020-05-15 17:38:54 +00:00
|
|
|
assert_eq!(
|
|
|
|
task.flush_stop().unwrap(),
|
|
|
|
TransitionStatus::Complete {
|
|
|
|
origin: TaskState::Flushing,
|
|
|
|
target: TaskState::Started,
|
|
|
|
},
|
|
|
|
);
|
2020-04-20 19:35:06 +00:00
|
|
|
assert_eq!(task.state(), TaskState::Started);
|
2021-11-25 18:26:26 +00:00
|
|
|
block_on(flush_stop_receiver.next());
|
|
|
|
block_on(started_receiver.next());
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
task.stop().unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
task.unprepare().unwrap();
|
|
|
|
}
|
|
|
|
|
2021-11-25 18:26:26 +00:00
|
|
|
#[test]
|
|
|
|
fn flush_concurrent_start() {
|
2020-04-20 19:35:06 +00:00
|
|
|
// Purpose: check the racy case of start being triggered in // after flush_start
|
|
|
|
// e.g.: a FlushStart event received on a Pad and the element starting after a Pause
|
|
|
|
gst::init().unwrap();
|
|
|
|
|
|
|
|
struct TaskStartTest {
|
|
|
|
flush_start_sender: mpsc::Sender<()>,
|
|
|
|
flush_stop_sender: mpsc::Sender<()>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TaskImpl for TaskStartTest {
|
|
|
|
fn iterate(&mut self) -> BoxFuture<'_, Result<(), gst::FlowError>> {
|
|
|
|
future::pending::<Result<(), gst::FlowError>>().boxed()
|
|
|
|
}
|
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
fn flush_start(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
|
2020-04-20 19:35:06 +00:00
|
|
|
async move {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "flush_concurrent_start: started flushing");
|
2020-04-20 19:35:06 +00:00
|
|
|
self.flush_start_sender.send(()).await.unwrap();
|
2020-05-15 17:38:54 +00:00
|
|
|
Ok(())
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
.boxed()
|
|
|
|
}
|
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
fn flush_stop(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
|
2020-04-20 19:35:06 +00:00
|
|
|
async move {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "flush_concurrent_start: stopped flushing");
|
2020-04-20 19:35:06 +00:00
|
|
|
self.flush_stop_sender.send(()).await.unwrap();
|
2020-05-15 17:38:54 +00:00
|
|
|
Ok(())
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
.boxed()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-26 09:54:34 +00:00
|
|
|
let context = Context::acquire("flush_concurrent_start", Duration::from_millis(2)).unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
|
|
|
let task = Task::default();
|
|
|
|
|
|
|
|
let (flush_start_sender, mut flush_start_receiver) = mpsc::channel(1);
|
|
|
|
let (flush_stop_sender, mut flush_stop_receiver) = mpsc::channel(1);
|
|
|
|
task.prepare(
|
|
|
|
TaskStartTest {
|
|
|
|
flush_start_sender,
|
|
|
|
flush_stop_sender,
|
|
|
|
},
|
|
|
|
context,
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
2021-05-26 09:54:34 +00:00
|
|
|
let oob_context =
|
|
|
|
Context::acquire("flush_concurrent_start_oob", Duration::from_millis(2)).unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
let task_clone = task.clone();
|
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
task.pause().unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
|
|
|
// Launch flush_start // start
|
|
|
|
let (ready_sender, ready_receiver) = oneshot::channel();
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "flush_concurrent_start: spawning flush_start");
|
2020-04-20 19:35:06 +00:00
|
|
|
let flush_start_handle = oob_context.spawn(async move {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "flush_concurrent_start: // flush_start");
|
2020-04-20 19:35:06 +00:00
|
|
|
ready_sender.send(()).unwrap();
|
2020-05-15 17:38:54 +00:00
|
|
|
let res = task_clone.flush_start().unwrap();
|
|
|
|
match res {
|
|
|
|
TransitionStatus::Async {
|
2020-05-25 15:18:31 +00:00
|
|
|
trigger: Trigger::FlushStart,
|
2020-05-15 17:38:54 +00:00
|
|
|
origin: TaskState::Paused,
|
|
|
|
} => (),
|
|
|
|
TransitionStatus::Async {
|
2020-05-25 15:18:31 +00:00
|
|
|
trigger: Trigger::FlushStart,
|
2020-05-15 17:38:54 +00:00
|
|
|
origin: TaskState::Started,
|
|
|
|
} => (),
|
|
|
|
other => unreachable!("{:?}", other),
|
|
|
|
}
|
2020-04-20 19:35:06 +00:00
|
|
|
Context::drain_sub_tasks().await.unwrap();
|
|
|
|
flush_start_receiver.next().await.unwrap();
|
|
|
|
});
|
2020-03-19 18:34:51 +00:00
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(
|
2020-03-19 18:34:51 +00:00
|
|
|
RUNTIME_CAT,
|
2020-04-20 19:35:06 +00:00
|
|
|
"flush_concurrent_start: awaiting for oob_context"
|
2020-03-19 18:34:51 +00:00
|
|
|
);
|
2021-11-25 18:26:26 +00:00
|
|
|
block_on(ready_receiver).unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "flush_concurrent_start: // start");
|
2020-05-15 17:38:54 +00:00
|
|
|
let res = task.start().unwrap();
|
|
|
|
match res {
|
|
|
|
TransitionStatus::Complete {
|
|
|
|
origin: TaskState::Paused,
|
|
|
|
target: TaskState::Started,
|
|
|
|
} => (),
|
|
|
|
TransitionStatus::Complete {
|
|
|
|
origin: TaskState::PausedFlushing,
|
|
|
|
target: TaskState::Flushing,
|
|
|
|
} => (),
|
|
|
|
other => unreachable!("{:?}", other),
|
|
|
|
}
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2021-11-25 18:26:26 +00:00
|
|
|
block_on(flush_start_handle).unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "flush_concurrent_start: requesting flush_stop");
|
2020-05-15 17:38:54 +00:00
|
|
|
assert_eq!(
|
|
|
|
task.flush_stop().unwrap(),
|
|
|
|
TransitionStatus::Complete {
|
|
|
|
origin: TaskState::Flushing,
|
|
|
|
target: TaskState::Started,
|
|
|
|
},
|
|
|
|
);
|
2020-04-20 19:35:06 +00:00
|
|
|
|
|
|
|
assert_eq!(task.state(), TaskState::Started);
|
2021-11-25 18:26:26 +00:00
|
|
|
block_on(flush_stop_receiver.next());
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
task.stop().unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
task.unprepare().unwrap();
|
|
|
|
}
|
|
|
|
|
2021-11-25 18:26:26 +00:00
|
|
|
#[test]
|
|
|
|
fn start_timer() {
|
2020-04-20 19:35:06 +00:00
|
|
|
// Purpose: make sure a Timer initialized in a transition is
|
|
|
|
// available when iterating in the loop.
|
|
|
|
gst::init().unwrap();
|
|
|
|
|
|
|
|
struct TaskTimerTest {
|
2021-12-14 18:40:27 +00:00
|
|
|
timer: Option<crate::runtime::Timer>,
|
2020-04-20 19:35:06 +00:00
|
|
|
timer_elapsed_sender: Option<oneshot::Sender<()>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TaskImpl for TaskTimerTest {
|
2020-05-15 17:38:54 +00:00
|
|
|
fn start(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
|
2020-04-20 19:35:06 +00:00
|
|
|
async move {
|
2021-12-14 18:40:27 +00:00
|
|
|
self.timer = Some(crate::runtime::time::delay_for(Duration::from_millis(50)));
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "start_timer: started");
|
2020-05-15 17:38:54 +00:00
|
|
|
Ok(())
|
2020-04-20 19:35:06 +00:00
|
|
|
}
|
|
|
|
.boxed()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn iterate(&mut self) -> BoxFuture<'_, Result<(), gst::FlowError>> {
|
|
|
|
async move {
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "start_timer: awaiting timer");
|
2020-04-20 19:35:06 +00:00
|
|
|
self.timer.take().unwrap().await;
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "start_timer: timer elapsed");
|
2020-04-20 19:35:06 +00:00
|
|
|
|
|
|
|
if let Some(timer_elapsed_sender) = self.timer_elapsed_sender.take() {
|
|
|
|
timer_elapsed_sender.send(()).unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
Err(gst::FlowError::Eos)
|
|
|
|
}
|
|
|
|
.boxed()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-26 09:54:34 +00:00
|
|
|
let context = Context::acquire("start_timer", Duration::from_millis(2)).unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
|
|
|
let task = Task::default();
|
|
|
|
|
|
|
|
let (timer_elapsed_sender, timer_elapsed_receiver) = oneshot::channel();
|
|
|
|
task.prepare(
|
|
|
|
TaskTimerTest {
|
|
|
|
timer: None,
|
|
|
|
timer_elapsed_sender: Some(timer_elapsed_sender),
|
|
|
|
},
|
|
|
|
context,
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "start_timer: start");
|
2020-05-15 17:38:54 +00:00
|
|
|
task.start().unwrap();
|
2020-04-20 19:35:06 +00:00
|
|
|
|
2021-11-25 18:26:26 +00:00
|
|
|
block_on(timer_elapsed_receiver).unwrap();
|
2022-02-21 17:43:46 +00:00
|
|
|
gst::debug!(RUNTIME_CAT, "start_timer: timer elapsed received");
|
2020-03-19 18:34:51 +00:00
|
|
|
|
2020-05-15 17:38:54 +00:00
|
|
|
task.stop().unwrap();
|
2020-03-19 18:34:51 +00:00
|
|
|
task.unprepare().unwrap();
|
|
|
|
}
|
2019-12-02 09:30:07 +00:00
|
|
|
}
|