/* 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 .
*
*/
#include "mtropolis/vthread.h"
namespace MTropolis {
VThreadTaskData::VThreadTaskData() {
}
VThreadTaskData::~VThreadTaskData() {
}
#ifdef MTROPOLIS_DEBUG_ENABLE
void VThreadTaskData::debugInit(const char *name) {
_debugName = name;
}
void VThreadTaskData::debugInspect(IDebugInspectionReport *report) const {
}
#endif
VThread::VThread() : _faultID(nullptr), _stackUnalignedBase(nullptr), _stackAlignedBase(nullptr), /* _size(0), */_alignment(1), _used(0) {
}
VThread::~VThread() {
void *dataPtr;
void *framePtr;
while (popFrame(dataPtr, framePtr)) {
static_cast(framePtr)->~VThreadStackFrame();
static_cast(dataPtr)->~VThreadTaskData();
}
if (_stackUnalignedBase)
free(_stackUnalignedBase);
}
VThreadState VThread::step() {
void *dataPtr;
void *framePtr;
while (popFrame(dataPtr, framePtr)) {
VThreadTaskData *data = static_cast(dataPtr);
const bool isHandling = (data->handlesFault(_faultID));
static_cast(framePtr)->~VThreadStackFrame();
if (isHandling) {
_faultID = nullptr;
VThreadState state = data->destructAndRunTask();
if (state != kVThreadReturn)
return state;
} else {
static_cast(dataPtr)->~VThreadTaskData();
}
}
return kVThreadReturn;
}
bool VThread::hasTasks() const {
return _used > 0;
}
void VThread::reserveFrame(size_t size, size_t alignment, void *&outFramePtr, void *&outUnadjustedDataPtr, size_t &outPrevFrameOffset) {
const size_t frameAlignment = alignof(VThreadStackFrame);
const size_t frameAlignmentMask = frameAlignment - 1;
size_t dataAlignmentMask = alignment - 1;
bool needToReallocate = false;
if (alignment > _alignment || frameAlignment > _alignment) {
if ((reinterpret_cast(_stackAlignedBase) & dataAlignmentMask) != 0) {
needToReallocate = true;
}
}
size_t dataAlignmentPaddingNeeded = (alignment - (_used & dataAlignmentMask));
if (dataAlignmentPaddingNeeded == alignment)
dataAlignmentPaddingNeeded = 0;
size_t offsetOfData = dataAlignmentPaddingNeeded + _used;
size_t offsetOfEndOfData = offsetOfData + size;
size_t frameAlignmentPaddingNeeded = (frameAlignment - (offsetOfData & frameAlignmentMask));
if (frameAlignmentPaddingNeeded == frameAlignment)
frameAlignmentPaddingNeeded = 0;
size_t offsetOfFrame = offsetOfEndOfData + frameAlignmentPaddingNeeded;
size_t offsetOfEndOfFrame = offsetOfFrame + sizeof(VThreadStackFrame);
size_t offsetOfPrevFrame = 0;
if (_used > 0)
offsetOfPrevFrame = _used - sizeof(VThreadStackFrame);
if (offsetOfEndOfFrame > _used)
needToReallocate = true;
if (needToReallocate) {
size_t maxAlignment = alignment;
if (maxAlignment < frameAlignment)
maxAlignment = frameAlignment;
void *unalignedBase = malloc(offsetOfEndOfFrame + maxAlignment - 1);
size_t alignPadding = maxAlignment - (reinterpret_cast(unalignedBase) % maxAlignment);
if (alignPadding == maxAlignment)
alignPadding = 0;
void *alignedBase = static_cast(unalignedBase) + alignPadding;
// Copy the previous frames
size_t framePos = 0;
if (_used > 0) {
framePos = _used - sizeof(VThreadStackFrame);
#ifdef MTROPOLIS_DEBUG_VTHREAD_STACKS
VThreadStackFrame *nextFrame = nullptr;
#endif
while (framePos != 0) {
VThreadStackFrame *oldFrame = reinterpret_cast(static_cast(_stackAlignedBase) + framePos);
size_t dataPos = oldFrame->taskDataOffset;
VThreadTaskData *oldData = reinterpret_cast(static_cast(_stackAlignedBase) + dataPos);
size_t nextPos = oldFrame->prevFrameOffset;
VThreadStackFrame *newFrame = reinterpret_cast(static_cast(alignedBase) + framePos);
VThreadTaskData *newData = reinterpret_cast(static_cast(alignedBase) + dataPos);
// Relocate the frame
new (newFrame) VThreadStackFrame(*oldFrame);
oldFrame->~VThreadStackFrame();
// Relocate the data
oldData->relocateTo(newData);
oldData->~VThreadTaskData();
#ifdef MTROPOLIS_DEBUG_VTHREAD_STACKS
newFrame->data = newData;
newFrame->prevFrame = nullptr;
if (nextFrame)
nextFrame->prevFrame = newFrame;
nextFrame = newFrame;
#endif
framePos = nextPos;
}
}
if (_stackUnalignedBase)
free(_stackUnalignedBase);
_stackUnalignedBase = unalignedBase;
_stackAlignedBase = alignedBase;
}
VThreadStackFrame *newFrame = reinterpret_cast(static_cast(_stackAlignedBase) + offsetOfFrame);
void *newData = static_cast(_stackAlignedBase) + offsetOfData;
_used = offsetOfEndOfFrame;
outFramePtr = newFrame;
outUnadjustedDataPtr = newData;
outPrevFrameOffset = offsetOfPrevFrame;
}
bool VThread::popFrame(void *&dataPtr, void *&outFramePtr) {
if (_used == 0)
return false;
VThreadStackFrame *frame = reinterpret_cast(static_cast(_stackAlignedBase) + _used - sizeof(VThreadStackFrame));
VThreadTaskData *data = reinterpret_cast(static_cast(_stackAlignedBase) + frame->taskDataOffset);
dataPtr = data;
outFramePtr = frame;
if (frame->prevFrameOffset == 0)
_used = 0;
else
_used = frame->prevFrameOffset + sizeof(VThreadStackFrame);
return true;
}
} // End of namespace MTropolis