mirror of
https://github.com/ruby/ruby.git
synced 2026-10-28 16:36:20 +08:00
coroutine: add native behavior tests. (#18817)
This commit is contained in:
1 parent
f30fdb01f3
commit
e96cea4e03
15 files changed
+477
-4
Notes:
git
2026-09-14 07:28:21 +00:00
Merged-By: ioquatix <samuel@codeotaku.com>
No files matched your search
@@ -59,6 +59,7 @@ lcov*.info
|
||||
/*-fake.rb
|
||||
/*.dll
|
||||
/*.exe
|
||||
/coroutine-test
|
||||
/*.ilk
|
||||
/*.res
|
||||
/*.pc
|
||||
|
||||
@@ -94,6 +94,13 @@ MAKE_ENC = -f $(ENC_MK) V="$(V)" UNICODE_HDR_DIR="$(UNICODE_HDR_DIR)" \
|
||||
|
||||
PRISM_BUILD_DIR = prism
|
||||
|
||||
COROUTINE_TEST = coroutine-test$(EXEEXT)
|
||||
COROUTINE_TEST_OBJS = coroutine-main.$(OBJEXT) \
|
||||
coroutine-stack.$(OBJEXT) \
|
||||
coroutine-test_initialize_destroy.$(OBJEXT) \
|
||||
coroutine-test_transfer_repeat.$(OBJEXT) \
|
||||
coroutine-test_transfer_return.$(OBJEXT)
|
||||
|
||||
LIBPRISM_OBJS = \
|
||||
prism/arena.$(OBJEXT) \
|
||||
prism/buffer.$(OBJEXT) \
|
||||
@@ -691,6 +698,7 @@ noarch_config_h = tmp/include/noarch/ruby/config.h
|
||||
clean: clean-ext clean-enc clean-golf clean-docs clean-extout clean-modular-gc clean-local clean-platform clean-spec
|
||||
clean-local:: clean-runnable
|
||||
$(Q)$(RM) $(ALLOBJS) $(LIBRUBY_A) $(LIBRUBY_SO) $(LIBRUBY) $(LIBRUBY_ALIASES)
|
||||
$(Q)$(RM) $(COROUTINE_TEST) $(COROUTINE_TEST_OBJS)
|
||||
$(Q)$(RM) $(PROGRAM) $(WPROGRAM) miniruby$(EXEEXT) dmyext.$(OBJEXT) dmyenc.$(OBJEXT) $(ARCHFILE) .*.time
|
||||
$(Q)$(RM) y.tab.c y.output encdb.h transdb.h config.log rbconfig.rb $(ruby_pc) $(COROUTINE_H:/Context.h=/.time)
|
||||
$(Q)$(RM) probes.h probes.$(OBJEXT) probes.stamp ruby-glommed.$(OBJEXT) ruby.imp ChangeLog $(STATIC_RUBY)$(EXEEXT)
|
||||
@@ -931,8 +939,15 @@ yes-test-tool: prog PHONY
|
||||
$(ACTIONS_ENDGROUP)
|
||||
no-test-tool: PHONY
|
||||
|
||||
test-coroutine: $(TEST_RUNNABLE)-test-coroutine
|
||||
yes-test-coroutine: $(COROUTINE_TEST) PHONY
|
||||
$(ACTIONS_GROUP)
|
||||
$(Q)$(exec) $(COROUTINE_TEST_RUN)
|
||||
$(ACTIONS_ENDGROUP)
|
||||
no-test-coroutine: PHONY
|
||||
|
||||
test-sample: test-basic # backward compatibility for mswin-build
|
||||
test-short: btest-ruby $(DOT_WAIT) test-knownbug $(DOT_WAIT) test-basic
|
||||
test-short: test-coroutine $(DOT_WAIT) btest-ruby $(DOT_WAIT) test-knownbug $(DOT_WAIT) test-basic
|
||||
test: test-short
|
||||
|
||||
# Separate to skip updating encs and exts by `make -o test-precheck`
|
||||
@@ -1043,7 +1058,7 @@ $(ENC_MK): $(srcdir)/enc/make_encmake.rb $(srcdir)/enc/Makefile.in $(srcdir)/enc
|
||||
.PHONY: distclean-srcs distclean-srcs-local distclean-srcs-ext
|
||||
.PHONY: realclean realclean-ext realclean-local realclean-enc realclean-golf realclean-extout
|
||||
.PHONY: realclean-srcs realclean-srcs-local realclean-srcs-ext
|
||||
.PHONY: exam check test test-short test-all btest btest-ruby test-basic test-knownbug
|
||||
.PHONY: exam check test test-short test-all test-coroutine btest btest-ruby test-basic test-knownbug
|
||||
.PHONY: run runruby parse benchmark gdb gdb-ruby
|
||||
.PHONY: update-mspec update-rubyspec test-rubyspec test-spec
|
||||
.PHONY: touch-unicode-files
|
||||
@@ -1137,6 +1152,30 @@ $(COROUTINE_H:/Context.h=/.time):
|
||||
$(Q) $(MAKEDIRS) $(@D)
|
||||
@$(NULLCMD) > $@
|
||||
|
||||
$(COROUTINE_TEST): $(COROUTINE_TEST_OBJS) $(COROUTINE_OBJ)
|
||||
$(ECHO) linking $@
|
||||
$(Q) $(COROUTINE_TEST_LINK)
|
||||
|
||||
coroutine-main.$(OBJEXT): {$(VPATH)}coroutine/test/main.c
|
||||
$(ECHO) compiling $(srcdir)/coroutine/test/main.c
|
||||
$(Q) $(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$(srcdir)/coroutine/test/main.c
|
||||
|
||||
coroutine-stack.$(OBJEXT): {$(VPATH)}coroutine/test/stack.c
|
||||
$(ECHO) compiling $(srcdir)/coroutine/test/stack.c
|
||||
$(Q) $(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$(srcdir)/coroutine/test/stack.c
|
||||
|
||||
coroutine-test_initialize_destroy.$(OBJEXT): {$(VPATH)}coroutine/test/test_initialize_destroy.c
|
||||
$(ECHO) compiling $(srcdir)/coroutine/test/test_initialize_destroy.c
|
||||
$(Q) $(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$(srcdir)/coroutine/test/test_initialize_destroy.c
|
||||
|
||||
coroutine-test_transfer_repeat.$(OBJEXT): {$(VPATH)}coroutine/test/test_transfer_repeat.c
|
||||
$(ECHO) compiling $(srcdir)/coroutine/test/test_transfer_repeat.c
|
||||
$(Q) $(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$(srcdir)/coroutine/test/test_transfer_repeat.c
|
||||
|
||||
coroutine-test_transfer_return.$(OBJEXT): {$(VPATH)}coroutine/test/test_transfer_return.c
|
||||
$(ECHO) compiling $(srcdir)/coroutine/test/test_transfer_return.c
|
||||
$(Q) $(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$(srcdir)/coroutine/test/test_transfer_return.c
|
||||
|
||||
###
|
||||
|
||||
# dependencies for generated C sources.
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
#include "test_initialize_destroy.h"
|
||||
#include "test_transfer_repeat.h"
|
||||
#include "test_transfer_return.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
static int
|
||||
run_tests(void)
|
||||
{
|
||||
int result = EXIT_SUCCESS;
|
||||
|
||||
if (test_initialize_destroy() != EXIT_SUCCESS) result = EXIT_FAILURE;
|
||||
if (test_transfer_repeat() != EXIT_SUCCESS) result = EXIT_FAILURE;
|
||||
if (test_transfer_return() != EXIT_SUCCESS) result = EXIT_FAILURE;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
return run_tests();
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
int
|
||||
wmain(void)
|
||||
{
|
||||
return run_tests();
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,49 @@
|
||||
#include "ruby/internal/config.h"
|
||||
|
||||
#include "stack.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#if defined(_WIN32)
|
||||
# include <windows.h>
|
||||
#elif defined(HAVE_SYS_MMAN_H)
|
||||
# include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
|
||||
# define MAP_ANONYMOUS MAP_ANON
|
||||
#endif
|
||||
|
||||
int
|
||||
coroutine_stack_allocate(struct coroutine_stack *stack, size_t size)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
stack->base = VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||
#elif defined(HAVE_MMAP) && defined(MAP_ANONYMOUS)
|
||||
stack->base = mmap(NULL, size, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
if (stack->base == MAP_FAILED) stack->base = NULL;
|
||||
#else
|
||||
stack->base = malloc(size);
|
||||
#endif
|
||||
|
||||
stack->size = stack->base == NULL ? 0 : size;
|
||||
return stack->base == NULL ? -1 : 0;
|
||||
}
|
||||
|
||||
void
|
||||
coroutine_stack_free(struct coroutine_stack *stack)
|
||||
{
|
||||
if (stack->base == NULL) return;
|
||||
|
||||
#if defined(_WIN32)
|
||||
VirtualFree(stack->base, 0, MEM_RELEASE);
|
||||
#elif defined(HAVE_MMAP) && defined(MAP_ANONYMOUS)
|
||||
munmap(stack->base, stack->size);
|
||||
#else
|
||||
free(stack->base);
|
||||
#endif
|
||||
|
||||
stack->base = NULL;
|
||||
stack->size = 0;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
struct coroutine_stack {
|
||||
void *base;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
int coroutine_stack_allocate(struct coroutine_stack *stack, size_t size);
|
||||
void coroutine_stack_free(struct coroutine_stack *stack);
|
||||
@@ -0,0 +1,41 @@
|
||||
#include "ruby/internal/config.h"
|
||||
|
||||
#include COROUTINE_H
|
||||
|
||||
#include "stack.h"
|
||||
#include "test_initialize_destroy.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define STACK_SIZE (1024 * 1024)
|
||||
|
||||
static COROUTINE
|
||||
never_started(struct coroutine_context *from, struct coroutine_context *self)
|
||||
{
|
||||
(void)from;
|
||||
(void)self;
|
||||
abort();
|
||||
}
|
||||
|
||||
int
|
||||
test_initialize_destroy(void)
|
||||
{
|
||||
struct coroutine_stack stack = {0};
|
||||
struct coroutine_context main_context;
|
||||
struct coroutine_context context;
|
||||
|
||||
if (coroutine_stack_allocate(&stack, STACK_SIZE) != 0) {
|
||||
fprintf(stderr, "failed to allocate coroutine stack\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
coroutine_initialize_main(&main_context);
|
||||
coroutine_initialize(&context, never_started, stack.base, stack.size);
|
||||
|
||||
coroutine_destroy(&context);
|
||||
coroutine_destroy(&main_context);
|
||||
coroutine_stack_free(&stack);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
int test_initialize_destroy(void);
|
||||
@@ -0,0 +1,141 @@
|
||||
#include "ruby/internal/config.h"
|
||||
|
||||
#include COROUTINE_H
|
||||
|
||||
#include "stack.h"
|
||||
#include "test_transfer_repeat.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define STACK_SIZE (1024 * 1024)
|
||||
#define TRANSFER_COUNT 1000
|
||||
|
||||
static struct coroutine_context main_context;
|
||||
static struct coroutine_context worker_context;
|
||||
static unsigned int yielded_iteration;
|
||||
static uint32_t yielded_state;
|
||||
static int completed;
|
||||
|
||||
static void
|
||||
check_context(const char *message, struct coroutine_context *actual,
|
||||
struct coroutine_context *expected)
|
||||
{
|
||||
if (actual != expected) {
|
||||
fprintf(stderr, "%s: expected %p, got %p\n",
|
||||
message, (void *)expected, (void *)actual);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
context_start(struct coroutine_context *from, struct coroutine_context *self)
|
||||
{
|
||||
#if defined(COROUTINE_SANITIZE_ADDRESS)
|
||||
__sanitizer_finish_switch_fiber(self->fake_stack,
|
||||
(const void **)&from->stack_base,
|
||||
&from->stack_size);
|
||||
#else
|
||||
(void)from;
|
||||
(void)self;
|
||||
#endif
|
||||
}
|
||||
|
||||
static struct coroutine_context *
|
||||
transfer(struct coroutine_context *current, struct coroutine_context *target)
|
||||
{
|
||||
#if defined(COROUTINE_SANITIZE_ADDRESS)
|
||||
__sanitizer_start_switch_fiber(¤t->fake_stack,
|
||||
target->stack_base, target->stack_size);
|
||||
#endif
|
||||
|
||||
#if defined(COROUTINE_SANITIZE_THREAD)
|
||||
__tsan_switch_to_fiber(target->tsan_fiber, 0);
|
||||
#endif
|
||||
|
||||
struct coroutine_context *from = coroutine_transfer(current, target);
|
||||
|
||||
#if defined(COROUTINE_SANITIZE_ADDRESS)
|
||||
__sanitizer_finish_switch_fiber(current->fake_stack, NULL, NULL);
|
||||
#endif
|
||||
|
||||
return from;
|
||||
}
|
||||
|
||||
static COROUTINE
|
||||
worker_entry(struct coroutine_context *from, struct coroutine_context *self)
|
||||
{
|
||||
context_start(from, self);
|
||||
|
||||
check_context("worker context was entered by", from, &main_context);
|
||||
check_context("worker context received self", self, &worker_context);
|
||||
|
||||
uint32_t state = UINT32_C(0x12345678);
|
||||
|
||||
for (unsigned int iteration = 1; iteration <= TRANSFER_COUNT; iteration++) {
|
||||
state = state * UINT32_C(1664525) + UINT32_C(1013904223);
|
||||
yielded_iteration = iteration;
|
||||
yielded_state = state;
|
||||
|
||||
from = transfer(self, &main_context);
|
||||
check_context("worker context was resumed by", from, &main_context);
|
||||
|
||||
if (yielded_iteration != iteration || yielded_state != state) {
|
||||
fprintf(stderr, "worker local state was not preserved\n");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
completed = 1;
|
||||
transfer(self, &main_context);
|
||||
abort();
|
||||
}
|
||||
|
||||
int
|
||||
test_transfer_repeat(void)
|
||||
{
|
||||
struct coroutine_stack stack = {0};
|
||||
int result = EXIT_FAILURE;
|
||||
|
||||
if (coroutine_stack_allocate(&stack, STACK_SIZE) != 0) {
|
||||
fprintf(stderr, "failed to allocate coroutine stack\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
yielded_iteration = 0;
|
||||
yielded_state = 0;
|
||||
completed = 0;
|
||||
|
||||
coroutine_initialize_main(&main_context);
|
||||
coroutine_initialize(&worker_context, worker_entry, stack.base, stack.size);
|
||||
|
||||
uint32_t expected_state = UINT32_C(0x12345678);
|
||||
|
||||
for (unsigned int iteration = 1; iteration <= TRANSFER_COUNT; iteration++) {
|
||||
expected_state = expected_state * UINT32_C(1664525) + UINT32_C(1013904223);
|
||||
|
||||
struct coroutine_context *from = transfer(&main_context, &worker_context);
|
||||
|
||||
if (from != &worker_context || yielded_iteration != iteration ||
|
||||
yielded_state != expected_state || completed) {
|
||||
fprintf(stderr, "repeated coroutine transfer failed at iteration %u\n",
|
||||
iteration);
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
|
||||
if (transfer(&main_context, &worker_context) != &worker_context || !completed) {
|
||||
fprintf(stderr, "worker context did not complete repeated transfers\n");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
result = EXIT_SUCCESS;
|
||||
|
||||
finish:
|
||||
coroutine_destroy(&worker_context);
|
||||
coroutine_destroy(&main_context);
|
||||
coroutine_stack_free(&stack);
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
int test_transfer_repeat(void);
|
||||
@@ -0,0 +1,142 @@
|
||||
#include "ruby/internal/config.h"
|
||||
|
||||
#include COROUTINE_H
|
||||
|
||||
#include "stack.h"
|
||||
#include "test_transfer_return.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define STACK_SIZE (1024 * 1024)
|
||||
|
||||
static struct coroutine_context main_context;
|
||||
static struct coroutine_context first_context;
|
||||
static struct coroutine_context second_context;
|
||||
|
||||
static void
|
||||
check_context(const char *message, struct coroutine_context *actual,
|
||||
struct coroutine_context *expected)
|
||||
{
|
||||
if (actual != expected) {
|
||||
fprintf(stderr, "%s: expected %p, got %p\n",
|
||||
message, (void *)expected, (void *)actual);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
context_start(struct coroutine_context *from, struct coroutine_context *self)
|
||||
{
|
||||
#if defined(COROUTINE_SANITIZE_ADDRESS)
|
||||
__sanitizer_finish_switch_fiber(self->fake_stack,
|
||||
(const void **)&from->stack_base,
|
||||
&from->stack_size);
|
||||
#else
|
||||
(void)from;
|
||||
(void)self;
|
||||
#endif
|
||||
}
|
||||
|
||||
static struct coroutine_context *
|
||||
transfer(struct coroutine_context *current, struct coroutine_context *target)
|
||||
{
|
||||
#if defined(COROUTINE_SANITIZE_ADDRESS)
|
||||
__sanitizer_start_switch_fiber(¤t->fake_stack,
|
||||
target->stack_base, target->stack_size);
|
||||
#endif
|
||||
|
||||
#if defined(COROUTINE_SANITIZE_THREAD)
|
||||
__tsan_switch_to_fiber(target->tsan_fiber, 0);
|
||||
#endif
|
||||
|
||||
struct coroutine_context *from = coroutine_transfer(current, target);
|
||||
|
||||
#if defined(COROUTINE_SANITIZE_ADDRESS)
|
||||
__sanitizer_finish_switch_fiber(current->fake_stack, NULL, NULL);
|
||||
#endif
|
||||
|
||||
return from;
|
||||
}
|
||||
|
||||
static COROUTINE
|
||||
second_entry(struct coroutine_context *from, struct coroutine_context *self)
|
||||
{
|
||||
context_start(from, self);
|
||||
|
||||
check_context("second context was entered by", from, &first_context);
|
||||
check_context("second context received self", self, &second_context);
|
||||
|
||||
from = transfer(self, &main_context);
|
||||
check_context("second context was resumed by", from, &main_context);
|
||||
|
||||
transfer(self, &first_context);
|
||||
abort();
|
||||
}
|
||||
|
||||
static COROUTINE
|
||||
first_entry(struct coroutine_context *from, struct coroutine_context *self)
|
||||
{
|
||||
context_start(from, self);
|
||||
|
||||
check_context("first context was entered by", from, &main_context);
|
||||
check_context("first context received self", self, &first_context);
|
||||
|
||||
from = transfer(self, &second_context);
|
||||
check_context("first context was resumed by", from, &second_context);
|
||||
|
||||
transfer(self, &main_context);
|
||||
abort();
|
||||
}
|
||||
|
||||
int
|
||||
test_transfer_return(void)
|
||||
{
|
||||
struct coroutine_stack first_stack = {0};
|
||||
struct coroutine_stack second_stack = {0};
|
||||
int result = EXIT_FAILURE;
|
||||
|
||||
if (coroutine_stack_allocate(&first_stack, STACK_SIZE) != 0 ||
|
||||
coroutine_stack_allocate(&second_stack, STACK_SIZE) != 0) {
|
||||
fprintf(stderr, "failed to allocate coroutine stacks\n");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
coroutine_initialize_main(&main_context);
|
||||
coroutine_initialize(&first_context, first_entry,
|
||||
first_stack.base, first_stack.size);
|
||||
coroutine_initialize(&second_context, second_entry,
|
||||
second_stack.base, second_stack.size);
|
||||
|
||||
result = EXIT_SUCCESS;
|
||||
|
||||
/* The original target is first_context, but second_context resumes us. */
|
||||
struct coroutine_context *from = transfer(&main_context, &first_context);
|
||||
|
||||
if (from != &second_context) {
|
||||
fprintf(stderr,
|
||||
"coroutine_transfer returned %p, expected actual resumer %p\n",
|
||||
(void *)from, (void *)&second_context);
|
||||
result = EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Resume second_context, which resumes first_context, which resumes us. */
|
||||
from = transfer(&main_context, &second_context);
|
||||
|
||||
if (from != &first_context) {
|
||||
fprintf(stderr,
|
||||
"coroutine_transfer returned %p, expected actual resumer %p\n",
|
||||
(void *)from, (void *)&first_context);
|
||||
result = EXIT_FAILURE;
|
||||
}
|
||||
|
||||
coroutine_destroy(&first_context);
|
||||
coroutine_destroy(&second_context);
|
||||
coroutine_destroy(&main_context);
|
||||
|
||||
finish:
|
||||
coroutine_stack_free(&first_stack);
|
||||
coroutine_stack_free(&second_stack);
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
int test_transfer_return(void);
|
||||
@@ -55,6 +55,11 @@ compile.$(OBJEXT): {$(VPATH)}compile.c
|
||||
complex.$(OBJEXT): {$(VPATH)}complex.c
|
||||
concurrent_set.$(OBJEXT): {$(VPATH)}concurrent_set.c
|
||||
cont.$(OBJEXT): {$(VPATH)}cont.c
|
||||
coroutine-main.$(OBJEXT): {$(VPATH)}coroutine/test/main.c
|
||||
coroutine-stack.$(OBJEXT): {$(VPATH)}coroutine/test/stack.c
|
||||
coroutine-test_initialize_destroy.$(OBJEXT): {$(VPATH)}coroutine/test/test_initialize_destroy.c
|
||||
coroutine-test_transfer_repeat.$(OBJEXT): {$(VPATH)}coroutine/test/test_transfer_repeat.c
|
||||
coroutine-test_transfer_return.$(OBJEXT): {$(VPATH)}coroutine/test/test_transfer_return.c
|
||||
debug.$(OBJEXT): {$(VPATH)}debug.c
|
||||
debug_counter.$(OBJEXT): {$(VPATH)}debug_counter.c
|
||||
dir.$(OBJEXT): {$(VPATH)}dir.c
|
||||
|
||||
@@ -185,6 +185,8 @@ BOOTSTRAPRUBY_FAKE = $(yes_baseruby:yes=$(arch)-fake.rb)
|
||||
COROUTINE_H = @X_COROUTINE_H@
|
||||
COROUTINE_OBJ = $(COROUTINE_H:.h=.$(OBJEXT))
|
||||
COROUTINE_SRC = @X_COROUTINE_SRC@
|
||||
COROUTINE_TEST_LINK = $(CC) $(EXE_LDFLAGS) $(XLDFLAGS) $(COROUTINE_TEST_OBJS) $(COROUTINE_OBJ) $(MAINLIBS) $(OUTFLAG)$(COROUTINE_TEST)
|
||||
COROUTINE_TEST_RUN = ./$(COROUTINE_TEST)
|
||||
|
||||
#### End of system configuration section. ####
|
||||
|
||||
|
||||
+2
-2
@@ -161,8 +161,8 @@ struct rb_native_thread {
|
||||
bool retiring;
|
||||
|
||||
// A terminating coroutine records its context here before its final
|
||||
// transfer; this nt's loop reclaims it. (Not via coroutine_transfer()'s
|
||||
// return value: its meaning differs between the amd64 asm and ucontext.)
|
||||
// transfer; this nt's loop reclaims it. coroutine_transfer() cannot be
|
||||
// used because a terminating coroutine never resumes to return a value.
|
||||
struct coroutine_context *dead_co;
|
||||
};
|
||||
|
||||
|
||||
@@ -344,6 +344,8 @@ COROUTINE_SRC = $(COROUTINE_OBJ:.obj=.asm)
|
||||
!error copy coroutine has been replaced with pthread implementation at 42130a64f02294dc8025af3a51bda518c67ab33d
|
||||
!endif
|
||||
COROUTINE_H = $(COROUTINE_OBJ:.obj=.h)
|
||||
COROUTINE_TEST_LINK = $(CC) $(COROUTINE_TEST_OBJS) $(COROUTINE_OBJ) $(MAINLIBS) $(OUTFLAG)$(COROUTINE_TEST) -link $(LDFLAGS) $(XLDFLAGS)
|
||||
COROUTINE_TEST_RUN = .\$(COROUTINE_TEST)
|
||||
|
||||
ARFLAGS = -machine:$(MACHINE) -out:
|
||||
LD = $(CC)
|
||||
|
||||
Reference in new issue
Block a user