私は、通常の makecontext/swapcontext ルーチンを使用して、C で小さな概念実証ファイバー ライブラリを作成することに取り組んでいますが、それは私にいくつかの問題を引き起こしています (私のプラットフォームは OSX 10.9 Mavericks で、 を使用していclang-503.0.40
ます)。
私が扱っているデータ構造は次のとおりです。
typedef enum {
/// Fiber is waiting to start execution
FIBER_PENDING,
/// Fiber is in the midst of executing
FIBER_EXECUTING,
/// Fiber has finished executing
FIBER_FINISHED,
/// Fiber is in the process of yielding
FIBER_YIELDING
} fiber_state;
typedef struct {
char *stack;
fiber_state state;
ucontext_t context;
} fiber;
これがミニライブラリです (3 つの関数fiber_init
、、、fiber_run
およびfiber_yield
:
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <assert.h>
#include <stdio.h>
// Generic log w/ file name and line number
#define LOG(x) fprintf(stderr, "%s:%d |-> %s\n", __FILE__, __LINE__, x)
// the current executing fiber in this thread (or NULL of none are executing)
// (TODO: make this TLS)
static fiber *current_fiber = NULL;
/// prepare fiber 'f' to be run
void fiber_init(fiber *f, void(* fiber_func)()) {
// zero out the fiber
memset(f, 0, sizeof(fiber));
f->state = FIBER_PENDING;
f->stack = (char*) malloc(SIGSTKSZ);
// init stack config of the fiber context
ucontext_t *f_context = &(f->context);
getcontext(f_context);
f_context->uc_stack.ss_sp = f->stack;
f_context->uc_stack.ss_size = SIGSTKSZ;
f_context->uc_stack.ss_flags = 0;
// initialize the context
makecontext(f_context, fiber_func, 0);
}
/// Deallocate resources associated with 'f'
void fiber_destroy(fiber *f) {
free(f->stack);
}
/// Start or resume fiber 'f'
void fiber_run(fiber *f) {
// context to switch back to when yielding, or when the fiber returns
ucontext_t return_context;
f->context.uc_link = &return_context;
// save the fiber being swapped away from (or NULL)
fiber *old_fiber = current_fiber;
current_fiber = f;
LOG("Swapping into fiber context");
getcontext(&return_context);
int status = swapcontext(
&return_context,
&(f->context));
assert(status == 0 && "Failed to swap to fiber context");
LOG("Back to parent context from swap");
if(f->state == FIBER_YIELDING) {
f->state = FIBER_EXECUTING;
LOG("Fiber yielded");
}
else {
LOG("Fiber done executing; marking as finished");
current_fiber->state = FIBER_FINISHED;
}
// restore old fiber
current_fiber = old_fiber;
}
/// from witin a fiber, yield control to the caller's context
void fiber_yield() {
assert(current_fiber && "No fiber is currently running!");
current_fiber->state = FIBER_YIELDING;
LOG("Yielding back to caller context");
int status = swapcontext(
&(current_fiber->context),
current_fiber->context.uc_link);
assert(status == 0 && "Failed to swap to parent context");
LOG("Swapped back into fiber context (post-yield)");
}
/// query fiber state
int fiber_is_pending(const fiber *const f) {
return f->state == FIBER_PENDING;
}
int fiber_is_finished(const fiber *const f) {
return f->state == FIBER_FINISHED;
}
int fiber_is_executing(const fiber *const f) {
return f->state == FIBER_EXECUTING;
}
ただし、ファイバー内で fiber_yield() を呼び出すと、コンテキストが呼び出し元のコンテキストと適切に交換されないようです (参照はファイバー コンテキストの uc_link に格納されます。current_fiber->context.uc_link
内を参照fiber_yield
) 。
このプログラムを実行した痕跡:
void my_func() {
LOG(" ------- I'm the fiber function! yielding");
fiber_yield();
LOG(" ------- End of my_func");
}
int main() {
fiber f;
fiber_init(&f, my_func);
while(!fiber_is_finished(&f)) {
fiber_run(&f);
LOG("Back in main run loop");
}
fiber_destroy(&f);
return 0;
}
出力が得られます:
fibers.c:70 |-> Swapping into fiber context
test_harness.c:5 |-> ------- I'm the fiber function! yielding
fibers.c:99 |-> Yielding back to caller context
Segmentation fault: 11
OSXにはスタックアラインメント制限(16バイト境界まで)があることを読みましたがmalloc
、スタックを割り当てるために使用しています。これにより、16バイト境界にアラインされたブロックが返されます(または読んだことがあります)。そうは言っても、宣言の順序を並べ替えるとセグメンテーション違反が発生しない場合があるようですが、非常に偽りであり、再現するのは困難です。
swapcontextfiber_yield
を呼び出す直前に調べると、current_fiber->context
スタック サイズが非常に大きいことがわかります。必要以上に大きくなります。おそらく、これは腐敗の兆候です。
(lldb) p current_fiber->context
(ucontext_t) $3 = {
uc_onstack = 0
uc_sigmask = 0
uc_stack = (ss_sp = 0x00007fff5fbff720, ss_size = 140734799804176, ss_flags = 0)
uc_link = 0x00007fff5fbff780
uc_mcsize = 0
uc_mcontext = 0x00007fff5fbff828
}
(lldb) p *(current_fiber->context.uc_link)
(__darwin_ucontext) $4 = {
uc_onstack = -541067328
uc_sigmask = 0
uc_stack = (ss_sp = 0x00007fff5fbff700, ss_size = 8388608, ss_flags = 0)
uc_link = 0x0000000000000000
uc_mcsize = 140734799804400
uc_mcontext = 0x00007fff5fbff7b8
}
何が起こっているのか手がかりはありますか?ありがとう!