This can be used to check if the fiber has started its execution. Change-Id: Ie9222b8076756363c9f82c1333c76a352bcaf817 Signed-off-by: Giacomo Travaglini <giacomo.travaglini@arm.com> Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/18648 Reviewed-by: Gabe Black <gabeblack@google.com> Maintainer: Gabe Black <gabeblack@google.com> Tested-by: kokoro <noreply+kokoro@google.com>
185 lines
5.6 KiB
C++
185 lines
5.6 KiB
C++
/*
|
|
* Copyright 2018 Google, Inc.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are
|
|
* met: redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer;
|
|
* redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution;
|
|
* neither the name of the copyright holders nor the names of its
|
|
* contributors may be used to endorse or promote products derived from
|
|
* this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
* Authors: Gabe Black
|
|
*/
|
|
|
|
#include "base/fiber.hh"
|
|
|
|
#if HAVE_VALGRIND
|
|
#include <valgrind/valgrind.h>
|
|
#endif
|
|
|
|
// Mac OS requires _DARWIN_C_SOURCE if _POSIX_C_SOURCE is defined,
|
|
// otherwise it will mask the definition of MAP_ANONYMOUS.
|
|
// _POSIX_C_SOURCE is already defined by including <ucontext.h> in
|
|
// base/fiber.hh
|
|
#if defined(__APPLE__) && defined(__MACH__)
|
|
#define _DARWIN_C_SOURCE
|
|
#endif
|
|
|
|
#include <sys/mman.h>
|
|
#include <unistd.h>
|
|
|
|
#include <cerrno>
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
|
|
#include "base/logging.hh"
|
|
|
|
using namespace std;
|
|
|
|
namespace
|
|
{
|
|
|
|
/*
|
|
* The PrimaryFiber class is a special case that attaches to the currently
|
|
* executing context. That makes handling the "primary" fiber, aka the one
|
|
* which most of gem5 is running under, no different than other Fibers.
|
|
*/
|
|
class PrimaryFiber : public Fiber
|
|
{
|
|
public:
|
|
PrimaryFiber() : Fiber(nullptr, 0) { setStarted(); }
|
|
void main() { panic("PrimaryFiber main executed.\n"); }
|
|
};
|
|
|
|
PrimaryFiber _primaryFiber;
|
|
|
|
// A pointer to whatever the currently executing Fiber is.
|
|
Fiber *_currentFiber = &_primaryFiber;
|
|
|
|
// A pointer to the Fiber which is currently being started/initialized.
|
|
Fiber *startingFiber = nullptr;
|
|
|
|
} // anonymous namespace
|
|
|
|
void
|
|
Fiber::entryTrampoline()
|
|
{
|
|
startingFiber->start();
|
|
}
|
|
|
|
Fiber::Fiber(size_t stack_size) : Fiber(primaryFiber(), stack_size)
|
|
{}
|
|
|
|
Fiber::Fiber(Fiber *link, size_t stack_size) :
|
|
link(link), stack(nullptr), stackSize(stack_size), guardPage(nullptr),
|
|
guardPageSize(sysconf(_SC_PAGE_SIZE)), _started(false), _finished(false)
|
|
{
|
|
if (stack_size) {
|
|
guardPage = mmap(nullptr, guardPageSize + stack_size,
|
|
PROT_READ | PROT_WRITE,
|
|
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
|
if (guardPage == (void *)MAP_FAILED) {
|
|
perror("mmap");
|
|
fatal("Could not mmap %d byte fiber stack.\n", stack_size);
|
|
}
|
|
stack = (void *)((uint8_t *)guardPage + guardPageSize);
|
|
if (mprotect(guardPage, guardPageSize, PROT_NONE)) {
|
|
perror("mprotect");
|
|
fatal("Could not forbid access to fiber stack guard page.");
|
|
}
|
|
}
|
|
#if HAVE_VALGRIND
|
|
valgrindStackId = VALGRIND_STACK_REGISTER(
|
|
stack, (uint8_t *)stack + stack_size);
|
|
#endif
|
|
}
|
|
|
|
Fiber::~Fiber()
|
|
{
|
|
panic_if(stack && _currentFiber == this, "Fiber stack is in use.");
|
|
#if HAVE_VALGRIND
|
|
VALGRIND_STACK_DEREGISTER(valgrindStackId);
|
|
#endif
|
|
if (guardPage)
|
|
munmap(guardPage, guardPageSize + stackSize);
|
|
}
|
|
|
|
void
|
|
Fiber::createContext()
|
|
{
|
|
// Set up a context for the new fiber, starting it in the trampoline.
|
|
getcontext(&ctx);
|
|
ctx.uc_stack.ss_sp = stack;
|
|
ctx.uc_stack.ss_size = stackSize;
|
|
ctx.uc_link = nullptr;
|
|
makecontext(&ctx, &entryTrampoline, 0);
|
|
|
|
// Swap to the new context so it can enter its start() function. It
|
|
// will then swap itself back out and return here.
|
|
startingFiber = this;
|
|
panic_if(!_currentFiber, "No active Fiber object.");
|
|
swapcontext(&_currentFiber->ctx, &ctx);
|
|
|
|
// The new context is now ready and about to call main().
|
|
}
|
|
|
|
void
|
|
Fiber::start()
|
|
{
|
|
// Avoid a dangling pointer.
|
|
startingFiber = nullptr;
|
|
|
|
setStarted();
|
|
|
|
// Swap back to the parent context which is still considered "current",
|
|
// now that we're ready to go.
|
|
int ret M5_VAR_USED = swapcontext(&ctx, &_currentFiber->ctx);
|
|
panic_if(ret == -1, strerror(errno));
|
|
|
|
// Call main() when we're been reactivated for the first time.
|
|
main();
|
|
|
|
// main has returned, so this Fiber has finished. Switch to the "link"
|
|
// Fiber.
|
|
_finished = true;
|
|
link->run();
|
|
}
|
|
|
|
void
|
|
Fiber::run()
|
|
{
|
|
panic_if(_finished, "Fiber has already run to completion.");
|
|
|
|
// If we're already running this fiber, we're done.
|
|
if (_currentFiber == this)
|
|
return;
|
|
|
|
if (!_started)
|
|
createContext();
|
|
|
|
// Switch out of the current Fiber's context and this one's in.
|
|
Fiber *prev = _currentFiber;
|
|
Fiber *next = this;
|
|
_currentFiber = next;
|
|
swapcontext(&prev->ctx, &next->ctx);
|
|
}
|
|
|
|
Fiber *Fiber::currentFiber() { return _currentFiber; }
|
|
Fiber *Fiber::primaryFiber() { return &_primaryFiber; }
|