/* 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/debug.h" namespace MTropolis { // 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 destructAndRunTask() = 0; virtual void relocateTo(void *newPosition) = 0; virtual bool handlesFault(const VThreadFaultIdentifier *faultID) const = 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 { size_t taskDataOffset; // Offset to VThreadTaskData size_t prevFrameOffset; #ifdef MTROPOLIS_DEBUG_VTHREAD_STACKS VThreadTaskData *data; VThreadStackFrame *prevFrame; #endif }; 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 destructAndRunTask() override; void relocateTo(void *newPosition) override; bool handlesFault(const VThreadFaultIdentifier *faultID) const 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 destructAndRunTask() override; void relocateTo(void *newPosition) override; bool handlesFault(const VThreadFaultIdentifier *faultID) const override; TData &getData(); private: const VThreadFaultIdentifier *_faultID; VThreadState (*_func)(const TData &data); TData _data; }; class VThread { public: VThread(); ~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)); template TData *pushFaultHandler(const char *name, TClass *obj, VThreadState (TClass::*method)(const TData &data)); template TData *pushFaultHandler(const char *name, VThreadState (*func)(const TData &data)); VThreadState step(); bool hasTasks() const; private: 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)); void reserveFrame(size_t size, size_t alignment, void *&outFramePtr, void *&outUnadjustedDataPtr, size_t &outPrevFrameOffset); bool popFrame(void *&dataPtr, void *&outFramePtr); void *_stackUnalignedBase; void *_stackAlignedBase; //size_t _size; size_t _alignment; size_t _used; VThreadFaultIdentifier *_faultID; }; 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::destructAndRunTask() { TData data(static_cast(_data)); TClass *target = _target; VThreadState (TClass::*method)(const TData &) = _method; this->~VThreadMethodData(); return (target->*method)(data); } template void VThreadMethodData::relocateTo(void *newPosition) { void *adjustedPtr = static_cast *>(static_cast(newPosition)); new (adjustedPtr) VThreadMethodData(static_cast &&>(*this)); } template bool VThreadMethodData::handlesFault(const VThreadFaultIdentifier* faultID) const { return _faultID == faultID; } 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::destructAndRunTask() { TData data(static_cast(_data)); VThreadState (*func)(const TData &) = _func; this->~VThreadFunctionData(); return func(data); } template void VThreadFunctionData::relocateTo(void *newPosition) { void *adjustedPtr = static_cast *>(static_cast(newPosition)); new (adjustedPtr) VThreadFunctionData(static_cast &&>(*this)); } template bool VThreadFunctionData::handlesFault(const VThreadFaultIdentifier *faultID) const { return _faultID == faultID; } template TData &VThreadFunctionData::getData() { return _data; } 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::pushFaultHandler(const char *name, TClass *obj, VThreadState (TClass::*method)(const TData &data)) { return this->pushTaskWithFaultHandler(&VThreadFaultIdentifierSingleton::_identifier, name, obj, method); } template TData *VThread::pushFaultHandler(const char *name, VThreadState (*func)(const TData &data)) { return this->pushTaskWithFaultHandler(&VThreadFaultIdentifierSingleton::_identifier, 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); const size_t maxAlignment = (frameAlignment < dataAlignment) ? dataAlignment : frameAlignment; void *framePtr; void *dataPtr; size_t prevFrameOffset; reserveFrame(sizeof(FrameData_t), maxAlignment, framePtr, dataPtr, prevFrameOffset); VThreadStackFrame *frame = new (framePtr) VThreadStackFrame(); frame->prevFrameOffset = prevFrameOffset; FrameData_t *frameData = new (dataPtr) FrameData_t(faultID, obj, method); frame->taskDataOffset = reinterpret_cast(static_cast(frameData)) - static_cast(_stackAlignedBase); #ifdef MTROPOLIS_DEBUG_VTHREAD_STACKS frame->prevFrame = reinterpret_cast(static_cast(_stackAlignedBase) + prevFrameOffset); frame->data = frameData; #endif #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); const size_t maxAlignment = (frameAlignment < dataAlignment) ? dataAlignment : frameAlignment; void *framePtr; void *dataPtr; size_t prevFrameOffset; reserveFrame(sizeof(FrameData_t), maxAlignment, framePtr, dataPtr, prevFrameOffset); VThreadStackFrame *frame = new (framePtr) VThreadStackFrame(); frame->prevFrameOffset = prevFrameOffset; FrameData_t *frameData = new (dataPtr) FrameData_t(faultID, func); frame->taskDataOffset = reinterpret_cast(static_cast(frameData)) - static_cast(_stackAlignedBase); #ifdef MTROPOLIS_DEBUG_VTHREAD_STACKS frame->prevFrame = reinterpret_cast(static_cast(_stackAlignedBase) + prevFrameOffset); frame->data = frameData; #endif #ifdef MTROPOLIS_DEBUG_ENABLE frameData->debugInit(name); #endif return &frameData->getData(); } } // End of namespace MTropolis #endif