gear_common/
scheduler.rs

1// This file is part of Gear.
2
3// Copyright (C) 2022-2025 Gear Technologies Inc.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19//! Module for scheduler implementation.
20//!
21//! Scheduler provides API for all available regular or time-dependent actions.
22
23use crate::storage::{
24    CountedByKey, DoubleMapStorage, EmptyCallback, KeyIterableByKeyMap, ValueStorage,
25};
26use core::{fmt::Debug, marker::PhantomData};
27
28/// Represents scheduler's logic of centralized delayed tasks management logic.
29pub trait Scheduler {
30    /// Block number type of the messenger.
31    type BlockNumber;
32    /// Task type.
33    type Task;
34    /// Cost type.
35    type Cost;
36    /// Inner error type generated by gear's storage types.
37    type Error: TaskPoolError;
38    /// Output error of each storage algorithm.
39    ///
40    /// Implements `From<Self::Error>` to be able to return
41    /// any required error type.
42    type OutputError: From<Self::Error> + Debug;
43
44    /// Storing costs per block.
45    type CostsPerBlock: SchedulingCostsPerBlock<BlockNumber = Self::BlockNumber, Cost = Self::Cost>;
46
47    /// The first block of incomplete tasks, which have already passed,
48    /// but still contain tasks to deal with.
49    ///
50    /// Used for checking if scheduler is able to process
51    /// current block aimed tasks, or there are some
52    /// incomplete job from previous blocks.
53    type FirstIncompleteTasksBlock: ValueStorage<Value = Self::BlockNumber>;
54
55    /// Gear task pool.
56    ///
57    /// Task pool contains tasks with block number when they should be done.
58    type TaskPool: TaskPool<
59            BlockNumber = Self::BlockNumber,
60            Task = Self::Task,
61            Error = Self::Error,
62            OutputError = Self::OutputError,
63        > + CountedByKey<Key = Self::BlockNumber, Length = usize>
64        + KeyIterableByKeyMap<Key1 = Self::BlockNumber, Key2 = Self::Task>;
65
66    /// Resets all related to messenger storages.
67    ///
68    /// It's a temporary production solution to avoid DB migrations
69    /// and would be available for test purposes only in the future.
70    fn reset() {
71        Self::FirstIncompleteTasksBlock::kill();
72        Self::TaskPool::clear();
73    }
74}
75
76/// Storing costs getter trait.
77pub trait SchedulingCostsPerBlock {
78    /// Block number type.
79    type BlockNumber;
80    /// Cost type.
81    type Cost;
82
83    /// Extra reserve for being able to pay for blocks with incomplete tasks.
84    fn reserve_for() -> Self::BlockNumber;
85
86    /// Cost for storing code per block.
87    fn code() -> Self::Cost;
88    /// Cost for storing message in mailbox per block.
89    fn mailbox() -> Self::Cost;
90    /// Cost for storing program per block.
91    fn program() -> Self::Cost;
92    /// Cost for storing message in waitlist per block.
93    fn waitlist() -> Self::Cost;
94    /// Cost for reservation holding.
95    fn reservation() -> Self::Cost;
96    /// Cost for storing message in dispatch stash.
97    /// Everything sent delayed goes into dispatch stash.
98    fn dispatch_stash() -> Self::Cost;
99
100    /// Derives the cost per block based on the lock identifier
101    fn by_storage_type(storage: StorageType) -> Self::Cost;
102}
103
104/// The type whose variants correspond to various storages used in Gear,
105/// including waitlist, mailbox, delayed messages stash etc.
106/// Used as a parameter in functions performing some common actions on storages
107/// like, for instance, holding cost calculation, to signal a concrete storage kind.
108#[derive(Debug, Clone, Copy)]
109pub enum StorageType {
110    Code,
111    Mailbox,
112    Program,
113    Waitlist,
114    Reservation,
115    DispatchStash,
116}
117
118/// Represents tasks managing logic.
119pub trait TaskPool {
120    /// Block number type.
121    type BlockNumber;
122    /// Task type.
123    type Task;
124    /// Inner error type of queue storing algorithm.
125    type Error: TaskPoolError;
126    /// Output error type of the queue.
127    type OutputError: From<Self::Error>;
128
129    /// Inserts given task in task pool.
130    fn add(bn: Self::BlockNumber, task: Self::Task) -> Result<(), Self::OutputError>;
131
132    /// Removes all tasks from task pool.
133    fn clear();
134
135    /// Returns bool, defining does task exist in task pool.
136    fn contains(bn: &Self::BlockNumber, task: &Self::Task) -> bool;
137
138    /// Removes task from task pool by given keys,
139    /// if present, else returns error.
140    fn delete(bn: Self::BlockNumber, task: Self::Task) -> Result<(), Self::OutputError>;
141}
142
143/// Represents store of task pool's action callbacks.
144pub trait TaskPoolCallbacks {
145    /// Callback on success `add`.
146    type OnAdd: EmptyCallback;
147    /// Callback on success `delete`.
148    type OnDelete: EmptyCallback;
149}
150
151/// Represents task pool error type.
152///
153/// Contains constructors for all existing errors.
154pub trait TaskPoolError {
155    /// Occurs when given task already exists in task pool.
156    fn duplicate_task() -> Self;
157
158    /// Occurs when task wasn't found in storage.
159    fn task_not_found() -> Self;
160}
161
162/// `TaskPool` implementation based on `DoubleMapStorage`.
163///
164/// Generic parameter `Error` requires `TaskPoolError` implementation.
165/// Generic parameter `Callbacks` presents actions for success operations
166/// over task pool.
167pub struct TaskPoolImpl<T, Task, Error, OutputError, Callbacks>(
168    PhantomData<(T, Task, Error, OutputError, Callbacks)>,
169)
170where
171    T: DoubleMapStorage<Key2 = Task, Value = ()>,
172    Error: TaskPoolError,
173    OutputError: From<Error>,
174    Callbacks: TaskPoolCallbacks;
175
176// Implementation of `TaskPool` for `TaskPoolImpl`.
177impl<T, Task, Error, OutputError, Callbacks> TaskPool
178    for TaskPoolImpl<T, Task, Error, OutputError, Callbacks>
179where
180    T: DoubleMapStorage<Key2 = Task, Value = ()>,
181    Error: TaskPoolError,
182    OutputError: From<Error>,
183    Callbacks: TaskPoolCallbacks,
184{
185    type BlockNumber = T::Key1;
186    type Task = T::Key2;
187    type Error = Error;
188    type OutputError = OutputError;
189
190    fn add(bn: Self::BlockNumber, task: Self::Task) -> Result<(), Self::OutputError> {
191        if !Self::contains(&bn, &task) {
192            T::insert(bn, task, ());
193            Callbacks::OnAdd::call();
194            Ok(())
195        } else {
196            Err(Self::Error::duplicate_task().into())
197        }
198    }
199
200    fn clear() {
201        T::clear()
202    }
203
204    fn contains(bn: &Self::BlockNumber, task: &Self::Task) -> bool {
205        T::contains_keys(bn, task)
206    }
207
208    fn delete(bn: Self::BlockNumber, task: Self::Task) -> Result<(), Self::OutputError> {
209        if T::contains_keys(&bn, &task) {
210            T::remove(bn, task);
211            Callbacks::OnDelete::call();
212            Ok(())
213        } else {
214            Err(Self::Error::task_not_found().into())
215        }
216    }
217}
218
219// Implementation of `CountedByKey` trait for `TaskPoolImpl` in case,
220// when inner `DoubleMapStorage` implements `CountedByKey`.
221impl<T, Task, Error, OutputError, Callbacks> CountedByKey
222    for TaskPoolImpl<T, Task, Error, OutputError, Callbacks>
223where
224    T: DoubleMapStorage<Key2 = Task, Value = ()> + CountedByKey<Key = T::Key1>,
225    Error: TaskPoolError,
226    OutputError: From<Error>,
227    Callbacks: TaskPoolCallbacks,
228{
229    type Key = T::Key1;
230    type Length = T::Length;
231
232    fn len(key: &Self::Key) -> Self::Length {
233        T::len(key)
234    }
235}
236
237// Implementation of `KeyIterableByKeyMap` trait for `TaskPoolImpl` in case,
238// when inner `DoubleMapStorage` implements `KeyIterableByKeyMap`.
239impl<T, Task, Error, OutputError, Callbacks> KeyIterableByKeyMap
240    for TaskPoolImpl<T, Task, Error, OutputError, Callbacks>
241where
242    T: DoubleMapStorage<Key2 = Task, Value = ()> + KeyIterableByKeyMap,
243    Error: TaskPoolError,
244    OutputError: From<Error>,
245    Callbacks: TaskPoolCallbacks,
246{
247    type Key1 = <T as KeyIterableByKeyMap>::Key1;
248    type Key2 = <T as KeyIterableByKeyMap>::Key2;
249    type DrainIter = T::DrainIter;
250    type Iter = T::Iter;
251
252    fn drain_prefix_keys(bn: Self::Key1) -> Self::DrainIter {
253        T::drain_prefix_keys(bn)
254    }
255
256    fn iter_prefix_keys(bn: Self::Key1) -> Self::Iter {
257        T::iter_prefix_keys(bn)
258    }
259}