ScummVM API documentation
scheduler.h
1/* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22#ifndef ALCACHOFA_SCHEDULER_H
23#define ALCACHOFA_SCHEDULER_H
24
25#include "alcachofa/common.h"
26
27#include "common/stack.h"
28#include "common/str.h"
29#include "common/type_traits.h"
30
31namespace Alcachofa {
32
33/* Tasks are generally written as coroutines however the common coroutines
34 * cannot be used for two reasons:
35 * 1. The scheduler is too limited in managing when to run what coroutines
36 * E.g. for the inventory/menu we need to pause a set of coroutines and
37 * continue them later on
38 * 2. We need to save and load the state of coroutines
39 * For this we either write the state machine ourselves or we use
40 * the following careful macros where the state ID is explicitly written
41 * This way it is stable and if it has to change we can migrate
42 * savestates upon loading.
43 *
44 * Tasks are usually private, so in order to load them they:
45 * - need a constructor MyPrivateTask(Process &, Serializer &)
46 * - need call the macro DECLARE_TASK(MyPrivateTask)
47 * - they have to listed in tasks.h
48 */
49
50struct Task;
51class Process;
52class ObjectBase;
53
54enum class TaskReturnType {
55 Yield,
56 Finished,
57 Waiting
58};
59
60struct TaskReturn {
61 static inline TaskReturn yield() { return {}; }
62 static TaskReturn finish(int32 returnValue);
63 static TaskReturn waitFor(Task *task);
64
65 inline TaskReturnType type() const { return _type; }
66 inline int32 returnValue() const {
67 assert(_type == TaskReturnType::Finished);
68 return _returnValue;
69 }
70 inline Task *taskToWaitFor() const {
71 assert(_type == TaskReturnType::Waiting);
72 return _taskToWaitFor;
73 }
74
75private:
76 TaskReturn();
77 TaskReturnType _type;
78 union {
79 int32 _returnValue;
80 Task *_taskToWaitFor;
81 };
82};
83
84struct Task {
85 Task(Process &process);
86 virtual ~Task() {}
87 virtual TaskReturn run() = 0;
88 virtual void debugPrint() = 0;
89 virtual void syncGame(Common::Serializer &s);
90 virtual const char *taskName() const = 0; // implemented by DECLARE_TASK
91
92 inline Process &process() const { return _process; }
93
94protected:
95 Task *delay(uint32 millis);
96
97 void syncObjectAsString(Common::Serializer &s, ObjectBase *&object, bool optional = false) const;
98 template<class TObject>
99 void syncObjectAsString(Common::Serializer &s, TObject *&object, bool optional = false) const {
100 // We could add is_const and therefore true_type, false_type, integral_constant
101 // or we could just use const_cast and promise that we won't modify the object itself
102 ObjectBase *base = const_cast<Common::remove_const_t<TObject> *>(object);
103 syncObjectAsString(s, base, optional);
104 object = dynamic_cast<TObject *>(base);
105 if (object == nullptr && base != nullptr)
106 errorForUnexpectedObjectType(base);
107 }
108
109 uint32 _stage = 0;
110private:
111 void errorForUnexpectedObjectType(const ObjectBase *base) const;
112
113 Process &_process;
114};
115
116// implemented in alcachofa.cpp to prevent a compiler warning when
117// the declaration of the construct function comes after the definition
118struct DelayTask final : public Task {
119 DelayTask(Process &process, uint32 millis);
121 TaskReturn run() override;
122 void debugPrint() override;
123 void syncGame(Common::Serializer &s) override;
124 const char *taskName() const override;
125
126private:
127 uint32 _endTime = 0;
128};
129
130#define DECLARE_TASK(TaskName) \
131 extern Task *constructTask_##TaskName(Process &process, Serializer &s) { \
132 return new TaskName(process, s); \
133 } \
134 const char *TaskName::taskName() const { \
135 return #TaskName; \
136 }
137
138#define TASK_BEGIN \
139 switch(_stage) { \
140 case 0:; \
141
142#define TASK_END \
143 TASK_RETURN(0); \
144 default: assert(false && "Invalid line in task"); \
145 } return TaskReturn::finish(0)
146
147#define TASK_INTERNAL_BREAK(stage, ret) \
148 do { \
149 _stage = stage; \
150 return ret; \
151 case stage:; \
152 } while(0)
153
154#define TASK_YIELD(stage) TASK_INTERNAL_BREAK((stage), TaskReturn::yield())
155#define TASK_WAIT(stage, task) TASK_INTERNAL_BREAK((stage), TaskReturn::waitFor(task))
156
157#define TASK_RETURN(value) \
158 do { \
159 _stage = UINT_MAX; \
160 return TaskReturn::finish(value); \
161 } while(0)
162
163using ProcessId = uint32;
164class Process {
165public:
166 Process(ProcessId pid, MainCharacterKind characterKind);
168 ~Process();
169
170 inline ProcessId pid() const { return _pid; }
171 inline MainCharacterKind &character() { return _character; } // is changed in changeCharacter
172 inline MainCharacterKind character() const { return _character; }
173 inline int32 returnValue() const { return _lastReturnValue; }
174 inline Common::String &name() { return _name; }
175 bool isActiveForPlayer() const;
176
177 TaskReturnType run();
178 void debugPrint();
179 void syncGame(Common::Serializer &s);
180 void lockInteraction();
181 void unlockInteraction();
182
183private:
184 friend class Scheduler;
185 ProcessId _pid;
186 MainCharacterKind _character;
188 Common::String _name;
189 int32 _lastReturnValue = 0;
190 FakeLock _interactionLock;
191};
192
194public:
195 ~Scheduler();
196
197 void run();
198 void backupContext();
199 void restoreContext();
200 void killAllProcesses();
201 void killAllProcessesFor(MainCharacterKind characterKind);
202 void killProcessByName(const Common::String &name);
203 bool hasProcessWithName(const Common::String &name);
204 void debugPrint();
205 void prepareSyncGame(Common::Serializer &s);
206 void syncGame(Common::Serializer &s);
207
208 template<typename TTask, typename... TaskArgs>
209 Process *createProcess(MainCharacterKind character, TaskArgs&&... args) {
210 Process *process = createProcessInternal(character);
211 process->_tasks.push(new TTask(*process, Common::forward<TaskArgs>(args)...));
212 return process;
213 }
214
215private:
216 Process *createProcessInternal(MainCharacterKind character);
217
218 inline Common::Array<Process *> &processesToRun() { return _processArrays[_currentArrayI]; }
219 inline Common::Array<Process *> &processesToRunNext() { return _processArrays[!_currentArrayI]; }
220 Common::Array<Process *> _processArrays[2];
221 Common::Array<Process *> _backupProcesses;
222 uint8 _currentArrayI = 0;
223 ProcessId _nextPid = 1;
224 uint _currentProcessI = UINT_MAX;
225
226};
227
228}
229
230#endif // ALCACHOFA_SCHEDULER_H
Definition: objects.h:37
Definition: scheduler.h:164
bool isActiveForPlayer() const
and thus should e.g. draw subtitles or effects
Definition: scheduler.h:193
Definition: array.h:52
Definition: serializer.h:80
Definition: stack.h:102
Definition: str.h:59
Definition: scheduler.h:118
Definition: common.h:107
Definition: scheduler.h:84
Definition: scheduler.h:60