/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef MTROPOLIS_VTHREAD_H #define MTROPOLIS_VTHREAD_H #include "mtropolis/coroutine_protos.h" #include "mtropolis/coroutine_return_value.h" #include "mtropolis/debug.h" namespace MTropolis { struct ICoroutineManager; class VThread; // Virtual thread, really a task stack enum VThreadState { kVThreadReturn, kVThreadSuspended, kVThreadError, }; struct VThreadFaultIdentifier { }; template struct VThreadFaultIdentifierSingleton { static VThreadFaultIdentifier _identifier; }; template VThreadFaultIdentifier VThreadFaultIdentifierSingleton::_identifier; class VThreadTaskData : public Debuggable { public: VThreadTaskData(); virtual ~VThreadTaskData(); virtual VThreadState execute(VThread *thread) = 0; #ifdef MTROPOLIS_DEBUG_ENABLE public: void debugInit(const char *name); protected: SupportStatus debugGetSupportStatus() const override { return kSupportStatusDone; } const char *debugGetTypeName() const override { return "Task"; } const Common::String &debugGetName() const override { return _debugName; } void debugInspect(IDebugInspectionReport *report) const override; Common::String _debugName; #endif }; struct VThreadStackFrame; class VThreadStackChunk { public: explicit VThreadStackChunk(size_t capacity); VThreadStackChunk(VThreadStackChunk &&other); ~VThreadStackChunk(); VThreadStackFrame *_topFrame; byte *_memory; size_t _size; private: VThreadStackChunk() = delete; VThreadStackChunk(const VThreadStackChunk &) = delete; }; struct VThreadStackFrame { VThreadTaskData *data; VThreadStackFrame *prevFrame; bool isLastInChunk; }; template class VThreadMethodData : public VThreadTaskData { public: VThreadMethodData(const VThreadFaultIdentifier *faultID, TClass *target, VThreadState (TClass::*method)(const TData &data)); VThreadMethodData(const VThreadMethodData &other); VThreadMethodData(VThreadMethodData &&other); VThreadState execute(VThread *thread) override; TData &getData(); private: const VThreadFaultIdentifier *_faultID; TClass *_target; VThreadState (TClass::*_method)(const TData &data); TData _data; }; template class VThreadFunctionData : public VThreadTaskData { public: explicit VThreadFunctionData(const VThreadFaultIdentifier *faultID, VThreadState (*func)(const TData &data)); VThreadFunctionData(const VThreadFunctionData &other); VThreadFunctionData(VThreadFunctionData &&other); VThreadState execute(VThread *thread) override; TData &getData(); private: const VThreadFaultIdentifier *_faultID; VThreadState (*_func)(const TData &data); TData _data; }; class VThread { public: explicit VThread(ICoroutineManager *coroManager); ~VThread(); template TData *pushTask(const char *name, TClass *obj, VThreadState (TClass::*method)(const TData &data)); template TData *pushTask(const char *name, VThreadState (*func)(const TData &data)); VThreadState step(); bool hasTasks() const; bool popFrame(); VThreadTaskData *pushCoroutineFrame(const CompiledCoroutine *compiledCoro, const CoroutineParamsBase ¶ms, const CoroutineReturnValueRefBase &returnValueRef); template void pushCoroutineWithReturn(TReturnValue *returnValuePtr, TParams &&...args); template void pushCoroutineWithReturn(TReturnValue *returnValuePtr); template void pushCoroutine(TParams &&...args); template void pushCoroutine(); private: void reserveFrame(size_t frameAlignment, size_t frameSize, VThreadStackFrame *&outFramePtr, size_t dataAlignment, size_t dataSize, void *&outDataPtr, bool &outIsNewChunk); static bool reserveFrameInChunk(VThreadStackChunk *chunk, size_t frameAlignment, size_t frameSize, VThreadStackFrame *&outFramePtr, size_t dataAlignment, size_t dataSize, void *&outDataPtr); void pushCoroutineInternal(CompiledCoroutine **compiledCoroPtr, CoroutineCompileFunction_t compileFunc, bool isVoidReturn, const CoroutineParamsBase ¶ms, const CoroutineReturnValueRefBase &returnValueRef); template TData *pushTaskWithFaultHandler(const VThreadFaultIdentifier *faultID, const char *name, TClass *obj, VThreadState (TClass::*method)(const TData &data)); template TData *pushTaskWithFaultHandler(const VThreadFaultIdentifier *faultID, const char *name, VThreadState (*func)(const TData &data)); Common::Array _stackChunks; ICoroutineManager *_coroManager; uint _numActiveStackChunks; }; template VThreadMethodData::VThreadMethodData(const VThreadFaultIdentifier *faultID, TClass *target, VThreadState (TClass::*method)(const TData &data)) : _faultID(faultID), _target(target), _method(method) { } template VThreadMethodData::VThreadMethodData(const VThreadMethodData& other) : _faultID(other._faultID), _target(other._target), _method(other._method), _data(other._data) { } template VThreadMethodData::VThreadMethodData(VThreadMethodData &&other) : _faultID(other._faultID), _target(other._target), _method(other._method), _data(static_cast(other._data)) { } template VThreadState VThreadMethodData::execute(VThread *thread) { TData data(static_cast(_data)); TClass *target = _target; VThreadState (TClass::*method)(const TData &) = _method; thread->popFrame(); return (target->*method)(data); } template TData &VThreadMethodData::getData() { return _data; } template VThreadFunctionData::VThreadFunctionData(const VThreadFaultIdentifier *faultID, VThreadState (*func)(const TData &data)) : _faultID(faultID), _func(func) { } template VThreadFunctionData::VThreadFunctionData(const VThreadFunctionData &other) : _faultID(other._faultID), _func(other._func), _data(other._data) { } template VThreadFunctionData::VThreadFunctionData(VThreadFunctionData &&other) : _faultID(other._faultID), _func(other._func), _data(static_cast(other._data)) { } template VThreadState VThreadFunctionData::execute(VThread *thread) { TData data(static_cast(_data)); VThreadState (*func)(const TData &) = _func; thread->popFrame(); return func(data); } template TData &VThreadFunctionData::getData() { return _data; } template void VThread::pushCoroutineWithReturn(TReturnValue *returnValuePtr, TParams &&...args) { assert(returnValuePtr != nullptr); this->pushCoroutineInternal(&TCoroutine::ms_compiledCoro, TCoroutine::compileCoroutine, CoroutineReturnValueRef::isVoid(), typename TCoroutine::Params(Common::forward(args)...), CoroutineReturnValueRef(returnValuePtr)); } template void VThread::pushCoroutineWithReturn(TReturnValue *returnValuePtr) { assert(returnValuePtr != nullptr); this->pushCoroutineInternal(&TCoroutine::ms_compiledCoro, TCoroutine::compileCoroutine, CoroutineReturnValueRef::isVoid(), typename TCoroutine::Params(), CoroutineReturnValueRef(returnValuePtr)); } template void VThread::pushCoroutine(TParams &&...args) { this->pushCoroutineInternal(&TCoroutine::ms_compiledCoro, TCoroutine::compileCoroutine, CoroutineReturnValueRef::isVoid(), typename TCoroutine::Params(Common::forward(args)...), CoroutineReturnValueRef()); } template void VThread::pushCoroutine() { this->pushCoroutineInternal(&TCoroutine::ms_compiledCoro, TCoroutine::compileCoroutine, CoroutineReturnValueRef::isVoid(), typename TCoroutine::Params(), CoroutineReturnValueRef()); } template TData *VThread::pushTask(const char *name, TClass *obj, VThreadState (TClass::*method)(const TData &data)) { return this->pushTaskWithFaultHandler(nullptr, name, obj, method); } template TData *VThread::pushTask(const char *name, VThreadState (*func)(const TData &data)) { return this->pushTaskWithFaultHandler(nullptr, name, func); } template TData *VThread::pushTaskWithFaultHandler(const VThreadFaultIdentifier *faultID, const char *name, TClass *obj, VThreadState (TClass::*method)(const TData &data)) { typedef VThreadMethodData FrameData_t; const size_t frameAlignment = alignof(VThreadStackFrame); const size_t dataAlignment = alignof(FrameData_t); VThreadStackFrame *prevFrame = nullptr; if (_numActiveStackChunks > 0) prevFrame = _stackChunks[_numActiveStackChunks - 1]._topFrame; VThreadStackFrame *framePtr = nullptr; void *dataPtr = nullptr; bool isNewChunk = false; reserveFrame(frameAlignment, sizeof(VThreadStackFrame), framePtr, dataAlignment, sizeof(FrameData_t), dataPtr, isNewChunk); VThreadStackFrame *frame = new (framePtr) VThreadStackFrame(); FrameData_t *frameData = new (dataPtr) FrameData_t(faultID, obj, method); frame->data = frameData; frame->prevFrame = prevFrame; frame->isLastInChunk = isNewChunk; #ifdef MTROPOLIS_DEBUG_ENABLE frameData->debugInit(name); #endif return &frameData->getData(); } template TData *VThread::pushTaskWithFaultHandler(const VThreadFaultIdentifier *faultID, const char *name, VThreadState (*func)(const TData &data)) { typedef VThreadFunctionData FrameData_t; const size_t frameAlignment = alignof(VThreadStackFrame); const size_t dataAlignment = alignof(FrameData_t); VThreadStackFrame *prevFrame = nullptr; if (_numActiveStackChunks > 0) prevFrame = _stackChunks[_numActiveStackChunks - 1]._topFrame; VThreadStackFrame *framePtr = nullptr; void *dataPtr = nullptr; bool isNewChunk = false; reserveFrame(frameAlignment, sizeof(VThreadStackFrame), framePtr, dataAlignment, sizeof(FrameData_t), dataPtr, isNewChunk); VThreadStackFrame *frame = new (framePtr) VThreadStackFrame(); FrameData_t *frameData = new (dataPtr) FrameData_t(faultID, func); frame->data = frameData; frame->prevFrame = prevFrame; frame->isLastInChunk = isNewChunk; #ifdef MTROPOLIS_DEBUG_ENABLE frameData->debugInit(name); #endif return &frameData->getData(); } } // End of namespace MTropolis #endif