1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

Add ucontext coroutine implementation for generic fallback.

This commit is contained in:
Samuel Williams 2019-06-24 23:54:19 +12:00
parent a84a99ffab
commit 6c6bf9ffcb
13 changed files with 211 additions and 174 deletions

View file

@ -917,8 +917,10 @@ strlcat.$(OBJEXT): {$(VPATH)}strlcat.c
strlcpy.$(OBJEXT): {$(VPATH)}strlcpy.c
strstr.$(OBJEXT): {$(VPATH)}strstr.c
nt.$(OBJEXT): {$(VPATH)}nt.c
COROUTINE_SRC = $(COROUTINE_H:.h=).$(ASMEXT)
.coroutine_obj $(COROUTINE_OBJ): \
{$(VPATH)}$(COROUTINE_H:.h=).$(ASMEXT) \
{$(VPATH)}$(COROUTINE_SRC:/ucontext/Context.S=/ucontext/Context.c) \
$(COROUTINE_H:/Context.h=/.time)
$(COROUTINE_H:/Context.h=/.time):
$(Q) $(MAKEDIRS) $(@D)

View file

@ -2282,7 +2282,7 @@ AS_CASE(["$rb_cv_fiber_coroutine"], [yes|''], [
rb_cv_fiber_coroutine=ppc64le
],
[*], [
rb_cv_fiber_coroutine=
rb_cv_fiber_coroutine=ucontext
]
)
AC_MSG_RESULT(${rb_cv_fiber_coroutine:-no})

8
cont.c
View file

@ -187,7 +187,7 @@ struct rb_fiber_struct {
#if FIBER_USE_NATIVE
#if defined(FIBER_USE_COROUTINE)
#define FIBER_ALLOCATE_STACK
coroutine_context context;
struct coroutine_context context;
void *ss_sp;
size_t ss_size;
#elif defined(_WIN32)
@ -802,7 +802,7 @@ cont_restore_thread(rb_context_t *cont)
#if FIBER_USE_NATIVE
#if defined(FIBER_USE_COROUTINE)
static COROUTINE
fiber_entry(coroutine_context * from, coroutine_context * to)
fiber_entry(struct coroutine_context * from, struct coroutine_context * to)
{
rb_fiber_start();
}
@ -914,7 +914,7 @@ fiber_initialize_machine_stack_context(rb_fiber_t *fib, size_t size)
ptr = fiber_machine_stack_alloc(size);
fib->ss_sp = ptr;
fib->ss_size = size;
coroutine_initialize(&fib->context, fiber_entry, ptr+size, size);
coroutine_initialize(&fib->context, fiber_entry, ptr, size);
sec->machine.stack_start = (VALUE*)(ptr + STACK_DIR_UPPER(0, size));
sec->machine.stack_maxsize = size - RB_PAGE_SIZE;
#elif defined(_WIN32)
@ -1530,7 +1530,7 @@ root_fiber_alloc(rb_thread_t *th)
#if FIBER_USE_NATIVE
#if defined(FIBER_USE_COROUTINE)
coroutine_initialize(&fib->context, NULL, NULL, 0);
coroutine_initialize_main(&fib->context);
#elif defined(_WIN32)
/* setup fib_handle for root Fiber */
if (fib->fib_handle == 0) {

View file

@ -27,7 +27,7 @@ PREFIXED_SYMBOL(SYMBOL_PREFIX,coroutine_transfer):
# Restore callee stack pointer
movq (%rsi), %rsp
# Restore callee stack
# Restore callee state
popq %r15
popq %r14
popq %r13

View file

@ -10,35 +10,32 @@
#include <assert.h>
#include <string.h>
#if __cplusplus
extern "C" {
#endif
#define COROUTINE __attribute__((noreturn)) void
enum {COROUTINE_REGISTERS = 6};
typedef struct
struct coroutine_context
{
void **stack_pointer;
} coroutine_context;
};
typedef COROUTINE(* coroutine_start)(coroutine_context *from, coroutine_context *self);
typedef COROUTINE(* coroutine_start)(struct coroutine_context *from, struct coroutine_context *self);
static inline void coroutine_initialize_main(struct coroutine_context * context) {
context->stack_pointer = NULL;
}
static inline void coroutine_initialize(
coroutine_context *context,
struct coroutine_context *context,
coroutine_start start,
void *stack_pointer,
size_t stack_size
void *stack,
size_t size
) {
/* Force 16-byte alignment */
context->stack_pointer = (void**)((uintptr_t)stack_pointer & ~0xF);
assert(start && stack && size >= 1024);
if (!start) {
assert(!context->stack_pointer);
/* We are main coroutine for this thread */
return;
}
// Stack grows down. Force 16-byte alignment.
char * top = (char*)stack + size;
context->stack_pointer = (void**)((uintptr_t)top & ~0xF);
*--context->stack_pointer = NULL;
*--context->stack_pointer = (void*)start;
@ -47,13 +44,9 @@ static inline void coroutine_initialize(
memset(context->stack_pointer, 0, sizeof(void*) * COROUTINE_REGISTERS);
}
coroutine_context * coroutine_transfer(coroutine_context * current, coroutine_context * target);
struct coroutine_context * coroutine_transfer(struct coroutine_context * current, struct coroutine_context * target);
static inline void coroutine_destroy(coroutine_context * context)
static inline void coroutine_destroy(struct coroutine_context * context)
{
context->stack_pointer = NULL;
}
#if __cplusplus
}
#endif

View file

@ -10,34 +10,32 @@
#include <assert.h>
#include <string.h>
#if __cplusplus
extern "C" {
#endif
#define COROUTINE __attribute__((noreturn)) void
enum {COROUTINE_REGISTERS = 8};
typedef struct
struct coroutine_context
{
void **stack_pointer;
} coroutine_context;
};
typedef COROUTINE(* coroutine_start)(coroutine_context *from, coroutine_context *self);
typedef COROUTINE(* coroutine_start)(struct coroutine_context *from, struct coroutine_context *self);
static inline void coroutine_initialize_main(struct coroutine_context * context) {
context->stack_pointer = NULL;
}
static inline void coroutine_initialize(
coroutine_context *context,
struct coroutine_context *context,
coroutine_start start,
void *stack_pointer,
size_t stack_size
void *stack,
size_t size
) {
context->stack_pointer = (void**)stack_pointer;
assert(start && stack && size >= 1024);
if (!start) {
assert(!context->stack_pointer);
/* We are main coroutine for this thread */
return;
}
// Stack grows down. Force 16-byte alignment.
char * top = (char*)stack + size;
context->stack_pointer = (void**)((uintptr_t)top & ~0xF);
*--context->stack_pointer = (void*)start;
@ -45,12 +43,8 @@ static inline void coroutine_initialize(
memset(context->stack_pointer, 0, sizeof(void*) * COROUTINE_REGISTERS);
}
coroutine_context * coroutine_transfer(coroutine_context * current, coroutine_context * target);
struct coroutine_context * coroutine_transfer(struct coroutine_context * current, struct coroutine_context * target);
static inline void coroutine_destroy(coroutine_context * context)
static inline void coroutine_destroy(struct coroutine_context * context)
{
}
#if __cplusplus
}
#endif

View file

@ -10,35 +10,32 @@
#include <assert.h>
#include <string.h>
#if __cplusplus
extern "C" {
#endif
#define COROUTINE __attribute__((noreturn)) void
enum {COROUTINE_REGISTERS = 0xb0 / 8};
typedef struct
struct coroutine_context
{
void **stack_pointer;
} coroutine_context;
};
typedef COROUTINE(* coroutine_start)(coroutine_context *from, coroutine_context *self);
typedef COROUTINE(* coroutine_start)(struct coroutine_context *from, struct coroutine_context *self);
static inline void coroutine_initialize_main(struct coroutine_context * context) {
context->stack_pointer = NULL;
}
static inline void coroutine_initialize(
coroutine_context *context,
struct coroutine_context *context,
coroutine_start start,
void *stack_pointer,
size_t stack_size
void *stack,
size_t size
) {
/* Force 16-byte alignment */
context->stack_pointer = (void**)((uintptr_t)stack_pointer & ~0xF);
assert(start && stack && size >= 1024);
if (!start) {
assert(!context->stack_pointer);
/* We are main coroutine for this thread */
return;
}
// Stack grows down. Force 16-byte alignment.
char * top = (char*)stack + size;
context->stack_pointer = (void**)((uintptr_t)top & ~0xF);
context->stack_pointer -= COROUTINE_REGISTERS;
memset(context->stack_pointer, 0, sizeof(void*) * COROUTINE_REGISTERS);
@ -46,12 +43,8 @@ static inline void coroutine_initialize(
context->stack_pointer[0xa0 / 8] = (void*)start;
}
coroutine_context * coroutine_transfer(coroutine_context * current, coroutine_context * target);
struct coroutine_context * coroutine_transfer(struct coroutine_context * current, struct coroutine_context * target);
static inline void coroutine_destroy(coroutine_context * context)
static inline void coroutine_destroy(struct coroutine_context * context)
{
}
#if __cplusplus
}
#endif

View file

@ -3,10 +3,6 @@
#include <assert.h>
#include <string.h>
#if __cplusplus
extern "C" {
#endif
#define COROUTINE __attribute__((noreturn)) void
enum {
@ -15,27 +11,28 @@ enum {
+ 4 /* space for fiber_entry() to store the link register */
};
typedef struct
struct coroutine_context
{
void **stack_pointer;
} coroutine_context;
};
typedef COROUTINE(* coroutine_start)(coroutine_context *from, coroutine_context *self);
typedef COROUTINE(* coroutine_start)(struct coroutine_context *from, struct coroutine_context *self);
static inline void coroutine_initialize_main(struct coroutine_context * context) {
context->stack_pointer = NULL;
}
static inline void coroutine_initialize(
coroutine_context *context,
struct coroutine_context *context,
coroutine_start start,
void *stack_pointer,
size_t stack_size
void *stack,
size_t size
) {
/* Force 16-byte alignment */
context->stack_pointer = (void**)((uintptr_t)stack_pointer & ~0xF);
assert(start && stack && size >= 1024);
if (!start) {
assert(!context->stack_pointer);
/* We are main coroutine for this thread */
return;
}
// Stack grows down. Force 16-byte alignment.
char * top = (char*)stack + size;
context->stack_pointer = (void**)((uintptr_t)top & ~0xF);
context->stack_pointer -= COROUTINE_REGISTERS;
memset(context->stack_pointer, 0, sizeof(void*) * COROUTINE_REGISTERS);
@ -44,13 +41,9 @@ static inline void coroutine_initialize(
context->stack_pointer[18] = ((char*)start) + 8;
}
coroutine_context * coroutine_transfer(coroutine_context * current, coroutine_context * target);
struct coroutine_context * coroutine_transfer(struct coroutine_context * current, struct coroutine_context * target);
static inline void coroutine_destroy(coroutine_context * context)
static inline void coroutine_destroy(struct coroutine_context * context)
{
context->stack_pointer = NULL;
}
#if __cplusplus
}
#endif

View file

@ -0,0 +1,16 @@
/*
* This file is part of the "Coroutine" project and released under the MIT License.
*
* Created by Samuel Williams on 24/6/2019.
* Copyright, 2019, by Samuel Williams. All rights reserved.
*/
#include "Context.h"
void coroutine_trampoline(void * _start, void * _context)
{
coroutine_start start = _start;
struct coroutine_context * context = _context;
start(context->from, context);
}

View file

@ -0,0 +1,66 @@
/*
* This file is part of the "Coroutine" project and released under the MIT License.
*
* Created by Samuel Williams on 24/6/2019.
* Copyright, 2019, by Samuel Williams. All rights reserved.
*/
#pragma once
#include <assert.h>
#include <stddef.h>
#include <ucontext.h>
#define COROUTINE __attribute__((noreturn)) void
struct coroutine_context
{
ucontext_t state;
struct coroutine_context * from;
};
typedef COROUTINE(* coroutine_start)(struct coroutine_context *from, struct coroutine_context *self);
COROUTINE coroutine_trampoline(void * _start, void * _context);
static inline void coroutine_initialize_main(struct coroutine_context * context) {
context->from = NULL;
getcontext(&context->state);
}
static inline void coroutine_initialize(
struct coroutine_context *context,
coroutine_start start,
void *stack,
size_t size
) {
assert(start && stack && size >= 1024);
coroutine_initialize_main(context);
context->state.uc_stack.ss_size = size;
// Despite what it's called, this is not actually a stack pointer. It points to the address of the stack allocation (the lowest address).
context->state.uc_stack.ss_sp = (char*)stack;
context->state.uc_stack.ss_flags = 0;
context->state.uc_link = NULL;
makecontext(&context->state, (void(*)(void))coroutine_trampoline, 2, (void*)start, (void*)context);
}
static inline struct coroutine_context * coroutine_transfer(struct coroutine_context * current, struct coroutine_context * target)
{
struct coroutine_context * previous = target->from;
target->from = current;
swapcontext(&current->state, &target->state);
target->from = previous;
return target;
}
static inline void coroutine_destroy(struct coroutine_context * context)
{
context->state.uc_stack.ss_sp = NULL;
context->state.uc_stack.ss_size = 0;
context->from = NULL;
}

View file

@ -10,53 +10,47 @@
#include <assert.h>
#include <string.h>
#if __cplusplus
extern "C" {
#endif
#define COROUTINE __declspec(noreturn) void __fastcall
/* This doesn't include thread information block */
enum {COROUTINE_REGISTERS = 4};
typedef struct
struct coroutine_context
{
void **stack_pointer;
} coroutine_context;
};
typedef void(__fastcall * coroutine_start)(coroutine_context *from, coroutine_context *self);
typedef void(__fastcall * coroutine_start)(struct coroutine_context *from, struct coroutine_context *self);
static inline void coroutine_initialize_main(struct coroutine_context * context) {
context->stack_pointer = NULL;
}
static inline void coroutine_initialize(
coroutine_context *context,
struct coroutine_context *context,
coroutine_start start,
void *stack_pointer,
size_t stack_size
void *stack,
size_t size
) {
context->stack_pointer = (void**)stack_pointer;
assert(start && stack && size >= 1024);
if (!start) {
assert(!context->stack_pointer);
/* We are main coroutine for this thread */
return;
}
// Stack grows down. Force 16-byte alignment.
char * top = (char*)stack + size;
context->stack_pointer = (void**)((uintptr_t)top & ~0xF);
*--context->stack_pointer = (void*)start;
/* Windows Thread Information Block */
*--context->stack_pointer = 0; /* fs:[0] */
*--context->stack_pointer = (void*)stack_pointer; /* fs:[4] */
*--context->stack_pointer = (void*)((char *)stack_pointer - stack_size); /* fs:[8] */
*--context->stack_pointer = (void*)top; /* fs:[4] */
*--context->stack_pointer = (void*)stack; /* fs:[8] */
context->stack_pointer -= COROUTINE_REGISTERS;
memset(context->stack_pointer, 0, sizeof(void*) * COROUTINE_REGISTERS);
}
coroutine_context * __fastcall coroutine_transfer(coroutine_context * current, coroutine_context * target);
struct coroutine_context * __fastcall coroutine_transfer(struct coroutine_context * current, struct coroutine_context * target);
static inline void coroutine_destroy(coroutine_context * context)
static inline void coroutine_destroy(struct coroutine_context * context)
{
}
#if __cplusplus
}
#endif

View file

@ -10,10 +10,6 @@
#include <assert.h>
#include <string.h>
#if __cplusplus
extern "C" {
#endif
#define COROUTINE __declspec(noreturn) void
enum {
@ -21,30 +17,31 @@ enum {
COROUTINE_XMM_REGISTERS = 1+10*2,
};
typedef struct
struct coroutine_context
{
void **stack_pointer;
} coroutine_context;
};
typedef void(* coroutine_start)(coroutine_context *from, coroutine_context *self);
typedef void(* coroutine_start)(struct coroutine_context *from, struct coroutine_context *self);
void coroutine_trampoline();
static inline void coroutine_initialize(
coroutine_context *context,
coroutine_start start,
void *stack_pointer,
size_t stack_size
) {
/* Force 16-byte alignment */
context->stack_pointer = (void**)((uintptr_t)stack_pointer & ~0xF);
if (!start) {
assert(!context->stack_pointer);
/* We are main coroutine for this thread */
return;
static inline void coroutine_initialize_main(struct coroutine_context * context) {
context->stack_pointer = NULL;
}
static inline void coroutine_initialize(
struct coroutine_context *context,
coroutine_start start,
void *stack,
size_t size
) {
assert(start && stack && size >= 1024);
// Stack grows down. Force 16-byte alignment.
char * top = (char*)stack + size;
context->stack_pointer = (void**)((uintptr_t)top & ~0xF);
/* Win64 ABI requires space for arguments */
context->stack_pointer -= 4;
@ -55,20 +52,16 @@ static inline void coroutine_initialize(
/* Windows Thread Information Block */
/* *--context->stack_pointer = 0; */ /* gs:[0x00] is not used */
*--context->stack_pointer = (void*)stack_pointer; /* gs:[0x08] */
*--context->stack_pointer = (void*)((char *)stack_pointer - stack_size); /* gs:[0x10] */
*--context->stack_pointer = (void*)top; /* gs:[0x08] */
*--context->stack_pointer = (void*)stack; /* gs:[0x10] */
context->stack_pointer -= COROUTINE_REGISTERS;
memset(context->stack_pointer, 0, sizeof(void*) * COROUTINE_REGISTERS);
memset(context->stack_pointer - COROUTINE_XMM_REGISTERS, 0, sizeof(void*) * COROUTINE_XMM_REGISTERS);
}
coroutine_context * coroutine_transfer(coroutine_context * current, coroutine_context * target);
struct coroutine_context * coroutine_transfer(struct coroutine_context * current, struct coroutine_context * target);
static inline void coroutine_destroy(coroutine_context * context)
static inline void coroutine_destroy(struct coroutine_context * context)
{
}
#if __cplusplus
}
#endif

View file

@ -10,35 +10,32 @@
#include <assert.h>
#include <string.h>
#if __cplusplus
extern "C" {
#endif
#define COROUTINE __attribute__((noreturn, fastcall)) void
enum {COROUTINE_REGISTERS = 4};
typedef struct
struct coroutine_context
{
void **stack_pointer;
} coroutine_context;
};
typedef COROUTINE(* coroutine_start)(coroutine_context *from, coroutine_context *self) __attribute__((fastcall));
typedef COROUTINE(* coroutine_start)(struct coroutine_context *from, struct coroutine_context *self) __attribute__((fastcall));
static inline void coroutine_initialize_main(struct coroutine_context * context) {
context->stack_pointer = NULL;
}
static inline void coroutine_initialize(
coroutine_context *context,
struct coroutine_context *context,
coroutine_start start,
void *stack_pointer,
size_t stack_size
void *stack,
size_t size
) {
/* Force 16-byte alignment */
context->stack_pointer = (void**)((uintptr_t)stack_pointer & ~0xF);
assert(start && stack && size >= 1024);
if (!start) {
assert(!context->stack_pointer);
/* We are main coroutine for this thread */
return;
}
// Stack grows down. Force 16-byte alignment.
char * top = (char*)stack + size;
context->stack_pointer = (void**)((uintptr_t)top & ~0xF);
*--context->stack_pointer = NULL;
*--context->stack_pointer = (void*)start;
@ -47,13 +44,9 @@ static inline void coroutine_initialize(
memset(context->stack_pointer, 0, sizeof(void*) * COROUTINE_REGISTERS);
}
coroutine_context * coroutine_transfer(coroutine_context * current, coroutine_context * target) __attribute__((fastcall));
struct coroutine_context * coroutine_transfer(struct coroutine_context * current, struct coroutine_context * target) __attribute__((fastcall));
static inline void coroutine_destroy(coroutine_context * context)
static inline void coroutine_destroy(struct coroutine_context * context)
{
context->stack_pointer = NULL;
}
#if __cplusplus
}
#endif