diff --git a/eval.c b/eval.c
index b705302..909cd3d 100644
--- a/eval.c
+++ b/eval.c
@@ -73,6 +73,7 @@ char *strrchr _((const char*,const char));
#endif
#include <time.h>
+#include <sys/mman.h>
#ifdef __BEOS__
#include <net/socket.h>
@@ -1022,7 +1023,7 @@ static struct tag *prot_tag;
_tag.blkid = 0; \
prot_tag = &_tag
-#define PROT_NONE Qfalse /* 0 */
+#define PROT_EMPTY Qfalse /* 0 */
#define PROT_THREAD Qtrue /* 2 */
#define PROT_FUNC INT2FIX(0) /* 1 */
#define PROT_LOOP INT2FIX(1) /* 3 */
@@ -1234,7 +1235,7 @@ error_print()
if (NIL_P(ruby_errinfo)) return;
- PUSH_TAG(PROT_NONE);
+ PUSH_TAG(PROT_EMPTY);
if (EXEC_TAG() == 0) {
errat = get_backtrace(ruby_errinfo);
}
@@ -1395,7 +1396,7 @@ ruby_init()
/* default visibility is private at toplevel */
SCOPE_SET(SCOPE_PRIVATE);
- PUSH_TAG(PROT_NONE);
+ PUSH_TAG(PROT_EMPTY);
if ((state = EXEC_TAG()) == 0) {
rb_call_inits();
ruby_class = rb_cObject;
@@ -1529,7 +1530,7 @@ ruby_options(argc, argv)
int state;
Init_stack((void*)&state);
- PUSH_TAG(PROT_NONE);
+ PUSH_TAG(PROT_EMPTY);
if ((state = EXEC_TAG()) == 0) {
ruby_process_options(argc, argv);
}
@@ -1546,7 +1547,7 @@ void rb_exec_end_proc _((void));
static void
ruby_finalize_0()
{
- PUSH_TAG(PROT_NONE);
+ PUSH_TAG(PROT_EMPTY);
if (EXEC_TAG() == 0) {
rb_trap_exit();
}
@@ -1584,7 +1585,7 @@ ruby_cleanup(ex)
Init_stack((void *)&state);
ruby_finalize_0();
errs[0] = ruby_errinfo;
- PUSH_TAG(PROT_NONE);
+ PUSH_TAG(PROT_EMPTY);
PUSH_ITER(ITER_NOT);
if ((state = EXEC_TAG()) == 0) {
rb_thread_cleanup();
@@ -1635,7 +1636,7 @@ ruby_exec_internal()
{
int state;
- PUSH_TAG(PROT_NONE);
+ PUSH_TAG(PROT_EMPTY);
PUSH_ITER(ITER_NOT);
/* default visibility is private at toplevel */
SCOPE_SET(SCOPE_PRIVATE);
@@ -1857,7 +1858,7 @@ rb_eval_cmd(cmd, arg, level)
}
if (TYPE(cmd) != T_STRING) {
PUSH_ITER(ITER_NOT);
- PUSH_TAG(PROT_NONE);
+ PUSH_TAG(PROT_EMPTY);
ruby_safe_level = level;
if ((state = EXEC_TAG()) == 0) {
val = rb_funcall2(cmd, rb_intern("call"), RARRAY(arg)->len, RARRAY(arg)->ptr);
@@ -1879,7 +1880,7 @@ rb_eval_cmd(cmd, arg, level)
ruby_safe_level = level;
- PUSH_TAG(PROT_NONE);
+ PUSH_TAG(PROT_EMPTY);
if ((state = EXEC_TAG()) == 0) {
val = eval(ruby_top_self, cmd, Qnil, 0, 0);
}
@@ -2386,7 +2387,7 @@ is_defined(self, node, buf)
val = self;
if (node->nd_recv == (NODE *)1) goto check_bound;
case NODE_CALL:
- PUSH_TAG(PROT_NONE);
+ PUSH_TAG(PROT_EMPTY);
if ((state = EXEC_TAG()) == 0) {
val = rb_eval(self, node->nd_recv);
}
@@ -2488,7 +2489,7 @@ is_defined(self, node, buf)
break;
case NODE_COLON2:
- PUSH_TAG(PROT_NONE);
+ PUSH_TAG(PROT_EMPTY);
if ((state = EXEC_TAG()) == 0) {
val = rb_eval(self, node->nd_head);
}
@@ -2537,7 +2538,7 @@ is_defined(self, node, buf)
goto again;
default:
- PUSH_TAG(PROT_NONE);
+ PUSH_TAG(PROT_EMPTY);
if ((state = EXEC_TAG()) == 0) {
rb_eval(self, node);
}
@@ -2741,7 +2742,7 @@ call_trace_func(event, node, self, id, klass)
klass = rb_iv_get(klass, "__attached__");
}
}
- PUSH_TAG(PROT_NONE);
+ PUSH_TAG(PROT_EMPTY);
raised = rb_thread_reset_raised(th);
if ((state = EXEC_TAG()) == 0) {
srcfile = rb_str_new2(ruby_sourcefile?ruby_sourcefile:"(ruby)");
@@ -3304,7 +3305,7 @@ rb_eval(self, n)
volatile VALUE e_info = ruby_errinfo;
volatile int rescuing = 0;
- PUSH_TAG(PROT_NONE);
+ PUSH_TAG(PROT_EMPTY);
if ((state = EXEC_TAG()) == 0) {
retry_entry:
result = rb_eval(self, node->nd_head);
@@ -3353,7 +3354,7 @@ rb_eval(self, n)
break;
case NODE_ENSURE:
- PUSH_TAG(PROT_NONE);
+ PUSH_TAG(PROT_EMPTY);
if ((state = EXEC_TAG()) == 0) {
result = rb_eval(self, node->nd_head);
}
@@ -3571,7 +3572,7 @@ rb_eval(self, n)
ruby_frame = &frame;
PUSH_SCOPE();
- PUSH_TAG(PROT_NONE);
+ PUSH_TAG(PROT_EMPTY);
if (node->nd_rval) {
saved_cref = ruby_cref;
ruby_cref = (NODE*)node->nd_rval;
@@ -4197,7 +4198,7 @@ module_setup(module, n)
}
PUSH_CREF(module);
- PUSH_TAG(PROT_NONE);
+ PUSH_TAG(PROT_EMPTY);
if ((state = EXEC_TAG()) == 0) {
EXEC_EVENT_HOOK(RUBY_EVENT_CLASS, n, ruby_cbase,
ruby_frame->last_func, ruby_frame->last_class);
@@ -4604,7 +4605,7 @@ rb_longjmp(tag, mesg)
VALUE e = ruby_errinfo;
int status;
- PUSH_TAG(PROT_NONE);
+ PUSH_TAG(PROT_EMPTY);
if ((status = EXEC_TAG()) == 0) {
StringValue(e);
warn_printf("Exception `%s' at %s:%d - %s\n",
@@ -4978,7 +4979,7 @@ rb_yield_0(val, self, klass, flags, avalue)
var = block->var;
if (var) {
- PUSH_TAG(PROT_NONE);
+ PUSH_TAG(PROT_EMPTY);
if ((state = EXEC_TAG()) == 0) {
NODE *bvar = NULL;
block_var:
@@ -5051,7 +5052,7 @@ rb_yield_0(val, self, klass, flags, avalue)
ruby_current_node = node;
PUSH_ITER(block->iter);
- PUSH_TAG(lambda ? PROT_NONE : PROT_YIELD);
+ PUSH_TAG(lambda ? PROT_EMPTY : PROT_YIELD);
if ((state = EXEC_TAG()) == 0) {
redo:
if (nd_type(node) == NODE_CFUNC || nd_type(node) == NODE_IFUNC) {
@@ -5464,7 +5465,7 @@ rb_rescue2(b_proc, data1, r_proc, data2, va_alist)
VALUE eclass;
va_list args;
- PUSH_TAG(PROT_NONE);
+ PUSH_TAG(PROT_EMPTY);
switch (state = EXEC_TAG()) {
case TAG_RETRY:
if (!handle) break;
@@ -5522,7 +5523,7 @@ rb_protect(proc, data, state)
VALUE result = Qnil; /* OK */
int status;
- PUSH_TAG(PROT_NONE);
+ PUSH_TAG(PROT_EMPTY);
cont_protect = (VALUE)rb_node_newnode(NODE_MEMO, cont_protect, 0, 0);
if ((status = EXEC_TAG()) == 0) {
result = (*proc)(data);
@@ -5550,7 +5551,7 @@ rb_ensure(b_proc, data1, e_proc, data2)
volatile VALUE result = Qnil;
VALUE retval;
- PUSH_TAG(PROT_NONE);
+ PUSH_TAG(PROT_EMPTY);
if ((state = EXEC_TAG()) == 0) {
result = (*b_proc)(data1);
}
@@ -5577,7 +5578,7 @@ rb_with_disable_interrupt(proc, data)
int thr_critical = rb_thread_critical;
rb_thread_critical = Qtrue;
- PUSH_TAG(PROT_NONE);
+ PUSH_TAG(PROT_EMPTY);
if ((status = EXEC_TAG()) == 0) {
result = (*proc)(data);
}
@@ -6264,7 +6265,7 @@ rb_funcall_rescue(recv, mid, n, va_alist)
va_init_list(ar, n);
- PUSH_TAG(PROT_NONE);
+ PUSH_TAG(PROT_EMPTY);
if ((status = EXEC_TAG()) == 0) {
result = vafuncall(recv, mid, n, &ar);
}
@@ -6539,7 +6540,7 @@ eval(self, src, scope, file, line)
if (TYPE(ruby_class) == T_ICLASS) {
ruby_class = RBASIC(ruby_class)->klass;
}
- PUSH_TAG(PROT_NONE);
+ PUSH_TAG(PROT_EMPTY);
if ((state = EXEC_TAG()) == 0) {
NODE *node;
@@ -6698,7 +6699,7 @@ exec_under(func, under, cbase, args)
mode = scope_vmode;
SCOPE_SET(SCOPE_PUBLIC);
- PUSH_TAG(PROT_NONE);
+ PUSH_TAG(PROT_EMPTY);
if ((state = EXEC_TAG()) == 0) {
val = (*func)(args);
}
@@ -7009,7 +7010,7 @@ rb_load(fname, wrap)
PUSH_SCOPE();
/* default visibility is private at loading toplevel */
SCOPE_SET(SCOPE_PRIVATE);
- PUSH_TAG(PROT_NONE);
+ PUSH_TAG(PROT_EMPTY);
state = EXEC_TAG();
last_func = ruby_frame->last_func;
last_node = ruby_current_node;
@@ -7068,7 +7069,7 @@ rb_load_protect(fname, wrap, state)
{
int status;
- PUSH_TAG(PROT_NONE);
+ PUSH_TAG(PROT_EMPTY);
if ((status = EXEC_TAG()) == 0) {
rb_load(fname, wrap);
}
@@ -7389,7 +7390,7 @@ rb_require_safe(fname, safe)
saved.node = ruby_current_node;
saved.func = ruby_frame->last_func;
saved.safe = ruby_safe_level;
- PUSH_TAG(PROT_NONE);
+ PUSH_TAG(PROT_EMPTY);
if ((state = EXEC_TAG()) == 0) {
VALUE feature, path;
long handle;
@@ -8097,7 +8098,7 @@ rb_exec_end_proc()
tmp_end_procs = link = ephemeral_end_procs;
ephemeral_end_procs = 0;
while (link) {
- PUSH_TAG(PROT_NONE);
+ PUSH_TAG(PROT_EMPTY);
if ((status = EXEC_TAG()) == 0) {
ruby_safe_level = link->safe;
(*link->func)(link->data);
@@ -8115,7 +8116,7 @@ rb_exec_end_proc()
tmp_end_procs = link = end_procs;
end_procs = 0;
while (link) {
- PUSH_TAG(PROT_NONE);
+ PUSH_TAG(PROT_EMPTY);
if ((status = EXEC_TAG()) == 0) {
ruby_safe_level = link->safe;
(*link->func)(link->data);
@@ -8852,7 +8853,7 @@ proc_invoke(proc, args, self, klass)
ruby_block = &_block;
PUSH_ITER(ITER_CUR);
ruby_frame->iter = ITER_CUR;
- PUSH_TAG(pcall ? PROT_LAMBDA : PROT_NONE);
+ PUSH_TAG(pcall ? PROT_LAMBDA : PROT_EMPTY);
state = EXEC_TAG();
if (state == 0) {
proc_set_safe_level(proc);
@@ -10179,6 +10180,7 @@ win32_set_exception_list(p)
int rb_thread_pending = 0;
VALUE rb_cThread;
+static unsigned int rb_thread_stack_size;
extern VALUE rb_last_status;
@@ -10406,12 +10408,20 @@ thread_mark(th)
rb_gc_mark(th->thread);
if (th->join) rb_gc_mark(th->join->thread);
- rb_gc_mark(th->klass);
- rb_gc_mark(th->wrapper);
- rb_gc_mark((VALUE)th->cref);
+ if (curr_thread == th) {
+ rb_gc_mark(ruby_class);
+ rb_gc_mark(ruby_wrapper);
+ rb_gc_mark((VALUE)ruby_cref);
+ rb_gc_mark((VALUE)ruby_scope);
+ rb_gc_mark((VALUE)ruby_dyna_vars);
+ } else {
+ rb_gc_mark(th->klass);
+ rb_gc_mark(th->wrapper);
+ rb_gc_mark((VALUE)th->cref);
+ rb_gc_mark((VALUE)th->scope);
+ rb_gc_mark((VALUE)th->dyna_vars);
+ }
- rb_gc_mark((VALUE)th->scope);
- rb_gc_mark((VALUE)th->dyna_vars);
rb_gc_mark(th->errinfo);
rb_gc_mark(th->last_status);
rb_gc_mark(th->last_line);
@@ -10421,11 +10431,11 @@ thread_mark(th)
rb_gc_mark_maybe(th->sandbox);
/* mark data in copied stack */
- if (th == curr_thread) return;
+ if (th == main_thread) return;
if (th->status == THREAD_KILLED) return;
if (th->stk_len == 0) return; /* stack not active, no need to mark. */
- if (th->stk_ptr) {
- rb_gc_mark_locations(th->stk_ptr, th->stk_ptr+th->stk_len);
+ if (th->stk_ptr && th != curr_thread) {
+ rb_gc_mark_locations(th->stk_pos, th->stk_base);
#if defined(THINK_C) || defined(__human68k__)
rb_gc_mark_locations(th->stk_ptr+2, th->stk_ptr+th->stk_len+2);
#endif
@@ -10435,24 +10445,30 @@ thread_mark(th)
}
#endif
}
- frame = th->frame;
+
+ if (curr_thread == th)
+ frame = ruby_frame;
+ else
+ frame = th->frame;
+
while (frame && frame != top_frame) {
- frame = ADJ(frame);
rb_gc_mark_frame(frame);
if (frame->tmp) {
struct FRAME *tmp = frame->tmp;
-
while (tmp && tmp != top_frame) {
- tmp = ADJ(tmp);
rb_gc_mark_frame(tmp);
tmp = tmp->prev;
}
}
frame = frame->prev;
}
- block = th->block;
+
+ if (curr_thread == th)
+ block = ruby_block;
+ else
+ block = th->block;
+
while (block) {
- block = ADJ(block);
rb_gc_mark_frame(&block->frame);
block = block->prev;
}
@@ -10515,7 +10531,7 @@ static inline void
stack_free(th)
rb_thread_t th;
{
- if (th->stk_ptr) free(th->stk_ptr);
+ if (th->stk_ptr) munmap(th->stk_ptr, th->stk_size);
th->stk_ptr = 0;
#ifdef __ia64
if (th->bstr_ptr) free(th->bstr_ptr);
@@ -10576,35 +10592,8 @@ rb_thread_save_context(th)
static VALUE tval;
len = ruby_stack_length(&pos);
- th->stk_len = 0;
- th->stk_pos = pos;
- if (len > th->stk_max) {
- VALUE *ptr = realloc(th->stk_ptr, sizeof(VALUE) * len);
- if (!ptr) rb_memerror();
- th->stk_ptr = ptr;
- th->stk_max = len;
- }
th->stk_len = len;
- FLUSH_REGISTER_WINDOWS;
- MEMCPY(th->stk_ptr, th->stk_pos, VALUE, th->stk_len);
-#ifdef __ia64
- th->bstr_pos = rb_gc_register_stack_start;
- len = (VALUE*)rb_ia64_bsp() - th->bstr_pos;
- th->bstr_len = 0;
- if (len > th->bstr_max) {
- VALUE *ptr = realloc(th->bstr_ptr, sizeof(VALUE) * len);
- if (!ptr) rb_memerror();
- th->bstr_ptr = ptr;
- th->bstr_max = len;
- }
- th->bstr_len = len;
- rb_ia64_flushrs();
- MEMCPY(th->bstr_ptr, th->bstr_pos, VALUE, th->bstr_len);
-#endif
-#ifdef SAVE_WIN32_EXCEPTION_LIST
- th->win32_exception_list = win32_get_exception_list();
-#endif
-
+ th->stk_pos = pos;
th->frame = ruby_frame;
th->scope = ruby_scope;
ruby_scope->flags |= SCOPE_DONT_RECYCLE;
@@ -10714,11 +10703,6 @@ rb_thread_restore_context_0(rb_thread_t th, int exit)
#endif
tmp = th;
ex = exit;
- FLUSH_REGISTER_WINDOWS;
- MEMCPY(tmp->stk_pos, tmp->stk_ptr, VALUE, tmp->stk_len);
-#ifdef __ia64
- MEMCPY(tmp->bstr_pos, tmp->bstr_ptr, VALUE, tmp->bstr_len);
-#endif
tval = rb_lastline_get();
rb_lastline_set(tmp->last_line);
@@ -10809,8 +10793,8 @@ rb_thread_restore_context(th, exit)
rb_thread_t th;
int exit;
{
- if (!th->stk_ptr) rb_bug("unsaved context");
- stack_extend(th, exit);
+ if (!th->stk_ptr && th != main_thread) rb_bug("unsaved context");
+ rb_thread_restore_context_0(th, exit);
}
static void
@@ -10829,7 +10813,6 @@ rb_thread_die(th)
{
th->thgroup = 0;
th->status = THREAD_KILLED;
- stack_free(th);
}
static void
@@ -12096,6 +12079,7 @@ rb_thread_group(thread)
\
th->stk_ptr = 0;\
th->stk_len = 0;\
+ th->stk_size = 0;\
th->stk_max = 0;\
th->wait_for = 0;\
IA64_INIT(th->bstr_ptr = 0);\
@@ -12143,6 +12127,48 @@ rb_thread_alloc(klass)
THREAD_ALLOC(th);
th->thread = Data_Wrap_Struct(klass, thread_mark, thread_free, th);
+ /* if main_thread != NULL, then this is NOT the main thread, so
+ * we create a heap-stack
+ */
+ if (main_thread) {
+ /* Allocate stack, don't forget to add 1 extra word because of the MATH below */
+ unsigned int pagesize = getpagesize();
+ unsigned int total_size = rb_thread_stack_size + pagesize + sizeof(int);
+ void *stack_area = NULL;
+
+ stack_area = mmap(NULL, total_size, PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_PRIVATE | MAP_ANON, -1, 0);
+
+ if (stack_area == MAP_FAILED) {
+ fprintf(stderr, "Thread stack allocation failed!\n");
+ rb_memerror();
+ }
+
+ th->stk_ptr = th->stk_pos = stack_area;
+ th->stk_size = total_size;
+
+ if (mprotect(th->stk_ptr, pagesize, PROT_NONE) == -1) {
+ fprintf(stderr, "Failed to create thread guard region: %s\n", strerror(errno));
+ rb_memerror();
+ }
+
+ th->guard = th->stk_ptr + (pagesize/sizeof(VALUE *));
+
+ /* point stk_base at the top of the stack */
+ /* ASSUMPTIONS:
+ * 1.) The address returned by malloc is "suitably aligned" for anything on this system
+ * 2.) Adding a value that is "aligned" for this platform should not unalign the address
+ * returned from malloc.
+ * 3.) Don't push anything on to the stack, otherwise it'll get unaligned.
+ * 4.) x86_64 ABI says aligned AFTER arguments have been pushed. You *must* then do a call[lq]
+ * or push[lq] something else on to the stack if you inted to do a ret.
+ */
+ th->stk_base = th->stk_ptr + ((total_size - sizeof(int))/sizeof(VALUE *));
+ th->stk_len = rb_thread_stack_size;
+ } else {
+ th->stk_ptr = th->stk_pos = rb_gc_stack_start;
+ }
+
for (vars = th->dyna_vars; vars; vars = vars->next) {
if (FL_TEST(vars, DVAR_DONT_RECYCLE)) break;
FL_SET(vars, DVAR_DONT_RECYCLE);
@@ -12246,17 +12272,22 @@ rb_thread_stop_timer()
int rb_thread_tick = THREAD_TICK;
#endif
+struct thread_start_args {
+ VALUE (*fn)();
+ void *arg;
+ rb_thread_t th;
+} new_th;
+
+static VALUE
+rb_thread_start_2();
+
static VALUE
rb_thread_start_0(fn, arg, th)
VALUE (*fn)();
void *arg;
rb_thread_t th;
{
- volatile rb_thread_t th_save = th;
volatile VALUE thread = th->thread;
- struct BLOCK *volatile saved_block = 0;
- enum rb_thread_status status;
- int state;
if (OBJ_FROZEN(curr_thread->thgroup)) {
rb_raise(rb_eThreadError,
@@ -12284,16 +12315,41 @@ rb_thread_start_0(fn, arg, th)
return thread;
}
- if (ruby_block) { /* should nail down higher blocks */
- struct BLOCK dummy;
+ new_th.fn = fn;
+ new_th.arg = arg;
+ new_th.th = th;
+
+#if defined(__i386__)
+ __asm__ __volatile__ ("movl %0, %%esp\n\t"
+ "calll *%1\n"
+ :: "r" (th->stk_base),
+ "r" (rb_thread_start_2));
+#elif defined(__x86_64__)
+ __asm__ __volatile__ ("movq %0, %%rsp\n\t"
+ "callq *%1\n"
+ :: "r" (th->stk_base),
+ "r" (rb_thread_start_2));
+#else
+ #error unsupported architecture!
+#endif
+ /* NOTREACHED */
+ return 0;
+}
- dummy.prev = ruby_block;
- blk_copy_prev(&dummy);
- saved_block = ruby_block = dummy.prev;
- }
- scope_dup(ruby_scope);
+static VALUE
+rb_thread_start_2()
+{
+ volatile rb_thread_t th = new_th.th;
+ volatile rb_thread_t th_save = th;
+ volatile VALUE thread = th->thread;
+ struct BLOCK *volatile saved_block = 0;
+ enum rb_thread_status status;
+ int state;
+ struct tag *tag;
+ struct RVarmap *vars;
+ struct FRAME dummy_frame;
- if (!th->next) {
+ if (!th->next) {
/* merge in thread list */
th->prev = curr_thread;
curr_thread->next->prev = th;
@@ -12301,13 +12357,27 @@ rb_thread_start_0(fn, arg, th)
curr_thread->next = th;
th->priority = curr_thread->priority;
th->thgroup = curr_thread->thgroup;
+ }
+ curr_thread = th;
+
+ dummy_frame = *ruby_frame;
+ dummy_frame.prev = top_frame;
+ ruby_frame = &dummy_frame;
+
+ if (ruby_block) { /* should nail down higher blocks */
+ struct BLOCK dummy;
+
+ dummy.prev = ruby_block;
+ blk_copy_prev(&dummy);
+ saved_block = ruby_block = dummy.prev;
}
+ scope_dup(ruby_scope);
+
PUSH_TAG(PROT_THREAD);
if ((state = EXEC_TAG()) == 0) {
if (THREAD_SAVE_CONTEXT(th) == 0) {
- curr_thread = th;
- th->result = (*fn)(arg, th);
+ th->result = (*new_th.fn)(new_th.arg, th);
}
th = th_save;
}
@@ -12644,6 +12714,43 @@ rb_thread_cleanup()
END_FOREACH_FROM(curr, th);
}
+/*
+ * call-seq:
+ * Thread.stack_size => fixnum
+ *
+ * Returns the thread stack size in bytes
+ */
+static VALUE
+rb_thread_stacksize_get()
+{
+ return INT2FIX(rb_thread_stack_size);
+}
+
+/*
+ * call-seq:
+ * Thread.stack_size= fixnum => Qnil
+ *
+ * Sets the global thread stacksize and returns Qnil.
+ */
+static VALUE
+rb_thread_stacksize_set(obj, val)
+ VALUE obj;
+ VALUE val;
+{
+
+ unsigned int size = FIX2UINT(val);
+
+ /* 16byte alignment works for both x86 and x86_64 */
+ if (size & (~0xf)) {
+ size += 0x10;
+ size = size & (~0xf);
+ }
+
+ rb_thread_stack_size = size;
+
+ return Qnil;
+}
+
int rb_thread_critical;
@@ -13473,7 +13580,7 @@ rb_exec_recursive(func, obj, arg)
int state;
hash = recursive_push(hash, objid);
- PUSH_TAG(PROT_NONE);
+ PUSH_TAG(PROT_EMPTY);
if ((state = EXEC_TAG()) == 0) {
result = (*func) (obj, arg, Qfalse);
}
@@ -13500,6 +13607,8 @@ Init_Thread()
{
VALUE cThGroup;
+ rb_thread_stack_size = (1024 * 1024);
+
recursive_key = rb_intern("__recursive_key__");
rb_eThreadError = rb_define_class("ThreadError", rb_eStandardError);
rb_cThread = rb_define_class("Thread", rb_cObject);
@@ -13524,6 +13633,9 @@ Init_Thread()
rb_define_singleton_method(rb_cThread, "abort_on_exception", rb_thread_s_abort_exc, 0);
rb_define_singleton_method(rb_cThread, "abort_on_exception=", rb_thread_s_abort_exc_set, 1);
+ rb_define_singleton_method(rb_cThread, "stack_size", rb_thread_stacksize_get, 0);
+ rb_define_singleton_method(rb_cThread, "stack_size=", rb_thread_stacksize_set, 1);
+
rb_define_method(rb_cThread, "run", rb_thread_run, 0);
rb_define_method(rb_cThread, "wakeup", rb_thread_wakeup, 0);
rb_define_method(rb_cThread, "kill", rb_thread_kill, 0);
diff --git a/gc.c b/gc.c
index a564f0b..d6d654d 100644
--- a/gc.c
+++ b/gc.c
@@ -506,12 +506,12 @@ stack_end_address(VALUE **stack_end_p)
# define STACK_END (stack_end)
#endif
#if STACK_GROW_DIRECTION < 0
-# define STACK_LENGTH (rb_gc_stack_start - STACK_END)
+# define STACK_LENGTH(start) ((start) - STACK_END)
#elif STACK_GROW_DIRECTION > 0
-# define STACK_LENGTH (STACK_END - rb_gc_stack_start + 1)
+# define STACK_LENGTH(start) (STACK_END - (start) + 1)
#else
-# define STACK_LENGTH ((STACK_END < rb_gc_stack_start) ? rb_gc_stack_start - STACK_END\
- : STACK_END - rb_gc_stack_start + 1)
+# define STACK_LENGTH(start) ((STACK_END < (start)) ? (start) - STACK_END\
+ : STACK_END - (start) + 1)
#endif
#if STACK_GROW_DIRECTION > 0
# define STACK_UPPER(x, a, b) a
@@ -534,27 +534,36 @@ stack_grow_direction(addr)
#define GC_WATER_MARK 512
-#define CHECK_STACK(ret) do {\
+#define CHECK_STACK(ret, start) do {\
SET_STACK_END;\
- (ret) = (STACK_LENGTH > STACK_LEVEL_MAX + GC_WATER_MARK);\
+ (ret) = (STACK_LENGTH(start) > STACK_LEVEL_MAX + GC_WATER_MARK);\
} while (0)
size_t
ruby_stack_length(p)
VALUE **p;
{
- SET_STACK_END;
- if (p) *p = STACK_UPPER(STACK_END, rb_gc_stack_start, STACK_END);
- return STACK_LENGTH;
+ SET_STACK_END;
+ VALUE *start;
+ if (rb_curr_thread == rb_main_thread) {
+ start = rb_gc_stack_start;
+ } else {
+ start = rb_curr_thread->stk_base;
+ }
+ if (p) *p = STACK_UPPER(STACK_END, start, STACK_END);
+ return STACK_LENGTH(start);
}
int
ruby_stack_check()
{
- int ret;
-
- CHECK_STACK(ret);
- return ret;
+ int ret;
+ if (rb_curr_thread == rb_main_thread) {
+ CHECK_STACK(ret, rb_gc_stack_start);
+ } else {
+ CHECK_STACK(ret, rb_curr_thread->stk_base);
+ }
+ return ret;
}
#define MARK_STACK_MAX 1024
@@ -1441,10 +1450,13 @@ garbage_collect()
init_mark_stack();
- gc_mark((VALUE)ruby_current_node, 0);
-
/* mark frame stack */
- for (frame = ruby_frame; frame; frame = frame->prev) {
+ if (rb_curr_thread == rb_main_thread)
+ frame = ruby_frame;
+ else
+ frame = rb_main_thread->frame;
+
+ for (; frame; frame = frame->prev) {
rb_gc_mark_frame(frame);
if (frame->tmp) {
struct FRAME *tmp = frame->tmp;
@@ -1454,16 +1466,35 @@ garbage_collect()
}
}
}
- gc_mark((VALUE)ruby_scope, 0);
- gc_mark((VALUE)ruby_dyna_vars, 0);
+
+ if (rb_curr_thread == rb_main_thread) {
+ gc_mark((VALUE)ruby_current_node, 0);
+ gc_mark((VALUE)ruby_scope, 0);
+ gc_mark((VALUE)ruby_dyna_vars, 0);
+ } else {
+ gc_mark((VALUE)rb_main_thread->node, 0);
+ gc_mark((VALUE)rb_main_thread->scope, 0);
+ gc_mark((VALUE)rb_main_thread->dyna_vars, 0);
+
+ /* scan the current thread's stack */
+ rb_gc_mark_locations((VALUE*)STACK_END, rb_curr_thread->stk_base);
+ }
+
if (finalizer_table) {
- mark_tbl(finalizer_table, 0);
+ mark_tbl(finalizer_table, 0);
}
FLUSH_REGISTER_WINDOWS;
/* This assumes that all registers are saved into the jmp_buf (and stack) */
rb_setjmp(save_regs_gc_mark);
mark_locations_array((VALUE*)save_regs_gc_mark, sizeof(save_regs_gc_mark) / sizeof(VALUE *));
+
+ /* If this is not the main thread, we need to scan the C stack, so
+ * set STACK_END to the end of the C stack.
+ */
+ if (rb_curr_thread != rb_main_thread)
+ STACK_END = rb_main_thread->stk_pos;
+
#if STACK_GROW_DIRECTION < 0
rb_gc_mark_locations((VALUE*)STACK_END, rb_gc_stack_start);
#elif STACK_GROW_DIRECTION > 0
@@ -1483,6 +1514,7 @@ garbage_collect()
rb_gc_mark_locations((VALUE*)((char*)STACK_END + 2),
(VALUE*)((char*)rb_gc_stack_start + 2));
#endif
+
rb_gc_mark_threads();
/* mark protected global variables */
diff --git a/lib/logger.rb b/lib/logger.rb
index 15d95fc..5a6d467 100644
--- a/lib/logger.rb
+++ b/lib/logger.rb
@@ -170,7 +170,7 @@ require 'monitor'
class Logger
VERSION = "1.2.6"
- id, name, rev = %w$Id$
+ id, name, rev = %w$Id logger.rb 1234$
ProgName = "#{name.chomp(",v")}/#{rev}"
class Error < RuntimeError; end
diff --git a/node.h b/node.h
index c209fa5..740e66a 100644
--- a/node.h
+++ b/node.h
@@ -411,8 +411,11 @@ struct rb_thread {
size_t stk_len;
size_t stk_max;
+ size_t stk_size;
VALUE *stk_ptr;
VALUE *stk_pos;
+ VALUE *stk_base;
+ VALUE *guard;
#ifdef __ia64
size_t bstr_len;
size_t bstr_max;
diff --git a/signal.c b/signal.c
index fb21fd3..acac6a7 100644
--- a/signal.c
+++ b/signal.c
@@ -14,6 +14,7 @@
#include "ruby.h"
#include "rubysig.h"
+#include "node.h"
#include <signal.h>
#include <stdio.h>
@@ -428,15 +429,22 @@ typedef RETSIGTYPE (*sighandler_t)_((int));
static sighandler_t
ruby_signal(signum, handler)
int signum;
- sighandler_t handler;
+ void *handler;
{
struct sigaction sigact, old;
rb_trap_accept_nativethreads[signum] = 0;
- sigact.sa_handler = handler;
+ if (signum == SIGSEGV || signum == SIGBUS) {
+ sigact.sa_sigaction = handler;
+ sigact.sa_flags = (SA_ONSTACK | SA_RESETHAND | SA_SIGINFO);
+ } else {
+ sigact.sa_handler = handler;
+ sigact.sa_flags = 0;
+ }
+
sigemptyset(&sigact.sa_mask);
- sigact.sa_flags = 0;
+
# ifdef SA_NOCLDWAIT
if (signum == SIGCHLD && handler == SIG_IGN)
sigact.sa_flags |= SA_NOCLDWAIT;
@@ -599,7 +607,132 @@ sighandler(sig)
}
}
+#include <stdio.h>
+#ifdef HAVE_STDARG_PROTOTYPES
+#include <stdarg.h>
+#define va_init_list(a,b) va_start(a,b)
+#else
+#include <varargs.h>
+#define va_init_list(a,b) va_start(a)
+#endif
+
+void
+#ifdef HAVE_STDARG_PROTOTYPES
+sig_printf(const char *fmt, ...)
+#else
+ sig_printf(fmt, va_alist)
+ const char *fmt;
+ va_dcl
+#endif
+{
+ char buf[BUFSIZ];
+ va_list args;
+ FILE *out = stderr;
+
+ va_init_list(args, fmt);
+ vfprintf(out, fmt, args);
+ va_end(args);
+ fprintf(out, "\n");
+}
+
+static void
+dump_machine_state(uc)
+ ucontext_t *uc;
+{
+ const char *dump64 =
+ " ----------------- Register state dump ----------------------\n"
+ "rax = 0x%.16x rbx = 0x%.16x rcx = 0x%.16x rdx = 0x%.16x\n"
+ "rdi = 0x%.16x rsi = 0x%.16x rbp = 0x%.16x rsp = 0x%.16x\n"
+ "r8 = 0x%.16x r9 = 0x%.16x r10 = 0x%.16x r11 = 0x%.16x\n"
+ "r12 = 0x%.16x r13 = 0x%.16x r14 = 0x%.16x r15 = 0x%.16x\n"
+ "rip = 0x%.16x rflags = 0x%.16x cs = 0x%.16x fs = 0x%.16x\n"
+ "gs = 0x%.16x";
+
+ const char *dump32 =
+ " ----------------- Register state dump -------------------\n"
+ "eax = 0x%.8x ebx = 0x%.8x ecx = 0x%.8x edx = 0x%.8x\n"
+ "edi = 0x%.8x esi = 0x%.8x ebp = 0x%.8x esp = 0x%.8x\n"
+ "ss = 0x%.8x eflags = 0x%.8x eip = 0x%.8x cs = 0x%.8x\n"
+ "ds = 0x%.8x es = 0x%.8x fs = 0x%.8x gs = 0x%.8x\n";
+
+#if defined(__LP64__) && defined(__APPLE__)
+ sig_printf(dump64, uc->uc_mcontext->__ss.__rax, uc->uc_mcontext->__ss.__rbx,
+ uc->uc_mcontext->__ss.__rcx, uc->uc_mcontext->__ss.__rdx, uc->uc_mcontext->__ss.__rdi,
+ uc->uc_mcontext->__ss.__rsi, uc->uc_mcontext->__ss.__rbp, uc->uc_mcontext->__ss.__rsp,
+ uc->uc_mcontext->__ss.__r8, uc->uc_mcontext->__ss.__r9, uc->uc_mcontext->__ss.__r10,
+ uc->uc_mcontext->__ss.__r11, uc->uc_mcontext->__ss.__r12, uc->uc_mcontext->__ss.__r13,
+ uc->uc_mcontext->__ss.__r14, uc->uc_mcontext->__ss.__r15, uc->uc_mcontext->__ss.__rip,
+ uc->uc_mcontext->__ss.__rflags, uc->uc_mcontext->__ss.__cs, uc->uc_mcontext->__ss.__fs,
+ uc->uc_mcontext->__ss.__gs);
+#elif !defined(__LP64__) && defined(__APPLE__)
+ sig_printf(dump32, uc->uc_mcontext->__ss.__eax, uc->uc_mcontext->__ss.__ebx,
+ uc->uc_mcontext->__ss.__ecx, uc->uc_mcontext->__ss.__edx,
+ uc->uc_mcontext->__ss.__edi, uc->uc_mcontext->__ss.__esi,
+ uc->uc_mcontext->__ss.__ebp, uc->uc_mcontext->__ss.__esp,
+ uc->uc_mcontext->__ss.__ss, uc->uc_mcontext->__ss.__eflags,
+ uc->uc_mcontext->__ss.__eip, uc->uc_mcontext->__ss.__cs,
+ uc->uc_mcontext->__ss.__ds, uc->uc_mcontext->__ss.__es,
+ uc->uc_mcontext->__ss.__fs, uc->uc_mcontext->__ss.__gs);
+#elif defined(__i386__)
+ sig_printf(dump32, uc->uc_mcontext.gregs[REG_EAX], uc->uc_mcontext.gregs[REG_EBX],
+ uc->uc_mcontext.gregs[REG_ECX], uc->uc_mcontext.gregs[REG_EDX],
+ uc->uc_mcontext.gregs[REG_EDI], uc->uc_mcontext.gregs[REG_ESI],
+ uc->uc_mcontext.gregs[REG_EBP], uc->uc_mcontext.gregs[REG_ESP],
+ uc->uc_mcontext.gregs[REG_SS], uc->uc_mcontext.gregs[REG_EFL],
+ uc->uc_mcontext.gregs[REG_EIP], uc->uc_mcontext.gregs[REG_EIP],
+ uc->uc_mcontext.gregs[REG_DS], uc->uc_mcontext.gregs[REG_ES],
+ uc->uc_mcontext.gregs[REG_FS], uc->uc_mcontext.gregs[REG_FS]);
+#elif defined(__x86_64__)
+ sig_printf(dump64, uc->uc_mcontext.gregs[REG_RAX], uc->uc_mcontext.gregs[REG_RBX],
+ uc->uc_mcontext.gregs[REG_RCX], uc->uc_mcontext.gregs[REG_RDX],
+ uc->uc_mcontext.gregs[REG_RDI], uc->uc_mcontext.gregs[REG_RSI],
+ uc->uc_mcontext.gregs[REG_RBP], uc->uc_mcontext.gregs[REG_RSP],
+ uc->uc_mcontext.gregs[REG_R8], uc->uc_mcontext.gregs[REG_R9],
+ uc->uc_mcontext.gregs[REG_R10], uc->uc_mcontext.gregs[REG_R11],
+ uc->uc_mcontext.gregs[REG_R12], uc->uc_mcontext.gregs[REG_R13],
+ uc->uc_mcontext.gregs[REG_R14], uc->uc_mcontext.gregs[REG_R15],
+ uc->uc_mcontext.gregs[REG_RIP], uc->uc_mcontext.gregs[REG_EFL],
+ uc->uc_mcontext.gregs[REG_CSGSFS]);
+#else
+#endif
+}
+
+static int
+check_guard(caddr_t fault_addr, rb_thread_t th) {
+ if(fault_addr <= (caddr_t)rb_curr_thread->guard &&
+ fault_addr >= (caddr_t)rb_curr_thread->stk_ptr) {
+ return 1;
+ }
+ return 0;
+}
+
#ifdef SIGBUS
+#ifdef POSIX_SIGNAL
+static void sigbus _((int, siginfo_t*, void*));
+static void
+sigbus(sig, ip, context)
+ int sig;
+ siginfo_t *ip;
+ void *context;
+{
+#if defined(HAVE_NATIVETHREAD) && defined(HAVE_NATIVETHREAD_KILL)
+ if (!is_ruby_native_thread() && !rb_trap_accept_nativethreads[sig]) {
+ sigsend_to_ruby_thread(sig);
+ return;
+ }
+#endif
+
+ dump_machine_state(context);
+ if (check_guard((caddr_t)ip->si_addr, rb_curr_thread)) {
+ /* we hit the guard page, print out a warning to help app developers */
+ rb_bug("Thread stack overflow! Try increasing it!");
+ } else {
+ rb_bug("Bus Error");
+ }
+}
+
+#else /* !defined(POSIX_SIGNAL) */
+
static RETSIGTYPE sigbus _((int));
static RETSIGTYPE
sigbus(sig)
@@ -615,8 +748,38 @@ sigbus(sig)
rb_bug("Bus Error");
}
#endif
+#endif
+
#ifdef SIGSEGV
+#ifdef POSIX_SIGNAL
+static void sigsegv _((int, siginfo_t*, void*));
+static void
+sigsegv(sig, ip, context)
+ int sig;
+ siginfo_t *ip;
+ void *context;
+{
+#if defined(HAVE_NATIVETHREAD) && defined(HAVE_NATIVETHREAD_KILL)
+ if (!is_ruby_native_thread() && !rb_trap_accept_nativethreads[sig]) {
+ sigsend_to_ruby_thread(sig);
+ return;
+ }
+#endif
+
+ extern int ruby_gc_stress;
+ ruby_gc_stress = 0;
+ dump_machine_state(context);
+ if (check_guard((caddr_t)ip->si_addr, rb_curr_thread)) {
+ /* we hit the guard page, print out a warning to help app developers */
+ rb_bug("Thread stack overflow! Try increasing it!");
+ } else {
+ rb_bug("Segmentation fault");
+ }
+}
+
+#else /* !defined(POSIX_SIGNAL) */
+
static RETSIGTYPE sigsegv _((int));
static RETSIGTYPE
sigsegv(sig)
@@ -634,6 +797,7 @@ sigsegv(sig)
rb_bug("Segmentation fault");
}
#endif
+#endif
#ifdef SIGPIPE
static RETSIGTYPE sigpipe _((int));
@@ -705,7 +869,8 @@ static VALUE
trap(arg)
struct trap_arg *arg;
{
- sighandler_t func, oldfunc;
+ sighandler_t oldfunc;
+ void *func;
VALUE command, oldcmd;
int sig = -1;
const char *s;
@@ -952,6 +1117,20 @@ sig_list()
}
static void
+create_sigstack()
+{
+ stack_t ss;
+ ss.ss_size = SIGSTKSZ;
+ ss.ss_sp = malloc(ss.ss_size);
+ ss.ss_flags = 0;
+ if (sigaltstack(&ss, NULL) < 0) {
+ free(ss.ss_sp);
+ fprintf(stderr, "Couldn't create signal stack! Error %d: %s\n", errno, strerror(errno));
+ exit(1);
+ }
+}
+
+static void
install_sighandler(signum, handler)
int signum;
sighandler_t handler;
@@ -960,7 +1139,7 @@ install_sighandler(signum, handler)
old = ruby_signal(signum, handler);
if (old != SIG_DFL) {
- ruby_signal(signum, old);
+ ruby_signal(signum, old);
}
}
@@ -1089,6 +1268,8 @@ Init_signal()
rb_alias(rb_eSignal, rb_intern("signm"), rb_intern("message"));
rb_define_method(rb_eInterrupt, "initialize", interrupt_init, -1);
+ create_sigstack();
+
install_sighandler(SIGINT, sighandler);
#ifdef SIGHUP
install_sighandler(SIGHUP, sighandler);