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 
31 namespace 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 
50 struct Task;
51 class Process;
52 class ObjectBase;
53 
54 enum class TaskReturnType {
55  Yield,
56  Finished,
57  Waiting
58 };
59 
60 struct 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 
75 private:
76  TaskReturn();
77  TaskReturnType _type;
78  union {
79  int32 _returnValue;
80  Task *_taskToWaitFor;
81  };
82 };
83 
84 struct 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 
94 protected:
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;
110 private:
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
118 struct DelayTask final : public Task {
119  DelayTask(Process &process, uint32 millis);
120  DelayTask(Process &process, Common::Serializer &s);
121  TaskReturn run() override;
122  void debugPrint() override;
123  void syncGame(Common::Serializer &s) override;
124  const char *taskName() const override;
125 
126 private:
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 
163 using ProcessId = uint32;
164 class Process {
165 public:
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 
181 private:
182  friend class Scheduler;
183  ProcessId _pid;
184  MainCharacterKind _character;
185  Common::Stack<Task *> _tasks;
186  Common::String _name;
187  int32 _lastReturnValue = 0;
188 };
189 
190 class Scheduler {
191 public:
192  ~Scheduler();
193 
194  void run();
195  void backupContext();
196  void restoreContext();
197  void killAllProcesses();
198  void killAllProcessesFor(MainCharacterKind characterKind);
199  void killProcessByName(const Common::String &name);
200  bool hasProcessWithName(const Common::String &name);
201  void debugPrint();
202  void prepareSyncGame(Common::Serializer &s);
203  void syncGame(Common::Serializer &s);
204 
205  template<typename TTask, typename... TaskArgs>
206  Process *createProcess(MainCharacterKind character, TaskArgs&&... args) {
207  Process *process = createProcessInternal(character);
208  process->_tasks.push(new TTask(*process, Common::forward<TaskArgs>(args)...));
209  return process;
210  }
211 
212 private:
213  Process *createProcessInternal(MainCharacterKind character);
214 
215  inline Common::Array<Process *> &processesToRun() { return _processArrays[_currentArrayI]; }
216  inline Common::Array<Process *> &processesToRunNext() { return _processArrays[!_currentArrayI]; }
217  Common::Array<Process *> _processArrays[2];
218  Common::Array<Process *> _backupProcesses;
219  uint8 _currentArrayI = 0;
220  ProcessId _nextPid = 1;
221  uint _currentProcessI = UINT_MAX;
222 
223 };
224 
225 }
226 
227 #endif // ALCACHOFA_SCHEDULER_H
Definition: alcachofa.h:45
Definition: str.h:59
Definition: scheduler.h:84
Definition: scheduler.h:164
Definition: objects.h:36
Definition: array.h:52
Definition: serializer.h:79
Definition: scheduler.h:190
Definition: scheduler.h:60
Definition: scheduler.h:118
Definition: stack.h:102