nautilus_common/
runner.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 Posei Systems Pty Ltd. All rights reserved.
3//  https://poseitrader.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16use std::{
17    cell::{OnceCell, RefCell},
18    collections::VecDeque,
19    rc::Rc,
20};
21
22use crate::{
23    clock::Clock,
24    messages::{DataEvent, data::DataCommand},
25    timer::TimeEvent,
26};
27
28pub type GlobalClock = Rc<RefCell<dyn Clock>>;
29
30/// # Panics
31///
32/// Panics if thread-local storage cannot be accessed or the global clock is uninitialized.
33#[must_use]
34pub fn get_global_clock() -> Rc<RefCell<dyn Clock>> {
35    CLOCK
36        .try_with(|clock| {
37            clock
38                .get()
39                .expect("Clock should be initialized by runner")
40                .clone()
41        })
42        .expect("Should be able to access thread local storage")
43}
44
45/// # Panics
46///
47/// Panics if thread-local storage cannot be accessed or the global clock is already set.
48pub fn set_global_clock(c: Rc<RefCell<dyn Clock>>) {
49    CLOCK
50        .try_with(|clock| {
51            assert!(clock.set(c).is_ok(), "Global clock already set");
52        })
53        .expect("Should be able to access thread local clock");
54}
55
56pub type DataCommandQueue = Rc<RefCell<VecDeque<DataCommand>>>;
57
58/// Get globally shared message bus command queue
59/// # Panics
60///
61/// Panics if thread-local storage cannot be accessed.
62#[must_use]
63pub fn get_data_cmd_queue() -> DataCommandQueue {
64    DATA_CMD_QUEUE
65        .try_with(std::clone::Clone::clone)
66        .expect("Should be able to access thread local storage")
67}
68
69pub trait DataQueue {
70    fn push(&mut self, event: DataEvent);
71}
72
73pub type GlobalDataQueue = Rc<RefCell<dyn DataQueue>>;
74
75#[derive(Debug)]
76pub struct SyncDataQueue(VecDeque<DataEvent>);
77
78impl DataQueue for SyncDataQueue {
79    fn push(&mut self, event: DataEvent) {
80        self.0.push_back(event);
81    }
82}
83
84/// # Panics
85///
86/// Panics if thread-local storage cannot be accessed or the data event queue is uninitialized.
87#[must_use]
88pub fn get_data_evt_queue() -> Rc<RefCell<dyn DataQueue>> {
89    DATA_EVT_QUEUE
90        .try_with(|dq| {
91            dq.get()
92                .expect("Data queue should be initialized by runner")
93                .clone()
94        })
95        .expect("Should be able to access thread local storage")
96}
97
98/// # Panics
99///
100/// Panics if thread-local storage cannot be accessed or the global data event queue is already set.
101pub fn set_data_evt_queue(dq: Rc<RefCell<dyn DataQueue>>) {
102    DATA_EVT_QUEUE
103        .try_with(|deque| {
104            assert!(deque.set(dq).is_ok(), "Global data queue already set");
105        })
106        .expect("Should be able to access thread local storage");
107}
108
109thread_local! {
110    static CLOCK: OnceCell<GlobalClock> = OnceCell::new();
111    static DATA_EVT_QUEUE: OnceCell<GlobalDataQueue> = OnceCell::new();
112    static DATA_CMD_QUEUE: DataCommandQueue = Rc::new(RefCell::new(VecDeque::new()));
113}
114
115// Represents different event types for the runner.
116#[allow(clippy::large_enum_variant)]
117#[derive(Debug)]
118pub enum RunnerEvent {
119    Data(DataEvent),
120    Timer(TimeEvent),
121}