Commit 27c8bfca authored by Martin Karsten's avatar Martin Karsten

- use alternative signal stack with split stacks

- add configurable stack guard size to Fibre
- add proper exit() routine to Fibre
- add test case to testsuite
- extend C interface
parent 750f624d
Pipeline #40658 passed with stage
in 6 minutes and 53 seconds
......@@ -27,6 +27,7 @@ void f1main(int* result) {
int f2real(int j) {
cout << "j: " << j << endl;
if (j > 10000) Fibre::exit();
Fibre::yield();
int y = random() % 1024;
int test = y;
......
......@@ -486,12 +486,12 @@ int main(int argc, char* argv[]) {
// set up alarm
#if !defined __U_CPLUSPLUS__
timer_t timer;
struct sigaction sa;
sa.sa_handler = alarmHandler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sigemptyset(&sa.sa_mask);
SYSCALL(sigaction(SIGALRM, &sa, 0));
timer_t timer;
SYSCALL(timer_create(CLOCK_REALTIME, nullptr, &timer));
itimerspec tval = { {1,0}, {1,0} };
#endif
......
......@@ -16,6 +16,7 @@
******************************************************************************/
#include "libfibre/Cluster.h"
#include <csignal> // sigaltstack
#include <limits.h> // PTHREAD_STACK_MIN
namespace Context {
......@@ -66,9 +67,19 @@ void installFake(EventScope* es, _friend<BaseThreadPoller>) {
Cluster::Worker::~Worker() {
if (maintenanceFibre) delete maintenanceFibre;
#ifdef SPLIT_STACK
if (sigStack) delete [] sigStack;
#endif
}
inline void Cluster::setupWorker(Fibre* fibre, Worker* worker) {
#ifdef SPLIT_STACK
worker->sigStack = new char[SIGSTKSZ];
stack_t ss = { .ss_sp = worker->sigStack, .ss_flags = 0, .ss_size = SIGSTKSZ };
SYSCALL(sigaltstack(&ss, nullptr));
int off = 0; // do not block signals (blocking signals is slow!)
__splitstack_block_signals(&off, nullptr);
#endif
worker->sysThreadId = pthread_self();
Context::install(fibre, worker, this, &scope, _friend<Cluster>());
worker->maintenanceFibre = new Fibre(*worker);
......
......@@ -47,7 +47,10 @@ class Cluster : public Scheduler {
struct Worker : public BaseProcessor {
pthread_t sysThreadId;
Fibre* maintenanceFibre;
Worker(Cluster& c) : BaseProcessor(c), maintenanceFibre(nullptr) { c.Scheduler::addProcessor(*this); }
char* sigStack; // only used with split stacks
Worker(Cluster& c) : BaseProcessor(c), maintenanceFibre(nullptr), sigStack(nullptr) {
c.Scheduler::addProcessor(*this);
}
~Worker();
void setIdleLoop(Fibre* f) { BaseProcessor::idleStack = f; }
void runIdleLoop() { BaseProcessor::idleLoop(); }
......
......@@ -45,7 +45,7 @@ class EventScope {
SyncSem RD;
SyncSem WR;
#if TESTING_LAZY_FD_REGISTRATION
FastMutex lock;
FastMutex lock;
BasePoller* poller;
size_t status;
SyncFD() : poller(nullptr), status(0) {}
......@@ -103,7 +103,7 @@ class EventScope {
public:
ConnectionStats* stats;
/** Bootstrap a new event scope, e.g., after a suitable call to `clone()` or `pthread_create()`. */
/** Create an event scope during bootstrap. */
static EventScope* bootstrap(size_t pollerCount = 1, size_t workerCount = 1) {
EventScope* es = new EventScope(pollerCount);
es->mainFibre = es->mainCluster->registerWorker(_friend<EventScope>());
......
......@@ -23,17 +23,21 @@
#include "runtime/BlockingSync.h"
#include "runtime-glue/RuntimeContext.h"
#include <cxxabi.h>
#include <sys/mman.h>
#ifdef SPLIT_STACK
extern "C" void __splitstack_block_signals(int* next, int* prev);
extern "C" void __splitstack_block_signals_context(void *context[10], int* next, int* prev);
extern "C" void __splitstack_getcontext(void *context[10]);
extern "C" void __splitstack_setcontext(void *context[10]);
extern "C" void *__splitstack_makecontext(size_t, void *context[10], size_t *);
extern "C" void __splitstack_releasecontext(void *context[10]);
static const size_t defaultStackSize = 2 * pagesize<1>();
static const size_t defaultStackSize = 2 * pagesize<1>();
static const size_t defaultStackGuard = 0 * pagesize<1>();
#else
static const size_t defaultStackSize = 16 * pagesize<1>();
static const size_t stackProtection = 1 * pagesize<1>();
static const size_t defaultStackSize = 16 * pagesize<1>();
static const size_t defaultStackGuard = 1 * pagesize<1>();
#endif
#if TESTING_ENABLE_DEBUGGING
......@@ -45,38 +49,41 @@ class Cluster;
/** A Fibre object represents an independent execution context backed by a stack. */
class Fibre : public StackContext {
FloatingPointFlags fp; // FP context
size_t stackSize; // stack size
FloatingPointFlags fp; // FP context
size_t stackSize; // stack size (including guard)
#ifdef SPLIT_STACK
void* splitStackContext[10]; // memory for split-stack context
void* splitStackContext[10]; // memory for split-stack context
#else
vaddr stackBottom; // bottom of allocated memory for stack
vaddr stackBottom; // bottom of allocated memory for stack (including guard)
#endif
SyncPoint<WorkerLock> done; // synchronization (join) at destructor
SyncPoint<WorkerLock> done; // synchronization (join) at destructor
size_t stackAlloc(size_t size) {
size_t stackAlloc(size_t size, size_t guard) {
#ifdef SPLIT_STACK
vaddr stackBottom = (vaddr)__splitstack_makecontext(size, splitStackContext, &size);
int off = 0; // do not block signals (blocking signals is slow!)
__splitstack_block_signals_context(splitStackContext, &off, nullptr);
#else
// check that requested size is a multiple of page size
RASSERT(aligned(size, stackProtection), size);
// check that requested size/guard is a multiple of page size
RASSERT(aligned(size, pagesize<1>()), size);
RASSERT(aligned(guard, pagesize<1>()), guard);
// reserve/map size + protection
ptr_t ptr = mmap(0, size + stackProtection, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
size += guard;
ptr_t ptr = mmap(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
RASSERT0(ptr != MAP_FAILED);
// set up protection page
SYSCALL(mprotect(ptr, stackProtection, PROT_NONE));
stackBottom = vaddr(ptr) + stackProtection;
if (guard) SYSCALL(mprotect(ptr, guard, PROT_NONE));
stackBottom = vaddr(ptr);
#endif
StackContext::initStackPointer(stackBottom + size);
return size;
}
void stackFree() {
if (!stackSize) return;
#ifdef SPLIT_STACK
__splitstack_releasecontext(splitStackContext);
if (stackSize) __splitstack_releasecontext(splitStackContext);
#else
SYSCALL(munmap(ptr_t(stackBottom - stackProtection), stackSize + stackProtection));
if (stackSize) SYSCALL(munmap(ptr_t(stackBottom), stackSize));
#endif
}
......@@ -101,12 +108,12 @@ class Fibre : public StackContext {
public:
/** Constructor. */
Fibre(Scheduler& sched = Context::CurrProcessor().getScheduler(), size_t size = defaultStackSize, bool background = false)
: StackContext(sched, background), stackSize(stackAlloc(size)) { initDebug(); }
Fibre(Scheduler& sched = Context::CurrProcessor().getScheduler(), size_t size = defaultStackSize, bool background = false, size_t guard = defaultStackGuard)
: StackContext(sched, background), stackSize(stackAlloc(size, guard)) { initDebug(); }
/** Constructor setting affinity to processor. */
Fibre(BaseProcessor &sp, size_t size = defaultStackSize)
: StackContext(sp, true), stackSize(stackAlloc(size)) { initDebug(); }
Fibre(BaseProcessor &sp, size_t size = defaultStackSize, size_t guard = defaultStackGuard)
: StackContext(sp, true), stackSize(stackAlloc(size, guard)) { initDebug(); }
/** Constructor to immediately start fibre with `func(arg)`. */
Fibre(funcvoid1_t func, ptr_t arg, bool background = false)
......@@ -127,6 +134,11 @@ public:
void join() { done.wait(); }
/** Detach fibre (no waiting for join synchronization). */
void detach() { done.detach(); }
/** Exit fibre (with join, if not detached). */
static void exit() __noreturn {
abi::__forced_unwind* dummy = nullptr;
throw dummy;
}
// callback from StackContext via Runtime after final context switch
void destroy(_friend<StackContext>) {
......
......@@ -17,6 +17,9 @@
#include "libfibre/fibre.h"
#include "libfibre/cfibre.h"
#include <sys/uio.h> // readv, writev
#include <sys/sendfile.h> // sendfile
struct _cfibre_t : public Fibre {};
struct _cfibre_sem_t : public fibre_sem_t {};
struct _cfibre_mutex_t : public fibre_mutex_t {};
......@@ -33,7 +36,8 @@ struct _cfibre_barrierattr_t : public fibre_barrierattr_t {};
struct _cfibre_fastmutex_t : public fibre_fastmutex_t {};
struct _cfibre_fastmutexattr_t : public fibre_fastmutexattr_t {};
struct _cfibre_cluster_t : public Cluster {};
struct _cfibre_cluster_t : public Cluster {};
struct _cfibre_eventscope_t : public EventScope {};
extern "C" void cfibre_init() {
FibreInit();
......@@ -55,7 +59,7 @@ extern "C" int cfibre_cluster_destroy(cfibre_cluster_t* cluster) {
}
extern "C" cfibre_cluster_t cfibre_cluster_self() {
return &reinterpret_cast<_cfibre_cluster_t&>(Context::CurrCluster());
return reinterpret_cast<_cfibre_cluster_t*>(&Context::CurrCluster());
}
extern "C" int cfibre_add_worker(cfibre_cluster_t cluster, pthread_t* tid, void (*init_routine) (void *), void *arg) {
......@@ -73,6 +77,14 @@ extern "C" int cfibre_resume(cfibre_cluster_t cluster) {
return 0;
}
extern "C" cfibre_eventscope_t cfibre_clone(void (*mainFunc)(void *), void* mainArg) {
return reinterpret_cast<_cfibre_eventscope_t*>(EventScope::clone(mainFunc, mainArg));
}
extern "C" cfibre_eventscope_t cfibre_es_self(void) {
return reinterpret_cast<_cfibre_eventscope_t*>(&Context::CurrEventScope());
}
extern "C" int cfibre_attr_init(cfibre_attr_t *attr) {
*attr = new _cfibre_attr_t;
return fibre_attr_init(*attr);
......@@ -101,6 +113,14 @@ extern "C" int cfibre_attr_getstacksize(const cfibre_attr_t *attr, size_t *stack
return fibre_attr_getstacksize(*attr, stacksize);
}
extern "C" int cfibre_attr_setguardsize(cfibre_attr_t *attr, size_t guardsize) {
return fibre_attr_setguardsize(*attr, guardsize);
}
extern "C" int cfibre_attr_getguardsize(const cfibre_attr_t *attr, size_t *guardsize) {
return fibre_attr_getguardsize(*attr, guardsize);
}
extern "C" int cfibre_attr_setdetachstate(cfibre_attr_t *attr, int detachstate) {
return fibre_attr_setdetachstate(*attr, detachstate);
}
......@@ -137,10 +157,22 @@ extern "C" int cfibre_join(cfibre_t thread, void **retval) {
return fibre_join(thread, retval);
}
extern "C" int cfibre_detach(cfibre_t thread) {
return fibre_detach(thread);
}
extern "C" void cfibre_exit() {
fibre_exit();
}
extern "C" cfibre_t cfibre_self(void) {
return (cfibre_t)fibre_self();
}
extern "C" int cfibre_equal(cfibre_t thread1, cfibre_t thread2) {
return fibre_equal(thread1, thread2);
}
extern "C" int cfibre_yield(void) {
return fibre_yield();
}
......@@ -405,6 +437,10 @@ extern "C" ssize_t cfibre_write(int fildes, const void *buf, size_t nbyte) {
return lfOutput(write, fildes, buf, nbyte);
}
extern "C" ssize_t cfibre_writev(int fildes, const struct iovec *iov, int iovcnt) {
return lfOutput(writev, fildes, iov, iovcnt);
}
extern "C" ssize_t cfibre_recv(int socket, void *buffer, size_t length, int flags) {
return lfInput(recv, socket, buffer, length, flags);
}
......@@ -421,6 +457,20 @@ extern "C" ssize_t cfibre_read(int fildes, void *buf, size_t nbyte) {
return lfInput(read, fildes, buf, nbyte);
}
extern "C" ssize_t cfibre_readv(int fildes, const struct iovec *iov, int iovcnt) {
return lfInput(readv, fildes, iov, iovcnt);
}
#if __FreeBSD__
extern "C" int cfibre_sendfile(int fd, int s, off_t offset, size_t nbytes, struct sf_hdtr *hdtr, off_t *sbytes, int flags) {
return lfOutput(sendfile, fd, s, offset, nbytes, hdtr, sbytes, flags);
}
#else
extern "C" ssize_t cfibre_sendfile(int out_fd, int in_fd, off_t *offset, size_t count) {
return lfOutput(sendfile, out_fd, in_fd, offset, count);
}
#endif
extern "C" void cfibre_suspendFD(int fd) {
Context::CurrEventScope().suspendFD(fd);
}
......
......@@ -22,10 +22,15 @@
Additionally, all routines in fibre.h are available as corresponding 'cfibre' version.
*/
#include <time.h> // struct timespec
#include <unistd.h> // useconds_t
#include <sys/socket.h> // socket types
#include <pthread.h>
#include <time.h> // struct timespec
#include <unistd.h> // read, write, useconds_t
#include <sys/types.h> // socket types (FreeBSD)
#include <sys/socket.h> // socket interface
#if __linux__
#include <sys/sendfile.h> // sendfile (Linux)
#endif
#include <sys/uio.h> // readv, writev
#include <pthread.h> // pthread_t
typedef struct _cfibre_t* cfibre_t;
typedef struct _cfibre_sem_t* cfibre_sem_t;
......@@ -43,7 +48,11 @@ typedef struct _cfibre_barrierattr_t* cfibre_barrierattr_t;
typedef struct _cfibre_fastmutex_t* cfibre_fastmutex_t;
typedef struct _cfibre_fastmutexattr_t* cfibre_fastmutexattr_t;
typedef struct _cfibre_cluster_t* cfibre_cluster_t;
typedef struct _cfibre_cluster_t* cfibre_cluster_t;
typedef struct _cfibre_eventscope_t* cfibre_eventscope_t;
static const int CFIBRE_MUTEX_RECURSIVE = PTHREAD_MUTEX_RECURSIVE;
static const int CFIBRE_MUTEX_DEFAULT = PTHREAD_MUTEX_DEFAULT;
#ifdef __cplusplus
extern "C" {
......@@ -69,6 +78,11 @@ int cfibre_pause(cfibre_cluster_t cluster);
/** @brief Resume processors in specified Cluster. */
int cfibre_resume(cfibre_cluster_t cluster);
/** @brief Create new event scope. */
cfibre_eventscope_t cfibre_eventscope_clone(void (*mainFunc) (void *), void* mainArg);
/** @brief Obtain current event scope. */
cfibre_eventscope_t cfibre_eventscope_self(void);
/** @brief Read OS-level `errno` variable (special routine due to TLS). */
int cfibre_get_errno(void);
/** @brief Write OS-level `errno` variable (special routine due to TLS). */
......@@ -78,6 +92,8 @@ int cfibre_attr_init(cfibre_attr_t *attr);
int cfibre_attr_destroy(cfibre_attr_t *attr);
int cfibre_attr_setstacksize(cfibre_attr_t *attr, size_t stacksize);
int cfibre_attr_getstacksize(const cfibre_attr_t *attr, size_t *stacksize);
int cfibre_attr_setguardsize(cfibre_attr_t *attr, size_t guardsize);
int cfibre_attr_getguardsize(const cfibre_attr_t *attr, size_t *guardsize);
int cfibre_attr_setdetachstate(cfibre_attr_t *attr, int detachstate);
int cfibre_attr_getdetachstate(const cfibre_attr_t *attr, int *detachstate);
int cfibre_attr_setbackground(cfibre_attr_t *attr, int background);
......@@ -87,7 +103,10 @@ int cfibre_attr_getcluster(const cfibre_attr_t *attr, cfibre_cluster_t *cluster)
int cfibre_create(cfibre_t *thread, const cfibre_attr_t *attr, void *(*start_routine) (void *), void *arg);
int cfibre_join(cfibre_t thread, void **retval);
int cfibre_detach(cfibre_t thread);
void cfibre_exit() __attribute__((__noreturn__));
cfibre_t cfibre_self(void);
int cfibre_equal(cfibre_t thread1, cfibre_t thread2);
int cfibre_yield(void);
int cfibre_migrate(cfibre_cluster_t cluster);
......@@ -168,6 +187,8 @@ ssize_t cfibre_sendto(int socket, const void *message, size_t length, int flags,
ssize_t cfibre_sendmsg(int socket, const struct msghdr *message, int flags);
/** @brief Output via socket/file. (`write`). */
ssize_t cfibre_write(int fildes, const void *buf, size_t nbyte);
/** @brief Output via socket/file. (`writev`). */
ssize_t cfibre_writev(int fildes, const struct iovec *iov, int iovcnt);
/** @brief Receive via socket. (`recv`). */
ssize_t cfibre_recv(int socket, void *buffer, size_t length, int flags);
/** @brief Receive via socket. (`recvfrom`). */
......@@ -176,6 +197,15 @@ ssize_t cfibre_recvfrom(int socket, void *restrict buffer, size_t length, int fl
ssize_t cfibre_recvmsg(int socket, struct msghdr *message, int flags);
/** @brief Receive via socket/file. (`read`). */
ssize_t cfibre_read(int fildes, void *buf, size_t nbyte);
/** @brief Receive via socket/file. (`readv`). */
ssize_t cfibre_readv(int fildes, const struct iovec *iov, int iovcnt);
/** @brief Transmit file via socket. (`sendfile`). */
#if __FreeBSD__
int cfibre_sendfile(int fd, int s, off_t offset, size_t nbytes, struct sf_hdtr *hdtr, off_t *sbytes, int flags);
#else
ssize_t cfibre_sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
#endif
/** @brief temporarily halt event handling for FD */
void cfibre_suspendFD(int fd);
......
......@@ -54,11 +54,13 @@ typedef FastMutex fibre_fastmutex_t;
struct fibre_attr_t {
size_t stackSize;
size_t guardSize;
bool detached;
bool background;
Cluster* cluster;
void init() {
stackSize = defaultStackSize;
guardSize = defaultStackGuard;
detached = false;
background = false;
cluster = &Context::CurrCluster();
......@@ -67,7 +69,7 @@ struct fibre_attr_t {
enum {
FIBRE_MUTEX_RECURSIVE = PTHREAD_MUTEX_RECURSIVE,
FIBRE_MUTEX_DEFAULT = PTHREAD_MUTEX_DEFAULT
FIBRE_MUTEX_DEFAULT = PTHREAD_MUTEX_DEFAULT
};
struct fibre_mutexattr_t {
......@@ -115,6 +117,18 @@ inline int fibre_attr_getstacksize(const fibre_attr_t *attr, size_t *stacksize)
return 0;
}
/** @brief Set guard size attribute for fibre creation. (`pthread_attr_setguardsize`) */
inline int fibre_attr_setguardsize(fibre_attr_t *attr, size_t guardsize) {
attr->guardSize = guardsize;
return 0;
}
/** @brief Get guard size attribute for fibre creation. (`pthread_attr_getguardsize`) */
inline int fibre_attr_getguardsize(const fibre_attr_t *attr, size_t *guardsize) {
*guardsize = attr->guardSize;
return 0;
}
/** @brief Set detach attribute for fibre creation. (`pthread_attr_setdetachstate`) */
inline int fibre_attr_setdetachstate(fibre_attr_t *attr, int detachstate) {
attr->detached = detachstate;
......@@ -157,7 +171,7 @@ inline int fibre_create(fibre_t *thread, const fibre_attr_t *attr, void *(*start
if (!attr) {
f = new Fibre;
} else {
f = new Fibre(*attr->cluster, attr->stackSize, attr->background);
f = new Fibre(*attr->cluster, attr->stackSize, attr->background, attr->guardSize);
if (attr->detached) f->detach();
}
*thread = f->run(start_routine, arg);
......@@ -171,11 +185,27 @@ inline int fibre_join(fibre_t thread, void **retval) {
return 0;
}
/** @brief Detach fibre. (`pthread_detach`) */
inline int fibre_detach(fibre_t thread) {
thread->detach();
return 0;
}
inline void fibre_exit() __noreturn;
inline void fibre_exit() {
Fibre::exit();
}
/** @brief Obtain fibre ID of currently running fibre. (`pthread_self`) */
inline fibre_t fibre_self(void) {
return CurrFibre();
}
/** @brief Compare fibre IDs. (`pthread_equal`) */
inline int fibre_equal(fibre_t thread1, fibre_t thread2) {
return thread1 == thread2;
}
/** @brief Voluntarily yield execution. (`pthread_yield`) */
inline int fibre_yield(void) {
Fibre::yield();
......
......@@ -19,6 +19,12 @@
#include "libfibre/Fibre.h"
inline void RuntimeStartStack(funcvoid3_t func, ptr_t arg1, ptr_t arg2, ptr_t arg3) {
try {
func(arg1, arg2, arg3);
} catch (abi::__forced_unwind*) {}
}
inline void RuntimePreStackSwitch(StackContext& currStack, StackContext& nextStack, _friend<StackContext> fs) {
Fibre& currFibre = reinterpret_cast<Fibre&>(currStack);
Fibre& nextFibre = reinterpret_cast<Fibre&>(nextStack);
......
......@@ -95,7 +95,7 @@ void StackContext::resumeDirect() {
extern "C" void invokeStack(funcvoid3_t func, ptr_t arg1, ptr_t arg2, ptr_t arg3) {
CHECK_PREEMPTION(0);
RuntimeEnablePreemption();
func(arg1, arg2, arg3);
RuntimeStartStack(func, arg1, arg2, arg3);
RuntimeDisablePreemption();
StackContext::terminate();
}
......
......@@ -91,7 +91,17 @@ function run_3() {
run_memcached 3
}
for ((e=0;e<4;e+=1)); do
function prep_4() {
sed -i -e 's/DYNSTACK=.*/DYNSTACK=1/' Makefile.config
}
function run_4() {
echo -n "STACKTEST: "
apps/stacktest > stacktest.out && echo SUCCESS || echo FAILURE
rm -f stacktest.out
}
for ((e=0;e<=4;e+=1)); do
addon=$(prep_$e)
pre $addon
run_$e
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment