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}