2006-12-31 18:02:22 +03:00
|
|
|
/**********************************************************************
|
|
|
|
|
|
|
|
thread.c -
|
|
|
|
|
|
|
|
$Author$
|
|
|
|
|
* blockinlining.c, compile.c, compile.h, debug.c, debug.h,
id.c, insnhelper.h, insns.def, thread.c, thread_pthread.ci,
thread_pthread.h, thread_win32.ci, thread_win32.h, vm.h,
vm_dump.c, vm_evalbody.ci, vm_opts.h: fix comments and
copyright year.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@13920 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-11-14 01:13:04 +03:00
|
|
|
Copyright (C) 2004-2007 Koichi Sasada
|
2006-12-31 18:02:22 +03:00
|
|
|
|
|
|
|
**********************************************************************/
|
|
|
|
|
|
|
|
/*
|
2009-11-03 20:46:28 +03:00
|
|
|
YARV Thread Design
|
2006-12-31 18:02:22 +03:00
|
|
|
|
|
|
|
model 1: Userlevel Thread
|
|
|
|
Same as traditional ruby thread.
|
|
|
|
|
2009-01-12 04:43:23 +03:00
|
|
|
model 2: Native Thread with Global VM lock
|
2006-12-31 18:02:22 +03:00
|
|
|
Using pthread (or Windows thread) and Ruby threads run concurrent.
|
|
|
|
|
|
|
|
model 3: Native Thread with fine grain lock
|
|
|
|
Using pthread and Ruby threads run concurrent or parallel.
|
|
|
|
|
2017-04-02 04:14:14 +03:00
|
|
|
model 4: M:N User:Native threads with Global VM lock
|
|
|
|
Combination of model 1 and 2
|
|
|
|
|
|
|
|
model 5: M:N User:Native thread with fine grain lock
|
|
|
|
Combination of model 1 and 3
|
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
------------------------------------------------------------------------
|
|
|
|
|
|
|
|
model 2:
|
2009-01-12 04:43:23 +03:00
|
|
|
A thread has mutex (GVL: Global VM Lock or Giant VM Lock) can run.
|
|
|
|
When thread scheduling, running thread release GVL. If running thread
|
2006-12-31 18:02:22 +03:00
|
|
|
try blocking operation, this thread must release GVL and another
|
|
|
|
thread can continue this flow. After blocking operation, thread
|
* blockinlining.c, compile.c, compile.h, error.c, eval.c,
eval_intern.h, eval_jump.h, eval_load.c, eval_method.h,
eval_safe.h, gc.c, insnhelper.h, insns.def, iseq.c, proc.c,
process.c, signal.c, thread.c, thread_pthread.ci, thread_win32.ci,
vm.c, vm.h, vm_dump.c, vm_evalbody.ci, vm_macro.def,
yarv.h, yarvcore.h, yarvcore.c: change type and macro names:
* yarv_*_t -> rb_*_t
* yarv_*_struct -> rb_*_struct
* yarv_tag -> rb_vm_tag
* YARV_* -> RUBY_VM_*
* proc.c, vm.c: move functions about env object creation
from proc.c to vm.c.
* proc.c, yarvcore.c: fix rb_cVM initialization place.
* inits.c: change Init_ISeq() order (after Init_VM).
* ruby.h, proc.c: change declaration place of rb_cEnv
from proc.c to ruby.c.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@11651 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-02-06 22:00:03 +03:00
|
|
|
must check interrupt (RUBY_VM_CHECK_INTS).
|
2006-12-31 18:02:22 +03:00
|
|
|
|
|
|
|
Every VM can run parallel.
|
|
|
|
|
|
|
|
Ruby threads are scheduled by OS thread scheduler.
|
|
|
|
|
|
|
|
------------------------------------------------------------------------
|
|
|
|
|
|
|
|
model 3:
|
|
|
|
Every threads run concurrent or parallel and to access shared object
|
|
|
|
exclusive access control is needed. For example, to access String
|
|
|
|
object or Array object, fine grain lock must be locked every time.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
2013-03-16 09:07:28 +04:00
|
|
|
/*
|
|
|
|
* FD_SET, FD_CLR and FD_ISSET have a small sanity check when using glibc
|
|
|
|
* 2.15 or later and set _FORTIFY_SOURCE > 0.
|
|
|
|
* However, the implementation is wrong. Even though Linux's select(2)
|
2013-04-27 14:33:44 +04:00
|
|
|
* supports large fd size (>FD_SETSIZE), it wrongly assumes fd is always
|
2013-03-16 09:07:28 +04:00
|
|
|
* less than FD_SETSIZE (i.e. 1024). And then when enabling HAVE_RB_FD_INIT,
|
|
|
|
* it doesn't work correctly and makes program abort. Therefore we need to
|
2014-02-26 22:57:09 +04:00
|
|
|
* disable FORTIFY_SOURCE until glibc fixes it.
|
2013-03-16 09:07:28 +04:00
|
|
|
*/
|
|
|
|
#undef _FORTIFY_SOURCE
|
|
|
|
#undef __USE_FORTIFY_LEVEL
|
|
|
|
#define __USE_FORTIFY_LEVEL 0
|
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
/* for model 2 */
|
|
|
|
|
2018-01-09 15:35:12 +03:00
|
|
|
#include "ruby/config.h"
|
|
|
|
#include "ruby/io.h"
|
2006-12-31 18:02:22 +03:00
|
|
|
#include "eval_intern.h"
|
2007-02-05 15:21:01 +03:00
|
|
|
#include "gc.h"
|
2013-03-31 01:08:36 +04:00
|
|
|
#include "timev.h"
|
2012-07-10 17:57:11 +04:00
|
|
|
#include "ruby/thread.h"
|
2014-05-14 14:55:38 +04:00
|
|
|
#include "ruby/thread_native.h"
|
2017-09-14 04:55:30 +03:00
|
|
|
#include "ruby/debug.h"
|
2013-07-02 12:22:30 +04:00
|
|
|
#include "internal.h"
|
2017-12-05 10:16:42 +03:00
|
|
|
#include "iseq.h"
|
2018-02-19 00:03:13 +03:00
|
|
|
#include "vm_core.h"
|
2018-10-20 09:53:00 +03:00
|
|
|
#include "mjit.h"
|
2018-08-25 09:58:35 +03:00
|
|
|
#include "hrtime.h"
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2008-08-13 11:53:35 +04:00
|
|
|
#ifndef USE_NATIVE_THREAD_PRIORITY
|
|
|
|
#define USE_NATIVE_THREAD_PRIORITY 0
|
|
|
|
#define RUBY_THREAD_PRIORITY_MAX 3
|
|
|
|
#define RUBY_THREAD_PRIORITY_MIN -3
|
|
|
|
#endif
|
|
|
|
|
2007-02-24 12:39:25 +03:00
|
|
|
#ifndef THREAD_DEBUG
|
2006-12-31 18:02:22 +03:00
|
|
|
#define THREAD_DEBUG 0
|
2007-02-24 12:39:25 +03:00
|
|
|
#endif
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2015-11-07 05:40:33 +03:00
|
|
|
static VALUE rb_cThreadShield;
|
2007-05-03 17:19:11 +04:00
|
|
|
|
2012-12-23 14:18:58 +04:00
|
|
|
static VALUE sym_immediate;
|
|
|
|
static VALUE sym_on_blocking;
|
|
|
|
static VALUE sym_never;
|
2013-05-02 11:55:50 +04:00
|
|
|
static ID id_locals;
|
2012-12-23 14:18:58 +04:00
|
|
|
|
2018-06-13 13:00:46 +03:00
|
|
|
enum SLEEP_FLAGS {
|
|
|
|
SLEEP_DEADLOCKABLE = 0x1,
|
2018-08-19 03:01:03 +03:00
|
|
|
SLEEP_SPURIOUS_CHECK = 0x2
|
2018-06-13 13:00:46 +03:00
|
|
|
};
|
|
|
|
|
2018-08-25 09:58:35 +03:00
|
|
|
static void sleep_hrtime(rb_thread_t *, rb_hrtime_t, unsigned int fl);
|
2018-06-13 13:00:46 +03:00
|
|
|
static void sleep_forever(rb_thread_t *th, unsigned int fl);
|
2017-01-31 09:39:01 +03:00
|
|
|
static void rb_thread_sleep_deadly_allow_spurious_wakeup(void);
|
2009-06-08 20:14:06 +04:00
|
|
|
static int rb_threadptr_dead(rb_thread_t *th);
|
2008-06-12 17:01:38 +04:00
|
|
|
static void rb_check_deadlock(rb_vm_t *vm);
|
2017-11-06 10:44:28 +03:00
|
|
|
static int rb_threadptr_pending_interrupt_empty_p(const rb_thread_t *th);
|
2017-12-15 06:07:10 +03:00
|
|
|
static const char *thread_status_name(rb_thread_t *th, int detail);
|
2018-08-25 09:58:35 +03:00
|
|
|
static int hrtime_update_expire(rb_hrtime_t *, const rb_hrtime_t);
|
2018-08-14 00:34:20 +03:00
|
|
|
NORETURN(static void async_bug_fd(const char *mesg, int errno_arg, int fd));
|
|
|
|
static int consume_communication_pipe(int fd);
|
|
|
|
static int check_signals_nogvl(rb_thread_t *, int sigwait_fd);
|
|
|
|
void rb_sigwait_fd_migrate(rb_vm_t *); /* process.c */
|
2008-06-12 17:01:38 +04:00
|
|
|
|
2011-05-18 17:36:46 +04:00
|
|
|
#define eKillSignal INT2FIX(0)
|
|
|
|
#define eTerminateSignal INT2FIX(1)
|
2008-11-08 18:31:05 +03:00
|
|
|
static volatile int system_working = 1;
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2017-05-20 12:47:14 +03:00
|
|
|
struct waiting_fd {
|
|
|
|
struct list_node wfd_node; /* <=> vm.waiting_fds */
|
|
|
|
rb_thread_t *th;
|
|
|
|
int fd;
|
|
|
|
};
|
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
inline static void
|
2008-05-08 01:27:34 +04:00
|
|
|
st_delete_wrap(st_table *table, st_data_t key)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2008-05-08 01:27:34 +04:00
|
|
|
st_delete(table, &key, 0);
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/********************************************************************************/
|
|
|
|
|
|
|
|
#define THREAD_SYSTEM_DEPENDENT_IMPLEMENTATION
|
|
|
|
|
2008-09-26 04:47:18 +04:00
|
|
|
struct rb_blocking_region_buffer {
|
|
|
|
enum rb_thread_status prev_status;
|
|
|
|
};
|
|
|
|
|
2017-06-12 10:49:33 +03:00
|
|
|
static int unblock_function_set(rb_thread_t *th, rb_unblock_function_t *func, void *arg, int fail_if_interrupted);
|
|
|
|
static void unblock_function_clear(rb_thread_t *th);
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2012-11-28 17:01:25 +04:00
|
|
|
static inline int blocking_region_begin(rb_thread_t *th, struct rb_blocking_region_buffer *region,
|
|
|
|
rb_unblock_function_t *ubf, void *arg, int fail_if_interrupted);
|
2009-02-12 10:21:42 +03:00
|
|
|
static inline void blocking_region_end(rb_thread_t *th, struct rb_blocking_region_buffer *region);
|
2008-09-26 04:47:18 +04:00
|
|
|
|
2012-12-02 14:28:27 +04:00
|
|
|
#ifdef __ia64
|
|
|
|
#define RB_GC_SAVE_MACHINE_REGISTER_STACK(th) \
|
2018-01-30 08:43:42 +03:00
|
|
|
do{(th)->ec->machine.register_stack_end = rb_ia64_bsp();}while(0)
|
2012-12-02 14:28:27 +04:00
|
|
|
#else
|
|
|
|
#define RB_GC_SAVE_MACHINE_REGISTER_STACK(th)
|
|
|
|
#endif
|
|
|
|
#define RB_GC_SAVE_MACHINE_CONTEXT(th) \
|
|
|
|
do { \
|
|
|
|
FLUSH_REGISTER_WINDOWS; \
|
|
|
|
RB_GC_SAVE_MACHINE_REGISTER_STACK(th); \
|
2017-10-26 11:32:49 +03:00
|
|
|
setjmp((th)->ec->machine.regs); \
|
|
|
|
SET_MACHINE_STACK_END(&(th)->ec->machine.stack_end); \
|
2012-12-02 14:28:27 +04:00
|
|
|
} while (0)
|
2009-11-09 14:18:17 +03:00
|
|
|
|
2018-05-17 00:54:42 +03:00
|
|
|
#define GVL_UNLOCK_BEGIN(th) do { \
|
|
|
|
RB_GC_SAVE_MACHINE_CONTEXT(th); \
|
|
|
|
gvl_release(th->vm);
|
|
|
|
|
|
|
|
#define GVL_UNLOCK_END(th) \
|
|
|
|
gvl_acquire(th->vm, th); \
|
|
|
|
rb_thread_set_current(th); \
|
2007-02-08 14:51:40 +03:00
|
|
|
} while(0)
|
|
|
|
|
2012-12-23 09:35:15 +04:00
|
|
|
#ifdef __GNUC__
|
2014-10-10 18:31:37 +04:00
|
|
|
#ifdef HAVE_BUILTIN___BUILTIN_CHOOSE_EXPR_CONSTANT_P
|
2014-10-10 16:45:21 +04:00
|
|
|
#define only_if_constant(expr, notconst) __builtin_choose_expr(__builtin_constant_p(expr), (expr), (notconst))
|
|
|
|
#else
|
2012-12-23 09:35:15 +04:00
|
|
|
#define only_if_constant(expr, notconst) (__builtin_constant_p(expr) ? (expr) : (notconst))
|
2014-10-10 16:45:21 +04:00
|
|
|
#endif
|
2012-12-23 09:35:15 +04:00
|
|
|
#else
|
|
|
|
#define only_if_constant(expr, notconst) notconst
|
|
|
|
#endif
|
2018-05-17 00:54:42 +03:00
|
|
|
#define BLOCKING_REGION(th, exec, ubf, ubfarg, fail_if_interrupted) do { \
|
2008-09-26 04:47:18 +04:00
|
|
|
struct rb_blocking_region_buffer __region; \
|
2018-05-17 00:54:42 +03:00
|
|
|
if (blocking_region_begin(th, &__region, (ubf), (ubfarg), fail_if_interrupted) || \
|
2012-12-23 09:35:15 +04:00
|
|
|
/* always return true unless fail_if_interrupted */ \
|
|
|
|
!only_if_constant(fail_if_interrupted, TRUE)) { \
|
2012-11-28 17:01:25 +04:00
|
|
|
exec; \
|
2018-05-17 00:54:42 +03:00
|
|
|
blocking_region_end(th, &__region); \
|
2012-11-28 17:01:25 +04:00
|
|
|
}; \
|
2006-12-31 18:02:22 +03:00
|
|
|
} while(0)
|
|
|
|
|
2018-08-18 12:07:36 +03:00
|
|
|
/*
|
|
|
|
* returns true if this thread was spuriously interrupted, false otherwise
|
|
|
|
* (e.g. hit by Thread#run or ran a Ruby-level Signal.trap handler)
|
|
|
|
*/
|
2017-11-06 10:44:28 +03:00
|
|
|
#define RUBY_VM_CHECK_INTS_BLOCKING(ec) vm_check_ints_blocking(ec)
|
2018-08-18 12:07:36 +03:00
|
|
|
static inline int
|
2017-11-06 10:44:28 +03:00
|
|
|
vm_check_ints_blocking(rb_execution_context_t *ec)
|
2015-07-17 10:28:36 +03:00
|
|
|
{
|
2017-11-06 10:44:28 +03:00
|
|
|
rb_thread_t *th = rb_ec_thread_ptr(ec);
|
|
|
|
|
2015-07-18 15:29:21 +03:00
|
|
|
if (LIKELY(rb_threadptr_pending_interrupt_empty_p(th))) {
|
2018-08-18 12:07:36 +03:00
|
|
|
if (LIKELY(!RUBY_VM_INTERRUPTED_ANY(ec))) return FALSE;
|
2015-07-18 15:29:21 +03:00
|
|
|
}
|
|
|
|
else {
|
2015-07-17 10:28:36 +03:00
|
|
|
th->pending_interrupt_queue_checked = 0;
|
2017-11-06 10:44:28 +03:00
|
|
|
RUBY_VM_SET_INTERRUPT(ec);
|
2015-07-17 10:28:36 +03:00
|
|
|
}
|
2018-08-18 12:07:36 +03:00
|
|
|
return rb_threadptr_execute_interrupts(th, 1);
|
2015-07-17 10:28:36 +03:00
|
|
|
}
|
|
|
|
|
2015-07-20 03:11:40 +03:00
|
|
|
static int
|
2018-05-01 02:12:03 +03:00
|
|
|
vm_living_thread_num(const rb_vm_t *vm)
|
2015-07-20 03:11:40 +03:00
|
|
|
{
|
2018-01-28 00:04:36 +03:00
|
|
|
return vm->living_thread_num;
|
2015-07-20 03:11:40 +03:00
|
|
|
}
|
|
|
|
|
2018-02-04 04:31:48 +03:00
|
|
|
/*
|
|
|
|
* poll() is supported by many OSes, but so far Linux is the only
|
|
|
|
* one we know of that supports using poll() in all places select()
|
|
|
|
* would work.
|
|
|
|
*/
|
2018-05-15 06:49:21 +03:00
|
|
|
#if defined(HAVE_POLL)
|
|
|
|
# if defined(__linux__)
|
|
|
|
# define USE_POLL
|
|
|
|
# endif
|
|
|
|
# if defined(__FreeBSD_version) && __FreeBSD_version >= 1100000
|
|
|
|
# define USE_POLL
|
|
|
|
/* FreeBSD does not set POLLOUT when POLLHUP happens */
|
|
|
|
# define POLLERR_SET (POLLHUP | POLLERR)
|
|
|
|
# endif
|
2018-02-04 04:31:48 +03:00
|
|
|
#endif
|
|
|
|
|
2018-06-16 11:27:56 +03:00
|
|
|
static void
|
2018-08-25 09:58:35 +03:00
|
|
|
timeout_prepare(rb_hrtime_t **to, rb_hrtime_t *rel, rb_hrtime_t *end,
|
|
|
|
const struct timeval *timeout)
|
2018-06-16 11:27:56 +03:00
|
|
|
{
|
|
|
|
if (timeout) {
|
2018-08-25 09:58:35 +03:00
|
|
|
*rel = rb_timeval2hrtime(timeout);
|
|
|
|
*end = rb_hrtime_add(rb_hrtime_now(), *rel);
|
|
|
|
*to = rel;
|
2018-06-16 11:27:56 +03:00
|
|
|
}
|
|
|
|
else {
|
2018-08-25 09:58:35 +03:00
|
|
|
*to = 0;
|
2018-06-16 11:27:56 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
#if THREAD_DEBUG
|
2008-07-09 15:18:52 +04:00
|
|
|
#ifdef HAVE_VA_ARGS_MACRO
|
|
|
|
void rb_thread_debug(const char *file, int line, const char *fmt, ...);
|
2017-04-24 07:20:02 +03:00
|
|
|
#define thread_debug(...) rb_thread_debug(__FILE__, __LINE__, __VA_ARGS__)
|
2008-07-09 15:18:52 +04:00
|
|
|
#define POSITION_FORMAT "%s:%d:"
|
|
|
|
#define POSITION_ARGS ,file, line
|
|
|
|
#else
|
2007-02-24 12:39:25 +03:00
|
|
|
void rb_thread_debug(const char *fmt, ...);
|
2008-07-09 15:18:52 +04:00
|
|
|
#define thread_debug rb_thread_debug
|
|
|
|
#define POSITION_FORMAT
|
|
|
|
#define POSITION_ARGS
|
|
|
|
#endif
|
2007-02-24 12:39:25 +03:00
|
|
|
|
2014-06-11 12:38:09 +04:00
|
|
|
# ifdef NON_SCALAR_THREAD_ID
|
2017-04-21 03:53:26 +03:00
|
|
|
#define fill_thread_id_string ruby_fill_thread_id_string
|
|
|
|
const char *
|
|
|
|
ruby_fill_thread_id_string(rb_nativethread_id_t thid, rb_thread_id_string_t buf)
|
2014-06-11 12:38:09 +04:00
|
|
|
{
|
|
|
|
extern const char ruby_digitmap[];
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
buf[0] = '0';
|
|
|
|
buf[1] = 'x';
|
|
|
|
for (i = 0; i < sizeof(thid); i++) {
|
|
|
|
# ifdef LITTLE_ENDIAN
|
|
|
|
size_t j = sizeof(thid) - i - 1;
|
|
|
|
# else
|
|
|
|
size_t j = i;
|
|
|
|
# endif
|
|
|
|
unsigned char c = (unsigned char)((char *)&thid)[j];
|
|
|
|
buf[2 + i * 2] = ruby_digitmap[(c >> 4) & 0xf];
|
|
|
|
buf[3 + i * 2] = ruby_digitmap[c & 0xf];
|
|
|
|
}
|
|
|
|
buf[sizeof(rb_thread_id_string_t)-1] = '\0';
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
# define fill_thread_id_str(th) fill_thread_id_string((th)->thread_id, (th)->thread_id_string)
|
|
|
|
# define thread_id_str(th) ((th)->thread_id_string)
|
|
|
|
# define PRI_THREAD_ID "s"
|
|
|
|
# endif
|
|
|
|
|
2007-02-24 12:39:25 +03:00
|
|
|
# if THREAD_DEBUG < 0
|
|
|
|
static int rb_thread_debug_enabled;
|
|
|
|
|
2009-09-17 08:51:33 +04:00
|
|
|
/*
|
|
|
|
* call-seq:
|
2010-05-18 01:07:33 +04:00
|
|
|
* Thread.DEBUG -> num
|
2009-09-17 08:51:33 +04:00
|
|
|
*
|
|
|
|
* Returns the thread debug level. Available only if compiled with
|
|
|
|
* THREAD_DEBUG=-1.
|
|
|
|
*/
|
|
|
|
|
2007-02-24 12:39:25 +03:00
|
|
|
static VALUE
|
|
|
|
rb_thread_s_debug(void)
|
|
|
|
{
|
|
|
|
return INT2NUM(rb_thread_debug_enabled);
|
|
|
|
}
|
|
|
|
|
2009-09-17 08:51:33 +04:00
|
|
|
/*
|
|
|
|
* call-seq:
|
|
|
|
* Thread.DEBUG = num
|
|
|
|
*
|
|
|
|
* Sets the thread debug level. Available only if compiled with
|
|
|
|
* THREAD_DEBUG=-1.
|
|
|
|
*/
|
|
|
|
|
2007-02-24 12:39:25 +03:00
|
|
|
static VALUE
|
|
|
|
rb_thread_s_debug_set(VALUE self, VALUE val)
|
|
|
|
{
|
2009-09-17 08:50:52 +04:00
|
|
|
rb_thread_debug_enabled = RTEST(val) ? NUM2INT(val) : 0;
|
2007-02-24 12:39:25 +03:00
|
|
|
return val;
|
|
|
|
}
|
|
|
|
# else
|
|
|
|
# define rb_thread_debug_enabled THREAD_DEBUG
|
|
|
|
# endif
|
2006-12-31 18:02:22 +03:00
|
|
|
#else
|
|
|
|
#define thread_debug if(0)printf
|
|
|
|
#endif
|
|
|
|
|
2014-06-11 12:38:09 +04:00
|
|
|
#ifndef fill_thread_id_str
|
2018-11-07 15:48:36 +03:00
|
|
|
# define fill_thread_id_string(thid, buf) ((void *)(uintptr_t)(thid))
|
2014-06-11 12:38:09 +04:00
|
|
|
# define fill_thread_id_str(th) (void)0
|
2018-11-07 15:48:36 +03:00
|
|
|
# define thread_id_str(th) ((void *)(uintptr_t)(th)->thread_id)
|
2014-06-11 12:38:09 +04:00
|
|
|
# define PRI_THREAD_ID "p"
|
|
|
|
#endif
|
|
|
|
|
2007-07-12 05:19:18 +04:00
|
|
|
#ifndef __ia64
|
|
|
|
#define thread_start_func_2(th, st, rst) thread_start_func_2(th, st)
|
|
|
|
#endif
|
|
|
|
NOINLINE(static int thread_start_func_2(rb_thread_t *th, VALUE *stack_start,
|
|
|
|
VALUE *register_stack_start));
|
2018-08-14 00:34:20 +03:00
|
|
|
static void timer_thread_function(void);
|
|
|
|
void ruby_sigchld_handler(rb_vm_t *); /* signal.c */
|
|
|
|
|
|
|
|
static void
|
|
|
|
ubf_sigwait(void *ignore)
|
|
|
|
{
|
|
|
|
rb_thread_wakeup_timer_thread(0);
|
|
|
|
}
|
2007-07-12 05:19:18 +04:00
|
|
|
|
2007-02-08 14:51:40 +03:00
|
|
|
#if defined(_WIN32)
|
2007-12-20 12:29:46 +03:00
|
|
|
#include "thread_win32.c"
|
2006-12-31 18:02:22 +03:00
|
|
|
|
|
|
|
#define DEBUG_OUT() \
|
|
|
|
WaitForSingleObject(&debug_mutex, INFINITE); \
|
2014-06-11 12:37:29 +04:00
|
|
|
printf(POSITION_FORMAT"%#lx - %s" POSITION_ARGS, GetCurrentThreadId(), buf); \
|
2007-02-24 12:39:25 +03:00
|
|
|
fflush(stdout); \
|
2006-12-31 18:02:22 +03:00
|
|
|
ReleaseMutex(&debug_mutex);
|
|
|
|
|
|
|
|
#elif defined(HAVE_PTHREAD_H)
|
2007-12-20 12:29:46 +03:00
|
|
|
#include "thread_pthread.c"
|
2006-12-31 18:02:22 +03:00
|
|
|
|
|
|
|
#define DEBUG_OUT() \
|
|
|
|
pthread_mutex_lock(&debug_mutex); \
|
2014-06-11 12:38:09 +04:00
|
|
|
printf(POSITION_FORMAT"%"PRI_THREAD_ID" - %s" POSITION_ARGS, \
|
|
|
|
fill_thread_id_string(pthread_self(), thread_id_string), buf); \
|
2007-02-24 12:39:25 +03:00
|
|
|
fflush(stdout); \
|
2006-12-31 18:02:22 +03:00
|
|
|
pthread_mutex_unlock(&debug_mutex);
|
|
|
|
|
|
|
|
#else
|
|
|
|
#error "unsupported thread type"
|
|
|
|
#endif
|
|
|
|
|
2018-08-14 00:34:20 +03:00
|
|
|
/*
|
|
|
|
* TODO: somebody with win32 knowledge should be able to get rid of
|
|
|
|
* timer-thread by busy-waiting on signals. And it should be possible
|
|
|
|
* to make the GVL in thread_pthread.c be platform-independent.
|
|
|
|
*/
|
|
|
|
#ifndef BUSY_WAIT_SIGNALS
|
|
|
|
# define BUSY_WAIT_SIGNALS (0)
|
|
|
|
#endif
|
|
|
|
|
2018-08-24 22:19:01 +03:00
|
|
|
#ifndef USE_EVENTFD
|
|
|
|
# define USE_EVENTFD (0)
|
|
|
|
#endif
|
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
#if THREAD_DEBUG
|
|
|
|
static int debug_mutex_initialized = 1;
|
2013-07-23 13:53:14 +04:00
|
|
|
static rb_nativethread_lock_t debug_mutex;
|
2006-12-31 18:02:22 +03:00
|
|
|
|
|
|
|
void
|
2008-07-09 15:18:52 +04:00
|
|
|
rb_thread_debug(
|
|
|
|
#ifdef HAVE_VA_ARGS_MACRO
|
|
|
|
const char *file, int line,
|
|
|
|
#endif
|
|
|
|
const char *fmt, ...)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
|
|
|
va_list args;
|
|
|
|
char buf[BUFSIZ];
|
2014-06-11 12:38:09 +04:00
|
|
|
#ifdef NON_SCALAR_THREAD_ID
|
|
|
|
rb_thread_id_string_t thread_id_string;
|
|
|
|
#endif
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2007-02-24 12:39:25 +03:00
|
|
|
if (!rb_thread_debug_enabled) return;
|
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
if (debug_mutex_initialized == 1) {
|
|
|
|
debug_mutex_initialized = 0;
|
mjit.c: merge MJIT infrastructure
that allows to JIT-compile Ruby methods by generating C code and
using C compiler. See the first comment of mjit.c to know what this
file does.
mjit.c is authored by Vladimir Makarov <vmakarov@redhat.com>.
After he invented great method JIT infrastructure for MRI as MJIT,
Lars Kanis <lars@greiz-reinsdorf.de> sent the patch to support MinGW
in MJIT. In addition to merging it, I ported pthread to Windows native
threads. Now this MJIT infrastructure can be compiled on Visual Studio.
This commit simplifies mjit.c to decrease code at initial merge. For
example, this commit does not provide multiple JIT threads support.
We can resurrect them later if we really want them, but I wanted to minimize
diff to make it easier to review this patch.
`/tmp/_mjitXXX` file is renamed to `/tmp/_ruby_mjitXXX` because non-Ruby
developers may not know the name "mjit" and the file name should make
sure it's from Ruby and not from some harmful programs. TODO: it may be
better to store this to some temporary directory which Ruby is already using
by Tempfile, if it's not bad for performance.
mjit.h: New. It has `mjit_exec` interface similar to `vm_exec`, which is
for triggering MJIT. This drops interface for AOT compared to the original
MJIT.
Makefile.in: define macros to let MJIT know the path of MJIT header.
Probably we can refactor this to reduce the number of macros (TODO).
win32/Makefile.sub: ditto.
common.mk: compile mjit.o and mjit_compile.o. Unlike original MJIT, this
commit separates MJIT infrastructure and JIT compiler code as independent
object files. As initial patch is NOT going to have ultra-fast JIT compiler,
it's likely to replace JIT compiler, e.g. original MJIT's compiler or some
future JIT impelementations which are not public now.
inits.c: define MJIT module. This is added because `MJIT.enabled?` was
necessary for testing.
test/lib/zombie_hunter.rb: skip if `MJIT.enabled?`. Obviously this
wouldn't work with current code when JIT is enabled.
test/ruby/test_io.rb: skip this too. This would make no sense with MJIT.
ruby.c: define MJIT CLI options. As major difference from original MJIT,
"-j:l"/"--jit:llvm" are renamed to "--jit-cc" because I want to support
not only gcc/clang but also cl.exe (Visual Studio) in the future. But it
takes only "--jit-cc=gcc", "--jit-cc=clang" for now. And only long "--jit"
options are allowed since some Ruby committers preferred it at Ruby
developers Meeting on January, and some of options are renamed.
This file also triggers to initialize MJIT thread and variables.
eval.c: finalize MJIT worker thread and variables.
test/ruby/test_rubyoptions.rb: fix number of CLI options for --jit.
thread_pthread.c: change for pthread abstraction in MJIT. Prefix rb_ for
functions which are used by other files.
thread_win32.c: ditto, for Windows. Those pthread porting is one of major
works that YARV-MJIT created, which is my fork of MJIT, in Feature 14235.
thread.c: follow rb_ prefix changes
vm.c: trigger MJIT call on VM invocation. Also trigger `mjit_mark` to avoid
SEGV by race between JIT and GC of ISeq. The improvement was provided by
wanabe <s.wanabe@gmail.com>.
In JIT compiler I created and am going to add in my next commit, I found
that having `mjit_exec` after `vm_loop_start:` is harmful because the
JIT-ed function doesn't proceed other ISeqs on RESTORE_REGS of leave insn.
Executing non-FINISH frame is unexpected for my JIT compiler and
`exception_handler` triggers executions of such ISeqs. So `mjit_exec`
here should be executed only when it directly comes from `vm_exec` call.
`RubyVM::MJIT` module and `.enabled?` method is added so that we can skip
some tests which don't expect JIT threads or compiler file descriptors.
vm_insnhelper.h: trigger MJIT on method calls during VM execution.
vm_core.h: add fields required for mjit.c. `bp` must be `cfp[6]` because
rb_control_frame_struct is likely to be casted to another struct. The
last position is the safest place to add the new field.
vm_insnhelper.c: save initial value of cfp->ep as cfp->bp. This is an
optimization which are done in both MJIT and YARV-MJIT. So this change
is added in this commit. Calculating bp from ep is a little heavy work,
so bp is kind of cache for it.
iseq.c: notify ISeq GC to MJIT. We should know which iseq in MJIT queue
is GCed to avoid SEGV. TODO: unload some GCed units in some safe way.
gc.c: add hooks so that MJIT can wait GC, and vice versa. Simultaneous
JIT and GC executions may cause SEGV and so we should synchronize them.
cont.c: save continuation information in MJIT worker. As MJIT shouldn't
unload JIT-ed code which is being used, MJIT wants to know full list of
saved execution contexts for continuation and detect ISeqs in use.
mjit_compile.c: added empty JIT compiler so that you can reuse this commit
to build your own JIT compiler. This commit tries to compile ISeqs but
all of them are considered as not supported in this commit. So you can't
use JIT compiler in this commit yet while we added --jit option now.
Patch author: Vladimir Makarov <vmakarov@redhat.com>.
Contributors:
Takashi Kokubun <takashikkbn@gmail.com>.
wanabe <s.wanabe@gmail.com>.
Lars Kanis <lars@greiz-reinsdorf.de>.
Part of Feature 12589 and 14235.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@62189 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-02-04 09:58:09 +03:00
|
|
|
rb_native_mutex_initialize(&debug_mutex);
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
va_start(args, fmt);
|
|
|
|
vsnprintf(buf, BUFSIZ, fmt, args);
|
|
|
|
va_end(args);
|
|
|
|
|
|
|
|
DEBUG_OUT();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2015-09-01 12:08:42 +03:00
|
|
|
#include "thread_sync.c"
|
2015-08-22 02:36:23 +03:00
|
|
|
|
2010-11-27 23:15:59 +03:00
|
|
|
void
|
|
|
|
rb_vm_gvl_destroy(rb_vm_t *vm)
|
|
|
|
{
|
|
|
|
gvl_release(vm);
|
|
|
|
gvl_destroy(vm);
|
2018-07-05 06:02:33 +03:00
|
|
|
if (0) {
|
|
|
|
/* may be held by running threads */
|
|
|
|
rb_native_mutex_destroy(&vm->waitpid_lock);
|
2018-11-30 06:56:29 +03:00
|
|
|
rb_native_mutex_destroy(&vm->workqueue_lock);
|
2018-07-05 06:02:33 +03:00
|
|
|
}
|
2010-11-27 23:15:59 +03:00
|
|
|
}
|
|
|
|
|
2010-01-23 23:18:36 +03:00
|
|
|
void
|
2013-07-23 13:53:14 +04:00
|
|
|
rb_nativethread_lock_initialize(rb_nativethread_lock_t *lock)
|
2010-01-23 23:18:36 +03:00
|
|
|
{
|
mjit.c: merge MJIT infrastructure
that allows to JIT-compile Ruby methods by generating C code and
using C compiler. See the first comment of mjit.c to know what this
file does.
mjit.c is authored by Vladimir Makarov <vmakarov@redhat.com>.
After he invented great method JIT infrastructure for MRI as MJIT,
Lars Kanis <lars@greiz-reinsdorf.de> sent the patch to support MinGW
in MJIT. In addition to merging it, I ported pthread to Windows native
threads. Now this MJIT infrastructure can be compiled on Visual Studio.
This commit simplifies mjit.c to decrease code at initial merge. For
example, this commit does not provide multiple JIT threads support.
We can resurrect them later if we really want them, but I wanted to minimize
diff to make it easier to review this patch.
`/tmp/_mjitXXX` file is renamed to `/tmp/_ruby_mjitXXX` because non-Ruby
developers may not know the name "mjit" and the file name should make
sure it's from Ruby and not from some harmful programs. TODO: it may be
better to store this to some temporary directory which Ruby is already using
by Tempfile, if it's not bad for performance.
mjit.h: New. It has `mjit_exec` interface similar to `vm_exec`, which is
for triggering MJIT. This drops interface for AOT compared to the original
MJIT.
Makefile.in: define macros to let MJIT know the path of MJIT header.
Probably we can refactor this to reduce the number of macros (TODO).
win32/Makefile.sub: ditto.
common.mk: compile mjit.o and mjit_compile.o. Unlike original MJIT, this
commit separates MJIT infrastructure and JIT compiler code as independent
object files. As initial patch is NOT going to have ultra-fast JIT compiler,
it's likely to replace JIT compiler, e.g. original MJIT's compiler or some
future JIT impelementations which are not public now.
inits.c: define MJIT module. This is added because `MJIT.enabled?` was
necessary for testing.
test/lib/zombie_hunter.rb: skip if `MJIT.enabled?`. Obviously this
wouldn't work with current code when JIT is enabled.
test/ruby/test_io.rb: skip this too. This would make no sense with MJIT.
ruby.c: define MJIT CLI options. As major difference from original MJIT,
"-j:l"/"--jit:llvm" are renamed to "--jit-cc" because I want to support
not only gcc/clang but also cl.exe (Visual Studio) in the future. But it
takes only "--jit-cc=gcc", "--jit-cc=clang" for now. And only long "--jit"
options are allowed since some Ruby committers preferred it at Ruby
developers Meeting on January, and some of options are renamed.
This file also triggers to initialize MJIT thread and variables.
eval.c: finalize MJIT worker thread and variables.
test/ruby/test_rubyoptions.rb: fix number of CLI options for --jit.
thread_pthread.c: change for pthread abstraction in MJIT. Prefix rb_ for
functions which are used by other files.
thread_win32.c: ditto, for Windows. Those pthread porting is one of major
works that YARV-MJIT created, which is my fork of MJIT, in Feature 14235.
thread.c: follow rb_ prefix changes
vm.c: trigger MJIT call on VM invocation. Also trigger `mjit_mark` to avoid
SEGV by race between JIT and GC of ISeq. The improvement was provided by
wanabe <s.wanabe@gmail.com>.
In JIT compiler I created and am going to add in my next commit, I found
that having `mjit_exec` after `vm_loop_start:` is harmful because the
JIT-ed function doesn't proceed other ISeqs on RESTORE_REGS of leave insn.
Executing non-FINISH frame is unexpected for my JIT compiler and
`exception_handler` triggers executions of such ISeqs. So `mjit_exec`
here should be executed only when it directly comes from `vm_exec` call.
`RubyVM::MJIT` module and `.enabled?` method is added so that we can skip
some tests which don't expect JIT threads or compiler file descriptors.
vm_insnhelper.h: trigger MJIT on method calls during VM execution.
vm_core.h: add fields required for mjit.c. `bp` must be `cfp[6]` because
rb_control_frame_struct is likely to be casted to another struct. The
last position is the safest place to add the new field.
vm_insnhelper.c: save initial value of cfp->ep as cfp->bp. This is an
optimization which are done in both MJIT and YARV-MJIT. So this change
is added in this commit. Calculating bp from ep is a little heavy work,
so bp is kind of cache for it.
iseq.c: notify ISeq GC to MJIT. We should know which iseq in MJIT queue
is GCed to avoid SEGV. TODO: unload some GCed units in some safe way.
gc.c: add hooks so that MJIT can wait GC, and vice versa. Simultaneous
JIT and GC executions may cause SEGV and so we should synchronize them.
cont.c: save continuation information in MJIT worker. As MJIT shouldn't
unload JIT-ed code which is being used, MJIT wants to know full list of
saved execution contexts for continuation and detect ISeqs in use.
mjit_compile.c: added empty JIT compiler so that you can reuse this commit
to build your own JIT compiler. This commit tries to compile ISeqs but
all of them are considered as not supported in this commit. So you can't
use JIT compiler in this commit yet while we added --jit option now.
Patch author: Vladimir Makarov <vmakarov@redhat.com>.
Contributors:
Takashi Kokubun <takashikkbn@gmail.com>.
wanabe <s.wanabe@gmail.com>.
Lars Kanis <lars@greiz-reinsdorf.de>.
Part of Feature 12589 and 14235.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@62189 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-02-04 09:58:09 +03:00
|
|
|
rb_native_mutex_initialize(lock);
|
2010-01-23 23:18:36 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2013-07-23 13:53:14 +04:00
|
|
|
rb_nativethread_lock_destroy(rb_nativethread_lock_t *lock)
|
2010-01-23 23:18:36 +03:00
|
|
|
{
|
mjit.c: merge MJIT infrastructure
that allows to JIT-compile Ruby methods by generating C code and
using C compiler. See the first comment of mjit.c to know what this
file does.
mjit.c is authored by Vladimir Makarov <vmakarov@redhat.com>.
After he invented great method JIT infrastructure for MRI as MJIT,
Lars Kanis <lars@greiz-reinsdorf.de> sent the patch to support MinGW
in MJIT. In addition to merging it, I ported pthread to Windows native
threads. Now this MJIT infrastructure can be compiled on Visual Studio.
This commit simplifies mjit.c to decrease code at initial merge. For
example, this commit does not provide multiple JIT threads support.
We can resurrect them later if we really want them, but I wanted to minimize
diff to make it easier to review this patch.
`/tmp/_mjitXXX` file is renamed to `/tmp/_ruby_mjitXXX` because non-Ruby
developers may not know the name "mjit" and the file name should make
sure it's from Ruby and not from some harmful programs. TODO: it may be
better to store this to some temporary directory which Ruby is already using
by Tempfile, if it's not bad for performance.
mjit.h: New. It has `mjit_exec` interface similar to `vm_exec`, which is
for triggering MJIT. This drops interface for AOT compared to the original
MJIT.
Makefile.in: define macros to let MJIT know the path of MJIT header.
Probably we can refactor this to reduce the number of macros (TODO).
win32/Makefile.sub: ditto.
common.mk: compile mjit.o and mjit_compile.o. Unlike original MJIT, this
commit separates MJIT infrastructure and JIT compiler code as independent
object files. As initial patch is NOT going to have ultra-fast JIT compiler,
it's likely to replace JIT compiler, e.g. original MJIT's compiler or some
future JIT impelementations which are not public now.
inits.c: define MJIT module. This is added because `MJIT.enabled?` was
necessary for testing.
test/lib/zombie_hunter.rb: skip if `MJIT.enabled?`. Obviously this
wouldn't work with current code when JIT is enabled.
test/ruby/test_io.rb: skip this too. This would make no sense with MJIT.
ruby.c: define MJIT CLI options. As major difference from original MJIT,
"-j:l"/"--jit:llvm" are renamed to "--jit-cc" because I want to support
not only gcc/clang but also cl.exe (Visual Studio) in the future. But it
takes only "--jit-cc=gcc", "--jit-cc=clang" for now. And only long "--jit"
options are allowed since some Ruby committers preferred it at Ruby
developers Meeting on January, and some of options are renamed.
This file also triggers to initialize MJIT thread and variables.
eval.c: finalize MJIT worker thread and variables.
test/ruby/test_rubyoptions.rb: fix number of CLI options for --jit.
thread_pthread.c: change for pthread abstraction in MJIT. Prefix rb_ for
functions which are used by other files.
thread_win32.c: ditto, for Windows. Those pthread porting is one of major
works that YARV-MJIT created, which is my fork of MJIT, in Feature 14235.
thread.c: follow rb_ prefix changes
vm.c: trigger MJIT call on VM invocation. Also trigger `mjit_mark` to avoid
SEGV by race between JIT and GC of ISeq. The improvement was provided by
wanabe <s.wanabe@gmail.com>.
In JIT compiler I created and am going to add in my next commit, I found
that having `mjit_exec` after `vm_loop_start:` is harmful because the
JIT-ed function doesn't proceed other ISeqs on RESTORE_REGS of leave insn.
Executing non-FINISH frame is unexpected for my JIT compiler and
`exception_handler` triggers executions of such ISeqs. So `mjit_exec`
here should be executed only when it directly comes from `vm_exec` call.
`RubyVM::MJIT` module and `.enabled?` method is added so that we can skip
some tests which don't expect JIT threads or compiler file descriptors.
vm_insnhelper.h: trigger MJIT on method calls during VM execution.
vm_core.h: add fields required for mjit.c. `bp` must be `cfp[6]` because
rb_control_frame_struct is likely to be casted to another struct. The
last position is the safest place to add the new field.
vm_insnhelper.c: save initial value of cfp->ep as cfp->bp. This is an
optimization which are done in both MJIT and YARV-MJIT. So this change
is added in this commit. Calculating bp from ep is a little heavy work,
so bp is kind of cache for it.
iseq.c: notify ISeq GC to MJIT. We should know which iseq in MJIT queue
is GCed to avoid SEGV. TODO: unload some GCed units in some safe way.
gc.c: add hooks so that MJIT can wait GC, and vice versa. Simultaneous
JIT and GC executions may cause SEGV and so we should synchronize them.
cont.c: save continuation information in MJIT worker. As MJIT shouldn't
unload JIT-ed code which is being used, MJIT wants to know full list of
saved execution contexts for continuation and detect ISeqs in use.
mjit_compile.c: added empty JIT compiler so that you can reuse this commit
to build your own JIT compiler. This commit tries to compile ISeqs but
all of them are considered as not supported in this commit. So you can't
use JIT compiler in this commit yet while we added --jit option now.
Patch author: Vladimir Makarov <vmakarov@redhat.com>.
Contributors:
Takashi Kokubun <takashikkbn@gmail.com>.
wanabe <s.wanabe@gmail.com>.
Lars Kanis <lars@greiz-reinsdorf.de>.
Part of Feature 12589 and 14235.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@62189 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-02-04 09:58:09 +03:00
|
|
|
rb_native_mutex_destroy(lock);
|
2010-01-23 23:18:36 +03:00
|
|
|
}
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2013-07-23 13:53:14 +04:00
|
|
|
void
|
|
|
|
rb_nativethread_lock_lock(rb_nativethread_lock_t *lock)
|
|
|
|
{
|
mjit.c: merge MJIT infrastructure
that allows to JIT-compile Ruby methods by generating C code and
using C compiler. See the first comment of mjit.c to know what this
file does.
mjit.c is authored by Vladimir Makarov <vmakarov@redhat.com>.
After he invented great method JIT infrastructure for MRI as MJIT,
Lars Kanis <lars@greiz-reinsdorf.de> sent the patch to support MinGW
in MJIT. In addition to merging it, I ported pthread to Windows native
threads. Now this MJIT infrastructure can be compiled on Visual Studio.
This commit simplifies mjit.c to decrease code at initial merge. For
example, this commit does not provide multiple JIT threads support.
We can resurrect them later if we really want them, but I wanted to minimize
diff to make it easier to review this patch.
`/tmp/_mjitXXX` file is renamed to `/tmp/_ruby_mjitXXX` because non-Ruby
developers may not know the name "mjit" and the file name should make
sure it's from Ruby and not from some harmful programs. TODO: it may be
better to store this to some temporary directory which Ruby is already using
by Tempfile, if it's not bad for performance.
mjit.h: New. It has `mjit_exec` interface similar to `vm_exec`, which is
for triggering MJIT. This drops interface for AOT compared to the original
MJIT.
Makefile.in: define macros to let MJIT know the path of MJIT header.
Probably we can refactor this to reduce the number of macros (TODO).
win32/Makefile.sub: ditto.
common.mk: compile mjit.o and mjit_compile.o. Unlike original MJIT, this
commit separates MJIT infrastructure and JIT compiler code as independent
object files. As initial patch is NOT going to have ultra-fast JIT compiler,
it's likely to replace JIT compiler, e.g. original MJIT's compiler or some
future JIT impelementations which are not public now.
inits.c: define MJIT module. This is added because `MJIT.enabled?` was
necessary for testing.
test/lib/zombie_hunter.rb: skip if `MJIT.enabled?`. Obviously this
wouldn't work with current code when JIT is enabled.
test/ruby/test_io.rb: skip this too. This would make no sense with MJIT.
ruby.c: define MJIT CLI options. As major difference from original MJIT,
"-j:l"/"--jit:llvm" are renamed to "--jit-cc" because I want to support
not only gcc/clang but also cl.exe (Visual Studio) in the future. But it
takes only "--jit-cc=gcc", "--jit-cc=clang" for now. And only long "--jit"
options are allowed since some Ruby committers preferred it at Ruby
developers Meeting on January, and some of options are renamed.
This file also triggers to initialize MJIT thread and variables.
eval.c: finalize MJIT worker thread and variables.
test/ruby/test_rubyoptions.rb: fix number of CLI options for --jit.
thread_pthread.c: change for pthread abstraction in MJIT. Prefix rb_ for
functions which are used by other files.
thread_win32.c: ditto, for Windows. Those pthread porting is one of major
works that YARV-MJIT created, which is my fork of MJIT, in Feature 14235.
thread.c: follow rb_ prefix changes
vm.c: trigger MJIT call on VM invocation. Also trigger `mjit_mark` to avoid
SEGV by race between JIT and GC of ISeq. The improvement was provided by
wanabe <s.wanabe@gmail.com>.
In JIT compiler I created and am going to add in my next commit, I found
that having `mjit_exec` after `vm_loop_start:` is harmful because the
JIT-ed function doesn't proceed other ISeqs on RESTORE_REGS of leave insn.
Executing non-FINISH frame is unexpected for my JIT compiler and
`exception_handler` triggers executions of such ISeqs. So `mjit_exec`
here should be executed only when it directly comes from `vm_exec` call.
`RubyVM::MJIT` module and `.enabled?` method is added so that we can skip
some tests which don't expect JIT threads or compiler file descriptors.
vm_insnhelper.h: trigger MJIT on method calls during VM execution.
vm_core.h: add fields required for mjit.c. `bp` must be `cfp[6]` because
rb_control_frame_struct is likely to be casted to another struct. The
last position is the safest place to add the new field.
vm_insnhelper.c: save initial value of cfp->ep as cfp->bp. This is an
optimization which are done in both MJIT and YARV-MJIT. So this change
is added in this commit. Calculating bp from ep is a little heavy work,
so bp is kind of cache for it.
iseq.c: notify ISeq GC to MJIT. We should know which iseq in MJIT queue
is GCed to avoid SEGV. TODO: unload some GCed units in some safe way.
gc.c: add hooks so that MJIT can wait GC, and vice versa. Simultaneous
JIT and GC executions may cause SEGV and so we should synchronize them.
cont.c: save continuation information in MJIT worker. As MJIT shouldn't
unload JIT-ed code which is being used, MJIT wants to know full list of
saved execution contexts for continuation and detect ISeqs in use.
mjit_compile.c: added empty JIT compiler so that you can reuse this commit
to build your own JIT compiler. This commit tries to compile ISeqs but
all of them are considered as not supported in this commit. So you can't
use JIT compiler in this commit yet while we added --jit option now.
Patch author: Vladimir Makarov <vmakarov@redhat.com>.
Contributors:
Takashi Kokubun <takashikkbn@gmail.com>.
wanabe <s.wanabe@gmail.com>.
Lars Kanis <lars@greiz-reinsdorf.de>.
Part of Feature 12589 and 14235.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@62189 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-02-04 09:58:09 +03:00
|
|
|
rb_native_mutex_lock(lock);
|
2013-07-23 13:53:14 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
rb_nativethread_lock_unlock(rb_nativethread_lock_t *lock)
|
|
|
|
{
|
mjit.c: merge MJIT infrastructure
that allows to JIT-compile Ruby methods by generating C code and
using C compiler. See the first comment of mjit.c to know what this
file does.
mjit.c is authored by Vladimir Makarov <vmakarov@redhat.com>.
After he invented great method JIT infrastructure for MRI as MJIT,
Lars Kanis <lars@greiz-reinsdorf.de> sent the patch to support MinGW
in MJIT. In addition to merging it, I ported pthread to Windows native
threads. Now this MJIT infrastructure can be compiled on Visual Studio.
This commit simplifies mjit.c to decrease code at initial merge. For
example, this commit does not provide multiple JIT threads support.
We can resurrect them later if we really want them, but I wanted to minimize
diff to make it easier to review this patch.
`/tmp/_mjitXXX` file is renamed to `/tmp/_ruby_mjitXXX` because non-Ruby
developers may not know the name "mjit" and the file name should make
sure it's from Ruby and not from some harmful programs. TODO: it may be
better to store this to some temporary directory which Ruby is already using
by Tempfile, if it's not bad for performance.
mjit.h: New. It has `mjit_exec` interface similar to `vm_exec`, which is
for triggering MJIT. This drops interface for AOT compared to the original
MJIT.
Makefile.in: define macros to let MJIT know the path of MJIT header.
Probably we can refactor this to reduce the number of macros (TODO).
win32/Makefile.sub: ditto.
common.mk: compile mjit.o and mjit_compile.o. Unlike original MJIT, this
commit separates MJIT infrastructure and JIT compiler code as independent
object files. As initial patch is NOT going to have ultra-fast JIT compiler,
it's likely to replace JIT compiler, e.g. original MJIT's compiler or some
future JIT impelementations which are not public now.
inits.c: define MJIT module. This is added because `MJIT.enabled?` was
necessary for testing.
test/lib/zombie_hunter.rb: skip if `MJIT.enabled?`. Obviously this
wouldn't work with current code when JIT is enabled.
test/ruby/test_io.rb: skip this too. This would make no sense with MJIT.
ruby.c: define MJIT CLI options. As major difference from original MJIT,
"-j:l"/"--jit:llvm" are renamed to "--jit-cc" because I want to support
not only gcc/clang but also cl.exe (Visual Studio) in the future. But it
takes only "--jit-cc=gcc", "--jit-cc=clang" for now. And only long "--jit"
options are allowed since some Ruby committers preferred it at Ruby
developers Meeting on January, and some of options are renamed.
This file also triggers to initialize MJIT thread and variables.
eval.c: finalize MJIT worker thread and variables.
test/ruby/test_rubyoptions.rb: fix number of CLI options for --jit.
thread_pthread.c: change for pthread abstraction in MJIT. Prefix rb_ for
functions which are used by other files.
thread_win32.c: ditto, for Windows. Those pthread porting is one of major
works that YARV-MJIT created, which is my fork of MJIT, in Feature 14235.
thread.c: follow rb_ prefix changes
vm.c: trigger MJIT call on VM invocation. Also trigger `mjit_mark` to avoid
SEGV by race between JIT and GC of ISeq. The improvement was provided by
wanabe <s.wanabe@gmail.com>.
In JIT compiler I created and am going to add in my next commit, I found
that having `mjit_exec` after `vm_loop_start:` is harmful because the
JIT-ed function doesn't proceed other ISeqs on RESTORE_REGS of leave insn.
Executing non-FINISH frame is unexpected for my JIT compiler and
`exception_handler` triggers executions of such ISeqs. So `mjit_exec`
here should be executed only when it directly comes from `vm_exec` call.
`RubyVM::MJIT` module and `.enabled?` method is added so that we can skip
some tests which don't expect JIT threads or compiler file descriptors.
vm_insnhelper.h: trigger MJIT on method calls during VM execution.
vm_core.h: add fields required for mjit.c. `bp` must be `cfp[6]` because
rb_control_frame_struct is likely to be casted to another struct. The
last position is the safest place to add the new field.
vm_insnhelper.c: save initial value of cfp->ep as cfp->bp. This is an
optimization which are done in both MJIT and YARV-MJIT. So this change
is added in this commit. Calculating bp from ep is a little heavy work,
so bp is kind of cache for it.
iseq.c: notify ISeq GC to MJIT. We should know which iseq in MJIT queue
is GCed to avoid SEGV. TODO: unload some GCed units in some safe way.
gc.c: add hooks so that MJIT can wait GC, and vice versa. Simultaneous
JIT and GC executions may cause SEGV and so we should synchronize them.
cont.c: save continuation information in MJIT worker. As MJIT shouldn't
unload JIT-ed code which is being used, MJIT wants to know full list of
saved execution contexts for continuation and detect ISeqs in use.
mjit_compile.c: added empty JIT compiler so that you can reuse this commit
to build your own JIT compiler. This commit tries to compile ISeqs but
all of them are considered as not supported in this commit. So you can't
use JIT compiler in this commit yet while we added --jit option now.
Patch author: Vladimir Makarov <vmakarov@redhat.com>.
Contributors:
Takashi Kokubun <takashikkbn@gmail.com>.
wanabe <s.wanabe@gmail.com>.
Lars Kanis <lars@greiz-reinsdorf.de>.
Part of Feature 12589 and 14235.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@62189 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-02-04 09:58:09 +03:00
|
|
|
rb_native_mutex_unlock(lock);
|
2013-07-23 13:53:14 +04:00
|
|
|
}
|
|
|
|
|
2012-11-28 17:01:25 +04:00
|
|
|
static int
|
2017-06-12 10:49:33 +03:00
|
|
|
unblock_function_set(rb_thread_t *th, rb_unblock_function_t *func, void *arg, int fail_if_interrupted)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2015-07-21 15:21:35 +03:00
|
|
|
do {
|
|
|
|
if (fail_if_interrupted) {
|
2017-11-06 10:44:28 +03:00
|
|
|
if (RUBY_VM_INTERRUPTED_ANY(th->ec)) {
|
2015-07-21 15:21:35 +03:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2017-11-06 10:44:28 +03:00
|
|
|
RUBY_VM_CHECK_INTS(th->ec);
|
2012-11-28 17:01:25 +04:00
|
|
|
}
|
|
|
|
|
mjit.c: merge MJIT infrastructure
that allows to JIT-compile Ruby methods by generating C code and
using C compiler. See the first comment of mjit.c to know what this
file does.
mjit.c is authored by Vladimir Makarov <vmakarov@redhat.com>.
After he invented great method JIT infrastructure for MRI as MJIT,
Lars Kanis <lars@greiz-reinsdorf.de> sent the patch to support MinGW
in MJIT. In addition to merging it, I ported pthread to Windows native
threads. Now this MJIT infrastructure can be compiled on Visual Studio.
This commit simplifies mjit.c to decrease code at initial merge. For
example, this commit does not provide multiple JIT threads support.
We can resurrect them later if we really want them, but I wanted to minimize
diff to make it easier to review this patch.
`/tmp/_mjitXXX` file is renamed to `/tmp/_ruby_mjitXXX` because non-Ruby
developers may not know the name "mjit" and the file name should make
sure it's from Ruby and not from some harmful programs. TODO: it may be
better to store this to some temporary directory which Ruby is already using
by Tempfile, if it's not bad for performance.
mjit.h: New. It has `mjit_exec` interface similar to `vm_exec`, which is
for triggering MJIT. This drops interface for AOT compared to the original
MJIT.
Makefile.in: define macros to let MJIT know the path of MJIT header.
Probably we can refactor this to reduce the number of macros (TODO).
win32/Makefile.sub: ditto.
common.mk: compile mjit.o and mjit_compile.o. Unlike original MJIT, this
commit separates MJIT infrastructure and JIT compiler code as independent
object files. As initial patch is NOT going to have ultra-fast JIT compiler,
it's likely to replace JIT compiler, e.g. original MJIT's compiler or some
future JIT impelementations which are not public now.
inits.c: define MJIT module. This is added because `MJIT.enabled?` was
necessary for testing.
test/lib/zombie_hunter.rb: skip if `MJIT.enabled?`. Obviously this
wouldn't work with current code when JIT is enabled.
test/ruby/test_io.rb: skip this too. This would make no sense with MJIT.
ruby.c: define MJIT CLI options. As major difference from original MJIT,
"-j:l"/"--jit:llvm" are renamed to "--jit-cc" because I want to support
not only gcc/clang but also cl.exe (Visual Studio) in the future. But it
takes only "--jit-cc=gcc", "--jit-cc=clang" for now. And only long "--jit"
options are allowed since some Ruby committers preferred it at Ruby
developers Meeting on January, and some of options are renamed.
This file also triggers to initialize MJIT thread and variables.
eval.c: finalize MJIT worker thread and variables.
test/ruby/test_rubyoptions.rb: fix number of CLI options for --jit.
thread_pthread.c: change for pthread abstraction in MJIT. Prefix rb_ for
functions which are used by other files.
thread_win32.c: ditto, for Windows. Those pthread porting is one of major
works that YARV-MJIT created, which is my fork of MJIT, in Feature 14235.
thread.c: follow rb_ prefix changes
vm.c: trigger MJIT call on VM invocation. Also trigger `mjit_mark` to avoid
SEGV by race between JIT and GC of ISeq. The improvement was provided by
wanabe <s.wanabe@gmail.com>.
In JIT compiler I created and am going to add in my next commit, I found
that having `mjit_exec` after `vm_loop_start:` is harmful because the
JIT-ed function doesn't proceed other ISeqs on RESTORE_REGS of leave insn.
Executing non-FINISH frame is unexpected for my JIT compiler and
`exception_handler` triggers executions of such ISeqs. So `mjit_exec`
here should be executed only when it directly comes from `vm_exec` call.
`RubyVM::MJIT` module and `.enabled?` method is added so that we can skip
some tests which don't expect JIT threads or compiler file descriptors.
vm_insnhelper.h: trigger MJIT on method calls during VM execution.
vm_core.h: add fields required for mjit.c. `bp` must be `cfp[6]` because
rb_control_frame_struct is likely to be casted to another struct. The
last position is the safest place to add the new field.
vm_insnhelper.c: save initial value of cfp->ep as cfp->bp. This is an
optimization which are done in both MJIT and YARV-MJIT. So this change
is added in this commit. Calculating bp from ep is a little heavy work,
so bp is kind of cache for it.
iseq.c: notify ISeq GC to MJIT. We should know which iseq in MJIT queue
is GCed to avoid SEGV. TODO: unload some GCed units in some safe way.
gc.c: add hooks so that MJIT can wait GC, and vice versa. Simultaneous
JIT and GC executions may cause SEGV and so we should synchronize them.
cont.c: save continuation information in MJIT worker. As MJIT shouldn't
unload JIT-ed code which is being used, MJIT wants to know full list of
saved execution contexts for continuation and detect ISeqs in use.
mjit_compile.c: added empty JIT compiler so that you can reuse this commit
to build your own JIT compiler. This commit tries to compile ISeqs but
all of them are considered as not supported in this commit. So you can't
use JIT compiler in this commit yet while we added --jit option now.
Patch author: Vladimir Makarov <vmakarov@redhat.com>.
Contributors:
Takashi Kokubun <takashikkbn@gmail.com>.
wanabe <s.wanabe@gmail.com>.
Lars Kanis <lars@greiz-reinsdorf.de>.
Part of Feature 12589 and 14235.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@62189 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-02-04 09:58:09 +03:00
|
|
|
rb_native_mutex_lock(&th->interrupt_lock);
|
2018-03-06 08:15:57 +03:00
|
|
|
} while (!th->ec->raised_flag && RUBY_VM_INTERRUPTED_ANY(th->ec) &&
|
mjit.c: merge MJIT infrastructure
that allows to JIT-compile Ruby methods by generating C code and
using C compiler. See the first comment of mjit.c to know what this
file does.
mjit.c is authored by Vladimir Makarov <vmakarov@redhat.com>.
After he invented great method JIT infrastructure for MRI as MJIT,
Lars Kanis <lars@greiz-reinsdorf.de> sent the patch to support MinGW
in MJIT. In addition to merging it, I ported pthread to Windows native
threads. Now this MJIT infrastructure can be compiled on Visual Studio.
This commit simplifies mjit.c to decrease code at initial merge. For
example, this commit does not provide multiple JIT threads support.
We can resurrect them later if we really want them, but I wanted to minimize
diff to make it easier to review this patch.
`/tmp/_mjitXXX` file is renamed to `/tmp/_ruby_mjitXXX` because non-Ruby
developers may not know the name "mjit" and the file name should make
sure it's from Ruby and not from some harmful programs. TODO: it may be
better to store this to some temporary directory which Ruby is already using
by Tempfile, if it's not bad for performance.
mjit.h: New. It has `mjit_exec` interface similar to `vm_exec`, which is
for triggering MJIT. This drops interface for AOT compared to the original
MJIT.
Makefile.in: define macros to let MJIT know the path of MJIT header.
Probably we can refactor this to reduce the number of macros (TODO).
win32/Makefile.sub: ditto.
common.mk: compile mjit.o and mjit_compile.o. Unlike original MJIT, this
commit separates MJIT infrastructure and JIT compiler code as independent
object files. As initial patch is NOT going to have ultra-fast JIT compiler,
it's likely to replace JIT compiler, e.g. original MJIT's compiler or some
future JIT impelementations which are not public now.
inits.c: define MJIT module. This is added because `MJIT.enabled?` was
necessary for testing.
test/lib/zombie_hunter.rb: skip if `MJIT.enabled?`. Obviously this
wouldn't work with current code when JIT is enabled.
test/ruby/test_io.rb: skip this too. This would make no sense with MJIT.
ruby.c: define MJIT CLI options. As major difference from original MJIT,
"-j:l"/"--jit:llvm" are renamed to "--jit-cc" because I want to support
not only gcc/clang but also cl.exe (Visual Studio) in the future. But it
takes only "--jit-cc=gcc", "--jit-cc=clang" for now. And only long "--jit"
options are allowed since some Ruby committers preferred it at Ruby
developers Meeting on January, and some of options are renamed.
This file also triggers to initialize MJIT thread and variables.
eval.c: finalize MJIT worker thread and variables.
test/ruby/test_rubyoptions.rb: fix number of CLI options for --jit.
thread_pthread.c: change for pthread abstraction in MJIT. Prefix rb_ for
functions which are used by other files.
thread_win32.c: ditto, for Windows. Those pthread porting is one of major
works that YARV-MJIT created, which is my fork of MJIT, in Feature 14235.
thread.c: follow rb_ prefix changes
vm.c: trigger MJIT call on VM invocation. Also trigger `mjit_mark` to avoid
SEGV by race between JIT and GC of ISeq. The improvement was provided by
wanabe <s.wanabe@gmail.com>.
In JIT compiler I created and am going to add in my next commit, I found
that having `mjit_exec` after `vm_loop_start:` is harmful because the
JIT-ed function doesn't proceed other ISeqs on RESTORE_REGS of leave insn.
Executing non-FINISH frame is unexpected for my JIT compiler and
`exception_handler` triggers executions of such ISeqs. So `mjit_exec`
here should be executed only when it directly comes from `vm_exec` call.
`RubyVM::MJIT` module and `.enabled?` method is added so that we can skip
some tests which don't expect JIT threads or compiler file descriptors.
vm_insnhelper.h: trigger MJIT on method calls during VM execution.
vm_core.h: add fields required for mjit.c. `bp` must be `cfp[6]` because
rb_control_frame_struct is likely to be casted to another struct. The
last position is the safest place to add the new field.
vm_insnhelper.c: save initial value of cfp->ep as cfp->bp. This is an
optimization which are done in both MJIT and YARV-MJIT. So this change
is added in this commit. Calculating bp from ep is a little heavy work,
so bp is kind of cache for it.
iseq.c: notify ISeq GC to MJIT. We should know which iseq in MJIT queue
is GCed to avoid SEGV. TODO: unload some GCed units in some safe way.
gc.c: add hooks so that MJIT can wait GC, and vice versa. Simultaneous
JIT and GC executions may cause SEGV and so we should synchronize them.
cont.c: save continuation information in MJIT worker. As MJIT shouldn't
unload JIT-ed code which is being used, MJIT wants to know full list of
saved execution contexts for continuation and detect ISeqs in use.
mjit_compile.c: added empty JIT compiler so that you can reuse this commit
to build your own JIT compiler. This commit tries to compile ISeqs but
all of them are considered as not supported in this commit. So you can't
use JIT compiler in this commit yet while we added --jit option now.
Patch author: Vladimir Makarov <vmakarov@redhat.com>.
Contributors:
Takashi Kokubun <takashikkbn@gmail.com>.
wanabe <s.wanabe@gmail.com>.
Lars Kanis <lars@greiz-reinsdorf.de>.
Part of Feature 12589 and 14235.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@62189 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-02-04 09:58:09 +03:00
|
|
|
(rb_native_mutex_unlock(&th->interrupt_lock), TRUE));
|
2015-07-21 15:21:35 +03:00
|
|
|
|
2017-06-12 10:49:33 +03:00
|
|
|
VM_ASSERT(th->unblock.func == NULL);
|
|
|
|
|
2015-07-21 15:21:35 +03:00
|
|
|
th->unblock.func = func;
|
|
|
|
th->unblock.arg = arg;
|
mjit.c: merge MJIT infrastructure
that allows to JIT-compile Ruby methods by generating C code and
using C compiler. See the first comment of mjit.c to know what this
file does.
mjit.c is authored by Vladimir Makarov <vmakarov@redhat.com>.
After he invented great method JIT infrastructure for MRI as MJIT,
Lars Kanis <lars@greiz-reinsdorf.de> sent the patch to support MinGW
in MJIT. In addition to merging it, I ported pthread to Windows native
threads. Now this MJIT infrastructure can be compiled on Visual Studio.
This commit simplifies mjit.c to decrease code at initial merge. For
example, this commit does not provide multiple JIT threads support.
We can resurrect them later if we really want them, but I wanted to minimize
diff to make it easier to review this patch.
`/tmp/_mjitXXX` file is renamed to `/tmp/_ruby_mjitXXX` because non-Ruby
developers may not know the name "mjit" and the file name should make
sure it's from Ruby and not from some harmful programs. TODO: it may be
better to store this to some temporary directory which Ruby is already using
by Tempfile, if it's not bad for performance.
mjit.h: New. It has `mjit_exec` interface similar to `vm_exec`, which is
for triggering MJIT. This drops interface for AOT compared to the original
MJIT.
Makefile.in: define macros to let MJIT know the path of MJIT header.
Probably we can refactor this to reduce the number of macros (TODO).
win32/Makefile.sub: ditto.
common.mk: compile mjit.o and mjit_compile.o. Unlike original MJIT, this
commit separates MJIT infrastructure and JIT compiler code as independent
object files. As initial patch is NOT going to have ultra-fast JIT compiler,
it's likely to replace JIT compiler, e.g. original MJIT's compiler or some
future JIT impelementations which are not public now.
inits.c: define MJIT module. This is added because `MJIT.enabled?` was
necessary for testing.
test/lib/zombie_hunter.rb: skip if `MJIT.enabled?`. Obviously this
wouldn't work with current code when JIT is enabled.
test/ruby/test_io.rb: skip this too. This would make no sense with MJIT.
ruby.c: define MJIT CLI options. As major difference from original MJIT,
"-j:l"/"--jit:llvm" are renamed to "--jit-cc" because I want to support
not only gcc/clang but also cl.exe (Visual Studio) in the future. But it
takes only "--jit-cc=gcc", "--jit-cc=clang" for now. And only long "--jit"
options are allowed since some Ruby committers preferred it at Ruby
developers Meeting on January, and some of options are renamed.
This file also triggers to initialize MJIT thread and variables.
eval.c: finalize MJIT worker thread and variables.
test/ruby/test_rubyoptions.rb: fix number of CLI options for --jit.
thread_pthread.c: change for pthread abstraction in MJIT. Prefix rb_ for
functions which are used by other files.
thread_win32.c: ditto, for Windows. Those pthread porting is one of major
works that YARV-MJIT created, which is my fork of MJIT, in Feature 14235.
thread.c: follow rb_ prefix changes
vm.c: trigger MJIT call on VM invocation. Also trigger `mjit_mark` to avoid
SEGV by race between JIT and GC of ISeq. The improvement was provided by
wanabe <s.wanabe@gmail.com>.
In JIT compiler I created and am going to add in my next commit, I found
that having `mjit_exec` after `vm_loop_start:` is harmful because the
JIT-ed function doesn't proceed other ISeqs on RESTORE_REGS of leave insn.
Executing non-FINISH frame is unexpected for my JIT compiler and
`exception_handler` triggers executions of such ISeqs. So `mjit_exec`
here should be executed only when it directly comes from `vm_exec` call.
`RubyVM::MJIT` module and `.enabled?` method is added so that we can skip
some tests which don't expect JIT threads or compiler file descriptors.
vm_insnhelper.h: trigger MJIT on method calls during VM execution.
vm_core.h: add fields required for mjit.c. `bp` must be `cfp[6]` because
rb_control_frame_struct is likely to be casted to another struct. The
last position is the safest place to add the new field.
vm_insnhelper.c: save initial value of cfp->ep as cfp->bp. This is an
optimization which are done in both MJIT and YARV-MJIT. So this change
is added in this commit. Calculating bp from ep is a little heavy work,
so bp is kind of cache for it.
iseq.c: notify ISeq GC to MJIT. We should know which iseq in MJIT queue
is GCed to avoid SEGV. TODO: unload some GCed units in some safe way.
gc.c: add hooks so that MJIT can wait GC, and vice versa. Simultaneous
JIT and GC executions may cause SEGV and so we should synchronize them.
cont.c: save continuation information in MJIT worker. As MJIT shouldn't
unload JIT-ed code which is being used, MJIT wants to know full list of
saved execution contexts for continuation and detect ISeqs in use.
mjit_compile.c: added empty JIT compiler so that you can reuse this commit
to build your own JIT compiler. This commit tries to compile ISeqs but
all of them are considered as not supported in this commit. So you can't
use JIT compiler in this commit yet while we added --jit option now.
Patch author: Vladimir Makarov <vmakarov@redhat.com>.
Contributors:
Takashi Kokubun <takashikkbn@gmail.com>.
wanabe <s.wanabe@gmail.com>.
Lars Kanis <lars@greiz-reinsdorf.de>.
Part of Feature 12589 and 14235.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@62189 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-02-04 09:58:09 +03:00
|
|
|
rb_native_mutex_unlock(&th->interrupt_lock);
|
2012-11-28 17:01:25 +04:00
|
|
|
|
|
|
|
return TRUE;
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
2008-05-30 05:52:38 +04:00
|
|
|
static void
|
2017-06-12 10:49:33 +03:00
|
|
|
unblock_function_clear(rb_thread_t *th)
|
2008-05-30 05:52:38 +04:00
|
|
|
{
|
mjit.c: merge MJIT infrastructure
that allows to JIT-compile Ruby methods by generating C code and
using C compiler. See the first comment of mjit.c to know what this
file does.
mjit.c is authored by Vladimir Makarov <vmakarov@redhat.com>.
After he invented great method JIT infrastructure for MRI as MJIT,
Lars Kanis <lars@greiz-reinsdorf.de> sent the patch to support MinGW
in MJIT. In addition to merging it, I ported pthread to Windows native
threads. Now this MJIT infrastructure can be compiled on Visual Studio.
This commit simplifies mjit.c to decrease code at initial merge. For
example, this commit does not provide multiple JIT threads support.
We can resurrect them later if we really want them, but I wanted to minimize
diff to make it easier to review this patch.
`/tmp/_mjitXXX` file is renamed to `/tmp/_ruby_mjitXXX` because non-Ruby
developers may not know the name "mjit" and the file name should make
sure it's from Ruby and not from some harmful programs. TODO: it may be
better to store this to some temporary directory which Ruby is already using
by Tempfile, if it's not bad for performance.
mjit.h: New. It has `mjit_exec` interface similar to `vm_exec`, which is
for triggering MJIT. This drops interface for AOT compared to the original
MJIT.
Makefile.in: define macros to let MJIT know the path of MJIT header.
Probably we can refactor this to reduce the number of macros (TODO).
win32/Makefile.sub: ditto.
common.mk: compile mjit.o and mjit_compile.o. Unlike original MJIT, this
commit separates MJIT infrastructure and JIT compiler code as independent
object files. As initial patch is NOT going to have ultra-fast JIT compiler,
it's likely to replace JIT compiler, e.g. original MJIT's compiler or some
future JIT impelementations which are not public now.
inits.c: define MJIT module. This is added because `MJIT.enabled?` was
necessary for testing.
test/lib/zombie_hunter.rb: skip if `MJIT.enabled?`. Obviously this
wouldn't work with current code when JIT is enabled.
test/ruby/test_io.rb: skip this too. This would make no sense with MJIT.
ruby.c: define MJIT CLI options. As major difference from original MJIT,
"-j:l"/"--jit:llvm" are renamed to "--jit-cc" because I want to support
not only gcc/clang but also cl.exe (Visual Studio) in the future. But it
takes only "--jit-cc=gcc", "--jit-cc=clang" for now. And only long "--jit"
options are allowed since some Ruby committers preferred it at Ruby
developers Meeting on January, and some of options are renamed.
This file also triggers to initialize MJIT thread and variables.
eval.c: finalize MJIT worker thread and variables.
test/ruby/test_rubyoptions.rb: fix number of CLI options for --jit.
thread_pthread.c: change for pthread abstraction in MJIT. Prefix rb_ for
functions which are used by other files.
thread_win32.c: ditto, for Windows. Those pthread porting is one of major
works that YARV-MJIT created, which is my fork of MJIT, in Feature 14235.
thread.c: follow rb_ prefix changes
vm.c: trigger MJIT call on VM invocation. Also trigger `mjit_mark` to avoid
SEGV by race between JIT and GC of ISeq. The improvement was provided by
wanabe <s.wanabe@gmail.com>.
In JIT compiler I created and am going to add in my next commit, I found
that having `mjit_exec` after `vm_loop_start:` is harmful because the
JIT-ed function doesn't proceed other ISeqs on RESTORE_REGS of leave insn.
Executing non-FINISH frame is unexpected for my JIT compiler and
`exception_handler` triggers executions of such ISeqs. So `mjit_exec`
here should be executed only when it directly comes from `vm_exec` call.
`RubyVM::MJIT` module and `.enabled?` method is added so that we can skip
some tests which don't expect JIT threads or compiler file descriptors.
vm_insnhelper.h: trigger MJIT on method calls during VM execution.
vm_core.h: add fields required for mjit.c. `bp` must be `cfp[6]` because
rb_control_frame_struct is likely to be casted to another struct. The
last position is the safest place to add the new field.
vm_insnhelper.c: save initial value of cfp->ep as cfp->bp. This is an
optimization which are done in both MJIT and YARV-MJIT. So this change
is added in this commit. Calculating bp from ep is a little heavy work,
so bp is kind of cache for it.
iseq.c: notify ISeq GC to MJIT. We should know which iseq in MJIT queue
is GCed to avoid SEGV. TODO: unload some GCed units in some safe way.
gc.c: add hooks so that MJIT can wait GC, and vice versa. Simultaneous
JIT and GC executions may cause SEGV and so we should synchronize them.
cont.c: save continuation information in MJIT worker. As MJIT shouldn't
unload JIT-ed code which is being used, MJIT wants to know full list of
saved execution contexts for continuation and detect ISeqs in use.
mjit_compile.c: added empty JIT compiler so that you can reuse this commit
to build your own JIT compiler. This commit tries to compile ISeqs but
all of them are considered as not supported in this commit. So you can't
use JIT compiler in this commit yet while we added --jit option now.
Patch author: Vladimir Makarov <vmakarov@redhat.com>.
Contributors:
Takashi Kokubun <takashikkbn@gmail.com>.
wanabe <s.wanabe@gmail.com>.
Lars Kanis <lars@greiz-reinsdorf.de>.
Part of Feature 12589 and 14235.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@62189 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-02-04 09:58:09 +03:00
|
|
|
rb_native_mutex_lock(&th->interrupt_lock);
|
2017-06-12 10:49:33 +03:00
|
|
|
th->unblock.func = NULL;
|
mjit.c: merge MJIT infrastructure
that allows to JIT-compile Ruby methods by generating C code and
using C compiler. See the first comment of mjit.c to know what this
file does.
mjit.c is authored by Vladimir Makarov <vmakarov@redhat.com>.
After he invented great method JIT infrastructure for MRI as MJIT,
Lars Kanis <lars@greiz-reinsdorf.de> sent the patch to support MinGW
in MJIT. In addition to merging it, I ported pthread to Windows native
threads. Now this MJIT infrastructure can be compiled on Visual Studio.
This commit simplifies mjit.c to decrease code at initial merge. For
example, this commit does not provide multiple JIT threads support.
We can resurrect them later if we really want them, but I wanted to minimize
diff to make it easier to review this patch.
`/tmp/_mjitXXX` file is renamed to `/tmp/_ruby_mjitXXX` because non-Ruby
developers may not know the name "mjit" and the file name should make
sure it's from Ruby and not from some harmful programs. TODO: it may be
better to store this to some temporary directory which Ruby is already using
by Tempfile, if it's not bad for performance.
mjit.h: New. It has `mjit_exec` interface similar to `vm_exec`, which is
for triggering MJIT. This drops interface for AOT compared to the original
MJIT.
Makefile.in: define macros to let MJIT know the path of MJIT header.
Probably we can refactor this to reduce the number of macros (TODO).
win32/Makefile.sub: ditto.
common.mk: compile mjit.o and mjit_compile.o. Unlike original MJIT, this
commit separates MJIT infrastructure and JIT compiler code as independent
object files. As initial patch is NOT going to have ultra-fast JIT compiler,
it's likely to replace JIT compiler, e.g. original MJIT's compiler or some
future JIT impelementations which are not public now.
inits.c: define MJIT module. This is added because `MJIT.enabled?` was
necessary for testing.
test/lib/zombie_hunter.rb: skip if `MJIT.enabled?`. Obviously this
wouldn't work with current code when JIT is enabled.
test/ruby/test_io.rb: skip this too. This would make no sense with MJIT.
ruby.c: define MJIT CLI options. As major difference from original MJIT,
"-j:l"/"--jit:llvm" are renamed to "--jit-cc" because I want to support
not only gcc/clang but also cl.exe (Visual Studio) in the future. But it
takes only "--jit-cc=gcc", "--jit-cc=clang" for now. And only long "--jit"
options are allowed since some Ruby committers preferred it at Ruby
developers Meeting on January, and some of options are renamed.
This file also triggers to initialize MJIT thread and variables.
eval.c: finalize MJIT worker thread and variables.
test/ruby/test_rubyoptions.rb: fix number of CLI options for --jit.
thread_pthread.c: change for pthread abstraction in MJIT. Prefix rb_ for
functions which are used by other files.
thread_win32.c: ditto, for Windows. Those pthread porting is one of major
works that YARV-MJIT created, which is my fork of MJIT, in Feature 14235.
thread.c: follow rb_ prefix changes
vm.c: trigger MJIT call on VM invocation. Also trigger `mjit_mark` to avoid
SEGV by race between JIT and GC of ISeq. The improvement was provided by
wanabe <s.wanabe@gmail.com>.
In JIT compiler I created and am going to add in my next commit, I found
that having `mjit_exec` after `vm_loop_start:` is harmful because the
JIT-ed function doesn't proceed other ISeqs on RESTORE_REGS of leave insn.
Executing non-FINISH frame is unexpected for my JIT compiler and
`exception_handler` triggers executions of such ISeqs. So `mjit_exec`
here should be executed only when it directly comes from `vm_exec` call.
`RubyVM::MJIT` module and `.enabled?` method is added so that we can skip
some tests which don't expect JIT threads or compiler file descriptors.
vm_insnhelper.h: trigger MJIT on method calls during VM execution.
vm_core.h: add fields required for mjit.c. `bp` must be `cfp[6]` because
rb_control_frame_struct is likely to be casted to another struct. The
last position is the safest place to add the new field.
vm_insnhelper.c: save initial value of cfp->ep as cfp->bp. This is an
optimization which are done in both MJIT and YARV-MJIT. So this change
is added in this commit. Calculating bp from ep is a little heavy work,
so bp is kind of cache for it.
iseq.c: notify ISeq GC to MJIT. We should know which iseq in MJIT queue
is GCed to avoid SEGV. TODO: unload some GCed units in some safe way.
gc.c: add hooks so that MJIT can wait GC, and vice versa. Simultaneous
JIT and GC executions may cause SEGV and so we should synchronize them.
cont.c: save continuation information in MJIT worker. As MJIT shouldn't
unload JIT-ed code which is being used, MJIT wants to know full list of
saved execution contexts for continuation and detect ISeqs in use.
mjit_compile.c: added empty JIT compiler so that you can reuse this commit
to build your own JIT compiler. This commit tries to compile ISeqs but
all of them are considered as not supported in this commit. So you can't
use JIT compiler in this commit yet while we added --jit option now.
Patch author: Vladimir Makarov <vmakarov@redhat.com>.
Contributors:
Takashi Kokubun <takashikkbn@gmail.com>.
wanabe <s.wanabe@gmail.com>.
Lars Kanis <lars@greiz-reinsdorf.de>.
Part of Feature 12589 and 14235.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@62189 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-02-04 09:58:09 +03:00
|
|
|
rb_native_mutex_unlock(&th->interrupt_lock);
|
2008-05-30 05:52:38 +04:00
|
|
|
}
|
|
|
|
|
2012-11-26 13:22:01 +04:00
|
|
|
static void
|
|
|
|
rb_threadptr_interrupt_common(rb_thread_t *th, int trap)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
mjit.c: merge MJIT infrastructure
that allows to JIT-compile Ruby methods by generating C code and
using C compiler. See the first comment of mjit.c to know what this
file does.
mjit.c is authored by Vladimir Makarov <vmakarov@redhat.com>.
After he invented great method JIT infrastructure for MRI as MJIT,
Lars Kanis <lars@greiz-reinsdorf.de> sent the patch to support MinGW
in MJIT. In addition to merging it, I ported pthread to Windows native
threads. Now this MJIT infrastructure can be compiled on Visual Studio.
This commit simplifies mjit.c to decrease code at initial merge. For
example, this commit does not provide multiple JIT threads support.
We can resurrect them later if we really want them, but I wanted to minimize
diff to make it easier to review this patch.
`/tmp/_mjitXXX` file is renamed to `/tmp/_ruby_mjitXXX` because non-Ruby
developers may not know the name "mjit" and the file name should make
sure it's from Ruby and not from some harmful programs. TODO: it may be
better to store this to some temporary directory which Ruby is already using
by Tempfile, if it's not bad for performance.
mjit.h: New. It has `mjit_exec` interface similar to `vm_exec`, which is
for triggering MJIT. This drops interface for AOT compared to the original
MJIT.
Makefile.in: define macros to let MJIT know the path of MJIT header.
Probably we can refactor this to reduce the number of macros (TODO).
win32/Makefile.sub: ditto.
common.mk: compile mjit.o and mjit_compile.o. Unlike original MJIT, this
commit separates MJIT infrastructure and JIT compiler code as independent
object files. As initial patch is NOT going to have ultra-fast JIT compiler,
it's likely to replace JIT compiler, e.g. original MJIT's compiler or some
future JIT impelementations which are not public now.
inits.c: define MJIT module. This is added because `MJIT.enabled?` was
necessary for testing.
test/lib/zombie_hunter.rb: skip if `MJIT.enabled?`. Obviously this
wouldn't work with current code when JIT is enabled.
test/ruby/test_io.rb: skip this too. This would make no sense with MJIT.
ruby.c: define MJIT CLI options. As major difference from original MJIT,
"-j:l"/"--jit:llvm" are renamed to "--jit-cc" because I want to support
not only gcc/clang but also cl.exe (Visual Studio) in the future. But it
takes only "--jit-cc=gcc", "--jit-cc=clang" for now. And only long "--jit"
options are allowed since some Ruby committers preferred it at Ruby
developers Meeting on January, and some of options are renamed.
This file also triggers to initialize MJIT thread and variables.
eval.c: finalize MJIT worker thread and variables.
test/ruby/test_rubyoptions.rb: fix number of CLI options for --jit.
thread_pthread.c: change for pthread abstraction in MJIT. Prefix rb_ for
functions which are used by other files.
thread_win32.c: ditto, for Windows. Those pthread porting is one of major
works that YARV-MJIT created, which is my fork of MJIT, in Feature 14235.
thread.c: follow rb_ prefix changes
vm.c: trigger MJIT call on VM invocation. Also trigger `mjit_mark` to avoid
SEGV by race between JIT and GC of ISeq. The improvement was provided by
wanabe <s.wanabe@gmail.com>.
In JIT compiler I created and am going to add in my next commit, I found
that having `mjit_exec` after `vm_loop_start:` is harmful because the
JIT-ed function doesn't proceed other ISeqs on RESTORE_REGS of leave insn.
Executing non-FINISH frame is unexpected for my JIT compiler and
`exception_handler` triggers executions of such ISeqs. So `mjit_exec`
here should be executed only when it directly comes from `vm_exec` call.
`RubyVM::MJIT` module and `.enabled?` method is added so that we can skip
some tests which don't expect JIT threads or compiler file descriptors.
vm_insnhelper.h: trigger MJIT on method calls during VM execution.
vm_core.h: add fields required for mjit.c. `bp` must be `cfp[6]` because
rb_control_frame_struct is likely to be casted to another struct. The
last position is the safest place to add the new field.
vm_insnhelper.c: save initial value of cfp->ep as cfp->bp. This is an
optimization which are done in both MJIT and YARV-MJIT. So this change
is added in this commit. Calculating bp from ep is a little heavy work,
so bp is kind of cache for it.
iseq.c: notify ISeq GC to MJIT. We should know which iseq in MJIT queue
is GCed to avoid SEGV. TODO: unload some GCed units in some safe way.
gc.c: add hooks so that MJIT can wait GC, and vice versa. Simultaneous
JIT and GC executions may cause SEGV and so we should synchronize them.
cont.c: save continuation information in MJIT worker. As MJIT shouldn't
unload JIT-ed code which is being used, MJIT wants to know full list of
saved execution contexts for continuation and detect ISeqs in use.
mjit_compile.c: added empty JIT compiler so that you can reuse this commit
to build your own JIT compiler. This commit tries to compile ISeqs but
all of them are considered as not supported in this commit. So you can't
use JIT compiler in this commit yet while we added --jit option now.
Patch author: Vladimir Makarov <vmakarov@redhat.com>.
Contributors:
Takashi Kokubun <takashikkbn@gmail.com>.
wanabe <s.wanabe@gmail.com>.
Lars Kanis <lars@greiz-reinsdorf.de>.
Part of Feature 12589 and 14235.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@62189 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-02-04 09:58:09 +03:00
|
|
|
rb_native_mutex_lock(&th->interrupt_lock);
|
2017-06-12 10:49:33 +03:00
|
|
|
if (trap) {
|
2017-11-06 10:44:28 +03:00
|
|
|
RUBY_VM_SET_TRAP_INTERRUPT(th->ec);
|
2017-06-12 10:49:33 +03:00
|
|
|
}
|
|
|
|
else {
|
2017-11-06 10:44:28 +03:00
|
|
|
RUBY_VM_SET_INTERRUPT(th->ec);
|
2017-06-12 10:49:33 +03:00
|
|
|
}
|
|
|
|
if (th->unblock.func != NULL) {
|
2008-05-30 05:52:38 +04:00
|
|
|
(th->unblock.func)(th->unblock.arg);
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* none */
|
|
|
|
}
|
mjit.c: merge MJIT infrastructure
that allows to JIT-compile Ruby methods by generating C code and
using C compiler. See the first comment of mjit.c to know what this
file does.
mjit.c is authored by Vladimir Makarov <vmakarov@redhat.com>.
After he invented great method JIT infrastructure for MRI as MJIT,
Lars Kanis <lars@greiz-reinsdorf.de> sent the patch to support MinGW
in MJIT. In addition to merging it, I ported pthread to Windows native
threads. Now this MJIT infrastructure can be compiled on Visual Studio.
This commit simplifies mjit.c to decrease code at initial merge. For
example, this commit does not provide multiple JIT threads support.
We can resurrect them later if we really want them, but I wanted to minimize
diff to make it easier to review this patch.
`/tmp/_mjitXXX` file is renamed to `/tmp/_ruby_mjitXXX` because non-Ruby
developers may not know the name "mjit" and the file name should make
sure it's from Ruby and not from some harmful programs. TODO: it may be
better to store this to some temporary directory which Ruby is already using
by Tempfile, if it's not bad for performance.
mjit.h: New. It has `mjit_exec` interface similar to `vm_exec`, which is
for triggering MJIT. This drops interface for AOT compared to the original
MJIT.
Makefile.in: define macros to let MJIT know the path of MJIT header.
Probably we can refactor this to reduce the number of macros (TODO).
win32/Makefile.sub: ditto.
common.mk: compile mjit.o and mjit_compile.o. Unlike original MJIT, this
commit separates MJIT infrastructure and JIT compiler code as independent
object files. As initial patch is NOT going to have ultra-fast JIT compiler,
it's likely to replace JIT compiler, e.g. original MJIT's compiler or some
future JIT impelementations which are not public now.
inits.c: define MJIT module. This is added because `MJIT.enabled?` was
necessary for testing.
test/lib/zombie_hunter.rb: skip if `MJIT.enabled?`. Obviously this
wouldn't work with current code when JIT is enabled.
test/ruby/test_io.rb: skip this too. This would make no sense with MJIT.
ruby.c: define MJIT CLI options. As major difference from original MJIT,
"-j:l"/"--jit:llvm" are renamed to "--jit-cc" because I want to support
not only gcc/clang but also cl.exe (Visual Studio) in the future. But it
takes only "--jit-cc=gcc", "--jit-cc=clang" for now. And only long "--jit"
options are allowed since some Ruby committers preferred it at Ruby
developers Meeting on January, and some of options are renamed.
This file also triggers to initialize MJIT thread and variables.
eval.c: finalize MJIT worker thread and variables.
test/ruby/test_rubyoptions.rb: fix number of CLI options for --jit.
thread_pthread.c: change for pthread abstraction in MJIT. Prefix rb_ for
functions which are used by other files.
thread_win32.c: ditto, for Windows. Those pthread porting is one of major
works that YARV-MJIT created, which is my fork of MJIT, in Feature 14235.
thread.c: follow rb_ prefix changes
vm.c: trigger MJIT call on VM invocation. Also trigger `mjit_mark` to avoid
SEGV by race between JIT and GC of ISeq. The improvement was provided by
wanabe <s.wanabe@gmail.com>.
In JIT compiler I created and am going to add in my next commit, I found
that having `mjit_exec` after `vm_loop_start:` is harmful because the
JIT-ed function doesn't proceed other ISeqs on RESTORE_REGS of leave insn.
Executing non-FINISH frame is unexpected for my JIT compiler and
`exception_handler` triggers executions of such ISeqs. So `mjit_exec`
here should be executed only when it directly comes from `vm_exec` call.
`RubyVM::MJIT` module and `.enabled?` method is added so that we can skip
some tests which don't expect JIT threads or compiler file descriptors.
vm_insnhelper.h: trigger MJIT on method calls during VM execution.
vm_core.h: add fields required for mjit.c. `bp` must be `cfp[6]` because
rb_control_frame_struct is likely to be casted to another struct. The
last position is the safest place to add the new field.
vm_insnhelper.c: save initial value of cfp->ep as cfp->bp. This is an
optimization which are done in both MJIT and YARV-MJIT. So this change
is added in this commit. Calculating bp from ep is a little heavy work,
so bp is kind of cache for it.
iseq.c: notify ISeq GC to MJIT. We should know which iseq in MJIT queue
is GCed to avoid SEGV. TODO: unload some GCed units in some safe way.
gc.c: add hooks so that MJIT can wait GC, and vice versa. Simultaneous
JIT and GC executions may cause SEGV and so we should synchronize them.
cont.c: save continuation information in MJIT worker. As MJIT shouldn't
unload JIT-ed code which is being used, MJIT wants to know full list of
saved execution contexts for continuation and detect ISeqs in use.
mjit_compile.c: added empty JIT compiler so that you can reuse this commit
to build your own JIT compiler. This commit tries to compile ISeqs but
all of them are considered as not supported in this commit. So you can't
use JIT compiler in this commit yet while we added --jit option now.
Patch author: Vladimir Makarov <vmakarov@redhat.com>.
Contributors:
Takashi Kokubun <takashikkbn@gmail.com>.
wanabe <s.wanabe@gmail.com>.
Lars Kanis <lars@greiz-reinsdorf.de>.
Part of Feature 12589 and 14235.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@62189 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-02-04 09:58:09 +03:00
|
|
|
rb_native_mutex_unlock(&th->interrupt_lock);
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
2012-11-26 13:22:01 +04:00
|
|
|
void
|
|
|
|
rb_threadptr_interrupt(rb_thread_t *th)
|
|
|
|
{
|
|
|
|
rb_threadptr_interrupt_common(th, 0);
|
|
|
|
}
|
|
|
|
|
2017-11-16 11:48:59 +03:00
|
|
|
static void
|
|
|
|
threadptr_trap_interrupt(rb_thread_t *th)
|
2012-11-26 13:22:01 +04:00
|
|
|
{
|
|
|
|
rb_threadptr_interrupt_common(th, 1);
|
|
|
|
}
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2014-05-28 05:48:11 +04:00
|
|
|
static void
|
|
|
|
terminate_all(rb_vm_t *vm, const rb_thread_t *main_thread)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2014-05-29 19:45:25 +04:00
|
|
|
rb_thread_t *th = 0;
|
2014-05-28 05:48:11 +04:00
|
|
|
|
|
|
|
list_for_each(&vm->living_threads, th, vmlt_node) {
|
|
|
|
if (th != main_thread) {
|
2017-12-15 06:07:10 +03:00
|
|
|
thread_debug("terminate_all: begin (thid: %"PRI_THREAD_ID", status: %s)\n",
|
|
|
|
thread_id_str(th), thread_status_name(th, TRUE));
|
2014-05-28 05:48:11 +04:00
|
|
|
rb_threadptr_pending_interrupt_enque(th, eTerminateSignal);
|
|
|
|
rb_threadptr_interrupt(th);
|
2017-12-15 06:07:10 +03:00
|
|
|
thread_debug("terminate_all: end (thid: %"PRI_THREAD_ID", status: %s)\n",
|
|
|
|
thread_id_str(th), thread_status_name(th, TRUE));
|
2014-05-28 05:48:11 +04:00
|
|
|
}
|
|
|
|
else {
|
2017-12-15 06:07:10 +03:00
|
|
|
thread_debug("terminate_all: main thread (%p)\n", (void *)th);
|
2014-05-28 05:48:11 +04:00
|
|
|
}
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-07-08 08:50:25 +04:00
|
|
|
void
|
|
|
|
rb_threadptr_unlock_all_locking_mutexes(rb_thread_t *th)
|
|
|
|
{
|
|
|
|
const char *err;
|
|
|
|
rb_mutex_t *mutex;
|
|
|
|
rb_mutex_t *mutexes = th->keeping_mutexes;
|
|
|
|
|
|
|
|
while (mutexes) {
|
|
|
|
mutex = mutexes;
|
|
|
|
/* rb_warn("mutex #<%p> remains to be locked by terminated thread",
|
2018-01-02 09:41:40 +03:00
|
|
|
(void *)mutexes); */
|
2011-07-08 08:50:25 +04:00
|
|
|
mutexes = mutex->next_mutex;
|
|
|
|
err = rb_mutex_unlock_th(mutex, th);
|
|
|
|
if (err) rb_bug("invalid keeping_mutexes: %s", err);
|
|
|
|
}
|
|
|
|
}
|
2008-07-28 16:27:43 +04:00
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
void
|
|
|
|
rb_thread_terminate_all(void)
|
|
|
|
{
|
2016-12-08 02:27:51 +03:00
|
|
|
rb_thread_t *volatile th = GET_THREAD(); /* main thread */
|
2017-11-06 10:44:28 +03:00
|
|
|
rb_execution_context_t * volatile ec = th->ec;
|
2016-12-08 02:27:51 +03:00
|
|
|
rb_vm_t *volatile vm = th->vm;
|
2015-07-20 03:11:40 +03:00
|
|
|
volatile int sleeping = 0;
|
2009-08-20 04:42:16 +04:00
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
if (vm->main_thread != th) {
|
* compile.c (iseq_compile_each), gc.c (assign_heap_slot),
(gc_mark_children), parse.y (vtable_alloc, vtable_free, vtable_add),
proc.c (proc_to_s), thread.c (terminate_i, rb_thread_terminate_all),
(thread_start_func_2, blocking_region_begin, blocking_region_end),
(rb_thread_kill), thread_pthread.c (native_thread_create),
(ubf_pthread_cond_signal), vm.c (check_env, thread_free), vm_dump.c
(vm_env_dump_raw, vm_stack_dump_each, vm_thread_dump_state),
(vm_call0): use void pointer for %p.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@20593 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2008-12-09 07:33:55 +03:00
|
|
|
rb_bug("rb_thread_terminate_all: called by child thread (%p, %p)",
|
|
|
|
(void *)vm->main_thread, (void *)th);
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
2008-06-12 17:01:38 +04:00
|
|
|
/* unlock all locking mutexes */
|
2011-07-08 08:50:25 +04:00
|
|
|
rb_threadptr_unlock_all_locking_mutexes(th);
|
2008-06-12 17:01:38 +04:00
|
|
|
|
2017-11-06 10:44:28 +03:00
|
|
|
EC_PUSH_TAG(ec);
|
2017-10-26 14:02:13 +03:00
|
|
|
if (EC_EXEC_TAG() == TAG_NONE) {
|
2015-07-20 03:11:40 +03:00
|
|
|
retry:
|
|
|
|
thread_debug("rb_thread_terminate_all (main thread: %p)\n", (void *)th);
|
|
|
|
terminate_all(vm, th);
|
2012-11-27 03:08:36 +04:00
|
|
|
|
2015-07-20 03:11:40 +03:00
|
|
|
while (vm_living_thread_num(vm) > 1) {
|
2018-08-25 09:58:35 +03:00
|
|
|
rb_hrtime_t rel = RB_HRTIME_PER_SEC;
|
2013-10-24 07:15:58 +04:00
|
|
|
/*
|
|
|
|
* Thread exiting routine in thread_start_func_2 notify
|
|
|
|
* me when the last sub-thread exit.
|
|
|
|
*/
|
2015-07-20 03:11:40 +03:00
|
|
|
sleeping = 1;
|
2018-08-25 09:58:35 +03:00
|
|
|
native_sleep(th, &rel);
|
2017-11-06 10:44:28 +03:00
|
|
|
RUBY_VM_CHECK_INTS_BLOCKING(ec);
|
2015-07-20 03:11:40 +03:00
|
|
|
sleeping = 0;
|
2007-08-17 11:24:03 +04:00
|
|
|
}
|
2015-07-20 03:11:40 +03:00
|
|
|
}
|
|
|
|
else {
|
2013-10-24 07:25:25 +04:00
|
|
|
/*
|
|
|
|
* When caught an exception (e.g. Ctrl+C), let's broadcast
|
|
|
|
* kill request again to ensure killing all threads even
|
|
|
|
* if they are blocked on sleep, mutex, etc.
|
|
|
|
*/
|
2015-07-20 03:11:40 +03:00
|
|
|
if (sleeping) {
|
|
|
|
sleeping = 0;
|
2012-11-27 07:18:29 +04:00
|
|
|
goto retry;
|
|
|
|
}
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
2017-10-26 14:02:13 +03:00
|
|
|
EC_POP_TAG();
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2008-05-11 08:15:29 +04:00
|
|
|
thread_cleanup_func_before_exec(void *th_ptr)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
* blockinlining.c, error.c, eval.c, eval_error.h, eval_intern.h,
eval_jump.h, eval_load.c, eval_safe.h, gc.c, proc.c, signal.c,
thread.c, thread_pthread.ci, thread_win32.ci, vm.c, vm.h,
vm_dump.c, vm_evalbody.ci, yarvcore.c, yarvcore.h:
fix typo (rb_thead_t -> rb_thread_t).
* eval_intern.h: remove unused definitions.
* common.mk: fix around vm_opts.h path
and remove harmful argument passed to insns2vm.rb.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@11658 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-02-08 09:37:46 +03:00
|
|
|
rb_thread_t *th = th_ptr;
|
2006-12-31 18:02:22 +03:00
|
|
|
th->status = THREAD_KILLED;
|
2017-10-26 11:32:49 +03:00
|
|
|
th->ec->machine.stack_start = th->ec->machine.stack_end = NULL;
|
2007-06-14 12:35:20 +04:00
|
|
|
#ifdef __ia64
|
2017-10-26 11:32:49 +03:00
|
|
|
th->ec->machine.register_stack_start = th->ec->machine.register_stack_end = NULL;
|
2007-06-14 12:35:20 +04:00
|
|
|
#endif
|
2008-05-11 08:15:29 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2010-12-20 18:16:41 +03:00
|
|
|
thread_cleanup_func(void *th_ptr, int atfork)
|
2008-05-11 08:15:29 +04:00
|
|
|
{
|
|
|
|
rb_thread_t *th = th_ptr;
|
2009-02-23 19:20:06 +03:00
|
|
|
|
2009-11-15 18:10:49 +03:00
|
|
|
th->locking_mutex = Qfalse;
|
2008-05-11 08:15:29 +04:00
|
|
|
thread_cleanup_func_before_exec(th_ptr);
|
2010-12-20 18:16:41 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Unfortunately, we can't release native threading resource at fork
|
|
|
|
* because libc may have unstable locking state therefore touching
|
|
|
|
* a threading resource may cause a deadlock.
|
2018-01-12 03:15:28 +03:00
|
|
|
*
|
|
|
|
* FIXME: Skipping native_mutex_destroy(pthread_mutex_destroy) is safe
|
|
|
|
* with NPTL, but native_thread_destroy calls pthread_cond_destroy
|
|
|
|
* which calls free(3), so there is a small memory leak atfork, here.
|
2010-12-20 18:16:41 +03:00
|
|
|
*/
|
|
|
|
if (atfork)
|
|
|
|
return;
|
|
|
|
|
mjit.c: merge MJIT infrastructure
that allows to JIT-compile Ruby methods by generating C code and
using C compiler. See the first comment of mjit.c to know what this
file does.
mjit.c is authored by Vladimir Makarov <vmakarov@redhat.com>.
After he invented great method JIT infrastructure for MRI as MJIT,
Lars Kanis <lars@greiz-reinsdorf.de> sent the patch to support MinGW
in MJIT. In addition to merging it, I ported pthread to Windows native
threads. Now this MJIT infrastructure can be compiled on Visual Studio.
This commit simplifies mjit.c to decrease code at initial merge. For
example, this commit does not provide multiple JIT threads support.
We can resurrect them later if we really want them, but I wanted to minimize
diff to make it easier to review this patch.
`/tmp/_mjitXXX` file is renamed to `/tmp/_ruby_mjitXXX` because non-Ruby
developers may not know the name "mjit" and the file name should make
sure it's from Ruby and not from some harmful programs. TODO: it may be
better to store this to some temporary directory which Ruby is already using
by Tempfile, if it's not bad for performance.
mjit.h: New. It has `mjit_exec` interface similar to `vm_exec`, which is
for triggering MJIT. This drops interface for AOT compared to the original
MJIT.
Makefile.in: define macros to let MJIT know the path of MJIT header.
Probably we can refactor this to reduce the number of macros (TODO).
win32/Makefile.sub: ditto.
common.mk: compile mjit.o and mjit_compile.o. Unlike original MJIT, this
commit separates MJIT infrastructure and JIT compiler code as independent
object files. As initial patch is NOT going to have ultra-fast JIT compiler,
it's likely to replace JIT compiler, e.g. original MJIT's compiler or some
future JIT impelementations which are not public now.
inits.c: define MJIT module. This is added because `MJIT.enabled?` was
necessary for testing.
test/lib/zombie_hunter.rb: skip if `MJIT.enabled?`. Obviously this
wouldn't work with current code when JIT is enabled.
test/ruby/test_io.rb: skip this too. This would make no sense with MJIT.
ruby.c: define MJIT CLI options. As major difference from original MJIT,
"-j:l"/"--jit:llvm" are renamed to "--jit-cc" because I want to support
not only gcc/clang but also cl.exe (Visual Studio) in the future. But it
takes only "--jit-cc=gcc", "--jit-cc=clang" for now. And only long "--jit"
options are allowed since some Ruby committers preferred it at Ruby
developers Meeting on January, and some of options are renamed.
This file also triggers to initialize MJIT thread and variables.
eval.c: finalize MJIT worker thread and variables.
test/ruby/test_rubyoptions.rb: fix number of CLI options for --jit.
thread_pthread.c: change for pthread abstraction in MJIT. Prefix rb_ for
functions which are used by other files.
thread_win32.c: ditto, for Windows. Those pthread porting is one of major
works that YARV-MJIT created, which is my fork of MJIT, in Feature 14235.
thread.c: follow rb_ prefix changes
vm.c: trigger MJIT call on VM invocation. Also trigger `mjit_mark` to avoid
SEGV by race between JIT and GC of ISeq. The improvement was provided by
wanabe <s.wanabe@gmail.com>.
In JIT compiler I created and am going to add in my next commit, I found
that having `mjit_exec` after `vm_loop_start:` is harmful because the
JIT-ed function doesn't proceed other ISeqs on RESTORE_REGS of leave insn.
Executing non-FINISH frame is unexpected for my JIT compiler and
`exception_handler` triggers executions of such ISeqs. So `mjit_exec`
here should be executed only when it directly comes from `vm_exec` call.
`RubyVM::MJIT` module and `.enabled?` method is added so that we can skip
some tests which don't expect JIT threads or compiler file descriptors.
vm_insnhelper.h: trigger MJIT on method calls during VM execution.
vm_core.h: add fields required for mjit.c. `bp` must be `cfp[6]` because
rb_control_frame_struct is likely to be casted to another struct. The
last position is the safest place to add the new field.
vm_insnhelper.c: save initial value of cfp->ep as cfp->bp. This is an
optimization which are done in both MJIT and YARV-MJIT. So this change
is added in this commit. Calculating bp from ep is a little heavy work,
so bp is kind of cache for it.
iseq.c: notify ISeq GC to MJIT. We should know which iseq in MJIT queue
is GCed to avoid SEGV. TODO: unload some GCed units in some safe way.
gc.c: add hooks so that MJIT can wait GC, and vice versa. Simultaneous
JIT and GC executions may cause SEGV and so we should synchronize them.
cont.c: save continuation information in MJIT worker. As MJIT shouldn't
unload JIT-ed code which is being used, MJIT wants to know full list of
saved execution contexts for continuation and detect ISeqs in use.
mjit_compile.c: added empty JIT compiler so that you can reuse this commit
to build your own JIT compiler. This commit tries to compile ISeqs but
all of them are considered as not supported in this commit. So you can't
use JIT compiler in this commit yet while we added --jit option now.
Patch author: Vladimir Makarov <vmakarov@redhat.com>.
Contributors:
Takashi Kokubun <takashikkbn@gmail.com>.
wanabe <s.wanabe@gmail.com>.
Lars Kanis <lars@greiz-reinsdorf.de>.
Part of Feature 12589 and 14235.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@62189 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-02-04 09:58:09 +03:00
|
|
|
rb_native_mutex_destroy(&th->interrupt_lock);
|
2007-02-08 23:24:55 +03:00
|
|
|
native_thread_destroy(th);
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
2009-06-08 20:14:06 +04:00
|
|
|
static VALUE rb_threadptr_raise(rb_thread_t *, int, VALUE *);
|
2017-08-10 06:20:39 +03:00
|
|
|
static VALUE rb_thread_to_s(VALUE thread);
|
2007-08-10 00:12:21 +04:00
|
|
|
|
2008-06-14 06:59:19 +04:00
|
|
|
void
|
|
|
|
ruby_thread_init_stack(rb_thread_t *th)
|
|
|
|
{
|
|
|
|
native_thread_init_stack(th);
|
|
|
|
}
|
|
|
|
|
2017-03-14 09:52:44 +03:00
|
|
|
const VALUE *
|
|
|
|
rb_vm_proc_local_ep(VALUE proc)
|
|
|
|
{
|
|
|
|
const VALUE *ep = vm_proc_ep(proc);
|
|
|
|
|
|
|
|
if (ep) {
|
|
|
|
return rb_vm_ep_local_ep(ep);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-14 08:54:33 +03:00
|
|
|
static void
|
2018-11-08 08:01:23 +03:00
|
|
|
thread_do_start(rb_thread_t *th)
|
2017-03-14 08:54:33 +03:00
|
|
|
{
|
|
|
|
native_set_thread_name(th);
|
2018-11-08 08:01:23 +03:00
|
|
|
|
|
|
|
if (th->invoke_type == thread_invoke_type_proc) {
|
|
|
|
VALUE args = th->invoke_arg.proc.args;
|
|
|
|
long args_len = RARRAY_LEN(args);
|
|
|
|
const VALUE *args_ptr;
|
|
|
|
VALUE procval = th->invoke_arg.proc.proc;
|
2017-03-14 08:54:33 +03:00
|
|
|
rb_proc_t *proc;
|
2018-11-08 08:01:23 +03:00
|
|
|
GetProcPtr(procval, proc);
|
|
|
|
|
|
|
|
th->ec->errinfo = Qnil;
|
2018-11-08 08:01:30 +03:00
|
|
|
th->ec->root_lep = rb_vm_proc_local_ep(procval);
|
2017-10-26 11:32:49 +03:00
|
|
|
th->ec->root_svar = Qfalse;
|
2018-11-08 08:01:23 +03:00
|
|
|
|
|
|
|
EXEC_EVENT_HOOK(th->ec, RUBY_EVENT_THREAD_BEGIN, th->self, 0, 0, 0, Qundef);
|
|
|
|
|
|
|
|
if (args_len < 8) {
|
|
|
|
/* free proc.args if the length is enough small */
|
|
|
|
args_ptr = ALLOCA_N(VALUE, args_len);
|
|
|
|
MEMCPY((VALUE *)args_ptr, RARRAY_CONST_PTR_TRANSIENT(args), VALUE, args_len);
|
|
|
|
th->invoke_arg.proc.args = Qnil;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
args_ptr = RARRAY_CONST_PTR(args);
|
|
|
|
}
|
|
|
|
|
|
|
|
th->value = rb_vm_invoke_proc(th->ec, proc,
|
|
|
|
(int)args_len, args_ptr,
|
|
|
|
VM_BLOCK_HANDLER_NONE);
|
|
|
|
|
|
|
|
EXEC_EVENT_HOOK(th->ec, RUBY_EVENT_THREAD_END, th->self, 0, 0, 0, Qundef);
|
2017-03-14 08:54:33 +03:00
|
|
|
}
|
|
|
|
else {
|
2018-11-08 08:01:30 +03:00
|
|
|
th->value = (*th->invoke_arg.func.func)(th->invoke_arg.func.arg);
|
2017-03-14 08:54:33 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-15 16:21:24 +03:00
|
|
|
void rb_ec_clear_current_thread_trace_func(const rb_execution_context_t *ec);
|
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
static int
|
2007-07-12 05:19:18 +04:00
|
|
|
thread_start_func_2(rb_thread_t *th, VALUE *stack_start, VALUE *register_stack_start)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2017-06-23 10:25:52 +03:00
|
|
|
enum ruby_tag_type state;
|
2012-11-28 16:34:15 +04:00
|
|
|
rb_thread_list_t *join_list;
|
2007-08-10 19:01:14 +04:00
|
|
|
rb_thread_t *main_th;
|
2007-08-10 00:12:21 +04:00
|
|
|
VALUE errinfo = Qnil;
|
|
|
|
|
2012-11-29 01:55:14 +04:00
|
|
|
if (th == th->vm->main_thread)
|
2013-04-27 14:33:44 +04:00
|
|
|
rb_bug("thread_start_func_2 must not be used for main thread");
|
2012-11-29 01:55:14 +04:00
|
|
|
|
2008-12-24 19:52:38 +03:00
|
|
|
ruby_thread_set_native(th);
|
|
|
|
|
2017-10-26 11:32:49 +03:00
|
|
|
th->ec->machine.stack_start = stack_start;
|
2007-06-14 12:35:20 +04:00
|
|
|
#ifdef __ia64
|
2017-10-26 11:32:49 +03:00
|
|
|
th->ec->machine.register_stack_start = register_stack_start;
|
2007-06-14 12:35:20 +04:00
|
|
|
#endif
|
* compile.c (iseq_compile_each), gc.c (assign_heap_slot),
(gc_mark_children), parse.y (vtable_alloc, vtable_free, vtable_add),
proc.c (proc_to_s), thread.c (terminate_i, rb_thread_terminate_all),
(thread_start_func_2, blocking_region_begin, blocking_region_end),
(rb_thread_kill), thread_pthread.c (native_thread_create),
(ubf_pthread_cond_signal), vm.c (check_env, thread_free), vm_dump.c
(vm_env_dump_raw, vm_stack_dump_each, vm_thread_dump_state),
(vm_call0): use void pointer for %p.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@20593 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2008-12-09 07:33:55 +03:00
|
|
|
thread_debug("thread start: %p\n", (void *)th);
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2010-11-27 23:15:59 +03:00
|
|
|
gvl_acquire(th->vm, th);
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
* compile.c (iseq_compile_each), gc.c (assign_heap_slot),
(gc_mark_children), parse.y (vtable_alloc, vtable_free, vtable_add),
proc.c (proc_to_s), thread.c (terminate_i, rb_thread_terminate_all),
(thread_start_func_2, blocking_region_begin, blocking_region_end),
(rb_thread_kill), thread_pthread.c (native_thread_create),
(ubf_pthread_cond_signal), vm.c (check_env, thread_free), vm_dump.c
(vm_env_dump_raw, vm_stack_dump_each, vm_thread_dump_state),
(vm_call0): use void pointer for %p.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@20593 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2008-12-09 07:33:55 +03:00
|
|
|
thread_debug("thread start (get lock): %p\n", (void *)th);
|
* blockinlining.c, compile.c, compile.h, error.c, eval.c,
eval_intern.h, eval_jump.h, eval_load.c, eval_method.h,
eval_safe.h, gc.c, insnhelper.h, insns.def, iseq.c, proc.c,
process.c, signal.c, thread.c, thread_pthread.ci, thread_win32.ci,
vm.c, vm.h, vm_dump.c, vm_evalbody.ci, vm_macro.def,
yarv.h, yarvcore.h, yarvcore.c: change type and macro names:
* yarv_*_t -> rb_*_t
* yarv_*_struct -> rb_*_struct
* yarv_tag -> rb_vm_tag
* YARV_* -> RUBY_VM_*
* proc.c, vm.c: move functions about env object creation
from proc.c to vm.c.
* proc.c, yarvcore.c: fix rb_cVM initialization place.
* inits.c: change Init_ISeq() order (after Init_VM).
* ruby.h, proc.c: change declaration place of rb_cEnv
from proc.c to ruby.c.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@11651 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-02-06 22:00:03 +03:00
|
|
|
rb_thread_set_current(th);
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2017-10-26 14:02:13 +03:00
|
|
|
EC_PUSH_TAG(th->ec);
|
2017-12-06 06:16:08 +03:00
|
|
|
if ((state = EC_EXEC_TAG()) == TAG_NONE) {
|
2018-11-08 08:01:30 +03:00
|
|
|
SAVE_ROOT_JMPBUF(th, thread_do_start(th));
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
else {
|
2017-10-26 11:32:49 +03:00
|
|
|
errinfo = th->ec->errinfo;
|
2008-08-06 01:55:23 +04:00
|
|
|
if (state == TAG_FATAL) {
|
|
|
|
/* fatal error within this thread, need to stop whole script */
|
|
|
|
}
|
2012-11-29 05:11:47 +04:00
|
|
|
else if (rb_obj_is_kind_of(errinfo, rb_eSystemExit)) {
|
2012-11-29 06:09:36 +04:00
|
|
|
/* exit on main_thread. */
|
2012-11-29 05:11:47 +04:00
|
|
|
}
|
2008-08-06 01:55:23 +04:00
|
|
|
else {
|
2017-09-19 05:42:08 +03:00
|
|
|
if (th->report_on_exception) {
|
|
|
|
VALUE mesg = rb_thread_to_s(th->self);
|
2017-12-12 21:43:42 +03:00
|
|
|
rb_str_cat_cstr(mesg, " terminated with exception (report_on_exception is true):\n");
|
2017-09-19 05:42:08 +03:00
|
|
|
rb_write_error_str(mesg);
|
2017-10-29 17:06:58 +03:00
|
|
|
rb_ec_error_print(th->ec, errinfo);
|
2017-09-19 05:42:08 +03:00
|
|
|
}
|
|
|
|
if (th->vm->thread_abort_on_exception ||
|
|
|
|
th->abort_on_exception || RTEST(ruby_debug)) {
|
|
|
|
/* exit on main_thread */
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
errinfo = Qnil;
|
|
|
|
}
|
2007-08-10 00:12:21 +04:00
|
|
|
}
|
2006-12-31 18:02:22 +03:00
|
|
|
th->value = Qnil;
|
|
|
|
}
|
|
|
|
|
|
|
|
th->status = THREAD_KILLED;
|
* compile.c (iseq_compile_each), gc.c (assign_heap_slot),
(gc_mark_children), parse.y (vtable_alloc, vtable_free, vtable_add),
proc.c (proc_to_s), thread.c (terminate_i, rb_thread_terminate_all),
(thread_start_func_2, blocking_region_begin, blocking_region_end),
(rb_thread_kill), thread_pthread.c (native_thread_create),
(ubf_pthread_cond_signal), vm.c (check_env, thread_free), vm_dump.c
(vm_env_dump_raw, vm_stack_dump_each, vm_thread_dump_state),
(vm_call0): use void pointer for %p.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@20593 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2008-12-09 07:33:55 +03:00
|
|
|
thread_debug("thread end: %p\n", (void *)th);
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2007-08-10 19:01:14 +04:00
|
|
|
main_th = th->vm->main_thread;
|
2014-05-10 08:32:22 +04:00
|
|
|
if (main_th == th) {
|
|
|
|
ruby_stop(0);
|
|
|
|
}
|
2012-11-29 01:55:14 +04:00
|
|
|
if (RB_TYPE_P(errinfo, T_OBJECT)) {
|
|
|
|
/* treat with normal error object */
|
|
|
|
rb_threadptr_raise(main_th, 1, &errinfo);
|
2007-08-17 11:24:03 +04:00
|
|
|
}
|
2017-10-26 14:02:13 +03:00
|
|
|
EC_POP_TAG();
|
2012-11-29 06:50:11 +04:00
|
|
|
|
2017-11-15 16:21:24 +03:00
|
|
|
rb_ec_clear_current_thread_trace_func(th->ec);
|
|
|
|
|
2012-11-29 06:50:11 +04:00
|
|
|
/* locking_mutex must be Qfalse */
|
|
|
|
if (th->locking_mutex != Qfalse) {
|
|
|
|
rb_bug("thread_start_func_2: locking_mutex must not be set (%p:%"PRIxVALUE")",
|
|
|
|
(void *)th, th->locking_mutex);
|
|
|
|
}
|
2008-06-12 17:01:38 +04:00
|
|
|
|
2009-02-18 04:29:13 +03:00
|
|
|
/* delete self other than main thread from living_threads */
|
vm*: doubly-linked list from ccan to manage vm->living_threads
A doubly-linked list for tracking living threads guarantees
constant-time insert/delete performance with no corner cases of a
hash table. I chose this ccan implementation of doubly-linked
lists over the BSD sys/queue.h implementation since:
1) insertion and removal are both branchless
2) locality is improved if a struct may be a member of multiple lists
(0002 patch in Feature 9632 will introduce a secondary list
for waiting FDs)
This also increases cache locality during iteration: improving
performance in a new IO#close benchmark with many sleeping threads
while still scanning the same number of threads.
vm_thread_close 1.762
* vm_core.h (rb_vm_t): list_head and counter for living_threads
(rb_thread_t): vmlt_node for living_threads linkage
(rb_vm_living_threads_init): new function wrapper
(rb_vm_living_threads_insert): ditto
(rb_vm_living_threads_remove): ditto
* vm.c (rb_vm_living_threads_foreach): new function wrapper
* thread.c (terminate_i, thread_start_func_2, thread_create_core,
thread_fd_close_i, thread_fd_close): update to use new APIs
* vm.c (vm_mark_each_thread_func, rb_vm_mark, ruby_vm_destruct,
vm_memsize, vm_init2, Init_VM): ditto
* vm_trace.c (clear_trace_func_i, rb_clear_trace_func): ditto
* benchmark/bm_vm_thread_close.rb: added to show improvement
* ccan/build_assert/build_assert.h: added as a dependency of list.h
* ccan/check_type/check_type.h: ditto
* ccan/container_of/container_of.h: ditto
* ccan/licenses/BSD-MIT: ditto
* ccan/licenses/CC0: ditto
* ccan/str/str.h: ditto (stripped of unused macros)
* ccan/list/list.h: ditto
* common.mk: add CCAN_LIST_INCLUDES
[ruby-core:61871][Feature 9632 (part 1)]
Apologies for the size of this commit, but I think a good
doubly-linked list will be useful for future features, too.
This may be used to add ordering to a container_of-based hash
table to preserve compatibility if required (e.g. feature 9614).
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@45913 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2014-05-11 03:48:51 +04:00
|
|
|
rb_vm_living_threads_remove(th->vm, th);
|
2015-06-15 01:52:01 +03:00
|
|
|
if (main_th->status == THREAD_KILLED && rb_thread_alone()) {
|
2012-11-29 01:55:14 +04:00
|
|
|
/* I'm last thread. wake up main thread from rb_thread_terminate_all */
|
|
|
|
rb_threadptr_interrupt(main_th);
|
2009-02-18 04:29:13 +03:00
|
|
|
}
|
2007-08-10 19:01:14 +04:00
|
|
|
|
2009-11-03 20:46:28 +03:00
|
|
|
/* wake up joining threads */
|
2012-11-28 16:34:15 +04:00
|
|
|
join_list = th->join_list;
|
|
|
|
while (join_list) {
|
|
|
|
rb_threadptr_interrupt(join_list->th);
|
|
|
|
switch (join_list->th->status) {
|
2008-07-16 23:19:36 +04:00
|
|
|
case THREAD_STOPPED: case THREAD_STOPPED_FOREVER:
|
2012-11-28 16:34:15 +04:00
|
|
|
join_list->th->status = THREAD_RUNNABLE;
|
2008-07-16 23:19:36 +04:00
|
|
|
default: break;
|
|
|
|
}
|
2012-11-28 16:34:15 +04:00
|
|
|
join_list = join_list->next;
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
2007-11-21 12:06:06 +03:00
|
|
|
|
2012-11-29 06:50:11 +04:00
|
|
|
rb_threadptr_unlock_all_locking_mutexes(th);
|
|
|
|
rb_check_deadlock(th->vm);
|
|
|
|
|
2017-11-06 08:41:48 +03:00
|
|
|
rb_fiber_close(th->ec->fiber_ptr);
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
2012-11-29 01:55:14 +04:00
|
|
|
thread_cleanup_func(th, FALSE);
|
|
|
|
gvl_release(th->vm);
|
2007-08-10 00:12:21 +04:00
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
2007-12-05 10:18:52 +03:00
|
|
|
thread_create_core(VALUE thval, VALUE args, VALUE (*fn)(ANYARGS))
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2017-06-28 07:49:30 +03:00
|
|
|
rb_thread_t *th = rb_thread_ptr(thval), *current_th = GET_THREAD();
|
2009-11-12 07:57:39 +03:00
|
|
|
int err;
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2015-07-16 23:42:43 +03:00
|
|
|
if (OBJ_FROZEN(current_th->thgroup)) {
|
2008-01-18 21:48:12 +03:00
|
|
|
rb_raise(rb_eThreadError,
|
|
|
|
"can't start a new thread (frozen ThreadGroup)");
|
|
|
|
}
|
2007-02-05 15:21:01 +03:00
|
|
|
|
2018-11-08 08:01:23 +03:00
|
|
|
if (fn) {
|
|
|
|
th->invoke_type = thread_invoke_type_func;
|
|
|
|
th->invoke_arg.func.func = fn;
|
|
|
|
th->invoke_arg.func.arg = (void *)args;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
th->invoke_type = thread_invoke_type_proc;
|
|
|
|
th->invoke_arg.proc.proc = rb_block_proc();
|
|
|
|
th->invoke_arg.proc.args = args;
|
|
|
|
}
|
2007-02-05 15:21:01 +03:00
|
|
|
|
2012-07-19 18:19:40 +04:00
|
|
|
th->priority = current_th->priority;
|
|
|
|
th->thgroup = current_th->thgroup;
|
2007-08-10 18:54:50 +04:00
|
|
|
|
2012-12-23 14:18:58 +04:00
|
|
|
th->pending_interrupt_queue = rb_ary_tmp_new(0);
|
|
|
|
th->pending_interrupt_queue_checked = 0;
|
|
|
|
th->pending_interrupt_mask_stack = rb_ary_dup(current_th->pending_interrupt_mask_stack);
|
* include/ruby/ruby.h: constify RBasic::klass and add
RBASIC_CLASS(obj) macro which returns a class of `obj'.
This change is a part of RGENGC branch [ruby-trunk - Feature #8339].
* object.c: add new function rb_obj_reveal().
This function reveal interal (hidden) object by rb_obj_hide().
Note that do not change class before and after hiding.
Only permitted example is:
klass = RBASIC_CLASS(obj);
rb_obj_hide(obj);
....
rb_obj_reveal(obj, klass);
TODO: API design. rb_obj_reveal() should be replaced with others.
TODO: modify constified variables using cast may be harmful for
compiler's analysis and optimizaton.
Any idea to prohibt inserting RBasic::klass directly?
If rename RBasic::klass and force to use RBASIC_CLASS(obj),
then all codes such as `RBASIC(obj)->klass' will be
compilation error. Is it acceptable? (We have similar
experience at Ruby 1.9,
for example "RARRAY(ary)->ptr" to "RARRAY_PTR(ary)".
* internal.h: add some macros.
* RBASIC_CLEAR_CLASS(obj) clear RBasic::klass to make it internal
object.
* RBASIC_SET_CLASS(obj, cls) set RBasic::klass.
* RBASIC_SET_CLASS_RAW(obj, cls) same as RBASIC_SET_CLASS
without write barrier (planned).
* RCLASS_SET_SUPER(a, b) set super class of a.
* array.c, class.c, compile.c, encoding.c, enum.c, error.c, eval.c,
file.c, gc.c, hash.c, io.c, iseq.c, marshal.c, object.c,
parse.y, proc.c, process.c, random.c, ruby.c, sprintf.c,
string.c, thread.c, transcode.c, vm.c, vm_eval.c, win32/file.c:
Use above macros and functions to access RBasic::klass.
* ext/coverage/coverage.c, ext/readline/readline.c,
ext/socket/ancdata.c, ext/socket/init.c,
* ext/zlib/zlib.c: ditto.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@40691 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2013-05-13 14:49:11 +04:00
|
|
|
RBASIC_CLEAR_CLASS(th->pending_interrupt_mask_stack);
|
2012-07-18 09:46:40 +04:00
|
|
|
|
mjit.c: merge MJIT infrastructure
that allows to JIT-compile Ruby methods by generating C code and
using C compiler. See the first comment of mjit.c to know what this
file does.
mjit.c is authored by Vladimir Makarov <vmakarov@redhat.com>.
After he invented great method JIT infrastructure for MRI as MJIT,
Lars Kanis <lars@greiz-reinsdorf.de> sent the patch to support MinGW
in MJIT. In addition to merging it, I ported pthread to Windows native
threads. Now this MJIT infrastructure can be compiled on Visual Studio.
This commit simplifies mjit.c to decrease code at initial merge. For
example, this commit does not provide multiple JIT threads support.
We can resurrect them later if we really want them, but I wanted to minimize
diff to make it easier to review this patch.
`/tmp/_mjitXXX` file is renamed to `/tmp/_ruby_mjitXXX` because non-Ruby
developers may not know the name "mjit" and the file name should make
sure it's from Ruby and not from some harmful programs. TODO: it may be
better to store this to some temporary directory which Ruby is already using
by Tempfile, if it's not bad for performance.
mjit.h: New. It has `mjit_exec` interface similar to `vm_exec`, which is
for triggering MJIT. This drops interface for AOT compared to the original
MJIT.
Makefile.in: define macros to let MJIT know the path of MJIT header.
Probably we can refactor this to reduce the number of macros (TODO).
win32/Makefile.sub: ditto.
common.mk: compile mjit.o and mjit_compile.o. Unlike original MJIT, this
commit separates MJIT infrastructure and JIT compiler code as independent
object files. As initial patch is NOT going to have ultra-fast JIT compiler,
it's likely to replace JIT compiler, e.g. original MJIT's compiler or some
future JIT impelementations which are not public now.
inits.c: define MJIT module. This is added because `MJIT.enabled?` was
necessary for testing.
test/lib/zombie_hunter.rb: skip if `MJIT.enabled?`. Obviously this
wouldn't work with current code when JIT is enabled.
test/ruby/test_io.rb: skip this too. This would make no sense with MJIT.
ruby.c: define MJIT CLI options. As major difference from original MJIT,
"-j:l"/"--jit:llvm" are renamed to "--jit-cc" because I want to support
not only gcc/clang but also cl.exe (Visual Studio) in the future. But it
takes only "--jit-cc=gcc", "--jit-cc=clang" for now. And only long "--jit"
options are allowed since some Ruby committers preferred it at Ruby
developers Meeting on January, and some of options are renamed.
This file also triggers to initialize MJIT thread and variables.
eval.c: finalize MJIT worker thread and variables.
test/ruby/test_rubyoptions.rb: fix number of CLI options for --jit.
thread_pthread.c: change for pthread abstraction in MJIT. Prefix rb_ for
functions which are used by other files.
thread_win32.c: ditto, for Windows. Those pthread porting is one of major
works that YARV-MJIT created, which is my fork of MJIT, in Feature 14235.
thread.c: follow rb_ prefix changes
vm.c: trigger MJIT call on VM invocation. Also trigger `mjit_mark` to avoid
SEGV by race between JIT and GC of ISeq. The improvement was provided by
wanabe <s.wanabe@gmail.com>.
In JIT compiler I created and am going to add in my next commit, I found
that having `mjit_exec` after `vm_loop_start:` is harmful because the
JIT-ed function doesn't proceed other ISeqs on RESTORE_REGS of leave insn.
Executing non-FINISH frame is unexpected for my JIT compiler and
`exception_handler` triggers executions of such ISeqs. So `mjit_exec`
here should be executed only when it directly comes from `vm_exec` call.
`RubyVM::MJIT` module and `.enabled?` method is added so that we can skip
some tests which don't expect JIT threads or compiler file descriptors.
vm_insnhelper.h: trigger MJIT on method calls during VM execution.
vm_core.h: add fields required for mjit.c. `bp` must be `cfp[6]` because
rb_control_frame_struct is likely to be casted to another struct. The
last position is the safest place to add the new field.
vm_insnhelper.c: save initial value of cfp->ep as cfp->bp. This is an
optimization which are done in both MJIT and YARV-MJIT. So this change
is added in this commit. Calculating bp from ep is a little heavy work,
so bp is kind of cache for it.
iseq.c: notify ISeq GC to MJIT. We should know which iseq in MJIT queue
is GCed to avoid SEGV. TODO: unload some GCed units in some safe way.
gc.c: add hooks so that MJIT can wait GC, and vice versa. Simultaneous
JIT and GC executions may cause SEGV and so we should synchronize them.
cont.c: save continuation information in MJIT worker. As MJIT shouldn't
unload JIT-ed code which is being used, MJIT wants to know full list of
saved execution contexts for continuation and detect ISeqs in use.
mjit_compile.c: added empty JIT compiler so that you can reuse this commit
to build your own JIT compiler. This commit tries to compile ISeqs but
all of them are considered as not supported in this commit. So you can't
use JIT compiler in this commit yet while we added --jit option now.
Patch author: Vladimir Makarov <vmakarov@redhat.com>.
Contributors:
Takashi Kokubun <takashikkbn@gmail.com>.
wanabe <s.wanabe@gmail.com>.
Lars Kanis <lars@greiz-reinsdorf.de>.
Part of Feature 12589 and 14235.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@62189 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-02-04 09:58:09 +03:00
|
|
|
rb_native_mutex_initialize(&th->interrupt_lock);
|
2010-03-24 17:47:00 +03:00
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
/* kick thread */
|
2009-11-12 07:57:39 +03:00
|
|
|
err = native_thread_create(th);
|
|
|
|
if (err) {
|
|
|
|
th->status = THREAD_KILLED;
|
2013-11-16 20:57:03 +04:00
|
|
|
rb_raise(rb_eThreadError, "can't create Thread: %s", strerror(err));
|
2009-11-12 07:57:39 +03:00
|
|
|
}
|
vm*: doubly-linked list from ccan to manage vm->living_threads
A doubly-linked list for tracking living threads guarantees
constant-time insert/delete performance with no corner cases of a
hash table. I chose this ccan implementation of doubly-linked
lists over the BSD sys/queue.h implementation since:
1) insertion and removal are both branchless
2) locality is improved if a struct may be a member of multiple lists
(0002 patch in Feature 9632 will introduce a secondary list
for waiting FDs)
This also increases cache locality during iteration: improving
performance in a new IO#close benchmark with many sleeping threads
while still scanning the same number of threads.
vm_thread_close 1.762
* vm_core.h (rb_vm_t): list_head and counter for living_threads
(rb_thread_t): vmlt_node for living_threads linkage
(rb_vm_living_threads_init): new function wrapper
(rb_vm_living_threads_insert): ditto
(rb_vm_living_threads_remove): ditto
* vm.c (rb_vm_living_threads_foreach): new function wrapper
* thread.c (terminate_i, thread_start_func_2, thread_create_core,
thread_fd_close_i, thread_fd_close): update to use new APIs
* vm.c (vm_mark_each_thread_func, rb_vm_mark, ruby_vm_destruct,
vm_memsize, vm_init2, Init_VM): ditto
* vm_trace.c (clear_trace_func_i, rb_clear_trace_func): ditto
* benchmark/bm_vm_thread_close.rb: added to show improvement
* ccan/build_assert/build_assert.h: added as a dependency of list.h
* ccan/check_type/check_type.h: ditto
* ccan/container_of/container_of.h: ditto
* ccan/licenses/BSD-MIT: ditto
* ccan/licenses/CC0: ditto
* ccan/str/str.h: ditto (stripped of unused macros)
* ccan/list/list.h: ditto
* common.mk: add CCAN_LIST_INCLUDES
[ruby-core:61871][Feature 9632 (part 1)]
Apologies for the size of this commit, but I think a good
doubly-linked list will be useful for future features, too.
This may be used to add ordering to a container_of-based hash
table to preserve compatibility if required (e.g. feature 9614).
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@45913 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2014-05-11 03:48:51 +04:00
|
|
|
rb_vm_living_threads_insert(th->vm, th);
|
2006-12-31 18:02:22 +03:00
|
|
|
return thval;
|
|
|
|
}
|
|
|
|
|
2018-11-08 08:01:23 +03:00
|
|
|
#define threadptr_initialized(th) ((th)->invoke_type != thread_invoke_type_none)
|
2016-04-15 15:12:25 +03:00
|
|
|
|
2013-02-25 07:50:20 +04:00
|
|
|
/*
|
|
|
|
* call-seq:
|
|
|
|
* Thread.new { ... } -> thread
|
|
|
|
* Thread.new(*args, &proc) -> thread
|
|
|
|
* Thread.new(*args) { |args| ... } -> thread
|
|
|
|
*
|
|
|
|
* Creates a new thread executing the given block.
|
|
|
|
*
|
|
|
|
* Any +args+ given to ::new will be passed to the block:
|
|
|
|
*
|
|
|
|
* arr = []
|
|
|
|
* a, b, c = 1, 2, 3
|
|
|
|
* Thread.new(a,b,c) { |d,e,f| arr << d << e << f }.join
|
|
|
|
* arr #=> [1, 2, 3]
|
|
|
|
*
|
|
|
|
* A ThreadError exception is raised if ::new is called without a block.
|
|
|
|
*
|
|
|
|
* If you're going to subclass Thread, be sure to call super in your
|
|
|
|
* +initialize+ method, otherwise a ThreadError will be raised.
|
|
|
|
*/
|
2007-02-05 15:21:01 +03:00
|
|
|
static VALUE
|
2007-12-05 10:18:52 +03:00
|
|
|
thread_s_new(int argc, VALUE *argv, VALUE klass)
|
2007-02-05 15:21:01 +03:00
|
|
|
{
|
2016-04-15 16:30:03 +03:00
|
|
|
rb_thread_t *th;
|
2007-12-05 10:18:52 +03:00
|
|
|
VALUE thread = rb_thread_alloc(klass);
|
2011-07-10 11:46:00 +04:00
|
|
|
|
2012-11-28 08:43:15 +04:00
|
|
|
if (GET_VM()->main_thread->status == THREAD_KILLED)
|
2011-07-10 11:46:00 +04:00
|
|
|
rb_raise(rb_eThreadError, "can't alloc thread");
|
|
|
|
|
2007-12-05 10:18:52 +03:00
|
|
|
rb_obj_call_init(thread, argc, argv);
|
2017-06-28 07:49:30 +03:00
|
|
|
th = rb_thread_ptr(thread);
|
2016-04-15 16:30:03 +03:00
|
|
|
if (!threadptr_initialized(th)) {
|
|
|
|
rb_raise(rb_eThreadError, "uninitialized thread - check `%"PRIsVALUE"#initialize'",
|
|
|
|
klass);
|
|
|
|
}
|
2007-12-05 10:18:52 +03:00
|
|
|
return thread;
|
|
|
|
}
|
|
|
|
|
2007-12-24 10:52:39 +03:00
|
|
|
/*
|
|
|
|
* call-seq:
|
2010-05-18 01:07:33 +04:00
|
|
|
* Thread.start([args]*) {|args| block } -> thread
|
|
|
|
* Thread.fork([args]*) {|args| block } -> thread
|
2007-12-24 10:52:39 +03:00
|
|
|
*
|
2013-02-25 07:50:20 +04:00
|
|
|
* Basically the same as ::new. However, if class Thread is subclassed, then
|
|
|
|
* calling +start+ in that subclass will not invoke the subclass's
|
|
|
|
* +initialize+ method.
|
2007-12-24 10:52:39 +03:00
|
|
|
*/
|
|
|
|
|
2007-12-05 10:18:52 +03:00
|
|
|
static VALUE
|
|
|
|
thread_start(VALUE klass, VALUE args)
|
|
|
|
{
|
|
|
|
return thread_create_core(rb_thread_alloc(klass), args, 0);
|
|
|
|
}
|
|
|
|
|
2018-11-08 08:01:23 +03:00
|
|
|
static VALUE
|
|
|
|
threadptr_invoke_proc_location(rb_thread_t *th)
|
|
|
|
{
|
|
|
|
if (th->invoke_type == thread_invoke_type_proc) {
|
|
|
|
return rb_proc_location(th->invoke_arg.proc.proc);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return Qnil;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-09-17 08:51:33 +04:00
|
|
|
/* :nodoc: */
|
2007-12-05 10:18:52 +03:00
|
|
|
static VALUE
|
|
|
|
thread_initialize(VALUE thread, VALUE args)
|
|
|
|
{
|
2017-06-28 07:49:30 +03:00
|
|
|
rb_thread_t *th = rb_thread_ptr(thread);
|
|
|
|
|
2007-12-05 10:18:52 +03:00
|
|
|
if (!rb_block_given_p()) {
|
2018-11-08 08:01:23 +03:00
|
|
|
rb_raise(rb_eThreadError, "must be called with a block");
|
|
|
|
}
|
|
|
|
else if (th->invoke_type != thread_invoke_type_none) {
|
|
|
|
VALUE loc = threadptr_invoke_proc_location(th);
|
|
|
|
if (!NIL_P(loc)) {
|
|
|
|
rb_raise(rb_eThreadError,
|
|
|
|
"already initialized thread - %"PRIsVALUE":%"PRIsVALUE,
|
|
|
|
RARRAY_AREF(loc, 0), RARRAY_AREF(loc, 1));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
rb_raise(rb_eThreadError, "already initialized thread");
|
|
|
|
}
|
2017-06-28 07:49:30 +03:00
|
|
|
}
|
|
|
|
else {
|
2018-11-08 08:01:23 +03:00
|
|
|
return thread_create_core(thread, args, NULL);
|
2007-12-05 10:18:52 +03:00
|
|
|
}
|
2007-02-05 15:21:01 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
VALUE
|
|
|
|
rb_thread_create(VALUE (*fn)(ANYARGS), void *arg)
|
|
|
|
{
|
2007-12-05 10:18:52 +03:00
|
|
|
return thread_create_core(rb_thread_alloc(rb_cThread), (VALUE)arg, fn);
|
2007-02-05 15:21:01 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-04-22 08:13:01 +04:00
|
|
|
struct join_arg {
|
|
|
|
rb_thread_t *target, *waiting;
|
2018-08-25 09:58:35 +03:00
|
|
|
rb_hrtime_t *limit;
|
2008-04-22 08:13:01 +04:00
|
|
|
};
|
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
static VALUE
|
2008-04-22 08:13:01 +04:00
|
|
|
remove_from_join_list(VALUE arg)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2008-04-22 08:13:01 +04:00
|
|
|
struct join_arg *p = (struct join_arg *)arg;
|
|
|
|
rb_thread_t *target_th = p->target, *th = p->waiting;
|
2006-12-31 18:02:22 +03:00
|
|
|
|
|
|
|
if (target_th->status != THREAD_KILLED) {
|
2012-11-28 16:34:15 +04:00
|
|
|
rb_thread_list_t **p = &target_th->join_list;
|
2008-04-22 08:13:01 +04:00
|
|
|
|
2012-11-28 16:34:15 +04:00
|
|
|
while (*p) {
|
|
|
|
if ((*p)->th == th) {
|
|
|
|
*p = (*p)->next;
|
2008-04-22 08:13:01 +04:00
|
|
|
break;
|
|
|
|
}
|
2012-11-28 16:34:15 +04:00
|
|
|
p = &(*p)->next;
|
2008-04-22 08:13:01 +04:00
|
|
|
}
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
2008-04-22 08:13:01 +04:00
|
|
|
|
|
|
|
return Qnil;
|
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
thread_join_sleep(VALUE arg)
|
|
|
|
{
|
|
|
|
struct join_arg *p = (struct join_arg *)arg;
|
|
|
|
rb_thread_t *target_th = p->target, *th = p->waiting;
|
2018-09-09 15:42:48 +03:00
|
|
|
rb_hrtime_t end = 0;
|
2018-02-03 22:59:16 +03:00
|
|
|
|
|
|
|
if (p->limit) {
|
2018-08-25 09:58:35 +03:00
|
|
|
end = rb_hrtime_add(*p->limit, rb_hrtime_now());
|
2018-02-03 22:59:16 +03:00
|
|
|
}
|
2008-04-22 08:13:01 +04:00
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
while (target_th->status != THREAD_KILLED) {
|
2018-02-03 22:59:16 +03:00
|
|
|
if (!p->limit) {
|
2017-12-15 10:27:04 +03:00
|
|
|
th->status = THREAD_STOPPED_FOREVER;
|
|
|
|
th->vm->sleeper++;
|
|
|
|
rb_check_deadlock(th->vm);
|
|
|
|
native_sleep(th, 0);
|
|
|
|
th->vm->sleeper--;
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
else {
|
2018-08-25 09:58:35 +03:00
|
|
|
if (hrtime_update_expire(p->limit, end)) {
|
2014-06-11 12:38:09 +04:00
|
|
|
thread_debug("thread_join: timeout (thid: %"PRI_THREAD_ID")\n",
|
|
|
|
thread_id_str(target_th));
|
2008-04-22 08:13:01 +04:00
|
|
|
return Qfalse;
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
2017-12-17 11:26:17 +03:00
|
|
|
th->status = THREAD_STOPPED;
|
2018-02-03 22:59:16 +03:00
|
|
|
native_sleep(th, p->limit);
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
2017-12-17 11:26:17 +03:00
|
|
|
RUBY_VM_CHECK_INTS_BLOCKING(th->ec);
|
|
|
|
th->status = THREAD_RUNNABLE;
|
2017-12-15 06:07:10 +03:00
|
|
|
thread_debug("thread_join: interrupted (thid: %"PRI_THREAD_ID", status: %s)\n",
|
|
|
|
thread_id_str(target_th), thread_status_name(target_th, TRUE));
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
2008-04-22 08:13:01 +04:00
|
|
|
return Qtrue;
|
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
2018-08-25 09:58:35 +03:00
|
|
|
thread_join(rb_thread_t *target_th, rb_hrtime_t *rel)
|
2008-04-22 08:13:01 +04:00
|
|
|
{
|
|
|
|
rb_thread_t *th = GET_THREAD();
|
|
|
|
struct join_arg arg;
|
|
|
|
|
2012-11-27 06:00:09 +04:00
|
|
|
if (th == target_th) {
|
|
|
|
rb_raise(rb_eThreadError, "Target thread must not be current thread");
|
|
|
|
}
|
2012-11-27 06:00:19 +04:00
|
|
|
if (GET_VM()->main_thread == target_th) {
|
|
|
|
rb_raise(rb_eThreadError, "Target thread must not be main thread");
|
|
|
|
}
|
2012-11-27 06:00:09 +04:00
|
|
|
|
2008-04-22 08:13:01 +04:00
|
|
|
arg.target = target_th;
|
|
|
|
arg.waiting = th;
|
2018-08-25 09:58:35 +03:00
|
|
|
arg.limit = rel;
|
2008-04-22 08:13:01 +04:00
|
|
|
|
2017-12-15 06:07:10 +03:00
|
|
|
thread_debug("thread_join (thid: %"PRI_THREAD_ID", status: %s)\n",
|
|
|
|
thread_id_str(target_th), thread_status_name(target_th, TRUE));
|
2008-04-22 08:13:01 +04:00
|
|
|
|
|
|
|
if (target_th->status != THREAD_KILLED) {
|
2012-11-28 16:34:15 +04:00
|
|
|
rb_thread_list_t list;
|
|
|
|
list.next = target_th->join_list;
|
|
|
|
list.th = th;
|
|
|
|
target_th->join_list = &list;
|
2008-04-22 08:13:01 +04:00
|
|
|
if (!rb_ensure(thread_join_sleep, (VALUE)&arg,
|
|
|
|
remove_from_join_list, (VALUE)&arg)) {
|
|
|
|
return Qnil;
|
|
|
|
}
|
|
|
|
}
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2017-12-15 06:07:10 +03:00
|
|
|
thread_debug("thread_join: success (thid: %"PRI_THREAD_ID", status: %s)\n",
|
|
|
|
thread_id_str(target_th), thread_status_name(target_th, TRUE));
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2017-10-26 11:32:49 +03:00
|
|
|
if (target_th->ec->errinfo != Qnil) {
|
|
|
|
VALUE err = target_th->ec->errinfo;
|
2006-12-31 18:02:22 +03:00
|
|
|
|
|
|
|
if (FIXNUM_P(err)) {
|
2015-03-10 11:28:22 +03:00
|
|
|
switch (err) {
|
|
|
|
case INT2FIX(TAG_FATAL):
|
2017-12-15 06:07:10 +03:00
|
|
|
thread_debug("thread_join: terminated (thid: %"PRI_THREAD_ID", status: %s)\n",
|
|
|
|
thread_id_str(target_th), thread_status_name(target_th, TRUE));
|
|
|
|
|
2015-03-10 11:28:22 +03:00
|
|
|
/* OK. killed. */
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
rb_bug("thread_join: Fixnum (%d) should not reach here.", FIX2INT(err));
|
|
|
|
}
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
2017-10-26 11:32:49 +03:00
|
|
|
else if (THROW_DATA_P(target_th->ec->errinfo)) {
|
2015-03-10 21:53:28 +03:00
|
|
|
rb_bug("thread_join: THROW_DATA should not reach here.");
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
else {
|
2007-02-14 05:42:11 +03:00
|
|
|
/* normal exception */
|
2006-12-31 18:02:22 +03:00
|
|
|
rb_exc_raise(err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return target_th->self;
|
|
|
|
}
|
|
|
|
|
2018-08-25 09:58:35 +03:00
|
|
|
static rb_hrtime_t *double2hrtime(rb_hrtime_t *, double);
|
2018-02-03 22:59:16 +03:00
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
/*
|
|
|
|
* call-seq:
|
2010-05-18 01:07:33 +04:00
|
|
|
* thr.join -> thr
|
|
|
|
* thr.join(limit) -> thr
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2013-02-28 00:36:59 +04:00
|
|
|
* The calling thread will suspend execution and run this +thr+.
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2013-02-28 00:36:59 +04:00
|
|
|
* Does not return until +thr+ exits or until the given +limit+ seconds have
|
|
|
|
* passed.
|
|
|
|
*
|
2013-04-27 14:33:44 +04:00
|
|
|
* If the time limit expires, +nil+ will be returned, otherwise +thr+ is
|
2013-02-28 00:36:59 +04:00
|
|
|
* returned.
|
|
|
|
*
|
|
|
|
* Any threads not joined will be killed when the main program exits.
|
|
|
|
*
|
|
|
|
* If +thr+ had previously raised an exception and the ::abort_on_exception or
|
|
|
|
* $DEBUG flags are not set, (so the exception has not yet been processed), it
|
|
|
|
* will be processed at this time.
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2006-12-31 18:02:22 +03:00
|
|
|
* a = Thread.new { print "a"; sleep(10); print "b"; print "c" }
|
|
|
|
* x = Thread.new { print "x"; Thread.pass; print "y"; print "z" }
|
2013-04-27 14:33:44 +04:00
|
|
|
* x.join # Let thread x finish, thread a will be killed on exit.
|
2013-02-28 00:36:59 +04:00
|
|
|
* #=> "axyz"
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2013-02-28 00:36:59 +04:00
|
|
|
* The following example illustrates the +limit+ parameter.
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2006-12-31 18:02:22 +03:00
|
|
|
* y = Thread.new { 4.times { sleep 0.1; puts 'tick... ' }}
|
|
|
|
* puts "Waiting" until y.join(0.15)
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2013-02-28 00:36:59 +04:00
|
|
|
* This will produce:
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2006-12-31 18:02:22 +03:00
|
|
|
* tick...
|
|
|
|
* Waiting
|
|
|
|
* tick...
|
2013-02-28 00:36:59 +04:00
|
|
|
* Waiting
|
|
|
|
* tick...
|
2006-12-31 18:02:22 +03:00
|
|
|
* tick...
|
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
2007-02-05 15:21:01 +03:00
|
|
|
thread_join_m(int argc, VALUE *argv, VALUE self)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
|
|
|
VALUE limit;
|
2018-08-25 09:58:35 +03:00
|
|
|
rb_hrtime_t rel, *to = 0;
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
|
2018-02-03 22:59:16 +03:00
|
|
|
/*
|
|
|
|
* This supports INFINITY and negative values, so we can't use
|
|
|
|
* rb_time_interval right now...
|
|
|
|
*/
|
2018-12-06 10:49:24 +03:00
|
|
|
if (!rb_check_arity(argc, 0, 1) || NIL_P(argv[0])) {
|
|
|
|
/* unlimited */
|
|
|
|
}
|
|
|
|
else if (FIXNUM_P(limit = argv[0])) {
|
2018-08-25 09:58:35 +03:00
|
|
|
rel = rb_sec2hrtime(NUM2TIMET(limit));
|
|
|
|
to = &rel;
|
2018-12-06 10:49:24 +03:00
|
|
|
}
|
|
|
|
else {
|
2018-08-25 09:58:35 +03:00
|
|
|
to = double2hrtime(&rel, rb_num2dbl(limit));
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
2007-12-25 07:16:06 +03:00
|
|
|
|
2018-08-25 09:58:35 +03:00
|
|
|
return thread_join(rb_thread_ptr(self), to);
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* call-seq:
|
2010-05-18 01:07:33 +04:00
|
|
|
* thr.value -> obj
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2015-01-28 15:32:31 +03:00
|
|
|
* Waits for +thr+ to complete, using #join, and returns its value or raises
|
|
|
|
* the exception which terminated the thread.
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2006-12-31 18:02:22 +03:00
|
|
|
* a = Thread.new { 2 + 2 }
|
|
|
|
* a.value #=> 4
|
2015-01-28 15:32:31 +03:00
|
|
|
*
|
|
|
|
* b = Thread.new { raise 'something went wrong' }
|
|
|
|
* b.value #=> RuntimeError: something went wrong
|
2006-12-31 18:02:22 +03:00
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
2007-02-05 15:21:01 +03:00
|
|
|
thread_value(VALUE self)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2017-06-28 07:49:30 +03:00
|
|
|
rb_thread_t *th = rb_thread_ptr(self);
|
2018-02-03 22:59:16 +03:00
|
|
|
thread_join(th, 0);
|
2006-12-31 18:02:22 +03:00
|
|
|
return th->value;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Thread Scheduling
|
|
|
|
*/
|
|
|
|
|
2013-04-27 07:30:50 +04:00
|
|
|
/*
|
2018-02-07 04:57:14 +03:00
|
|
|
* Back when we used "struct timeval", not all platforms implemented
|
|
|
|
* tv_sec as time_t. Nowadays we use "struct timespec" and tv_sec
|
|
|
|
* seems to be implemented more consistently across platforms.
|
|
|
|
* At least other parts of our code hasn't had to deal with non-time_t
|
|
|
|
* tv_sec in timespec...
|
2013-04-27 07:30:50 +04:00
|
|
|
*/
|
2018-02-07 04:57:14 +03:00
|
|
|
#define TIMESPEC_SEC_MAX TIMET_MAX
|
|
|
|
#define TIMESPEC_SEC_MIN TIMET_MIN
|
2013-04-27 07:30:50 +04:00
|
|
|
|
2018-08-25 09:58:35 +03:00
|
|
|
static rb_hrtime_t *
|
|
|
|
double2hrtime(rb_hrtime_t *hrt, double d)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2018-02-07 04:57:14 +03:00
|
|
|
/* assume timespec.tv_sec has same signedness as time_t */
|
|
|
|
const double TIMESPEC_SEC_MAX_PLUS_ONE = TIMET_MAX_PLUS_ONE;
|
2013-03-31 01:08:42 +04:00
|
|
|
|
2018-02-07 04:57:14 +03:00
|
|
|
if (TIMESPEC_SEC_MAX_PLUS_ONE <= d) {
|
2018-02-18 06:00:28 +03:00
|
|
|
return NULL;
|
2013-03-26 19:30:27 +04:00
|
|
|
}
|
2018-02-18 06:00:33 +03:00
|
|
|
else if (d <= 0) {
|
2018-08-25 09:58:35 +03:00
|
|
|
*hrt = 0;
|
2013-03-26 17:16:31 +04:00
|
|
|
}
|
2013-03-26 19:30:27 +04:00
|
|
|
else {
|
2018-08-25 09:58:35 +03:00
|
|
|
*hrt = (rb_hrtime_t)(d * (double)RB_HRTIME_PER_SEC);
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
2018-08-25 09:58:35 +03:00
|
|
|
return hrt;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
getclockofday(struct timespec *ts)
|
|
|
|
{
|
|
|
|
#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
|
|
|
|
if (clock_gettime(CLOCK_MONOTONIC, ts) == 0)
|
|
|
|
return;
|
|
|
|
#endif
|
|
|
|
rb_timespec_now(ts);
|
|
|
|
}
|
|
|
|
|
2018-08-25 12:02:50 +03:00
|
|
|
/*
|
|
|
|
* Don't inline this, since library call is already time consuming
|
|
|
|
* and we don't want "struct timespec" on stack too long for GC
|
|
|
|
*/
|
|
|
|
NOINLINE(rb_hrtime_t rb_hrtime_now(void));
|
2018-08-25 09:58:35 +03:00
|
|
|
rb_hrtime_t
|
|
|
|
rb_hrtime_now(void)
|
|
|
|
{
|
|
|
|
struct timespec ts;
|
|
|
|
|
|
|
|
getclockofday(&ts);
|
|
|
|
return rb_timespec2hrtime(&ts);
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2018-06-13 13:00:46 +03:00
|
|
|
sleep_forever(rb_thread_t *th, unsigned int fl)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2012-11-20 05:21:19 +04:00
|
|
|
enum rb_thread_status prev_status = th->status;
|
2018-06-13 13:00:46 +03:00
|
|
|
enum rb_thread_status status;
|
2018-08-18 12:07:36 +03:00
|
|
|
int woke;
|
2008-07-16 23:19:36 +04:00
|
|
|
|
2018-06-13 13:00:46 +03:00
|
|
|
status = fl & SLEEP_DEADLOCKABLE ? THREAD_STOPPED_FOREVER : THREAD_STOPPED;
|
2018-08-18 21:29:28 +03:00
|
|
|
th->status = status;
|
2018-08-19 03:01:03 +03:00
|
|
|
RUBY_VM_CHECK_INTS_BLOCKING(th->ec);
|
2012-12-04 22:38:21 +04:00
|
|
|
while (th->status == status) {
|
2018-06-13 13:00:46 +03:00
|
|
|
if (fl & SLEEP_DEADLOCKABLE) {
|
2012-11-20 05:21:19 +04:00
|
|
|
th->vm->sleeper++;
|
|
|
|
rb_check_deadlock(th->vm);
|
2008-07-16 23:19:36 +04:00
|
|
|
}
|
2012-11-20 05:21:19 +04:00
|
|
|
native_sleep(th, 0);
|
2018-06-13 13:00:46 +03:00
|
|
|
if (fl & SLEEP_DEADLOCKABLE) {
|
2012-11-20 05:21:19 +04:00
|
|
|
th->vm->sleeper--;
|
2008-07-16 23:19:36 +04:00
|
|
|
}
|
2018-08-18 12:07:36 +03:00
|
|
|
woke = vm_check_ints_blocking(th->ec);
|
|
|
|
if (woke && !(fl & SLEEP_SPURIOUS_CHECK))
|
2012-12-04 22:38:21 +04:00
|
|
|
break;
|
|
|
|
}
|
2012-11-20 05:21:19 +04:00
|
|
|
th->status = prev_status;
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
2018-08-26 00:33:55 +03:00
|
|
|
/*
|
|
|
|
* at least gcc 7.2 and 7.3 complains about "rb_hrtime_t end"
|
|
|
|
* being uninitialized, maybe other versions, too.
|
|
|
|
*/
|
|
|
|
COMPILER_WARNING_PUSH
|
|
|
|
#if defined(__GNUC__) && __GNUC__ == 7 && __GNUC_MINOR__ <= 3
|
|
|
|
COMPILER_WARNING_IGNORED(-Wmaybe-uninitialized)
|
|
|
|
#endif
|
2018-09-14 11:27:44 +03:00
|
|
|
#ifndef PRIu64
|
|
|
|
#define PRIu64 PRI_64_PREFIX "u"
|
|
|
|
#endif
|
2018-02-18 03:38:40 +03:00
|
|
|
/*
|
|
|
|
* @end is the absolute time when @ts is set to expire
|
|
|
|
* Returns true if @end has past
|
|
|
|
* Updates @ts and returns false otherwise
|
|
|
|
*/
|
2017-12-17 11:26:12 +03:00
|
|
|
static int
|
2018-08-25 09:58:35 +03:00
|
|
|
hrtime_update_expire(rb_hrtime_t *timeout, const rb_hrtime_t end)
|
|
|
|
{
|
|
|
|
rb_hrtime_t now = rb_hrtime_now();
|
|
|
|
|
|
|
|
if (now > end) return 1;
|
|
|
|
thread_debug("hrtime_update_expire: "
|
2018-09-14 11:27:44 +03:00
|
|
|
"%"PRIu64" > %"PRIu64"\n",
|
2018-09-13 13:59:12 +03:00
|
|
|
(uint64_t)end, (uint64_t)now);
|
2018-08-25 09:58:35 +03:00
|
|
|
*timeout = end - now;
|
2017-12-17 11:26:12 +03:00
|
|
|
return 0;
|
|
|
|
}
|
2018-08-26 00:33:55 +03:00
|
|
|
COMPILER_WARNING_POP
|
2017-12-17 11:26:12 +03:00
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
static void
|
2018-08-25 09:58:35 +03:00
|
|
|
sleep_hrtime(rb_thread_t *th, rb_hrtime_t rel, unsigned int fl)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2012-11-20 05:21:19 +04:00
|
|
|
enum rb_thread_status prev_status = th->status;
|
2018-08-18 12:07:36 +03:00
|
|
|
int woke;
|
2018-08-25 09:58:35 +03:00
|
|
|
rb_hrtime_t end = rb_hrtime_add(rb_hrtime_now(), rel);
|
2008-07-16 23:19:36 +04:00
|
|
|
|
2018-08-18 21:29:28 +03:00
|
|
|
th->status = THREAD_STOPPED;
|
2018-08-19 03:01:03 +03:00
|
|
|
RUBY_VM_CHECK_INTS_BLOCKING(th->ec);
|
2012-12-04 22:38:21 +04:00
|
|
|
while (th->status == THREAD_STOPPED) {
|
2018-08-25 09:58:35 +03:00
|
|
|
native_sleep(th, &rel);
|
2018-08-18 12:07:36 +03:00
|
|
|
woke = vm_check_ints_blocking(th->ec);
|
|
|
|
if (woke && !(fl & SLEEP_SPURIOUS_CHECK))
|
2012-12-04 22:38:21 +04:00
|
|
|
break;
|
2018-08-25 09:58:35 +03:00
|
|
|
if (hrtime_update_expire(&rel, end))
|
2018-06-22 09:17:15 +03:00
|
|
|
break;
|
2012-12-04 22:38:21 +04:00
|
|
|
}
|
2012-11-20 05:21:19 +04:00
|
|
|
th->status = prev_status;
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
* cont.c (rb_fiber_current), dln.c (dln_print_undef, dln_undefined),
eval.c (rb_iterator_p, rb_need_block), load.c: (Init_load), ruby.c
(uscore_get, rb_f_chop), st.c (stat_col), signal.c
(rb_signal_buff_size, ruby_sig_finalize), thread.c
(rb_thread_sleep_forever, rb_thread_sleep_deadly, rb_thread_alone):
protoized.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@21929 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2009-02-01 15:51:44 +03:00
|
|
|
rb_thread_sleep_forever(void)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
|
|
|
thread_debug("rb_thread_sleep_forever\n");
|
2018-06-13 13:00:46 +03:00
|
|
|
sleep_forever(GET_THREAD(), SLEEP_SPURIOUS_CHECK);
|
2008-06-12 17:01:38 +04:00
|
|
|
}
|
|
|
|
|
2013-09-06 19:15:07 +04:00
|
|
|
void
|
* cont.c (rb_fiber_current), dln.c (dln_print_undef, dln_undefined),
eval.c (rb_iterator_p, rb_need_block), load.c: (Init_load), ruby.c
(uscore_get, rb_f_chop), st.c (stat_col), signal.c
(rb_signal_buff_size, ruby_sig_finalize), thread.c
(rb_thread_sleep_forever, rb_thread_sleep_deadly, rb_thread_alone):
protoized.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@21929 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2009-02-01 15:51:44 +03:00
|
|
|
rb_thread_sleep_deadly(void)
|
2008-06-12 17:01:38 +04:00
|
|
|
{
|
|
|
|
thread_debug("rb_thread_sleep_deadly\n");
|
2018-06-13 13:00:46 +03:00
|
|
|
sleep_forever(GET_THREAD(), SLEEP_DEADLOCKABLE|SLEEP_SPURIOUS_CHECK);
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
2018-08-27 20:17:13 +03:00
|
|
|
void
|
|
|
|
rb_thread_sleep_interruptible(void)
|
|
|
|
{
|
|
|
|
rb_thread_t *th = GET_THREAD();
|
|
|
|
enum rb_thread_status prev_status = th->status;
|
|
|
|
|
|
|
|
th->status = THREAD_STOPPED;
|
|
|
|
native_sleep(th, 0);
|
|
|
|
RUBY_VM_CHECK_INTS_BLOCKING(th->ec);
|
|
|
|
th->status = prev_status;
|
|
|
|
}
|
|
|
|
|
2017-01-31 09:39:01 +03:00
|
|
|
static void
|
|
|
|
rb_thread_sleep_deadly_allow_spurious_wakeup(void)
|
|
|
|
{
|
|
|
|
thread_debug("rb_thread_sleep_deadly_allow_spurious_wakeup\n");
|
2018-06-13 13:00:46 +03:00
|
|
|
sleep_forever(GET_THREAD(), SLEEP_DEADLOCKABLE);
|
2017-01-31 09:39:01 +03:00
|
|
|
}
|
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
void
|
|
|
|
rb_thread_wait_for(struct timeval time)
|
|
|
|
{
|
* blockinlining.c, error.c, eval.c, eval_error.h, eval_intern.h,
eval_jump.h, eval_load.c, eval_safe.h, gc.c, proc.c, signal.c,
thread.c, thread_pthread.ci, thread_win32.ci, vm.c, vm.h,
vm_dump.c, vm_evalbody.ci, yarvcore.c, yarvcore.h:
fix typo (rb_thead_t -> rb_thread_t).
* eval_intern.h: remove unused definitions.
* common.mk: fix around vm_opts.h path
and remove harmful argument passed to insns2vm.rb.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@11658 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-02-08 09:37:46 +03:00
|
|
|
rb_thread_t *th = GET_THREAD();
|
2018-02-07 04:57:14 +03:00
|
|
|
|
2018-08-25 09:58:35 +03:00
|
|
|
sleep_hrtime(th, rb_timeval2hrtime(&time), SLEEP_SPURIOUS_CHECK);
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
2008-08-13 12:21:24 +04:00
|
|
|
/*
|
|
|
|
* CAUTION: This function causes thread switching.
|
|
|
|
* rb_thread_check_ints() check ruby's interrupts.
|
|
|
|
* some interrupt needs thread switching/invoke handlers,
|
|
|
|
* and so on.
|
|
|
|
*/
|
2012-11-20 05:21:19 +04:00
|
|
|
|
2008-08-13 12:21:24 +04:00
|
|
|
void
|
|
|
|
rb_thread_check_ints(void)
|
|
|
|
{
|
2017-11-06 10:44:28 +03:00
|
|
|
RUBY_VM_CHECK_INTS_BLOCKING(GET_EC());
|
2008-08-13 12:21:24 +04:00
|
|
|
}
|
|
|
|
|
2008-09-04 16:00:24 +04:00
|
|
|
/*
|
|
|
|
* Hidden API for tcl/tk wrapper.
|
|
|
|
* There is no guarantee to perpetuate it.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
rb_thread_check_trap_pending(void)
|
|
|
|
{
|
2011-07-01 02:29:34 +04:00
|
|
|
return rb_signal_buff_size() != 0;
|
2008-09-04 16:00:24 +04:00
|
|
|
}
|
|
|
|
|
2008-09-23 12:17:17 +04:00
|
|
|
/* This function can be called in blocking region. */
|
|
|
|
int
|
|
|
|
rb_thread_interrupted(VALUE thval)
|
|
|
|
{
|
2017-11-06 10:44:28 +03:00
|
|
|
return (int)RUBY_VM_INTERRUPTED(rb_thread_ptr(thval)->ec);
|
2008-09-23 12:17:17 +04:00
|
|
|
}
|
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
void
|
|
|
|
rb_thread_sleep(int sec)
|
|
|
|
{
|
|
|
|
rb_thread_wait_for(rb_time_timeval(INT2FIX(sec)));
|
|
|
|
}
|
|
|
|
|
2009-08-22 20:19:18 +04:00
|
|
|
static void
|
2017-05-07 11:06:02 +03:00
|
|
|
rb_thread_schedule_limits(uint32_t limits_us)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
|
|
|
thread_debug("rb_thread_schedule\n");
|
|
|
|
if (!rb_thread_alone()) {
|
* blockinlining.c, error.c, eval.c, eval_error.h, eval_intern.h,
eval_jump.h, eval_load.c, eval_safe.h, gc.c, proc.c, signal.c,
thread.c, thread_pthread.ci, thread_win32.ci, vm.c, vm.h,
vm_dump.c, vm_evalbody.ci, yarvcore.c, yarvcore.h:
fix typo (rb_thead_t -> rb_thread_t).
* eval_intern.h: remove unused definitions.
* common.mk: fix around vm_opts.h path
and remove harmful argument passed to insns2vm.rb.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@11658 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-02-08 09:37:46 +03:00
|
|
|
rb_thread_t *th = GET_THREAD();
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2011-06-29 21:43:58 +04:00
|
|
|
if (th->running_time_us >= limits_us) {
|
|
|
|
thread_debug("rb_thread_schedule/switch start\n");
|
|
|
|
RB_GC_SAVE_MACHINE_CONTEXT(th);
|
2011-06-13 19:06:30 +04:00
|
|
|
gvl_yield(th->vm, th);
|
2011-06-29 21:43:58 +04:00
|
|
|
rb_thread_set_current(th);
|
|
|
|
thread_debug("rb_thread_schedule/switch done\n");
|
|
|
|
}
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-08-22 20:19:18 +04:00
|
|
|
void
|
|
|
|
rb_thread_schedule(void)
|
|
|
|
{
|
2011-06-29 21:36:00 +04:00
|
|
|
rb_thread_schedule_limits(0);
|
2017-11-06 10:44:28 +03:00
|
|
|
RUBY_VM_CHECK_INTS(GET_EC());
|
2009-08-22 20:19:18 +04:00
|
|
|
}
|
|
|
|
|
2008-09-26 04:47:18 +04:00
|
|
|
/* blocking region */
|
|
|
|
|
2012-11-28 17:01:25 +04:00
|
|
|
static inline int
|
|
|
|
blocking_region_begin(rb_thread_t *th, struct rb_blocking_region_buffer *region,
|
|
|
|
rb_unblock_function_t *ubf, void *arg, int fail_if_interrupted)
|
|
|
|
{
|
|
|
|
region->prev_status = th->status;
|
2017-06-12 10:49:33 +03:00
|
|
|
if (unblock_function_set(th, ubf, arg, fail_if_interrupted)) {
|
2012-11-28 17:01:25 +04:00
|
|
|
th->blocking_region_buffer = region;
|
|
|
|
th->status = THREAD_STOPPED;
|
|
|
|
thread_debug("enter blocking region (%p)\n", (void *)th);
|
|
|
|
RB_GC_SAVE_MACHINE_CONTEXT(th);
|
|
|
|
gvl_release(th->vm);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-09-26 04:47:18 +04:00
|
|
|
static inline void
|
|
|
|
blocking_region_end(rb_thread_t *th, struct rb_blocking_region_buffer *region)
|
|
|
|
{
|
2018-07-28 06:10:10 +03:00
|
|
|
/* entry to ubf_list still permitted at this point, make it impossible: */
|
|
|
|
unblock_function_clear(th);
|
|
|
|
/* entry to ubf_list impossible at this point, so unregister is safe: */
|
|
|
|
unregister_ubf_list(th);
|
|
|
|
|
2010-11-27 23:15:59 +03:00
|
|
|
gvl_acquire(th->vm, th);
|
2008-09-26 04:47:18 +04:00
|
|
|
rb_thread_set_current(th);
|
* compile.c (iseq_compile_each), gc.c (assign_heap_slot),
(gc_mark_children), parse.y (vtable_alloc, vtable_free, vtable_add),
proc.c (proc_to_s), thread.c (terminate_i, rb_thread_terminate_all),
(thread_start_func_2, blocking_region_begin, blocking_region_end),
(rb_thread_kill), thread_pthread.c (native_thread_create),
(ubf_pthread_cond_signal), vm.c (check_env, thread_free), vm_dump.c
(vm_env_dump_raw, vm_stack_dump_each, vm_thread_dump_state),
(vm_call0): use void pointer for %p.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@20593 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2008-12-09 07:33:55 +03:00
|
|
|
thread_debug("leave blocking region (%p)\n", (void *)th);
|
2009-01-12 04:43:23 +03:00
|
|
|
th->blocking_region_buffer = 0;
|
2008-09-26 04:47:18 +04:00
|
|
|
if (th->status == THREAD_STOPPED) {
|
|
|
|
th->status = region->prev_status;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-04 16:14:11 +03:00
|
|
|
void *
|
|
|
|
rb_nogvl(void *(*func)(void *), void *data1,
|
|
|
|
rb_unblock_function_t *ubf, void *data2,
|
|
|
|
int flags)
|
2012-11-28 17:01:25 +04:00
|
|
|
{
|
|
|
|
void *val = 0;
|
2017-11-15 08:50:10 +03:00
|
|
|
rb_execution_context_t *ec = GET_EC();
|
|
|
|
rb_thread_t *th = rb_ec_thread_ptr(ec);
|
2012-11-28 17:01:25 +04:00
|
|
|
int saved_errno = 0;
|
2019-01-04 15:53:06 +03:00
|
|
|
VALUE ubf_th = Qfalse;
|
2012-11-28 17:01:25 +04:00
|
|
|
|
|
|
|
if (ubf == RUBY_UBF_IO || ubf == RUBY_UBF_PROCESS) {
|
|
|
|
ubf = ubf_select;
|
|
|
|
data2 = th;
|
|
|
|
}
|
2019-01-04 15:53:06 +03:00
|
|
|
else if (ubf && vm_living_thread_num(th->vm) == 1) {
|
2019-01-04 16:14:11 +03:00
|
|
|
if (RB_NOGVL_UBF_ASYNC_SAFE) {
|
|
|
|
th->vm->ubf_async_safe = 1;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ubf_th = rb_thread_start_unblock_thread();
|
|
|
|
}
|
2019-01-04 15:53:06 +03:00
|
|
|
}
|
2012-11-28 17:01:25 +04:00
|
|
|
|
2018-05-17 00:54:42 +03:00
|
|
|
BLOCKING_REGION(th, {
|
2012-11-28 17:01:25 +04:00
|
|
|
val = func(data1);
|
|
|
|
saved_errno = errno;
|
2019-01-04 16:14:11 +03:00
|
|
|
}, ubf, data2, flags & RB_NOGVL_INTR_FAIL);
|
|
|
|
|
|
|
|
th->vm->ubf_async_safe = 0;
|
2012-11-28 17:01:25 +04:00
|
|
|
|
2019-01-04 16:14:11 +03:00
|
|
|
if ((flags & RB_NOGVL_INTR_FAIL) == 0) {
|
2017-11-15 08:50:10 +03:00
|
|
|
RUBY_VM_CHECK_INTS_BLOCKING(ec);
|
2012-11-28 17:01:25 +04:00
|
|
|
}
|
|
|
|
|
2019-01-04 15:53:06 +03:00
|
|
|
if (ubf_th != Qfalse) {
|
2019-01-04 15:53:10 +03:00
|
|
|
thread_value(rb_thread_kill(ubf_th));
|
2019-01-04 15:53:06 +03:00
|
|
|
}
|
|
|
|
|
2012-11-28 17:01:25 +04:00
|
|
|
errno = saved_errno;
|
|
|
|
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
2008-09-23 12:51:33 +04:00
|
|
|
/*
|
2012-07-18 10:47:43 +04:00
|
|
|
* rb_thread_call_without_gvl - permit concurrent/parallel execution.
|
2012-11-28 17:01:25 +04:00
|
|
|
* rb_thread_call_without_gvl2 - permit concurrent/parallel execution
|
2013-05-19 07:10:21 +04:00
|
|
|
* without interrupt process.
|
2008-09-23 12:51:33 +04:00
|
|
|
*
|
2012-07-18 10:47:43 +04:00
|
|
|
* rb_thread_call_without_gvl() does:
|
2012-11-28 17:01:25 +04:00
|
|
|
* (1) Check interrupts.
|
|
|
|
* (2) release GVL.
|
2008-09-23 12:51:33 +04:00
|
|
|
* Other Ruby threads may run in parallel.
|
2012-11-28 17:01:25 +04:00
|
|
|
* (3) call func with data1
|
|
|
|
* (4) acquire GVL.
|
2008-09-23 12:51:33 +04:00
|
|
|
* Other Ruby threads can not run in parallel any more.
|
2012-11-28 17:01:25 +04:00
|
|
|
* (5) Check interrupts.
|
2012-07-18 10:47:43 +04:00
|
|
|
*
|
|
|
|
* rb_thread_call_without_gvl2() does:
|
2012-11-28 17:01:25 +04:00
|
|
|
* (1) Check interrupt and return if interrupted.
|
|
|
|
* (2) release GVL.
|
|
|
|
* (3) call func with data1 and a pointer to the flags.
|
|
|
|
* (4) acquire GVL.
|
2008-09-23 12:51:33 +04:00
|
|
|
*
|
2012-10-31 04:07:17 +04:00
|
|
|
* If another thread interrupts this thread (Thread#kill, signal delivery,
|
|
|
|
* VM-shutdown request, and so on), `ubf()' is called (`ubf()' means
|
2012-10-31 04:29:27 +04:00
|
|
|
* "un-blocking function"). `ubf()' should interrupt `func()' execution by
|
|
|
|
* toggling a cancellation flag, canceling the invocation of a call inside
|
|
|
|
* `func()' or similar. Note that `ubf()' may not be called with the GVL.
|
2008-09-23 12:58:13 +04:00
|
|
|
*
|
2012-10-31 04:07:17 +04:00
|
|
|
* There are built-in ubfs and you can specify these ubfs:
|
2008-09-23 12:51:33 +04:00
|
|
|
*
|
2012-10-31 04:07:17 +04:00
|
|
|
* * RUBY_UBF_IO: ubf for IO operation
|
|
|
|
* * RUBY_UBF_PROCESS: ubf for process operation
|
2008-09-23 12:51:33 +04:00
|
|
|
*
|
2012-10-31 04:07:17 +04:00
|
|
|
* However, we can not guarantee our built-in ubfs interrupt your `func()'
|
|
|
|
* correctly. Be careful to use rb_thread_call_without_gvl(). If you don't
|
|
|
|
* provide proper ubf(), your program will not stop for Control+C or other
|
|
|
|
* shutdown events.
|
2012-07-18 10:47:43 +04:00
|
|
|
*
|
2013-04-27 14:33:44 +04:00
|
|
|
* "Check interrupts" on above list means checking asynchronous
|
2012-10-31 04:07:17 +04:00
|
|
|
* interrupt events (such as Thread#kill, signal delivery, VM-shutdown
|
2013-04-27 14:33:44 +04:00
|
|
|
* request, and so on) and calling corresponding procedures
|
2012-10-31 04:07:17 +04:00
|
|
|
* (such as `trap' for signals, raise an exception for Thread#raise).
|
2013-04-27 14:33:44 +04:00
|
|
|
* If `func()' finished and received interrupts, you may skip interrupt
|
|
|
|
* checking. For example, assume the following func() it reads data from file.
|
2012-07-18 10:47:43 +04:00
|
|
|
*
|
2012-10-31 04:07:17 +04:00
|
|
|
* read_func(...) {
|
|
|
|
* // (a) before read
|
|
|
|
* read(buffer); // (b) reading
|
|
|
|
* // (c) after read
|
|
|
|
* }
|
2012-07-18 10:47:43 +04:00
|
|
|
*
|
2012-10-31 04:07:17 +04:00
|
|
|
* If an interrupt occurs at (a) or (b), then `ubf()' cancels this
|
|
|
|
* `read_func()' and interrupts are checked. However, if an interrupt occurs
|
2013-04-27 14:33:44 +04:00
|
|
|
* at (c), after *read* operation is completed, checking interrupts is harmful
|
2012-10-31 04:07:17 +04:00
|
|
|
* because it causes irrevocable side-effect, the read data will vanish. To
|
2012-11-28 17:01:25 +04:00
|
|
|
* avoid such problem, the `read_func()' should be used with
|
|
|
|
* `rb_thread_call_without_gvl2()'.
|
2012-07-18 10:47:43 +04:00
|
|
|
*
|
2013-04-27 14:33:44 +04:00
|
|
|
* If `rb_thread_call_without_gvl2()' detects interrupt, it returns
|
2012-11-28 17:01:25 +04:00
|
|
|
* immediately. This function does not show when the execution was interrupted.
|
|
|
|
* For example, there are 4 possible timing (a), (b), (c) and before calling
|
|
|
|
* read_func(). You need to record progress of a read_func() and check
|
|
|
|
* the progress after `rb_thread_call_without_gvl2()'. You may need to call
|
|
|
|
* `rb_thread_check_ints()' correctly or your program can not process proper
|
|
|
|
* process such as `trap' and so on.
|
2012-07-18 10:47:43 +04:00
|
|
|
*
|
2012-10-31 04:07:17 +04:00
|
|
|
* NOTE: You can not execute most of Ruby C API and touch Ruby
|
|
|
|
* objects in `func()' and `ubf()', including raising an
|
|
|
|
* exception, because current thread doesn't acquire GVL
|
2012-10-31 04:29:27 +04:00
|
|
|
* (it causes synchronization problems). If you need to
|
|
|
|
* call ruby functions either use rb_thread_call_with_gvl()
|
|
|
|
* or read source code of C APIs and confirm safety by
|
|
|
|
* yourself.
|
2008-09-23 12:51:33 +04:00
|
|
|
*
|
2012-10-31 04:07:17 +04:00
|
|
|
* NOTE: In short, this API is difficult to use safely. I recommend you
|
|
|
|
* use other ways if you have. We lack experiences to use this API.
|
|
|
|
* Please report your problem related on it.
|
2008-12-30 10:57:53 +03:00
|
|
|
*
|
2012-10-31 04:07:17 +04:00
|
|
|
* NOTE: Releasing GVL and re-acquiring GVL may be expensive operations
|
2012-10-31 04:29:27 +04:00
|
|
|
* for a short running `func()'. Be sure to benchmark and use this
|
2012-10-31 04:07:17 +04:00
|
|
|
* mechanism when `func()' consumes enough time.
|
2012-07-18 10:47:43 +04:00
|
|
|
*
|
2012-10-31 04:07:17 +04:00
|
|
|
* Safe C API:
|
|
|
|
* * rb_thread_interrupted() - check interrupt flag
|
|
|
|
* * ruby_xmalloc(), ruby_xrealloc(), ruby_xfree() -
|
|
|
|
* they will work without GVL, and may acquire GVL when GC is needed.
|
2008-09-23 12:51:33 +04:00
|
|
|
*/
|
2012-07-10 17:57:11 +04:00
|
|
|
void *
|
2012-11-28 17:01:25 +04:00
|
|
|
rb_thread_call_without_gvl2(void *(*func)(void *), void *data1,
|
2012-07-18 10:47:43 +04:00
|
|
|
rb_unblock_function_t *ubf, void *data2)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2019-01-04 16:14:11 +03:00
|
|
|
return rb_nogvl(func, data1, ubf, data2, RB_NOGVL_INTR_FAIL);
|
2012-07-18 10:47:43 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void *
|
|
|
|
rb_thread_call_without_gvl(void *(*func)(void *data), void *data1,
|
|
|
|
rb_unblock_function_t *ubf, void *data2)
|
|
|
|
{
|
2019-01-04 16:14:11 +03:00
|
|
|
return rb_nogvl(func, data1, ubf, data2, 0);
|
2012-07-18 10:47:43 +04:00
|
|
|
}
|
|
|
|
|
2011-02-12 08:44:23 +03:00
|
|
|
VALUE
|
|
|
|
rb_thread_io_blocking_region(rb_blocking_function_t *func, void *data1, int fd)
|
|
|
|
{
|
2015-03-03 18:45:00 +03:00
|
|
|
volatile VALUE val = Qundef; /* shouldn't be used */
|
2017-11-06 10:44:28 +03:00
|
|
|
rb_execution_context_t * volatile ec = GET_EC();
|
2015-03-03 18:45:00 +03:00
|
|
|
volatile int saved_errno = 0;
|
2017-06-23 10:25:52 +03:00
|
|
|
enum ruby_tag_type state;
|
2017-05-20 12:47:14 +03:00
|
|
|
struct waiting_fd wfd;
|
2011-02-12 08:44:23 +03:00
|
|
|
|
2017-05-20 12:47:14 +03:00
|
|
|
wfd.fd = fd;
|
2017-11-06 10:44:28 +03:00
|
|
|
wfd.th = rb_ec_thread_ptr(ec);
|
|
|
|
list_add(&rb_ec_vm_ptr(ec)->waiting_fds, &wfd.wfd_node);
|
2012-07-18 09:46:40 +04:00
|
|
|
|
2017-11-06 10:44:28 +03:00
|
|
|
EC_PUSH_TAG(ec);
|
2017-12-06 06:16:08 +03:00
|
|
|
if ((state = EC_EXEC_TAG()) == TAG_NONE) {
|
2018-05-17 00:54:42 +03:00
|
|
|
BLOCKING_REGION(wfd.th, {
|
2012-07-18 09:46:40 +04:00
|
|
|
val = func(data1);
|
|
|
|
saved_errno = errno;
|
2018-05-17 00:54:42 +03:00
|
|
|
}, ubf_select, wfd.th, FALSE);
|
2012-07-18 09:46:40 +04:00
|
|
|
}
|
2017-10-26 14:02:13 +03:00
|
|
|
EC_POP_TAG();
|
2012-07-18 09:46:40 +04:00
|
|
|
|
2018-04-21 06:12:36 +03:00
|
|
|
/*
|
|
|
|
* must be deleted before jump
|
|
|
|
* this will delete either from waiting_fds or on-stack LIST_HEAD(busy)
|
|
|
|
*/
|
2017-05-20 12:47:14 +03:00
|
|
|
list_del(&wfd.wfd_node);
|
2012-07-18 09:46:40 +04:00
|
|
|
|
|
|
|
if (state) {
|
2017-11-06 10:44:28 +03:00
|
|
|
EC_JUMP_TAG(ec, state);
|
2012-07-18 09:46:40 +04:00
|
|
|
}
|
|
|
|
/* TODO: check func() */
|
2017-11-06 10:44:28 +03:00
|
|
|
RUBY_VM_CHECK_INTS_BLOCKING(ec);
|
2012-07-18 09:46:40 +04:00
|
|
|
|
2011-02-12 08:44:23 +03:00
|
|
|
errno = saved_errno;
|
|
|
|
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
2008-12-30 10:57:53 +03:00
|
|
|
/*
|
2012-10-31 04:07:17 +04:00
|
|
|
* rb_thread_call_with_gvl - re-enter the Ruby world after GVL release.
|
2008-12-30 10:57:53 +03:00
|
|
|
*
|
* include/ruby/intern.h,
include/ruby/io.h,
include/ruby/ruby.h,
include/ruby/win32.h,
include/ruby/backward/rubysig.h,
bignum.c,
gc.c,
io.c,
process.c,
safe.c,
struct.c,
thread.c,
ext/socket/rubysocket.h,
ext/-test-/old_thread_select: Remove deprecated definitions
[ruby-core:60581] [Feature #9502]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@44955 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2014-02-14 19:16:31 +04:00
|
|
|
* After releasing GVL using
|
2012-10-31 04:07:17 +04:00
|
|
|
* rb_thread_call_without_gvl() you can not access Ruby values or invoke
|
|
|
|
* methods. If you need to access Ruby you must use this function
|
|
|
|
* rb_thread_call_with_gvl().
|
2008-12-30 10:57:53 +03:00
|
|
|
*
|
|
|
|
* This function rb_thread_call_with_gvl() does:
|
|
|
|
* (1) acquire GVL.
|
|
|
|
* (2) call passed function `func'.
|
|
|
|
* (3) release GVL.
|
|
|
|
* (4) return a value which is returned at (2).
|
|
|
|
*
|
|
|
|
* NOTE: You should not return Ruby object at (2) because such Object
|
2013-04-27 14:33:44 +04:00
|
|
|
* will not be marked.
|
2008-12-30 10:57:53 +03:00
|
|
|
*
|
2012-10-31 04:07:17 +04:00
|
|
|
* NOTE: If an exception is raised in `func', this function DOES NOT
|
2008-12-30 10:57:53 +03:00
|
|
|
* protect (catch) the exception. If you have any resources
|
|
|
|
* which should free before throwing exception, you need use
|
|
|
|
* rb_protect() in `func' and return a value which represents
|
2013-04-27 14:33:44 +04:00
|
|
|
* exception was raised.
|
2008-12-30 10:57:53 +03:00
|
|
|
*
|
2012-10-31 04:07:17 +04:00
|
|
|
* NOTE: This function should not be called by a thread which was not
|
|
|
|
* created as Ruby thread (created by Thread.new or so). In other
|
|
|
|
* words, this function *DOES NOT* associate or convert a NON-Ruby
|
|
|
|
* thread to a Ruby thread.
|
2008-12-30 10:57:53 +03:00
|
|
|
*/
|
|
|
|
void *
|
|
|
|
rb_thread_call_with_gvl(void *(*func)(void *), void *data1)
|
|
|
|
{
|
2012-11-20 05:21:19 +04:00
|
|
|
rb_thread_t *th = ruby_thread_from_native();
|
2008-12-30 10:57:53 +03:00
|
|
|
struct rb_blocking_region_buffer *brb;
|
|
|
|
struct rb_unblock_callback prev_unblock;
|
|
|
|
void *r;
|
|
|
|
|
2012-11-20 05:21:19 +04:00
|
|
|
if (th == 0) {
|
2013-04-27 14:33:44 +04:00
|
|
|
/* Error has occurred, but we can't use rb_bug()
|
2008-12-30 10:57:53 +03:00
|
|
|
* because this thread is not Ruby's thread.
|
|
|
|
* What should we do?
|
|
|
|
*/
|
|
|
|
|
|
|
|
fprintf(stderr, "[BUG] rb_thread_call_with_gvl() is called by non-ruby thread\n");
|
2011-07-10 12:29:46 +04:00
|
|
|
exit(EXIT_FAILURE);
|
2008-12-30 10:57:53 +03:00
|
|
|
}
|
|
|
|
|
2012-11-20 05:21:19 +04:00
|
|
|
brb = (struct rb_blocking_region_buffer *)th->blocking_region_buffer;
|
|
|
|
prev_unblock = th->unblock;
|
2008-12-30 10:57:53 +03:00
|
|
|
|
2009-01-12 04:43:23 +03:00
|
|
|
if (brb == 0) {
|
|
|
|
rb_bug("rb_thread_call_with_gvl: called by a thread which has GVL.");
|
|
|
|
}
|
|
|
|
|
2012-11-20 05:21:19 +04:00
|
|
|
blocking_region_end(th, brb);
|
2008-12-30 10:57:53 +03:00
|
|
|
/* enter to Ruby world: You can access Ruby values, methods and so on. */
|
|
|
|
r = (*func)(data1);
|
2009-11-03 20:46:28 +03:00
|
|
|
/* leave from Ruby world: You can not access Ruby values, etc. */
|
2012-11-28 17:01:25 +04:00
|
|
|
blocking_region_begin(th, brb, prev_unblock.func, prev_unblock.arg, FALSE);
|
2008-12-30 10:57:53 +03:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2009-01-12 04:43:23 +03:00
|
|
|
/*
|
|
|
|
* ruby_thread_has_gvl_p - check if current native thread has GVL.
|
|
|
|
*
|
|
|
|
***
|
|
|
|
*** This API is EXPERIMENTAL!
|
|
|
|
*** We do not guarantee that this API remains in ruby 1.9.2 or later.
|
|
|
|
***
|
|
|
|
*/
|
|
|
|
|
|
|
|
int
|
|
|
|
ruby_thread_has_gvl_p(void)
|
|
|
|
{
|
|
|
|
rb_thread_t *th = ruby_thread_from_native();
|
|
|
|
|
|
|
|
if (th && th->blocking_region_buffer == 0) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
/*
|
2011-06-30 01:23:36 +04:00
|
|
|
* call-seq:
|
|
|
|
* Thread.pass -> nil
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2011-06-30 01:23:36 +04:00
|
|
|
* Give the thread scheduler a hint to pass execution to another thread.
|
|
|
|
* A running thread may or may not switch, it depends on OS and processor.
|
2006-12-31 18:02:22 +03:00
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
2007-02-05 15:21:01 +03:00
|
|
|
thread_s_pass(VALUE klass)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
|
|
|
rb_thread_schedule();
|
|
|
|
return Qnil;
|
|
|
|
}
|
|
|
|
|
2012-07-19 18:19:40 +04:00
|
|
|
/*****************************************************/
|
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
/*
|
2012-12-23 14:18:58 +04:00
|
|
|
* rb_threadptr_pending_interrupt_* - manage asynchronous error queue
|
2012-07-19 18:19:40 +04:00
|
|
|
*
|
2013-05-19 07:10:21 +04:00
|
|
|
* Async events such as an exception thrown by Thread#raise,
|
2012-07-19 18:19:40 +04:00
|
|
|
* Thread#kill and thread termination (after main thread termination)
|
2012-12-23 14:18:58 +04:00
|
|
|
* will be queued to th->pending_interrupt_queue.
|
2012-07-19 18:19:40 +04:00
|
|
|
* - clear: clear the queue.
|
2013-04-27 14:33:44 +04:00
|
|
|
* - enque: enqueue err object into queue.
|
|
|
|
* - deque: dequeue err object from queue.
|
2012-07-19 18:19:40 +04:00
|
|
|
* - active_p: return 1 if the queue should be checked.
|
2006-12-31 18:02:22 +03:00
|
|
|
*
|
2012-12-23 14:18:58 +04:00
|
|
|
* All rb_threadptr_pending_interrupt_* functions are called by
|
2012-07-19 18:19:40 +04:00
|
|
|
* a GVL acquired thread, of course.
|
|
|
|
* Note that all "rb_" prefix APIs need GVL to call.
|
2006-12-31 18:02:22 +03:00
|
|
|
*/
|
|
|
|
|
2012-07-19 18:19:40 +04:00
|
|
|
void
|
2012-12-23 14:18:58 +04:00
|
|
|
rb_threadptr_pending_interrupt_clear(rb_thread_t *th)
|
2012-07-19 18:19:40 +04:00
|
|
|
{
|
2012-12-23 14:18:58 +04:00
|
|
|
rb_ary_clear(th->pending_interrupt_queue);
|
2012-07-19 18:19:40 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2012-12-23 14:18:58 +04:00
|
|
|
rb_threadptr_pending_interrupt_enque(rb_thread_t *th, VALUE v)
|
2012-07-19 18:19:40 +04:00
|
|
|
{
|
2012-12-23 14:18:58 +04:00
|
|
|
rb_ary_push(th->pending_interrupt_queue, v);
|
|
|
|
th->pending_interrupt_queue_checked = 0;
|
2012-07-19 18:19:40 +04:00
|
|
|
}
|
|
|
|
|
2016-01-07 08:49:31 +03:00
|
|
|
static void
|
|
|
|
threadptr_check_pending_interrupt_queue(rb_thread_t *th)
|
|
|
|
{
|
|
|
|
if (!th->pending_interrupt_queue) {
|
|
|
|
rb_raise(rb_eThreadError, "uninitialized thread");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-12-23 14:18:58 +04:00
|
|
|
enum handle_interrupt_timing {
|
2012-07-19 18:19:40 +04:00
|
|
|
INTERRUPT_NONE,
|
|
|
|
INTERRUPT_IMMEDIATE,
|
|
|
|
INTERRUPT_ON_BLOCKING,
|
2012-12-23 14:18:58 +04:00
|
|
|
INTERRUPT_NEVER
|
2012-07-19 18:19:40 +04:00
|
|
|
};
|
|
|
|
|
2012-12-23 14:18:58 +04:00
|
|
|
static enum handle_interrupt_timing
|
|
|
|
rb_threadptr_pending_interrupt_check_mask(rb_thread_t *th, VALUE err)
|
2012-07-19 18:19:40 +04:00
|
|
|
{
|
|
|
|
VALUE mask;
|
2012-12-23 14:18:58 +04:00
|
|
|
long mask_stack_len = RARRAY_LEN(th->pending_interrupt_mask_stack);
|
* include/ruby/ruby.h: rename RARRAY_RAWPTR() to RARRAY_CONST_PTR().
RARRAY_RAWPTR(ary) returns (const VALUE *) type pointer and
usecase of this macro is not acquire raw pointer, but acquire
read-only pointer. So we rename to better name.
RSTRUCT_RAWPTR() is also renamed to RSTRUCT_CONST_PTR()
(I expect that nobody use it).
* array.c, compile.c, cont.c, enumerator.c, gc.c, proc.c, random.c,
string.c, struct.c, thread.c, vm_eval.c, vm_insnhelper.c:
catch up this change.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@43043 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2013-09-25 12:24:34 +04:00
|
|
|
const VALUE *mask_stack = RARRAY_CONST_PTR(th->pending_interrupt_mask_stack);
|
2017-01-10 13:57:26 +03:00
|
|
|
VALUE mod;
|
|
|
|
long i;
|
2012-07-19 18:19:40 +04:00
|
|
|
|
|
|
|
for (i=0; i<mask_stack_len; i++) {
|
|
|
|
mask = mask_stack[mask_stack_len-(i+1)];
|
|
|
|
|
2017-01-10 13:57:26 +03:00
|
|
|
for (mod = err; mod; mod = RCLASS_SUPER(mod)) {
|
|
|
|
VALUE klass = mod;
|
2012-07-19 18:19:40 +04:00
|
|
|
VALUE sym;
|
|
|
|
|
2017-01-10 13:57:26 +03:00
|
|
|
if (BUILTIN_TYPE(mod) == T_ICLASS) {
|
|
|
|
klass = RBASIC(mod)->klass;
|
|
|
|
}
|
|
|
|
else if (mod != RCLASS_ORIGIN(mod)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2012-07-19 18:19:40 +04:00
|
|
|
if ((sym = rb_hash_aref(mask, klass)) != Qnil) {
|
2012-12-23 14:18:58 +04:00
|
|
|
if (sym == sym_immediate) {
|
2012-07-19 18:19:40 +04:00
|
|
|
return INTERRUPT_IMMEDIATE;
|
|
|
|
}
|
2012-12-23 14:18:58 +04:00
|
|
|
else if (sym == sym_on_blocking) {
|
2012-07-19 18:19:40 +04:00
|
|
|
return INTERRUPT_ON_BLOCKING;
|
|
|
|
}
|
2012-12-23 14:18:58 +04:00
|
|
|
else if (sym == sym_never) {
|
|
|
|
return INTERRUPT_NEVER;
|
2012-07-19 18:19:40 +04:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
rb_raise(rb_eThreadError, "unknown mask signature");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* try to next mask */
|
|
|
|
}
|
|
|
|
return INTERRUPT_NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2017-11-06 10:44:28 +03:00
|
|
|
rb_threadptr_pending_interrupt_empty_p(const rb_thread_t *th)
|
2012-07-19 18:19:40 +04:00
|
|
|
{
|
2012-12-23 14:18:58 +04:00
|
|
|
return RARRAY_LEN(th->pending_interrupt_queue) == 0;
|
2012-07-19 18:19:40 +04:00
|
|
|
}
|
|
|
|
|
2012-11-30 13:28:35 +04:00
|
|
|
static int
|
2012-12-23 14:18:58 +04:00
|
|
|
rb_threadptr_pending_interrupt_include_p(rb_thread_t *th, VALUE err)
|
2012-11-30 13:28:35 +04:00
|
|
|
{
|
|
|
|
int i;
|
2012-12-23 14:18:58 +04:00
|
|
|
for (i=0; i<RARRAY_LEN(th->pending_interrupt_queue); i++) {
|
2013-05-13 13:56:22 +04:00
|
|
|
VALUE e = RARRAY_AREF(th->pending_interrupt_queue, i);
|
2012-11-30 13:28:35 +04:00
|
|
|
if (rb_class_inherited_p(e, err)) {
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2012-07-19 18:19:40 +04:00
|
|
|
static VALUE
|
2012-12-23 14:18:58 +04:00
|
|
|
rb_threadptr_pending_interrupt_deque(rb_thread_t *th, enum handle_interrupt_timing timing)
|
2012-07-19 18:19:40 +04:00
|
|
|
{
|
2012-12-23 14:18:58 +04:00
|
|
|
#if 1 /* 1 to enable Thread#handle_interrupt, 0 to ignore it */
|
2012-07-19 18:19:40 +04:00
|
|
|
int i;
|
|
|
|
|
2012-12-23 14:18:58 +04:00
|
|
|
for (i=0; i<RARRAY_LEN(th->pending_interrupt_queue); i++) {
|
2013-05-13 13:56:22 +04:00
|
|
|
VALUE err = RARRAY_AREF(th->pending_interrupt_queue, i);
|
2012-07-19 18:19:40 +04:00
|
|
|
|
2012-12-23 14:18:58 +04:00
|
|
|
enum handle_interrupt_timing mask_timing = rb_threadptr_pending_interrupt_check_mask(th, CLASS_OF(err));
|
2012-07-19 18:19:40 +04:00
|
|
|
|
|
|
|
switch (mask_timing) {
|
|
|
|
case INTERRUPT_ON_BLOCKING:
|
|
|
|
if (timing != INTERRUPT_ON_BLOCKING) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* fall through */
|
|
|
|
case INTERRUPT_NONE: /* default: IMMEDIATE */
|
|
|
|
case INTERRUPT_IMMEDIATE:
|
2012-12-23 14:18:58 +04:00
|
|
|
rb_ary_delete_at(th->pending_interrupt_queue, i);
|
2012-07-19 18:19:40 +04:00
|
|
|
return err;
|
2012-12-23 14:18:58 +04:00
|
|
|
case INTERRUPT_NEVER:
|
2012-07-19 18:19:40 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-12-23 14:18:58 +04:00
|
|
|
th->pending_interrupt_queue_checked = 1;
|
2012-07-19 18:19:40 +04:00
|
|
|
return Qundef;
|
|
|
|
#else
|
2012-12-23 14:18:58 +04:00
|
|
|
VALUE err = rb_ary_shift(th->pending_interrupt_queue);
|
|
|
|
if (rb_threadptr_pending_interrupt_empty_p(th)) {
|
|
|
|
th->pending_interrupt_queue_checked = 1;
|
2012-07-19 18:19:40 +04:00
|
|
|
}
|
|
|
|
return err;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2018-01-10 01:59:58 +03:00
|
|
|
static int
|
2017-11-16 11:48:59 +03:00
|
|
|
threadptr_pending_interrupt_active_p(rb_thread_t *th)
|
2012-07-19 18:19:40 +04:00
|
|
|
{
|
2012-11-19 15:13:40 +04:00
|
|
|
/*
|
|
|
|
* For optimization, we don't check async errinfo queue
|
2013-04-27 14:33:44 +04:00
|
|
|
* if the queue and the thread interrupt mask were not changed
|
2012-11-19 15:13:40 +04:00
|
|
|
* since last check.
|
|
|
|
*/
|
2012-12-23 14:18:58 +04:00
|
|
|
if (th->pending_interrupt_queue_checked) {
|
2012-07-19 18:19:40 +04:00
|
|
|
return 0;
|
|
|
|
}
|
2012-11-19 15:13:40 +04:00
|
|
|
|
2012-12-23 14:18:58 +04:00
|
|
|
if (rb_threadptr_pending_interrupt_empty_p(th)) {
|
2012-11-19 15:13:40 +04:00
|
|
|
return 0;
|
2012-07-19 18:19:40 +04:00
|
|
|
}
|
2012-11-19 15:13:40 +04:00
|
|
|
|
|
|
|
return 1;
|
2012-07-19 18:19:40 +04:00
|
|
|
}
|
|
|
|
|
2012-11-30 21:39:48 +04:00
|
|
|
static int
|
2015-07-30 05:53:57 +03:00
|
|
|
handle_interrupt_arg_check_i(VALUE key, VALUE val, VALUE args)
|
2012-11-30 21:39:48 +04:00
|
|
|
{
|
2015-07-30 05:53:57 +03:00
|
|
|
VALUE *maskp = (VALUE *)args;
|
|
|
|
|
2012-12-23 14:18:58 +04:00
|
|
|
if (val != sym_immediate && val != sym_on_blocking && val != sym_never) {
|
2012-11-30 21:39:48 +04:00
|
|
|
rb_raise(rb_eArgError, "unknown mask signature");
|
|
|
|
}
|
|
|
|
|
2015-07-30 05:53:57 +03:00
|
|
|
if (!*maskp) {
|
|
|
|
*maskp = rb_ident_hash_new();
|
|
|
|
}
|
|
|
|
rb_hash_aset(*maskp, key, val);
|
|
|
|
|
2012-11-30 21:39:48 +04:00
|
|
|
return ST_CONTINUE;
|
|
|
|
}
|
|
|
|
|
2012-07-19 18:19:40 +04:00
|
|
|
/*
|
|
|
|
* call-seq:
|
2012-12-23 14:18:58 +04:00
|
|
|
* Thread.handle_interrupt(hash) { ... } -> result of the block
|
2012-07-19 18:19:40 +04:00
|
|
|
*
|
2013-02-23 11:00:51 +04:00
|
|
|
* Changes asynchronous interrupt timing.
|
2012-07-19 18:19:40 +04:00
|
|
|
*
|
2012-12-23 14:18:58 +04:00
|
|
|
* _interrupt_ means asynchronous event and corresponding procedure
|
2012-07-19 18:19:40 +04:00
|
|
|
* by Thread#raise, Thread#kill, signal trap (not supported yet)
|
|
|
|
* and main thread termination (if main thread terminates, then all
|
|
|
|
* other thread will be killed).
|
|
|
|
*
|
2013-02-23 11:00:51 +04:00
|
|
|
* The given +hash+ has pairs like <code>ExceptionClass =>
|
|
|
|
* :TimingSymbol</code>. Where the ExceptionClass is the interrupt handled by
|
|
|
|
* the given block. The TimingSymbol can be one of the following symbols:
|
|
|
|
*
|
|
|
|
* [+:immediate+] Invoke interrupts immediately.
|
|
|
|
* [+:on_blocking+] Invoke interrupts while _BlockingOperation_.
|
|
|
|
* [+:never+] Never invoke all interrupts.
|
2012-07-19 18:19:40 +04:00
|
|
|
*
|
|
|
|
* _BlockingOperation_ means that the operation will block the calling thread,
|
2013-02-23 11:00:51 +04:00
|
|
|
* such as read and write. On CRuby implementation, _BlockingOperation_ is any
|
2012-07-19 18:19:40 +04:00
|
|
|
* operation executed without GVL.
|
|
|
|
*
|
2013-02-23 11:00:51 +04:00
|
|
|
* Masked asynchronous interrupts are delayed until they are enabled.
|
2012-07-19 18:19:40 +04:00
|
|
|
* This method is similar to sigprocmask(3).
|
|
|
|
*
|
2013-02-23 11:00:51 +04:00
|
|
|
* === NOTE
|
|
|
|
*
|
|
|
|
* Asynchronous interrupts are difficult to use.
|
|
|
|
*
|
|
|
|
* If you need to communicate between threads, please consider to use another way such as Queue.
|
|
|
|
*
|
|
|
|
* Or use them with deep understanding about this method.
|
2012-07-19 18:19:40 +04:00
|
|
|
*
|
2013-02-23 11:00:51 +04:00
|
|
|
* === Usage
|
2012-07-19 18:19:40 +04:00
|
|
|
*
|
2013-02-23 11:00:51 +04:00
|
|
|
* In this example, we can guard from Thread#raise exceptions.
|
|
|
|
*
|
|
|
|
* Using the +:never+ TimingSymbol the RuntimeError exception will always be
|
|
|
|
* ignored in the first block of the main thread. In the second
|
|
|
|
* ::handle_interrupt block we can purposefully handle RuntimeError exceptions.
|
2012-07-19 18:19:40 +04:00
|
|
|
*
|
|
|
|
* th = Thread.new do
|
2013-05-19 07:10:21 +04:00
|
|
|
* Thread.handle_interrupt(RuntimeError => :never) {
|
2012-07-19 18:19:40 +04:00
|
|
|
* begin
|
|
|
|
* # You can write resource allocation code safely.
|
2012-12-23 14:18:58 +04:00
|
|
|
* Thread.handle_interrupt(RuntimeError => :immediate) {
|
2013-02-23 11:00:51 +04:00
|
|
|
* # ...
|
2012-07-19 18:19:40 +04:00
|
|
|
* }
|
|
|
|
* ensure
|
2013-02-23 11:00:51 +04:00
|
|
|
* # You can write resource deallocation code safely.
|
2012-07-19 18:19:40 +04:00
|
|
|
* end
|
|
|
|
* }
|
|
|
|
* end
|
|
|
|
* Thread.pass
|
|
|
|
* # ...
|
|
|
|
* th.raise "stop"
|
|
|
|
*
|
2013-02-23 11:00:51 +04:00
|
|
|
* While we are ignoring the RuntimeError exception, it's safe to write our
|
2013-02-24 09:23:51 +04:00
|
|
|
* resource allocation code. Then, the ensure block is where we can safely
|
2013-02-23 11:00:51 +04:00
|
|
|
* deallocate your resources.
|
|
|
|
*
|
2014-10-08 00:00:09 +04:00
|
|
|
* ==== Guarding from Timeout::Error
|
2013-02-23 11:00:51 +04:00
|
|
|
*
|
2014-10-08 00:00:09 +04:00
|
|
|
* In the next example, we will guard from the Timeout::Error exception. This
|
|
|
|
* will help prevent from leaking resources when Timeout::Error exceptions occur
|
2013-02-23 11:00:51 +04:00
|
|
|
* during normal ensure clause. For this example we use the help of the
|
|
|
|
* standard library Timeout, from lib/timeout.rb
|
|
|
|
*
|
2012-07-19 18:19:40 +04:00
|
|
|
* require 'timeout'
|
2014-10-08 00:00:09 +04:00
|
|
|
* Thread.handle_interrupt(Timeout::Error => :never) {
|
2012-07-19 18:19:40 +04:00
|
|
|
* timeout(10){
|
2014-10-08 00:00:09 +04:00
|
|
|
* # Timeout::Error doesn't occur here
|
|
|
|
* Thread.handle_interrupt(Timeout::Error => :on_blocking) {
|
|
|
|
* # possible to be killed by Timeout::Error
|
2012-07-19 18:19:40 +04:00
|
|
|
* # while blocking operation
|
|
|
|
* }
|
2014-10-08 00:00:09 +04:00
|
|
|
* # Timeout::Error doesn't occur here
|
2012-07-19 18:19:40 +04:00
|
|
|
* }
|
|
|
|
* }
|
|
|
|
*
|
2014-10-08 00:00:09 +04:00
|
|
|
* In the first part of the +timeout+ block, we can rely on Timeout::Error being
|
|
|
|
* ignored. Then in the <code>Timeout::Error => :on_blocking</code> block, any
|
2013-02-23 11:00:51 +04:00
|
|
|
* operation that will block the calling thread is susceptible to a
|
2014-10-08 00:00:09 +04:00
|
|
|
* Timeout::Error exception being raised.
|
2013-02-23 11:00:51 +04:00
|
|
|
*
|
|
|
|
* ==== Stack control settings
|
|
|
|
*
|
|
|
|
* It's possible to stack multiple levels of ::handle_interrupt blocks in order
|
|
|
|
* to control more than one ExceptionClass and TimingSymbol at a time.
|
|
|
|
*
|
2012-12-23 14:18:58 +04:00
|
|
|
* Thread.handle_interrupt(FooError => :never) {
|
|
|
|
* Thread.handle_interrupt(BarError => :never) {
|
2012-07-19 18:19:40 +04:00
|
|
|
* # FooError and BarError are prohibited.
|
|
|
|
* }
|
|
|
|
* }
|
|
|
|
*
|
2013-02-23 11:00:51 +04:00
|
|
|
* ==== Inheritance with ExceptionClass
|
|
|
|
*
|
|
|
|
* All exceptions inherited from the ExceptionClass parameter will be considered.
|
|
|
|
*
|
2012-12-23 14:18:58 +04:00
|
|
|
* Thread.handle_interrupt(Exception => :never) {
|
2012-07-19 18:19:40 +04:00
|
|
|
* # all exceptions inherited from Exception are prohibited.
|
|
|
|
* }
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
static VALUE
|
2012-12-23 14:18:58 +04:00
|
|
|
rb_thread_s_handle_interrupt(VALUE self, VALUE mask_arg)
|
2012-07-19 18:19:40 +04:00
|
|
|
{
|
2012-11-30 21:39:59 +04:00
|
|
|
VALUE mask;
|
2017-11-06 10:44:28 +03:00
|
|
|
rb_execution_context_t * volatile ec = GET_EC();
|
|
|
|
rb_thread_t * volatile th = rb_ec_thread_ptr(ec);
|
2015-03-03 18:45:00 +03:00
|
|
|
volatile VALUE r = Qnil;
|
2017-06-23 10:25:52 +03:00
|
|
|
enum ruby_tag_type state;
|
2012-11-30 21:39:59 +04:00
|
|
|
|
2012-07-19 18:19:40 +04:00
|
|
|
if (!rb_block_given_p()) {
|
|
|
|
rb_raise(rb_eArgError, "block is needed.");
|
|
|
|
}
|
|
|
|
|
2015-07-30 05:53:57 +03:00
|
|
|
mask = 0;
|
2017-10-26 10:23:23 +03:00
|
|
|
mask_arg = rb_to_hash_type(mask_arg);
|
2015-07-30 05:53:57 +03:00
|
|
|
rb_hash_foreach(mask_arg, handle_interrupt_arg_check_i, (VALUE)&mask);
|
|
|
|
if (!mask) {
|
|
|
|
return rb_yield(Qnil);
|
|
|
|
}
|
2015-08-13 10:22:10 +03:00
|
|
|
OBJ_FREEZE_RAW(mask);
|
2012-12-23 14:18:58 +04:00
|
|
|
rb_ary_push(th->pending_interrupt_mask_stack, mask);
|
|
|
|
if (!rb_threadptr_pending_interrupt_empty_p(th)) {
|
|
|
|
th->pending_interrupt_queue_checked = 0;
|
2017-11-06 10:44:28 +03:00
|
|
|
RUBY_VM_SET_INTERRUPT(th->ec);
|
2012-11-30 21:39:59 +04:00
|
|
|
}
|
|
|
|
|
2017-10-26 14:02:13 +03:00
|
|
|
EC_PUSH_TAG(th->ec);
|
2017-12-06 06:16:08 +03:00
|
|
|
if ((state = EC_EXEC_TAG()) == TAG_NONE) {
|
2012-11-30 21:39:59 +04:00
|
|
|
r = rb_yield(Qnil);
|
|
|
|
}
|
2017-10-26 14:02:13 +03:00
|
|
|
EC_POP_TAG();
|
2012-11-30 21:39:59 +04:00
|
|
|
|
2012-12-23 14:18:58 +04:00
|
|
|
rb_ary_pop(th->pending_interrupt_mask_stack);
|
|
|
|
if (!rb_threadptr_pending_interrupt_empty_p(th)) {
|
|
|
|
th->pending_interrupt_queue_checked = 0;
|
2017-11-06 10:44:28 +03:00
|
|
|
RUBY_VM_SET_INTERRUPT(th->ec);
|
2012-11-30 21:39:59 +04:00
|
|
|
}
|
|
|
|
|
2017-11-06 10:44:28 +03:00
|
|
|
RUBY_VM_CHECK_INTS(th->ec);
|
2012-12-05 13:54:58 +04:00
|
|
|
|
2012-11-30 21:39:59 +04:00
|
|
|
if (state) {
|
2017-10-26 14:02:13 +03:00
|
|
|
EC_JUMP_TAG(th->ec, state);
|
2012-11-30 21:39:59 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return r;
|
2012-07-19 18:19:40 +04:00
|
|
|
}
|
2012-12-25 11:17:55 +04:00
|
|
|
|
2012-12-23 14:18:58 +04:00
|
|
|
/*
|
|
|
|
* call-seq:
|
2013-02-23 11:00:51 +04:00
|
|
|
* target_thread.pending_interrupt?(error = nil) -> true/false
|
|
|
|
*
|
2013-05-19 07:10:21 +04:00
|
|
|
* Returns whether or not the asynchronous queue is empty for the target thread.
|
2012-12-23 14:18:58 +04:00
|
|
|
*
|
2013-02-23 11:00:51 +04:00
|
|
|
* If +error+ is given, then check only for +error+ type deferred events.
|
|
|
|
*
|
|
|
|
* See ::pending_interrupt? for more information.
|
2012-12-23 14:18:58 +04:00
|
|
|
*/
|
|
|
|
static VALUE
|
|
|
|
rb_thread_pending_interrupt_p(int argc, VALUE *argv, VALUE target_thread)
|
|
|
|
{
|
2017-06-28 07:49:30 +03:00
|
|
|
rb_thread_t *target_th = rb_thread_ptr(target_thread);
|
2012-12-23 14:18:58 +04:00
|
|
|
|
2016-01-07 08:49:31 +03:00
|
|
|
if (!target_th->pending_interrupt_queue) {
|
|
|
|
return Qfalse;
|
|
|
|
}
|
2012-12-23 14:18:58 +04:00
|
|
|
if (rb_threadptr_pending_interrupt_empty_p(target_th)) {
|
|
|
|
return Qfalse;
|
|
|
|
}
|
2018-12-06 10:49:24 +03:00
|
|
|
if (rb_check_arity(argc, 0, 1)) {
|
|
|
|
VALUE err = argv[0];
|
|
|
|
if (!rb_obj_is_kind_of(err, rb_cModule)) {
|
|
|
|
rb_raise(rb_eTypeError, "class or module required for rescue clause");
|
|
|
|
}
|
|
|
|
if (rb_threadptr_pending_interrupt_include_p(target_th, err)) {
|
|
|
|
return Qtrue;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return Qfalse;
|
|
|
|
}
|
|
|
|
}
|
2012-12-23 14:18:58 +04:00
|
|
|
else {
|
|
|
|
return Qtrue;
|
|
|
|
}
|
|
|
|
}
|
2012-07-19 18:19:40 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* call-seq:
|
2013-02-23 11:00:51 +04:00
|
|
|
* Thread.pending_interrupt?(error = nil) -> true/false
|
|
|
|
*
|
|
|
|
* Returns whether or not the asynchronous queue is empty.
|
2012-07-19 18:19:40 +04:00
|
|
|
*
|
2013-04-27 14:33:44 +04:00
|
|
|
* Since Thread::handle_interrupt can be used to defer asynchronous events,
|
|
|
|
* this method can be used to determine if there are any deferred events.
|
2012-07-19 18:19:40 +04:00
|
|
|
*
|
2013-02-23 11:00:51 +04:00
|
|
|
* If you find this method returns true, then you may finish +:never+ blocks.
|
2012-07-19 18:19:40 +04:00
|
|
|
*
|
2013-02-23 11:00:51 +04:00
|
|
|
* For example, the following method processes deferred asynchronous events
|
|
|
|
* immediately.
|
2012-07-19 18:19:40 +04:00
|
|
|
*
|
2012-12-23 14:18:58 +04:00
|
|
|
* def Thread.kick_interrupt_immediately
|
|
|
|
* Thread.handle_interrupt(Object => :immediate) {
|
2012-07-19 18:19:40 +04:00
|
|
|
* Thread.pass
|
|
|
|
* }
|
|
|
|
* end
|
|
|
|
*
|
2013-02-23 11:00:51 +04:00
|
|
|
* If +error+ is given, then check only for +error+ type deferred events.
|
2012-11-30 13:28:35 +04:00
|
|
|
*
|
2013-02-23 11:00:51 +04:00
|
|
|
* === Usage
|
2012-07-19 18:19:40 +04:00
|
|
|
*
|
|
|
|
* th = Thread.new{
|
2012-12-23 14:18:58 +04:00
|
|
|
* Thread.handle_interrupt(RuntimeError => :on_blocking){
|
2012-07-19 18:19:40 +04:00
|
|
|
* while true
|
|
|
|
* ...
|
|
|
|
* # reach safe point to invoke interrupt
|
2012-12-23 14:18:58 +04:00
|
|
|
* if Thread.pending_interrupt?
|
|
|
|
* Thread.handle_interrupt(Object => :immediate){}
|
2012-11-30 13:28:35 +04:00
|
|
|
* end
|
2012-07-19 18:19:40 +04:00
|
|
|
* ...
|
|
|
|
* end
|
|
|
|
* }
|
|
|
|
* }
|
|
|
|
* ...
|
|
|
|
* th.raise # stop thread
|
|
|
|
*
|
2013-02-23 11:00:51 +04:00
|
|
|
* This example can also be written as the following, which you should use to
|
|
|
|
* avoid asynchronous interrupts.
|
2012-07-19 18:19:40 +04:00
|
|
|
*
|
|
|
|
* flag = true
|
|
|
|
* th = Thread.new{
|
2012-12-23 14:18:58 +04:00
|
|
|
* Thread.handle_interrupt(RuntimeError => :on_blocking){
|
2012-07-19 18:19:40 +04:00
|
|
|
* while true
|
|
|
|
* ...
|
|
|
|
* # reach safe point to invoke interrupt
|
|
|
|
* break if flag == false
|
|
|
|
* ...
|
|
|
|
* end
|
|
|
|
* }
|
|
|
|
* }
|
|
|
|
* ...
|
|
|
|
* flag = false # stop thread
|
|
|
|
*/
|
2012-11-20 05:21:19 +04:00
|
|
|
|
2012-07-19 18:19:40 +04:00
|
|
|
static VALUE
|
2012-12-23 14:18:58 +04:00
|
|
|
rb_thread_s_pending_interrupt_p(int argc, VALUE *argv, VALUE self)
|
2012-07-19 18:19:40 +04:00
|
|
|
{
|
2012-12-23 14:18:58 +04:00
|
|
|
return rb_thread_pending_interrupt_p(argc, argv, GET_THREAD()->self);
|
2012-07-19 18:19:40 +04:00
|
|
|
}
|
|
|
|
|
2018-01-18 12:44:46 +03:00
|
|
|
NORETURN(static void rb_threadptr_to_kill(rb_thread_t *th));
|
|
|
|
|
2012-07-19 18:19:40 +04:00
|
|
|
static void
|
|
|
|
rb_threadptr_to_kill(rb_thread_t *th)
|
|
|
|
{
|
2012-12-23 14:18:58 +04:00
|
|
|
rb_threadptr_pending_interrupt_clear(th);
|
2012-11-28 12:31:03 +04:00
|
|
|
th->status = THREAD_RUNNABLE;
|
|
|
|
th->to_kill = 1;
|
2017-10-26 11:32:49 +03:00
|
|
|
th->ec->errinfo = INT2FIX(TAG_FATAL);
|
2017-10-26 14:02:13 +03:00
|
|
|
EC_JUMP_TAG(th->ec, TAG_FATAL);
|
2012-07-19 18:19:40 +04:00
|
|
|
}
|
|
|
|
|
2013-10-10 22:36:54 +04:00
|
|
|
static inline rb_atomic_t
|
|
|
|
threadptr_get_interrupts(rb_thread_t *th)
|
|
|
|
{
|
2017-11-06 10:44:28 +03:00
|
|
|
rb_execution_context_t *ec = th->ec;
|
2013-10-10 22:36:54 +04:00
|
|
|
rb_atomic_t interrupt;
|
|
|
|
rb_atomic_t old;
|
|
|
|
|
|
|
|
do {
|
2017-11-06 10:44:28 +03:00
|
|
|
interrupt = ec->interrupt_flag;
|
|
|
|
old = ATOMIC_CAS(ec->interrupt_flag, interrupt, interrupt & ec->interrupt_mask);
|
2013-10-10 22:36:54 +04:00
|
|
|
} while (old != interrupt);
|
2017-11-06 10:44:28 +03:00
|
|
|
return interrupt & (rb_atomic_t)~ec->interrupt_mask;
|
2013-10-10 22:36:54 +04:00
|
|
|
}
|
|
|
|
|
2018-08-18 12:07:36 +03:00
|
|
|
MJIT_FUNC_EXPORTED int
|
2012-11-20 05:21:19 +04:00
|
|
|
rb_threadptr_execute_interrupts(rb_thread_t *th, int blocking_timing)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2013-10-10 22:36:54 +04:00
|
|
|
rb_atomic_t interrupt;
|
|
|
|
int postponed_job_interrupt = 0;
|
2018-08-18 12:07:36 +03:00
|
|
|
int ret = FALSE;
|
2013-10-10 22:36:54 +04:00
|
|
|
|
2018-08-18 12:07:36 +03:00
|
|
|
if (th->ec->raised_flag) return ret;
|
2008-07-27 09:59:32 +04:00
|
|
|
|
2013-10-10 22:36:54 +04:00
|
|
|
while ((interrupt = threadptr_get_interrupts(th)) != 0) {
|
2011-07-01 02:29:34 +04:00
|
|
|
int sig;
|
2012-11-26 14:57:39 +04:00
|
|
|
int timer_interrupt;
|
2012-12-23 14:18:58 +04:00
|
|
|
int pending_interrupt;
|
2012-11-26 14:57:39 +04:00
|
|
|
int trap_interrupt;
|
|
|
|
|
2012-11-26 16:17:10 +04:00
|
|
|
timer_interrupt = interrupt & TIMER_INTERRUPT_MASK;
|
2012-12-23 14:18:58 +04:00
|
|
|
pending_interrupt = interrupt & PENDING_INTERRUPT_MASK;
|
2013-05-27 01:30:44 +04:00
|
|
|
postponed_job_interrupt = interrupt & POSTPONED_JOB_INTERRUPT_MASK;
|
2012-11-26 16:17:10 +04:00
|
|
|
trap_interrupt = interrupt & TRAP_INTERRUPT_MASK;
|
2008-07-27 09:59:32 +04:00
|
|
|
|
2013-10-22 10:24:54 +04:00
|
|
|
if (postponed_job_interrupt) {
|
|
|
|
rb_postponed_job_flush(th->vm);
|
|
|
|
}
|
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
/* signal handling */
|
2012-11-26 13:22:01 +04:00
|
|
|
if (trap_interrupt && (th == th->vm->main_thread)) {
|
2012-11-28 12:31:03 +04:00
|
|
|
enum rb_thread_status prev_status = th->status;
|
2018-08-14 00:34:20 +03:00
|
|
|
int sigwait_fd = rb_sigwait_fd_get(th);
|
|
|
|
|
|
|
|
if (sigwait_fd >= 0) {
|
|
|
|
(void)consume_communication_pipe(sigwait_fd);
|
|
|
|
ruby_sigchld_handler(th->vm);
|
|
|
|
rb_sigwait_fd_put(th, sigwait_fd);
|
|
|
|
rb_sigwait_fd_migrate(th->vm);
|
|
|
|
}
|
2012-11-28 12:31:03 +04:00
|
|
|
th->status = THREAD_RUNNABLE;
|
2011-07-01 02:29:34 +04:00
|
|
|
while ((sig = rb_get_next_signal()) != 0) {
|
2018-08-18 12:07:36 +03:00
|
|
|
ret |= rb_signal_exec(th, sig);
|
2011-07-01 02:29:34 +04:00
|
|
|
}
|
2012-11-28 12:31:03 +04:00
|
|
|
th->status = prev_status;
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* exception from another thread */
|
2017-11-16 11:48:59 +03:00
|
|
|
if (pending_interrupt && threadptr_pending_interrupt_active_p(th)) {
|
2012-12-23 14:18:58 +04:00
|
|
|
VALUE err = rb_threadptr_pending_interrupt_deque(th, blocking_timing ? INTERRUPT_ON_BLOCKING : INTERRUPT_NONE);
|
2010-10-12 19:03:51 +04:00
|
|
|
thread_debug("rb_thread_execute_interrupts: %"PRIdVALUE"\n", err);
|
2018-08-18 12:07:36 +03:00
|
|
|
ret = TRUE;
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2012-07-19 18:19:40 +04:00
|
|
|
if (err == Qundef) {
|
|
|
|
/* no error */
|
|
|
|
}
|
2013-05-19 07:10:21 +04:00
|
|
|
else if (err == eKillSignal /* Thread#kill received */ ||
|
2012-12-22 10:45:28 +04:00
|
|
|
err == eTerminateSignal /* Terminate thread */ ||
|
|
|
|
err == INT2FIX(TAG_FATAL) /* Thread.exit etc. */ ) {
|
2012-11-20 05:21:19 +04:00
|
|
|
rb_threadptr_to_kill(th);
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
else {
|
2017-04-17 05:31:35 +03:00
|
|
|
if (err == th->vm->special_exceptions[ruby_error_stream_closed]) {
|
2018-01-17 16:15:13 +03:00
|
|
|
/* the only special exception to be queued across thread */
|
2017-04-17 05:31:35 +03:00
|
|
|
err = ruby_vm_special_exception_copy(err);
|
|
|
|
}
|
2012-11-28 12:31:03 +04:00
|
|
|
/* set runnable if th was slept. */
|
|
|
|
if (th->status == THREAD_STOPPED ||
|
|
|
|
th->status == THREAD_STOPPED_FOREVER)
|
|
|
|
th->status = THREAD_RUNNABLE;
|
2006-12-31 18:02:22 +03:00
|
|
|
rb_exc_raise(err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-06-29 21:36:00 +04:00
|
|
|
if (timer_interrupt) {
|
2017-05-07 11:06:02 +03:00
|
|
|
uint32_t limits_us = TIME_QUANTUM_USEC;
|
2011-06-13 18:14:53 +04:00
|
|
|
|
2012-11-20 05:21:19 +04:00
|
|
|
if (th->priority > 0)
|
|
|
|
limits_us <<= th->priority;
|
2011-06-13 18:14:53 +04:00
|
|
|
else
|
2012-11-20 05:21:19 +04:00
|
|
|
limits_us >>= -th->priority;
|
2011-06-13 18:14:53 +04:00
|
|
|
|
2012-11-28 12:31:03 +04:00
|
|
|
if (th->status == THREAD_RUNNABLE)
|
2012-11-20 05:21:19 +04:00
|
|
|
th->running_time_us += TIME_QUANTUM_USEC;
|
2011-06-13 18:14:53 +04:00
|
|
|
|
2017-10-29 16:19:14 +03:00
|
|
|
EXEC_EVENT_HOOK(th->ec, RUBY_INTERNAL_EVENT_SWITCH, th->ec->cfp->self,
|
2017-05-09 08:06:41 +03:00
|
|
|
0, 0, 0, Qundef);
|
2008-08-14 01:26:49 +04:00
|
|
|
|
2011-06-29 21:36:00 +04:00
|
|
|
rb_thread_schedule_limits(limits_us);
|
2008-07-27 09:59:32 +04:00
|
|
|
}
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
2018-08-18 12:07:36 +03:00
|
|
|
return ret;
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
2011-06-09 19:02:46 +04:00
|
|
|
void
|
2011-06-11 05:17:11 +04:00
|
|
|
rb_thread_execute_interrupts(VALUE thval)
|
2011-06-09 19:02:46 +04:00
|
|
|
{
|
2017-06-28 07:49:30 +03:00
|
|
|
rb_threadptr_execute_interrupts(rb_thread_ptr(thval), 1);
|
2012-07-18 09:46:40 +04:00
|
|
|
}
|
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
static void
|
2009-06-08 20:14:06 +04:00
|
|
|
rb_threadptr_ready(rb_thread_t *th)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2009-06-08 20:14:06 +04:00
|
|
|
rb_threadptr_interrupt(th);
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
2017-11-07 09:09:47 +03:00
|
|
|
rb_threadptr_raise(rb_thread_t *target_th, int argc, VALUE *argv)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
|
|
|
VALUE exc;
|
|
|
|
|
2017-11-07 09:09:47 +03:00
|
|
|
if (rb_threadptr_dead(target_th)) {
|
2006-12-31 18:02:22 +03:00
|
|
|
return Qnil;
|
|
|
|
}
|
|
|
|
|
2012-07-19 18:19:40 +04:00
|
|
|
if (argc == 0) {
|
|
|
|
exc = rb_exc_new(rb_eRuntimeError, 0, 0);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
exc = rb_make_exception(argc, argv);
|
|
|
|
}
|
2017-02-10 11:15:39 +03:00
|
|
|
|
|
|
|
/* making an exception object can switch thread,
|
|
|
|
so we need to check thread deadness again */
|
2017-11-07 09:09:47 +03:00
|
|
|
if (rb_threadptr_dead(target_th)) {
|
2017-02-10 11:15:39 +03:00
|
|
|
return Qnil;
|
|
|
|
}
|
|
|
|
|
2017-11-07 09:09:47 +03:00
|
|
|
rb_ec_setup_exception(GET_EC(), exc, Qundef);
|
|
|
|
rb_threadptr_pending_interrupt_enque(target_th, exc);
|
|
|
|
rb_threadptr_interrupt(target_th);
|
2006-12-31 18:02:22 +03:00
|
|
|
return Qnil;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-06-08 20:14:06 +04:00
|
|
|
rb_threadptr_signal_raise(rb_thread_t *th, int sig)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2007-04-19 21:37:03 +04:00
|
|
|
VALUE argv[2];
|
|
|
|
|
|
|
|
argv[0] = rb_eSignal;
|
|
|
|
argv[1] = INT2FIX(sig);
|
2009-06-08 20:14:06 +04:00
|
|
|
rb_threadptr_raise(th->vm->main_thread, 2, argv);
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-06-08 20:14:06 +04:00
|
|
|
rb_threadptr_signal_exit(rb_thread_t *th)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2007-04-19 21:37:03 +04:00
|
|
|
VALUE argv[2];
|
|
|
|
|
|
|
|
argv[0] = rb_eSystemExit;
|
|
|
|
argv[1] = rb_str_new2("exit");
|
2009-06-08 20:14:06 +04:00
|
|
|
rb_threadptr_raise(th->vm->main_thread, 2, argv);
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
2007-02-05 15:21:01 +03:00
|
|
|
int
|
2017-11-07 08:12:39 +03:00
|
|
|
rb_ec_set_raised(rb_execution_context_t *ec)
|
2007-02-05 15:21:01 +03:00
|
|
|
{
|
2017-11-07 08:12:39 +03:00
|
|
|
if (ec->raised_flag & RAISED_EXCEPTION) {
|
2007-02-05 15:21:01 +03:00
|
|
|
return 1;
|
|
|
|
}
|
2017-11-07 08:12:39 +03:00
|
|
|
ec->raised_flag |= RAISED_EXCEPTION;
|
2007-02-05 15:21:01 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2017-11-07 07:01:13 +03:00
|
|
|
rb_ec_reset_raised(rb_execution_context_t *ec)
|
2007-02-05 15:21:01 +03:00
|
|
|
{
|
2017-11-07 07:01:13 +03:00
|
|
|
if (!(ec->raised_flag & RAISED_EXCEPTION)) {
|
2007-02-05 15:21:01 +03:00
|
|
|
return 0;
|
|
|
|
}
|
2017-11-07 07:01:13 +03:00
|
|
|
ec->raised_flag &= ~RAISED_EXCEPTION;
|
2007-02-05 15:21:01 +03:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2017-01-25 17:34:07 +03:00
|
|
|
int
|
2018-04-21 06:12:36 +03:00
|
|
|
rb_notify_fd_close(int fd, struct list_head *busy)
|
2007-02-05 15:21:01 +03:00
|
|
|
{
|
2014-05-28 05:48:11 +04:00
|
|
|
rb_vm_t *vm = GET_THREAD()->vm;
|
2018-08-21 00:34:44 +03:00
|
|
|
struct waiting_fd *wfd = 0, *next;
|
2014-05-28 05:48:11 +04:00
|
|
|
|
2018-04-21 06:12:36 +03:00
|
|
|
list_for_each_safe(&vm->waiting_fds, wfd, next, wfd_node) {
|
2017-05-20 12:47:14 +03:00
|
|
|
if (wfd->fd == fd) {
|
|
|
|
rb_thread_t *th = wfd->th;
|
2017-06-07 01:55:35 +03:00
|
|
|
VALUE err;
|
|
|
|
|
2018-04-21 06:12:36 +03:00
|
|
|
list_del(&wfd->wfd_node);
|
|
|
|
list_add(busy, &wfd->wfd_node);
|
|
|
|
|
2017-06-07 01:55:35 +03:00
|
|
|
err = th->vm->special_exceptions[ruby_error_stream_closed];
|
2014-05-28 05:48:11 +04:00
|
|
|
rb_threadptr_pending_interrupt_enque(th, err);
|
|
|
|
rb_threadptr_interrupt(th);
|
|
|
|
}
|
|
|
|
}
|
2018-04-21 06:12:36 +03:00
|
|
|
return !list_empty(busy);
|
2007-02-05 15:21:01 +03:00
|
|
|
}
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2017-03-13 05:14:15 +03:00
|
|
|
void
|
|
|
|
rb_thread_fd_close(int fd)
|
|
|
|
{
|
2018-05-01 07:41:10 +03:00
|
|
|
struct list_head busy;
|
2018-04-21 06:12:36 +03:00
|
|
|
|
2018-05-01 11:35:19 +03:00
|
|
|
list_head_init(&busy);
|
2018-04-21 06:12:36 +03:00
|
|
|
if (rb_notify_fd_close(fd, &busy)) {
|
|
|
|
do rb_thread_schedule(); while (!list_empty(&busy));
|
|
|
|
}
|
2017-03-13 05:14:15 +03:00
|
|
|
}
|
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
/*
|
|
|
|
* call-seq:
|
2009-10-12 02:08:51 +04:00
|
|
|
* thr.raise
|
|
|
|
* thr.raise(string)
|
|
|
|
* thr.raise(exception [, string [, array]])
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2013-02-27 05:24:08 +04:00
|
|
|
* Raises an exception from the given thread. The caller does not have to be
|
|
|
|
* +thr+. See Kernel#raise for more information.
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2006-12-31 18:02:22 +03:00
|
|
|
* Thread.abort_on_exception = true
|
|
|
|
* a = Thread.new { sleep(200) }
|
|
|
|
* a.raise("Gotcha")
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2013-02-28 00:36:59 +04:00
|
|
|
* This will produce:
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2006-12-31 18:02:22 +03:00
|
|
|
* prog.rb:3: Gotcha (RuntimeError)
|
|
|
|
* from prog.rb:2:in `initialize'
|
|
|
|
* from prog.rb:2:in `new'
|
|
|
|
* from prog.rb:2
|
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
2007-02-05 15:21:01 +03:00
|
|
|
thread_raise_m(int argc, VALUE *argv, VALUE self)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2017-06-28 07:49:30 +03:00
|
|
|
rb_thread_t *target_th = rb_thread_ptr(self);
|
|
|
|
const rb_thread_t *current_th = GET_THREAD();
|
|
|
|
|
2016-01-07 08:49:31 +03:00
|
|
|
threadptr_check_pending_interrupt_queue(target_th);
|
2012-10-23 17:37:45 +04:00
|
|
|
rb_threadptr_raise(target_th, argc, argv);
|
|
|
|
|
|
|
|
/* To perform Thread.current.raise as Kernel.raise */
|
2017-06-28 07:49:30 +03:00
|
|
|
if (current_th == target_th) {
|
2017-11-06 10:44:28 +03:00
|
|
|
RUBY_VM_CHECK_INTS(target_th->ec);
|
2012-10-23 17:37:45 +04:00
|
|
|
}
|
2006-12-31 18:02:22 +03:00
|
|
|
return Qnil;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* call-seq:
|
2010-05-18 01:07:33 +04:00
|
|
|
* thr.exit -> thr or nil
|
|
|
|
* thr.kill -> thr or nil
|
|
|
|
* thr.terminate -> thr or nil
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2013-02-28 00:36:59 +04:00
|
|
|
* Terminates +thr+ and schedules another thread to be run.
|
|
|
|
*
|
|
|
|
* If this thread is already marked to be killed, #exit returns the Thread.
|
|
|
|
*
|
|
|
|
* If this is the main thread, or the last thread, exits the process.
|
2006-12-31 18:02:22 +03:00
|
|
|
*/
|
|
|
|
|
|
|
|
VALUE
|
|
|
|
rb_thread_kill(VALUE thread)
|
|
|
|
{
|
2017-06-28 07:49:30 +03:00
|
|
|
rb_thread_t *th = rb_thread_ptr(thread);
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2012-11-28 12:31:03 +04:00
|
|
|
if (th->to_kill || th->status == THREAD_KILLED) {
|
2006-12-31 18:02:22 +03:00
|
|
|
return thread;
|
|
|
|
}
|
|
|
|
if (th == th->vm->main_thread) {
|
|
|
|
rb_exit(EXIT_SUCCESS);
|
|
|
|
}
|
|
|
|
|
2014-06-11 12:38:09 +04:00
|
|
|
thread_debug("rb_thread_kill: %p (%"PRI_THREAD_ID")\n", (void *)th, thread_id_str(th));
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2012-07-19 18:19:40 +04:00
|
|
|
if (th == GET_THREAD()) {
|
|
|
|
/* kill myself immediately */
|
|
|
|
rb_threadptr_to_kill(th);
|
|
|
|
}
|
|
|
|
else {
|
2016-01-07 08:49:31 +03:00
|
|
|
threadptr_check_pending_interrupt_queue(th);
|
2012-12-23 14:18:58 +04:00
|
|
|
rb_threadptr_pending_interrupt_enque(th, eKillSignal);
|
2012-07-19 18:19:40 +04:00
|
|
|
rb_threadptr_interrupt(th);
|
|
|
|
}
|
2006-12-31 18:02:22 +03:00
|
|
|
return thread;
|
|
|
|
}
|
|
|
|
|
2015-09-08 17:09:30 +03:00
|
|
|
int
|
|
|
|
rb_thread_to_be_killed(VALUE thread)
|
|
|
|
{
|
2017-06-28 07:49:30 +03:00
|
|
|
rb_thread_t *th = rb_thread_ptr(thread);
|
2015-09-08 17:09:30 +03:00
|
|
|
|
|
|
|
if (th->to_kill || th->status == THREAD_KILLED) {
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
2006-12-31 18:02:22 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* call-seq:
|
2010-05-18 01:07:33 +04:00
|
|
|
* Thread.kill(thread) -> thread
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2013-02-28 00:36:59 +04:00
|
|
|
* Causes the given +thread+ to exit, see also Thread::exit.
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2006-12-31 18:02:22 +03:00
|
|
|
* count = 0
|
|
|
|
* a = Thread.new { loop { count += 1 } }
|
|
|
|
* sleep(0.1) #=> 0
|
|
|
|
* Thread.kill(a) #=> #<Thread:0x401b3d30 dead>
|
|
|
|
* count #=> 93947
|
|
|
|
* a.alive? #=> false
|
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
rb_thread_s_kill(VALUE obj, VALUE th)
|
|
|
|
{
|
|
|
|
return rb_thread_kill(th);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* call-seq:
|
2010-05-18 01:07:33 +04:00
|
|
|
* Thread.exit -> thread
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2006-12-31 18:02:22 +03:00
|
|
|
* Terminates the currently running thread and schedules another thread to be
|
2013-02-28 00:36:59 +04:00
|
|
|
* run.
|
|
|
|
*
|
|
|
|
* If this thread is already marked to be killed, ::exit returns the Thread.
|
|
|
|
*
|
|
|
|
* If this is the main thread, or the last thread, exit the process.
|
2006-12-31 18:02:22 +03:00
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
2007-03-19 06:58:57 +03:00
|
|
|
rb_thread_exit(void)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2012-07-19 18:19:40 +04:00
|
|
|
rb_thread_t *th = GET_THREAD();
|
|
|
|
return rb_thread_kill(th->self);
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* call-seq:
|
2010-05-18 01:07:33 +04:00
|
|
|
* thr.wakeup -> thr
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2013-02-27 07:58:36 +04:00
|
|
|
* Marks a given thread as eligible for scheduling, however it may still
|
|
|
|
* remain blocked on I/O.
|
|
|
|
*
|
|
|
|
* *Note:* This does not invoke the scheduler, see #run for more information.
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2006-12-31 18:02:22 +03:00
|
|
|
* c = Thread.new { Thread.stop; puts "hey!" }
|
2011-06-29 19:51:54 +04:00
|
|
|
* sleep 0.1 while c.status!='sleep'
|
2006-12-31 18:02:22 +03:00
|
|
|
* c.wakeup
|
2011-06-29 19:51:54 +04:00
|
|
|
* c.join
|
2013-02-28 00:36:59 +04:00
|
|
|
* #=> "hey!"
|
2006-12-31 18:02:22 +03:00
|
|
|
*/
|
|
|
|
|
|
|
|
VALUE
|
|
|
|
rb_thread_wakeup(VALUE thread)
|
2010-07-17 08:04:51 +04:00
|
|
|
{
|
|
|
|
if (!RTEST(rb_thread_wakeup_alive(thread))) {
|
|
|
|
rb_raise(rb_eThreadError, "killed thread");
|
|
|
|
}
|
|
|
|
return thread;
|
|
|
|
}
|
|
|
|
|
|
|
|
VALUE
|
|
|
|
rb_thread_wakeup_alive(VALUE thread)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2017-06-28 07:49:30 +03:00
|
|
|
rb_thread_t *target_th = rb_thread_ptr(thread);
|
|
|
|
if (target_th->status == THREAD_KILLED) return Qnil;
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2017-06-28 07:49:30 +03:00
|
|
|
rb_threadptr_ready(target_th);
|
|
|
|
|
|
|
|
if (target_th->status == THREAD_STOPPED ||
|
|
|
|
target_th->status == THREAD_STOPPED_FOREVER) {
|
|
|
|
target_th->status = THREAD_RUNNABLE;
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
2017-06-28 07:49:30 +03:00
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
return thread;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* call-seq:
|
2010-05-18 01:07:33 +04:00
|
|
|
* thr.run -> thr
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2013-02-28 00:36:59 +04:00
|
|
|
* Wakes up +thr+, making it eligible for scheduling.
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2006-12-31 18:02:22 +03:00
|
|
|
* a = Thread.new { puts "a"; Thread.stop; puts "c" }
|
2011-06-29 19:47:24 +04:00
|
|
|
* sleep 0.1 while a.status!='sleep'
|
2006-12-31 18:02:22 +03:00
|
|
|
* puts "Got here"
|
|
|
|
* a.run
|
|
|
|
* a.join
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2013-02-28 00:36:59 +04:00
|
|
|
* This will produce:
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2006-12-31 18:02:22 +03:00
|
|
|
* a
|
|
|
|
* Got here
|
|
|
|
* c
|
2013-02-28 00:36:59 +04:00
|
|
|
*
|
|
|
|
* See also the instance method #wakeup.
|
2006-12-31 18:02:22 +03:00
|
|
|
*/
|
|
|
|
|
|
|
|
VALUE
|
2007-02-25 05:36:58 +03:00
|
|
|
rb_thread_run(VALUE thread)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
|
|
|
rb_thread_wakeup(thread);
|
|
|
|
rb_thread_schedule();
|
|
|
|
return thread;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* call-seq:
|
2010-05-18 01:07:33 +04:00
|
|
|
* Thread.stop -> nil
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2006-12-31 18:02:22 +03:00
|
|
|
* Stops execution of the current thread, putting it into a ``sleep'' state,
|
2007-12-25 16:14:23 +03:00
|
|
|
* and schedules execution of another thread.
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2006-12-31 18:02:22 +03:00
|
|
|
* a = Thread.new { print "a"; Thread.stop; print "c" }
|
2011-06-29 19:55:44 +04:00
|
|
|
* sleep 0.1 while a.status!='sleep'
|
2006-12-31 18:02:22 +03:00
|
|
|
* print "b"
|
|
|
|
* a.run
|
|
|
|
* a.join
|
2013-02-28 00:36:59 +04:00
|
|
|
* #=> "abc"
|
2006-12-31 18:02:22 +03:00
|
|
|
*/
|
|
|
|
|
|
|
|
VALUE
|
|
|
|
rb_thread_stop(void)
|
|
|
|
{
|
|
|
|
if (rb_thread_alone()) {
|
|
|
|
rb_raise(rb_eThreadError,
|
|
|
|
"stopping only thread\n\tnote: use sleep to stop forever");
|
|
|
|
}
|
2008-06-12 17:01:38 +04:00
|
|
|
rb_thread_sleep_deadly();
|
2006-12-31 18:02:22 +03:00
|
|
|
return Qnil;
|
|
|
|
}
|
|
|
|
|
|
|
|
/********************************************************************/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* call-seq:
|
2010-05-18 01:07:33 +04:00
|
|
|
* Thread.list -> array
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2013-02-28 00:36:59 +04:00
|
|
|
* Returns an array of Thread objects for all threads that are either runnable
|
|
|
|
* or stopped.
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2006-12-31 18:02:22 +03:00
|
|
|
* Thread.new { sleep(200) }
|
|
|
|
* Thread.new { 1000000.times {|i| i*i } }
|
|
|
|
* Thread.new { Thread.stop }
|
|
|
|
* Thread.list.each {|t| p t}
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2013-02-28 00:36:59 +04:00
|
|
|
* This will produce:
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2006-12-31 18:02:22 +03:00
|
|
|
* #<Thread:0x401b3e84 sleep>
|
|
|
|
* #<Thread:0x401b3f38 run>
|
|
|
|
* #<Thread:0x401b3fb0 sleep>
|
|
|
|
* #<Thread:0x401bdf4c run>
|
|
|
|
*/
|
|
|
|
|
|
|
|
VALUE
|
|
|
|
rb_thread_list(void)
|
|
|
|
{
|
|
|
|
VALUE ary = rb_ary_new();
|
2014-05-28 05:48:11 +04:00
|
|
|
rb_vm_t *vm = GET_THREAD()->vm;
|
2014-05-29 19:45:25 +04:00
|
|
|
rb_thread_t *th = 0;
|
2014-05-28 05:48:11 +04:00
|
|
|
|
|
|
|
list_for_each(&vm->living_threads, th, vmlt_node) {
|
|
|
|
switch (th->status) {
|
|
|
|
case THREAD_RUNNABLE:
|
|
|
|
case THREAD_STOPPED:
|
|
|
|
case THREAD_STOPPED_FOREVER:
|
|
|
|
rb_ary_push(ary, th->self);
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2006-12-31 18:02:22 +03:00
|
|
|
return ary;
|
|
|
|
}
|
|
|
|
|
2007-02-05 15:21:01 +03:00
|
|
|
VALUE
|
|
|
|
rb_thread_current(void)
|
|
|
|
{
|
|
|
|
return GET_THREAD()->self;
|
|
|
|
}
|
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
/*
|
|
|
|
* call-seq:
|
2010-05-18 01:07:33 +04:00
|
|
|
* Thread.current -> thread
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2006-12-31 18:02:22 +03:00
|
|
|
* Returns the currently executing thread.
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2006-12-31 18:02:22 +03:00
|
|
|
* Thread.current #=> #<Thread:0x401bdf4c run>
|
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
2007-02-05 15:21:01 +03:00
|
|
|
thread_s_current(VALUE klass)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2007-02-05 15:21:01 +03:00
|
|
|
return rb_thread_current();
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
VALUE
|
|
|
|
rb_thread_main(void)
|
|
|
|
{
|
|
|
|
return GET_THREAD()->vm->main_thread->self;
|
|
|
|
}
|
|
|
|
|
2009-09-17 08:51:33 +04:00
|
|
|
/*
|
|
|
|
* call-seq:
|
2010-05-18 01:07:33 +04:00
|
|
|
* Thread.main -> thread
|
2009-09-17 08:51:33 +04:00
|
|
|
*
|
|
|
|
* Returns the main thread.
|
|
|
|
*/
|
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
static VALUE
|
|
|
|
rb_thread_s_main(VALUE klass)
|
|
|
|
{
|
|
|
|
return rb_thread_main();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* call-seq:
|
2010-05-18 01:07:33 +04:00
|
|
|
* Thread.abort_on_exception -> true or false
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2013-02-28 00:36:59 +04:00
|
|
|
* Returns the status of the global ``abort on exception'' condition.
|
|
|
|
*
|
|
|
|
* The default is +false+.
|
|
|
|
*
|
2016-11-29 15:54:43 +03:00
|
|
|
* When set to +true+, if any thread is aborted by an exception, the
|
|
|
|
* raised exception will be re-raised in the main thread.
|
2013-02-28 00:36:59 +04:00
|
|
|
*
|
|
|
|
* Can also be specified by the global $DEBUG flag or command line option
|
|
|
|
* +-d+.
|
|
|
|
*
|
|
|
|
* See also ::abort_on_exception=.
|
|
|
|
*
|
|
|
|
* There is also an instance level method to set this for a specific thread,
|
|
|
|
* see #abort_on_exception.
|
2006-12-31 18:02:22 +03:00
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
2007-03-19 06:58:57 +03:00
|
|
|
rb_thread_s_abort_exc(void)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
|
|
|
return GET_THREAD()->vm->thread_abort_on_exception ? Qtrue : Qfalse;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* call-seq:
|
2010-05-18 01:07:33 +04:00
|
|
|
* Thread.abort_on_exception= boolean -> true or false
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2016-11-29 15:54:43 +03:00
|
|
|
* When set to +true+, if any thread is aborted by an exception, the
|
|
|
|
* raised exception will be re-raised in the main thread.
|
2013-02-28 00:36:59 +04:00
|
|
|
* Returns the new state.
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2006-12-31 18:02:22 +03:00
|
|
|
* Thread.abort_on_exception = true
|
|
|
|
* t1 = Thread.new do
|
|
|
|
* puts "In new thread"
|
|
|
|
* raise "Exception from thread"
|
|
|
|
* end
|
|
|
|
* sleep(1)
|
|
|
|
* puts "not reached"
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2013-02-28 00:36:59 +04:00
|
|
|
* This will produce:
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2006-12-31 18:02:22 +03:00
|
|
|
* In new thread
|
|
|
|
* prog.rb:4: Exception from thread (RuntimeError)
|
|
|
|
* from prog.rb:2:in `initialize'
|
|
|
|
* from prog.rb:2:in `new'
|
|
|
|
* from prog.rb:2
|
2013-02-28 00:36:59 +04:00
|
|
|
*
|
|
|
|
* See also ::abort_on_exception.
|
|
|
|
*
|
|
|
|
* There is also an instance level method to set this for a specific thread,
|
|
|
|
* see #abort_on_exception=.
|
2006-12-31 18:02:22 +03:00
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
rb_thread_s_abort_exc_set(VALUE self, VALUE val)
|
|
|
|
{
|
|
|
|
GET_THREAD()->vm->thread_abort_on_exception = RTEST(val);
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* call-seq:
|
2010-05-18 01:07:33 +04:00
|
|
|
* thr.abort_on_exception -> true or false
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2006-12-31 18:02:22 +03:00
|
|
|
* Returns the status of the thread-local ``abort on exception'' condition for
|
2013-02-28 00:36:59 +04:00
|
|
|
* this +thr+.
|
|
|
|
*
|
|
|
|
* The default is +false+.
|
|
|
|
*
|
|
|
|
* See also #abort_on_exception=.
|
|
|
|
*
|
|
|
|
* There is also a class level method to set this for all threads, see
|
|
|
|
* ::abort_on_exception.
|
2006-12-31 18:02:22 +03:00
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
rb_thread_abort_exc(VALUE thread)
|
|
|
|
{
|
2017-06-28 07:49:30 +03:00
|
|
|
return rb_thread_ptr(thread)->abort_on_exception ? Qtrue : Qfalse;
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* call-seq:
|
2010-05-18 01:07:33 +04:00
|
|
|
* thr.abort_on_exception= boolean -> true or false
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2016-11-29 15:54:43 +03:00
|
|
|
* When set to +true+, if this +thr+ is aborted by an exception, the
|
|
|
|
* raised exception will be re-raised in the main thread.
|
2013-02-28 00:36:59 +04:00
|
|
|
*
|
|
|
|
* See also #abort_on_exception.
|
|
|
|
*
|
|
|
|
* There is also a class level method to set this for all threads, see
|
|
|
|
* ::abort_on_exception=.
|
2006-12-31 18:02:22 +03:00
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
rb_thread_abort_exc_set(VALUE thread, VALUE val)
|
|
|
|
{
|
2017-06-28 07:49:30 +03:00
|
|
|
rb_thread_ptr(thread)->abort_on_exception = RTEST(val);
|
2006-12-31 18:02:22 +03:00
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-06-06 03:25:38 +03:00
|
|
|
/*
|
|
|
|
* call-seq:
|
|
|
|
* Thread.report_on_exception -> true or false
|
|
|
|
*
|
|
|
|
* Returns the status of the global ``report on exception'' condition.
|
|
|
|
*
|
2017-12-13 18:59:26 +03:00
|
|
|
* The default is +true+ since Ruby 2.5.
|
|
|
|
*
|
|
|
|
* All threads created when this flag is true will report
|
|
|
|
* a message on $stderr if an exception kills the thread.
|
|
|
|
*
|
|
|
|
* Thread.new { 1.times { raise } }
|
|
|
|
*
|
|
|
|
* will produce this output on $stderr:
|
2016-06-06 03:25:38 +03:00
|
|
|
*
|
2017-12-13 18:59:26 +03:00
|
|
|
* #<Thread:...> terminated with exception (report_on_exception is true):
|
|
|
|
* Traceback (most recent call last):
|
|
|
|
* 2: from -e:1:in `block in <main>'
|
|
|
|
* 1: from -e:1:in `times'
|
|
|
|
*
|
|
|
|
* This is done to catch errors in threads early.
|
|
|
|
* In some cases, you might not want this output.
|
|
|
|
* There are multiple ways to avoid the extra output:
|
|
|
|
*
|
|
|
|
* * If the exception is not intended, the best is to fix the cause of
|
|
|
|
* the exception so it does not happen anymore.
|
|
|
|
* * If the exception is intended, it might be better to rescue it closer to
|
|
|
|
* where it is raised rather then let it kill the Thread.
|
|
|
|
* * If it is guaranteed the Thread will be joined with Thread#join or
|
|
|
|
* Thread#value, then it is safe to disable this report with
|
|
|
|
* <code>Thread.current.report_on_exception = false</code>
|
|
|
|
* when starting the Thread.
|
|
|
|
* However, this might handle the exception much later, or not at all
|
|
|
|
* if the Thread is never joined due to the parent thread being blocked, etc.
|
2016-06-06 03:25:38 +03:00
|
|
|
*
|
|
|
|
* See also ::report_on_exception=.
|
|
|
|
*
|
|
|
|
* There is also an instance level method to set this for a specific thread,
|
2017-12-13 18:59:26 +03:00
|
|
|
* see #report_on_exception=.
|
|
|
|
*
|
2016-06-06 03:25:38 +03:00
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
rb_thread_s_report_exc(void)
|
|
|
|
{
|
|
|
|
return GET_THREAD()->vm->thread_report_on_exception ? Qtrue : Qfalse;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* call-seq:
|
|
|
|
* Thread.report_on_exception= boolean -> true or false
|
|
|
|
*
|
2017-12-13 18:59:26 +03:00
|
|
|
* Returns the new state.
|
|
|
|
* When set to +true+, all threads created afterwards will inherit the
|
|
|
|
* condition and report a message on $stderr if an exception kills a thread:
|
2016-06-06 03:25:38 +03:00
|
|
|
*
|
|
|
|
* Thread.report_on_exception = true
|
|
|
|
* t1 = Thread.new do
|
|
|
|
* puts "In new thread"
|
|
|
|
* raise "Exception from thread"
|
|
|
|
* end
|
|
|
|
* sleep(1)
|
|
|
|
* puts "In the main thread"
|
|
|
|
*
|
|
|
|
* This will produce:
|
|
|
|
*
|
|
|
|
* In new thread
|
2017-12-13 18:59:26 +03:00
|
|
|
* #<Thread:...prog.rb:2> terminated with exception (report_on_exception is true):
|
|
|
|
* Traceback (most recent call last):
|
|
|
|
* prog.rb:4:in `block in <main>': Exception from thread (RuntimeError)
|
2016-06-06 03:25:38 +03:00
|
|
|
* In the main thread
|
|
|
|
*
|
|
|
|
* See also ::report_on_exception.
|
|
|
|
*
|
|
|
|
* There is also an instance level method to set this for a specific thread,
|
|
|
|
* see #report_on_exception=.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
rb_thread_s_report_exc_set(VALUE self, VALUE val)
|
|
|
|
{
|
|
|
|
GET_THREAD()->vm->thread_report_on_exception = RTEST(val);
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* call-seq:
|
|
|
|
* thr.report_on_exception -> true or false
|
|
|
|
*
|
|
|
|
* Returns the status of the thread-local ``report on exception'' condition for
|
|
|
|
* this +thr+.
|
|
|
|
*
|
2017-12-13 18:59:26 +03:00
|
|
|
* The default value when creating a Thread is the value of
|
|
|
|
* the global flag Thread.report_on_exception.
|
2016-06-06 03:25:38 +03:00
|
|
|
*
|
|
|
|
* See also #report_on_exception=.
|
|
|
|
*
|
2017-12-13 18:59:26 +03:00
|
|
|
* There is also a class level method to set this for all new threads, see
|
|
|
|
* ::report_on_exception=.
|
2016-06-06 03:25:38 +03:00
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
rb_thread_report_exc(VALUE thread)
|
|
|
|
{
|
2017-06-28 07:49:30 +03:00
|
|
|
return rb_thread_ptr(thread)->report_on_exception ? Qtrue : Qfalse;
|
2016-06-06 03:25:38 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* call-seq:
|
|
|
|
* thr.report_on_exception= boolean -> true or false
|
|
|
|
*
|
2017-12-13 18:59:26 +03:00
|
|
|
* When set to +true+, a message is printed on $stderr if an exception
|
|
|
|
* kills this +thr+. See ::report_on_exception for details.
|
2016-06-06 03:25:38 +03:00
|
|
|
*
|
|
|
|
* See also #report_on_exception.
|
|
|
|
*
|
2017-12-13 18:59:26 +03:00
|
|
|
* There is also a class level method to set this for all new threads, see
|
2016-06-06 03:25:38 +03:00
|
|
|
* ::report_on_exception=.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
rb_thread_report_exc_set(VALUE thread, VALUE val)
|
|
|
|
{
|
2017-06-28 07:49:30 +03:00
|
|
|
rb_thread_ptr(thread)->report_on_exception = RTEST(val);
|
2016-06-06 03:25:38 +03:00
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
/*
|
|
|
|
* call-seq:
|
2010-05-18 01:07:33 +04:00
|
|
|
* thr.group -> thgrp or nil
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2013-02-27 07:53:13 +04:00
|
|
|
* Returns the ThreadGroup which contains the given thread, or returns +nil+
|
|
|
|
* if +thr+ is not a member of any group.
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2006-12-31 18:02:22 +03:00
|
|
|
* Thread.main.group #=> #<ThreadGroup:0x4029d914>
|
|
|
|
*/
|
|
|
|
|
|
|
|
VALUE
|
|
|
|
rb_thread_group(VALUE thread)
|
|
|
|
{
|
2017-06-28 07:49:30 +03:00
|
|
|
VALUE group = rb_thread_ptr(thread)->thgroup;
|
|
|
|
return group == 0 ? Qnil : group;
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static const char *
|
2016-06-13 15:34:06 +03:00
|
|
|
thread_status_name(rb_thread_t *th, int detail)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2012-11-28 12:31:03 +04:00
|
|
|
switch (th->status) {
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
case THREAD_RUNNABLE:
|
2017-06-28 07:49:30 +03:00
|
|
|
return th->to_kill ? "aborting" : "run";
|
2008-06-12 17:01:38 +04:00
|
|
|
case THREAD_STOPPED_FOREVER:
|
2016-06-13 15:34:06 +03:00
|
|
|
if (detail) return "sleep_forever";
|
|
|
|
case THREAD_STOPPED:
|
2006-12-31 18:02:22 +03:00
|
|
|
return "sleep";
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
case THREAD_KILLED:
|
2006-12-31 18:02:22 +03:00
|
|
|
return "dead";
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
default:
|
2006-12-31 18:02:22 +03:00
|
|
|
return "unknown";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2009-06-08 20:14:06 +04:00
|
|
|
rb_threadptr_dead(rb_thread_t *th)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
|
|
|
return th->status == THREAD_KILLED;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* call-seq:
|
2010-05-18 01:07:33 +04:00
|
|
|
* thr.status -> string, false or nil
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2013-02-28 00:36:59 +04:00
|
|
|
* Returns the status of +thr+.
|
|
|
|
*
|
|
|
|
* [<tt>"sleep"</tt>]
|
|
|
|
* Returned if this thread is sleeping or waiting on I/O
|
|
|
|
* [<tt>"run"</tt>]
|
|
|
|
* When this thread is executing
|
|
|
|
* [<tt>"aborting"</tt>]
|
|
|
|
* If this thread is aborting
|
|
|
|
* [+false+]
|
|
|
|
* When this thread is terminated normally
|
|
|
|
* [+nil+]
|
|
|
|
* If terminated with an exception.
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2006-12-31 18:02:22 +03:00
|
|
|
* a = Thread.new { raise("die now") }
|
|
|
|
* b = Thread.new { Thread.stop }
|
|
|
|
* c = Thread.new { Thread.exit }
|
|
|
|
* d = Thread.new { sleep }
|
|
|
|
* d.kill #=> #<Thread:0x401b3678 aborting>
|
|
|
|
* a.status #=> nil
|
|
|
|
* b.status #=> "sleep"
|
|
|
|
* c.status #=> false
|
|
|
|
* d.status #=> "aborting"
|
|
|
|
* Thread.current.status #=> "run"
|
2013-02-28 00:36:59 +04:00
|
|
|
*
|
|
|
|
* See also the instance methods #alive? and #stop?
|
2006-12-31 18:02:22 +03:00
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
rb_thread_status(VALUE thread)
|
|
|
|
{
|
2017-06-28 07:49:30 +03:00
|
|
|
rb_thread_t *target_th = rb_thread_ptr(thread);
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2017-06-28 07:49:30 +03:00
|
|
|
if (rb_threadptr_dead(target_th)) {
|
2017-10-26 11:32:49 +03:00
|
|
|
if (!NIL_P(target_th->ec->errinfo) &&
|
|
|
|
!FIXNUM_P(target_th->ec->errinfo)) {
|
2006-12-31 18:02:22 +03:00
|
|
|
return Qnil;
|
|
|
|
}
|
2017-06-28 07:49:30 +03:00
|
|
|
else {
|
|
|
|
return Qfalse;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return rb_str_new2(thread_status_name(target_th, FALSE));
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* call-seq:
|
2010-05-18 01:07:33 +04:00
|
|
|
* thr.alive? -> true or false
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2013-02-28 00:36:59 +04:00
|
|
|
* Returns +true+ if +thr+ is running or sleeping.
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2006-12-31 18:02:22 +03:00
|
|
|
* thr = Thread.new { }
|
|
|
|
* thr.join #=> #<Thread:0x401b3fb0 dead>
|
|
|
|
* Thread.current.alive? #=> true
|
|
|
|
* thr.alive? #=> false
|
2013-02-28 00:36:59 +04:00
|
|
|
*
|
|
|
|
* See also #stop? and #status.
|
2006-12-31 18:02:22 +03:00
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
rb_thread_alive_p(VALUE thread)
|
|
|
|
{
|
2017-06-28 07:49:30 +03:00
|
|
|
if (rb_threadptr_dead(rb_thread_ptr(thread))) {
|
2006-12-31 18:02:22 +03:00
|
|
|
return Qfalse;
|
2017-06-28 07:49:30 +03:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
return Qtrue;
|
|
|
|
}
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* call-seq:
|
2010-05-18 01:07:33 +04:00
|
|
|
* thr.stop? -> true or false
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2013-02-28 00:36:59 +04:00
|
|
|
* Returns +true+ if +thr+ is dead or sleeping.
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2006-12-31 18:02:22 +03:00
|
|
|
* a = Thread.new { Thread.stop }
|
|
|
|
* b = Thread.current
|
|
|
|
* a.stop? #=> true
|
|
|
|
* b.stop? #=> false
|
2013-02-28 00:36:59 +04:00
|
|
|
*
|
|
|
|
* See also #alive? and #status.
|
2006-12-31 18:02:22 +03:00
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
rb_thread_stop_p(VALUE thread)
|
|
|
|
{
|
2017-06-28 07:49:30 +03:00
|
|
|
rb_thread_t *th = rb_thread_ptr(thread);
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2017-06-28 07:49:30 +03:00
|
|
|
if (rb_threadptr_dead(th)) {
|
2006-12-31 18:02:22 +03:00
|
|
|
return Qtrue;
|
2017-06-28 07:49:30 +03:00
|
|
|
}
|
|
|
|
else if (th->status == THREAD_STOPPED ||
|
|
|
|
th->status == THREAD_STOPPED_FOREVER) {
|
2006-12-31 18:02:22 +03:00
|
|
|
return Qtrue;
|
2017-06-28 07:49:30 +03:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
return Qfalse;
|
|
|
|
}
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* call-seq:
|
2010-05-18 01:07:33 +04:00
|
|
|
* thr.safe_level -> integer
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2017-12-28 23:09:24 +03:00
|
|
|
* Returns the safe level.
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2017-12-28 23:09:24 +03:00
|
|
|
* This method is obsolete because $SAFE is a process global state.
|
|
|
|
* Simply check $SAFE.
|
2006-12-31 18:02:22 +03:00
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
rb_thread_safe_level(VALUE thread)
|
|
|
|
{
|
2017-12-28 23:09:24 +03:00
|
|
|
return UINT2NUM(rb_safe_level());
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
2015-06-13 11:39:30 +03:00
|
|
|
/*
|
|
|
|
* call-seq:
|
|
|
|
* thr.name -> string
|
|
|
|
*
|
2015-06-13 13:58:14 +03:00
|
|
|
* show the name of the thread.
|
2015-06-13 11:39:30 +03:00
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
rb_thread_getname(VALUE thread)
|
|
|
|
{
|
2017-06-28 07:49:30 +03:00
|
|
|
return rb_thread_ptr(thread)->name;
|
2015-06-13 11:39:30 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* call-seq:
|
|
|
|
* thr.name=(name) -> string
|
|
|
|
*
|
2015-06-13 13:58:14 +03:00
|
|
|
* set given name to the ruby thread.
|
|
|
|
* On some platform, it may set the name to pthread and/or kernel.
|
2015-06-13 11:39:30 +03:00
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
rb_thread_setname(VALUE thread, VALUE name)
|
|
|
|
{
|
2017-06-28 07:49:30 +03:00
|
|
|
rb_thread_t *target_th = rb_thread_ptr(thread);
|
|
|
|
|
2015-12-01 17:36:42 +03:00
|
|
|
if (!NIL_P(name)) {
|
2015-12-04 05:22:44 +03:00
|
|
|
rb_encoding *enc;
|
2015-12-01 17:36:42 +03:00
|
|
|
StringValueCStr(name);
|
2015-12-04 05:22:44 +03:00
|
|
|
enc = rb_enc_get(name);
|
|
|
|
if (!rb_enc_asciicompat(enc)) {
|
|
|
|
rb_raise(rb_eArgError, "ASCII incompatible encoding (%s)",
|
|
|
|
rb_enc_name(enc));
|
|
|
|
}
|
2015-12-01 17:36:42 +03:00
|
|
|
name = rb_str_new_frozen(name);
|
|
|
|
}
|
2017-06-28 07:49:30 +03:00
|
|
|
target_th->name = name;
|
|
|
|
if (threadptr_initialized(target_th)) {
|
2017-08-26 03:30:03 +03:00
|
|
|
native_set_another_thread_name(target_th->thread_id, name);
|
2016-04-15 16:30:03 +03:00
|
|
|
}
|
2015-06-13 11:39:30 +03:00
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
2015-01-28 20:06:40 +03:00
|
|
|
/*
|
|
|
|
* call-seq:
|
2017-08-10 06:20:39 +03:00
|
|
|
* thr.to_s -> string
|
2015-01-28 20:06:40 +03:00
|
|
|
*
|
|
|
|
* Dump the name, id, and status of _thr_ to a string.
|
|
|
|
*/
|
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
static VALUE
|
2017-08-10 06:20:39 +03:00
|
|
|
rb_thread_to_s(VALUE thread)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2014-05-26 11:00:02 +04:00
|
|
|
VALUE cname = rb_class_path(rb_obj_class(thread));
|
2017-06-28 07:49:30 +03:00
|
|
|
rb_thread_t *target_th = rb_thread_ptr(thread);
|
2006-12-31 18:02:22 +03:00
|
|
|
const char *status;
|
2018-11-08 08:01:23 +03:00
|
|
|
VALUE str, loc;
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2017-06-28 07:49:30 +03:00
|
|
|
status = thread_status_name(target_th, TRUE);
|
2015-01-28 20:06:40 +03:00
|
|
|
str = rb_sprintf("#<%"PRIsVALUE":%p", cname, (void *)thread);
|
2017-06-28 07:49:30 +03:00
|
|
|
if (!NIL_P(target_th->name)) {
|
2018-11-08 08:01:23 +03:00
|
|
|
rb_str_catf(str, "@%"PRIsVALUE, target_th->name);
|
|
|
|
}
|
|
|
|
if ((loc = threadptr_invoke_proc_location(target_th)) != Qnil) {
|
|
|
|
rb_str_catf(str, "@%"PRIsVALUE":%"PRIsVALUE,
|
|
|
|
RARRAY_AREF(loc, 0), RARRAY_AREF(loc, 1));
|
|
|
|
rb_gc_force_recycle(loc);
|
2014-05-26 11:38:57 +04:00
|
|
|
}
|
2015-01-28 20:06:40 +03:00
|
|
|
rb_str_catf(str, " %s>", status);
|
2006-12-31 18:02:22 +03:00
|
|
|
OBJ_INFECT(str, thread);
|
|
|
|
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
2014-11-27 04:56:38 +03:00
|
|
|
/* variables for recursive traversals */
|
|
|
|
static ID recursive_key;
|
|
|
|
|
2014-06-13 12:50:11 +04:00
|
|
|
static VALUE
|
|
|
|
threadptr_local_aref(rb_thread_t *th, ID id)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2014-11-27 04:56:38 +03:00
|
|
|
if (id == recursive_key) {
|
2017-10-26 11:32:49 +03:00
|
|
|
return th->ec->local_storage_recursive_hash;
|
2014-11-27 04:56:38 +03:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
st_data_t val;
|
2017-10-26 11:32:49 +03:00
|
|
|
st_table *local_storage = th->ec->local_storage;
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2017-06-28 05:50:56 +03:00
|
|
|
if (local_storage != NULL && st_lookup(local_storage, id, &val)) {
|
2014-11-27 04:56:38 +03:00
|
|
|
return (VALUE)val;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return Qnil;
|
|
|
|
}
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-13 12:50:11 +04:00
|
|
|
VALUE
|
|
|
|
rb_thread_local_aref(VALUE thread, ID id)
|
|
|
|
{
|
2017-06-28 07:49:30 +03:00
|
|
|
return threadptr_local_aref(rb_thread_ptr(thread), id);
|
2014-06-13 12:50:11 +04:00
|
|
|
}
|
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
/*
|
|
|
|
* call-seq:
|
2010-05-18 01:07:33 +04:00
|
|
|
* thr[sym] -> obj or nil
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2012-01-03 11:01:15 +04:00
|
|
|
* Attribute Reference---Returns the value of a fiber-local variable (current thread's root fiber
|
2013-05-19 07:10:21 +04:00
|
|
|
* if not explicitly inside a Fiber), using either a symbol or a string name.
|
2013-02-28 00:36:59 +04:00
|
|
|
* If the specified variable does not exist, returns +nil+.
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2011-06-28 00:09:59 +04:00
|
|
|
* [
|
|
|
|
* Thread.new { Thread.current["name"] = "A" },
|
|
|
|
* Thread.new { Thread.current[:name] = "B" },
|
|
|
|
* Thread.new { Thread.current["name"] = "C" }
|
|
|
|
* ].each do |th|
|
|
|
|
* th.join
|
|
|
|
* puts "#{th.inspect}: #{th[:name]}"
|
|
|
|
* end
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2013-02-28 00:36:59 +04:00
|
|
|
* This will produce:
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2011-06-28 00:09:59 +04:00
|
|
|
* #<Thread:0x00000002a54220 dead>: A
|
|
|
|
* #<Thread:0x00000002a541a8 dead>: B
|
|
|
|
* #<Thread:0x00000002a54130 dead>: C
|
2012-07-02 17:15:29 +04:00
|
|
|
*
|
|
|
|
* Thread#[] and Thread#[]= are not thread-local but fiber-local.
|
2012-10-06 23:51:40 +04:00
|
|
|
* This confusion did not exist in Ruby 1.8 because
|
2013-04-27 14:33:44 +04:00
|
|
|
* fibers are only available since Ruby 1.9.
|
2012-07-02 17:15:29 +04:00
|
|
|
* Ruby 1.9 chooses that the methods behaves fiber-local to save
|
|
|
|
* following idiom for dynamic scope.
|
|
|
|
*
|
|
|
|
* def meth(newvalue)
|
|
|
|
* begin
|
|
|
|
* oldvalue = Thread.current[:name]
|
|
|
|
* Thread.current[:name] = newvalue
|
|
|
|
* yield
|
|
|
|
* ensure
|
|
|
|
* Thread.current[:name] = oldvalue
|
|
|
|
* end
|
|
|
|
* end
|
|
|
|
*
|
|
|
|
* The idiom may not work as dynamic scope if the methods are thread-local
|
|
|
|
* and a given block switches fiber.
|
|
|
|
*
|
|
|
|
* f = Fiber.new {
|
|
|
|
* meth(1) {
|
|
|
|
* Fiber.yield
|
|
|
|
* }
|
|
|
|
* }
|
|
|
|
* meth(2) {
|
|
|
|
* f.resume
|
|
|
|
* }
|
|
|
|
* f.resume
|
|
|
|
* p Thread.current[:name]
|
|
|
|
* #=> nil if fiber-local
|
|
|
|
* #=> 2 if thread-local (The value 2 is leaked to outside of meth method.)
|
|
|
|
*
|
2013-02-28 00:36:59 +04:00
|
|
|
* For thread-local variables, please see #thread_variable_get and
|
|
|
|
* #thread_variable_set.
|
2012-10-29 21:22:36 +04:00
|
|
|
*
|
2006-12-31 18:02:22 +03:00
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
2013-04-04 09:37:21 +04:00
|
|
|
rb_thread_aref(VALUE thread, VALUE key)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2014-07-09 10:14:41 +04:00
|
|
|
ID id = rb_check_id(&key);
|
2013-04-04 09:37:21 +04:00
|
|
|
if (!id) return Qnil;
|
|
|
|
return rb_thread_local_aref(thread, id);
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
2018-02-23 05:18:52 +03:00
|
|
|
/*
|
|
|
|
* call-seq:
|
|
|
|
* thr.fetch(sym) -> obj
|
|
|
|
* thr.fetch(sym) { } -> obj
|
|
|
|
* thr.fetch(sym, default) -> obj
|
|
|
|
*
|
|
|
|
* Returns a fiber-local for the given key. If the key can't be
|
|
|
|
* found, there are several options: With no other arguments, it will
|
2019-03-28 06:33:35 +03:00
|
|
|
* raise a KeyError exception; if <i>default</i> is given, then that
|
|
|
|
* will be returned; if the optional code block is specified, then
|
|
|
|
* that will be run and its result returned. See Thread#[] and
|
|
|
|
* Hash#fetch.
|
2018-02-23 05:18:52 +03:00
|
|
|
*/
|
2017-02-22 10:16:13 +03:00
|
|
|
static VALUE
|
|
|
|
rb_thread_fetch(int argc, VALUE *argv, VALUE self)
|
|
|
|
{
|
|
|
|
VALUE key, val;
|
|
|
|
ID id;
|
2017-06-28 07:49:30 +03:00
|
|
|
rb_thread_t *target_th = rb_thread_ptr(self);
|
2017-02-22 10:16:13 +03:00
|
|
|
int block_given;
|
|
|
|
|
|
|
|
rb_check_arity(argc, 1, 2);
|
|
|
|
key = argv[0];
|
|
|
|
|
|
|
|
block_given = rb_block_given_p();
|
|
|
|
if (block_given && argc == 2) {
|
|
|
|
rb_warn("block supersedes default value argument");
|
|
|
|
}
|
|
|
|
|
|
|
|
id = rb_check_id(&key);
|
|
|
|
|
|
|
|
if (id == recursive_key) {
|
2017-10-26 11:32:49 +03:00
|
|
|
return target_th->ec->local_storage_recursive_hash;
|
2017-02-22 10:16:13 +03:00
|
|
|
}
|
2017-10-26 11:32:49 +03:00
|
|
|
else if (id && target_th->ec->local_storage &&
|
|
|
|
st_lookup(target_th->ec->local_storage, id, &val)) {
|
2017-02-22 10:16:13 +03:00
|
|
|
return val;
|
|
|
|
}
|
2017-06-28 05:50:56 +03:00
|
|
|
else if (block_given) {
|
2017-02-22 10:16:13 +03:00
|
|
|
return rb_yield(key);
|
2017-06-28 05:50:56 +03:00
|
|
|
}
|
|
|
|
else if (argc == 1) {
|
2017-12-28 03:00:05 +03:00
|
|
|
rb_key_err_raise(rb_sprintf("key not found: %+"PRIsVALUE, key), self, key);
|
2017-06-28 05:50:56 +03:00
|
|
|
}
|
|
|
|
else {
|
2017-02-22 10:16:13 +03:00
|
|
|
return argv[1];
|
2017-06-28 05:50:56 +03:00
|
|
|
}
|
2017-02-22 10:16:13 +03:00
|
|
|
}
|
|
|
|
|
2014-06-13 12:50:11 +04:00
|
|
|
static VALUE
|
|
|
|
threadptr_local_aset(rb_thread_t *th, ID id, VALUE val)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2014-11-27 04:56:38 +03:00
|
|
|
if (id == recursive_key) {
|
2017-10-26 11:32:49 +03:00
|
|
|
th->ec->local_storage_recursive_hash = val;
|
2014-11-27 04:56:38 +03:00
|
|
|
return val;
|
|
|
|
}
|
|
|
|
else {
|
2017-10-26 11:32:49 +03:00
|
|
|
st_table *local_storage = th->ec->local_storage;
|
2017-06-28 05:50:56 +03:00
|
|
|
|
|
|
|
if (NIL_P(val)) {
|
|
|
|
if (!local_storage) return Qnil;
|
|
|
|
st_delete_wrap(local_storage, id);
|
|
|
|
return Qnil;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (local_storage == NULL) {
|
2017-10-26 11:32:49 +03:00
|
|
|
th->ec->local_storage = local_storage = st_init_numtable();
|
2017-06-28 05:50:56 +03:00
|
|
|
}
|
|
|
|
st_insert(local_storage, id, val);
|
|
|
|
return val;
|
2014-11-27 04:56:38 +03:00
|
|
|
}
|
2013-04-05 19:21:02 +04:00
|
|
|
}
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
2014-06-13 12:50:11 +04:00
|
|
|
|
|
|
|
VALUE
|
|
|
|
rb_thread_local_aset(VALUE thread, ID id, VALUE val)
|
|
|
|
{
|
|
|
|
if (OBJ_FROZEN(thread)) {
|
|
|
|
rb_error_frozen("thread locals");
|
|
|
|
}
|
|
|
|
|
2017-06-28 07:49:30 +03:00
|
|
|
return threadptr_local_aset(rb_thread_ptr(thread), id, val);
|
2014-06-13 12:50:11 +04:00
|
|
|
}
|
2006-12-31 18:02:22 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* call-seq:
|
2010-05-18 01:07:33 +04:00
|
|
|
* thr[sym] = obj -> obj
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2012-01-03 11:01:15 +04:00
|
|
|
* Attribute Assignment---Sets or creates the value of a fiber-local variable,
|
2013-02-28 00:36:59 +04:00
|
|
|
* using either a symbol or a string.
|
|
|
|
*
|
|
|
|
* See also Thread#[].
|
|
|
|
*
|
|
|
|
* For thread-local variables, please see #thread_variable_set and
|
|
|
|
* #thread_variable_get.
|
2006-12-31 18:02:22 +03:00
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
2010-05-08 19:27:48 +04:00
|
|
|
rb_thread_aset(VALUE self, VALUE id, VALUE val)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
|
|
|
return rb_thread_local_aset(self, rb_to_id(id), val);
|
|
|
|
}
|
|
|
|
|
2012-10-29 21:22:36 +04:00
|
|
|
/*
|
|
|
|
* call-seq:
|
|
|
|
* thr.thread_variable_get(key) -> obj or nil
|
|
|
|
*
|
|
|
|
* Returns the value of a thread local variable that has been set. Note that
|
|
|
|
* these are different than fiber local values. For fiber local values,
|
|
|
|
* please see Thread#[] and Thread#[]=.
|
|
|
|
*
|
|
|
|
* Thread local values are carried along with threads, and do not respect
|
|
|
|
* fibers. For example:
|
|
|
|
*
|
|
|
|
* Thread.new {
|
|
|
|
* Thread.current.thread_variable_set("foo", "bar") # set a thread local
|
|
|
|
* Thread.current["foo"] = "bar" # set a fiber local
|
|
|
|
*
|
|
|
|
* Fiber.new {
|
|
|
|
* Fiber.yield [
|
|
|
|
* Thread.current.thread_variable_get("foo"), # get the thread local
|
|
|
|
* Thread.current["foo"], # get the fiber local
|
|
|
|
* ]
|
|
|
|
* }.resume
|
|
|
|
* }.join.value # => ['bar', nil]
|
|
|
|
*
|
|
|
|
* The value "bar" is returned for the thread local, where nil is returned
|
|
|
|
* for the fiber local. The fiber is executed in the same thread, so the
|
|
|
|
* thread local values are available.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
2013-04-04 09:37:21 +04:00
|
|
|
rb_thread_variable_get(VALUE thread, VALUE key)
|
2012-10-29 21:22:36 +04:00
|
|
|
{
|
|
|
|
VALUE locals;
|
|
|
|
|
2013-05-02 11:55:50 +04:00
|
|
|
locals = rb_ivar_get(thread, id_locals);
|
2014-12-29 05:18:20 +03:00
|
|
|
return rb_hash_aref(locals, rb_to_symbol(key));
|
2012-10-29 21:22:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* call-seq:
|
|
|
|
* thr.thread_variable_set(key, value)
|
|
|
|
*
|
|
|
|
* Sets a thread local with +key+ to +value+. Note that these are local to
|
|
|
|
* threads, and not to fibers. Please see Thread#thread_variable_get and
|
|
|
|
* Thread#[] for more information.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
rb_thread_variable_set(VALUE thread, VALUE id, VALUE val)
|
|
|
|
{
|
|
|
|
VALUE locals;
|
|
|
|
|
|
|
|
if (OBJ_FROZEN(thread)) {
|
|
|
|
rb_error_frozen("thread locals");
|
|
|
|
}
|
|
|
|
|
2013-05-02 11:55:50 +04:00
|
|
|
locals = rb_ivar_get(thread, id_locals);
|
2014-07-30 16:36:34 +04:00
|
|
|
return rb_hash_aset(locals, rb_to_symbol(id), val);
|
2012-10-29 21:22:36 +04:00
|
|
|
}
|
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
/*
|
|
|
|
* call-seq:
|
2010-05-18 01:07:33 +04:00
|
|
|
* thr.key?(sym) -> true or false
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2013-02-28 00:36:59 +04:00
|
|
|
* Returns +true+ if the given string (or symbol) exists as a fiber-local
|
|
|
|
* variable.
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2006-12-31 18:02:22 +03:00
|
|
|
* me = Thread.current
|
|
|
|
* me[:oliver] = "a"
|
|
|
|
* me.key?(:oliver) #=> true
|
|
|
|
* me.key?(:stanley) #=> false
|
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
2008-05-07 17:24:03 +04:00
|
|
|
rb_thread_key_p(VALUE self, VALUE key)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2014-07-09 10:14:41 +04:00
|
|
|
ID id = rb_check_id(&key);
|
2017-10-26 11:32:49 +03:00
|
|
|
st_table *local_storage = rb_thread_ptr(self)->ec->local_storage;
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2017-06-28 05:50:56 +03:00
|
|
|
if (!id || local_storage == NULL) {
|
2006-12-31 18:02:22 +03:00
|
|
|
return Qfalse;
|
|
|
|
}
|
2017-06-28 05:50:56 +03:00
|
|
|
else if (st_lookup(local_storage, id, 0)) {
|
2006-12-31 18:02:22 +03:00
|
|
|
return Qtrue;
|
|
|
|
}
|
2017-06-28 05:50:56 +03:00
|
|
|
else {
|
|
|
|
return Qfalse;
|
|
|
|
}
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
thread_keys_i(ID key, VALUE value, VALUE ary)
|
|
|
|
{
|
|
|
|
rb_ary_push(ary, ID2SYM(key));
|
|
|
|
return ST_CONTINUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
* cont.c (rb_fiber_current), dln.c (dln_print_undef, dln_undefined),
eval.c (rb_iterator_p, rb_need_block), load.c: (Init_load), ruby.c
(uscore_get, rb_f_chop), st.c (stat_col), signal.c
(rb_signal_buff_size, ruby_sig_finalize), thread.c
(rb_thread_sleep_forever, rb_thread_sleep_deadly, rb_thread_alone):
protoized.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@21929 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2009-02-01 15:51:44 +03:00
|
|
|
rb_thread_alone(void)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2015-07-17 04:42:18 +03:00
|
|
|
return vm_living_thread_num(GET_VM()) == 1;
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* call-seq:
|
2010-05-18 01:07:33 +04:00
|
|
|
* thr.keys -> array
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2013-04-09 04:26:22 +04:00
|
|
|
* Returns an array of the names of the fiber-local variables (as Symbols).
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2006-12-31 18:02:22 +03:00
|
|
|
* thr = Thread.new do
|
|
|
|
* Thread.current[:cat] = 'meow'
|
|
|
|
* Thread.current["dog"] = 'woof'
|
|
|
|
* end
|
|
|
|
* thr.join #=> #<Thread:0x401b3f10 dead>
|
|
|
|
* thr.keys #=> [:dog, :cat]
|
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
rb_thread_keys(VALUE self)
|
|
|
|
{
|
2017-10-26 11:32:49 +03:00
|
|
|
st_table *local_storage = rb_thread_ptr(self)->ec->local_storage;
|
2006-12-31 18:02:22 +03:00
|
|
|
VALUE ary = rb_ary_new();
|
|
|
|
|
2017-06-28 07:49:30 +03:00
|
|
|
if (local_storage) {
|
|
|
|
st_foreach(local_storage, thread_keys_i, ary);
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
return ary;
|
|
|
|
}
|
|
|
|
|
2012-10-29 21:22:36 +04:00
|
|
|
static int
|
|
|
|
keys_i(VALUE key, VALUE value, VALUE ary)
|
|
|
|
{
|
|
|
|
rb_ary_push(ary, key);
|
|
|
|
return ST_CONTINUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* call-seq:
|
|
|
|
* thr.thread_variables -> array
|
|
|
|
*
|
2013-04-09 04:26:22 +04:00
|
|
|
* Returns an array of the names of the thread-local variables (as Symbols).
|
2012-10-29 21:22:36 +04:00
|
|
|
*
|
|
|
|
* thr = Thread.new do
|
|
|
|
* Thread.current.thread_variable_set(:cat, 'meow')
|
|
|
|
* Thread.current.thread_variable_set("dog", 'woof')
|
|
|
|
* end
|
|
|
|
* thr.join #=> #<Thread:0x401b3f10 dead>
|
|
|
|
* thr.thread_variables #=> [:dog, :cat]
|
|
|
|
*
|
|
|
|
* Note that these are not fiber local variables. Please see Thread#[] and
|
|
|
|
* Thread#thread_variable_get for more details.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
rb_thread_variables(VALUE thread)
|
|
|
|
{
|
|
|
|
VALUE locals;
|
|
|
|
VALUE ary;
|
|
|
|
|
2013-05-02 11:55:50 +04:00
|
|
|
locals = rb_ivar_get(thread, id_locals);
|
2012-10-29 21:22:36 +04:00
|
|
|
ary = rb_ary_new();
|
|
|
|
rb_hash_foreach(locals, keys_i, ary);
|
|
|
|
|
|
|
|
return ary;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* call-seq:
|
|
|
|
* thr.thread_variable?(key) -> true or false
|
|
|
|
*
|
2013-02-28 00:36:59 +04:00
|
|
|
* Returns +true+ if the given string (or symbol) exists as a thread-local
|
|
|
|
* variable.
|
2012-10-29 21:22:36 +04:00
|
|
|
*
|
|
|
|
* me = Thread.current
|
|
|
|
* me.thread_variable_set(:oliver, "a")
|
|
|
|
* me.thread_variable?(:oliver) #=> true
|
|
|
|
* me.thread_variable?(:stanley) #=> false
|
|
|
|
*
|
|
|
|
* Note that these are not fiber local variables. Please see Thread#[] and
|
|
|
|
* Thread#thread_variable_get for more details.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
rb_thread_variable_p(VALUE thread, VALUE key)
|
|
|
|
{
|
|
|
|
VALUE locals;
|
2014-07-09 10:14:41 +04:00
|
|
|
ID id = rb_check_id(&key);
|
2013-04-04 09:37:21 +04:00
|
|
|
|
|
|
|
if (!id) return Qfalse;
|
2012-10-29 21:22:36 +04:00
|
|
|
|
2013-05-02 11:55:50 +04:00
|
|
|
locals = rb_ivar_get(thread, id_locals);
|
2012-10-29 21:22:36 +04:00
|
|
|
|
2018-10-31 01:11:51 +03:00
|
|
|
if (rb_hash_lookup(locals, ID2SYM(id)) != Qnil) {
|
|
|
|
return Qtrue;
|
|
|
|
}
|
|
|
|
else {
|
2012-10-29 21:22:36 +04:00
|
|
|
return Qfalse;
|
|
|
|
}
|
|
|
|
|
|
|
|
return Qfalse;
|
|
|
|
}
|
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
/*
|
|
|
|
* call-seq:
|
2010-05-18 01:07:33 +04:00
|
|
|
* thr.priority -> integer
|
2007-05-10 05:12:10 +04:00
|
|
|
*
|
|
|
|
* Returns the priority of <i>thr</i>. Default is inherited from the
|
|
|
|
* current thread which creating the new thread, or zero for the
|
2010-04-15 17:44:52 +04:00
|
|
|
* initial main thread; higher-priority thread will run more frequently
|
|
|
|
* than lower-priority threads (but lower-priority threads can also run).
|
|
|
|
*
|
|
|
|
* This is just hint for Ruby thread scheduler. It may be ignored on some
|
|
|
|
* platform.
|
2007-05-10 05:12:10 +04:00
|
|
|
*
|
2006-12-31 18:02:22 +03:00
|
|
|
* Thread.current.priority #=> 0
|
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
rb_thread_priority(VALUE thread)
|
|
|
|
{
|
2017-06-28 07:49:30 +03:00
|
|
|
return INT2NUM(rb_thread_ptr(thread)->priority);
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* call-seq:
|
2010-05-18 01:07:33 +04:00
|
|
|
* thr.priority= integer -> thr
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2006-12-31 18:02:22 +03:00
|
|
|
* Sets the priority of <i>thr</i> to <i>integer</i>. Higher-priority threads
|
2010-04-15 17:44:52 +04:00
|
|
|
* will run more frequently than lower-priority threads (but lower-priority
|
|
|
|
* threads can also run).
|
|
|
|
*
|
|
|
|
* This is just hint for Ruby thread scheduler. It may be ignored on some
|
|
|
|
* platform.
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2006-12-31 18:02:22 +03:00
|
|
|
* count1 = count2 = 0
|
|
|
|
* a = Thread.new do
|
|
|
|
* loop { count1 += 1 }
|
|
|
|
* end
|
|
|
|
* a.priority = -1
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2006-12-31 18:02:22 +03:00
|
|
|
* b = Thread.new do
|
|
|
|
* loop { count2 += 1 }
|
|
|
|
* end
|
|
|
|
* b.priority = -2
|
|
|
|
* sleep 1 #=> 1
|
|
|
|
* count1 #=> 622504
|
|
|
|
* count2 #=> 5832
|
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
rb_thread_priority_set(VALUE thread, VALUE prio)
|
|
|
|
{
|
2017-06-28 07:49:30 +03:00
|
|
|
rb_thread_t *target_th = rb_thread_ptr(thread);
|
2008-08-13 11:53:35 +04:00
|
|
|
int priority;
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2008-08-13 11:53:35 +04:00
|
|
|
#if USE_NATIVE_THREAD_PRIORITY
|
2017-06-28 07:49:30 +03:00
|
|
|
target_th->priority = NUM2INT(prio);
|
2006-12-31 18:02:22 +03:00
|
|
|
native_thread_apply_priority(th);
|
2008-08-13 11:53:35 +04:00
|
|
|
#else
|
|
|
|
priority = NUM2INT(prio);
|
|
|
|
if (priority > RUBY_THREAD_PRIORITY_MAX) {
|
|
|
|
priority = RUBY_THREAD_PRIORITY_MAX;
|
|
|
|
}
|
|
|
|
else if (priority < RUBY_THREAD_PRIORITY_MIN) {
|
|
|
|
priority = RUBY_THREAD_PRIORITY_MIN;
|
|
|
|
}
|
2018-08-15 08:54:41 +03:00
|
|
|
target_th->priority = (int8_t)priority;
|
2008-08-13 11:53:35 +04:00
|
|
|
#endif
|
2017-06-28 07:49:30 +03:00
|
|
|
return INT2NUM(target_th->priority);
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* for IO */
|
|
|
|
|
2007-02-05 15:21:01 +03:00
|
|
|
#if defined(NFDBITS) && defined(HAVE_RB_FD_INIT)
|
2008-12-02 13:00:10 +03:00
|
|
|
|
|
|
|
/*
|
2009-01-05 09:55:14 +03:00
|
|
|
* several Unix platforms support file descriptors bigger than FD_SETSIZE
|
2008-12-02 13:00:10 +03:00
|
|
|
* in select(2) system call.
|
|
|
|
*
|
|
|
|
* - Linux 2.2.12 (?)
|
|
|
|
* - NetBSD 1.2 (src/sys/kern/sys_generic.c:1.25)
|
|
|
|
* select(2) documents how to allocate fd_set dynamically.
|
|
|
|
* http://netbsd.gw.com/cgi-bin/man-cgi?select++NetBSD-4.0
|
|
|
|
* - FreeBSD 2.2 (src/sys/kern/sys_generic.c:1.19)
|
|
|
|
* - OpenBSD 2.0 (src/sys/kern/sys_generic.c:1.4)
|
|
|
|
* select(2) documents how to allocate fd_set dynamically.
|
|
|
|
* http://www.openbsd.org/cgi-bin/man.cgi?query=select&manpath=OpenBSD+4.4
|
2009-02-18 04:29:13 +03:00
|
|
|
* - HP-UX documents how to allocate fd_set dynamically.
|
2008-12-02 13:00:10 +03:00
|
|
|
* http://docs.hp.com/en/B2355-60105/select.2.html
|
|
|
|
* - Solaris 8 has select_large_fdset
|
2011-08-19 08:17:04 +04:00
|
|
|
* - Mac OS X 10.7 (Lion)
|
|
|
|
* select(2) returns EINVAL if nfds is greater than FD_SET_SIZE and
|
|
|
|
* _DARWIN_UNLIMITED_SELECT (or _DARWIN_C_SOURCE) isn't defined.
|
|
|
|
* http://developer.apple.com/library/mac/#releasenotes/Darwin/SymbolVariantsRelNotes/_index.html
|
2008-12-02 13:00:10 +03:00
|
|
|
*
|
|
|
|
* When fd_set is not big enough to hold big file descriptors,
|
|
|
|
* it should be allocated dynamically.
|
|
|
|
* Note that this assumes fd_set is structured as bitmap.
|
|
|
|
*
|
|
|
|
* rb_fd_init allocates the memory.
|
|
|
|
* rb_fd_term free the memory.
|
|
|
|
* rb_fd_set may re-allocates bitmap.
|
|
|
|
*
|
|
|
|
* So rb_fd_set doesn't reject file descriptors bigger than FD_SETSIZE.
|
|
|
|
*/
|
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
void
|
2011-05-07 17:40:56 +04:00
|
|
|
rb_fd_init(rb_fdset_t *fds)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2007-02-05 15:21:01 +03:00
|
|
|
fds->maxfd = 0;
|
|
|
|
fds->fdset = ALLOC(fd_set);
|
|
|
|
FD_ZERO(fds->fdset);
|
|
|
|
}
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2011-05-07 17:36:08 +04:00
|
|
|
void
|
|
|
|
rb_fd_init_copy(rb_fdset_t *dst, rb_fdset_t *src)
|
|
|
|
{
|
|
|
|
size_t size = howmany(rb_fd_max(src), NFDBITS) * sizeof(fd_mask);
|
|
|
|
|
|
|
|
if (size < sizeof(fd_set))
|
|
|
|
size = sizeof(fd_set);
|
|
|
|
dst->maxfd = src->maxfd;
|
|
|
|
dst->fdset = xmalloc(size);
|
|
|
|
memcpy(dst->fdset, src->fdset, size);
|
|
|
|
}
|
|
|
|
|
2007-02-05 15:21:01 +03:00
|
|
|
void
|
|
|
|
rb_fd_term(rb_fdset_t *fds)
|
|
|
|
{
|
* array.c, bignum.c, cont.c, dir.c, dln.c, encoding.c, enumerator.c,
enumerator.c (enumerator_allocate), eval_jump.c, file.c, hash.c,
io.c, load.c, pack.c, proc.c, random.c, re.c, ruby.c, st.c,
string.c, thread.c, thread_pthread.c, time.c, util.c, variable.c,
vm.c, gc.c:
allocated memory objects by xmalloc (ruby_xmalloc) should be
freed by xfree (ruby_xfree).
* ext/curses/curses.c, ext/dbm/dbm.c, ext/digest/digest.c,
ext/gdbm/gdbm.c, ext/json/ext/parser/parser.c,
ext/json/ext/parser/unicode.c, ext/openssl/ossl_cipher.c,
ext/openssl/ossl_hmac.c, ext/openssl/ossl_pkey_ec.c,
ext/sdbm/init.c, ext/strscan/strscan.c, ext/zlib/zlib.c:
ditto.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@17017 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2008-06-08 14:01:40 +04:00
|
|
|
if (fds->fdset) xfree(fds->fdset);
|
2007-02-05 15:21:01 +03:00
|
|
|
fds->maxfd = 0;
|
|
|
|
fds->fdset = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
rb_fd_zero(rb_fdset_t *fds)
|
|
|
|
{
|
2011-05-07 17:44:27 +04:00
|
|
|
if (fds->fdset)
|
2007-02-05 15:21:01 +03:00
|
|
|
MEMZERO(fds->fdset, fd_mask, howmany(fds->maxfd, NFDBITS));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
rb_fd_resize(int n, rb_fdset_t *fds)
|
|
|
|
{
|
2009-05-09 07:25:16 +04:00
|
|
|
size_t m = howmany(n + 1, NFDBITS) * sizeof(fd_mask);
|
|
|
|
size_t o = howmany(fds->maxfd, NFDBITS) * sizeof(fd_mask);
|
2007-02-05 15:21:01 +03:00
|
|
|
|
|
|
|
if (m < sizeof(fd_set)) m = sizeof(fd_set);
|
|
|
|
if (o < sizeof(fd_set)) o = sizeof(fd_set);
|
|
|
|
|
|
|
|
if (m > o) {
|
2010-06-28 16:58:03 +04:00
|
|
|
fds->fdset = xrealloc(fds->fdset, m);
|
2007-02-05 15:21:01 +03:00
|
|
|
memset((char *)fds->fdset + o, 0, m - o);
|
|
|
|
}
|
|
|
|
if (n >= fds->maxfd) fds->maxfd = n + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
rb_fd_set(int n, rb_fdset_t *fds)
|
|
|
|
{
|
|
|
|
rb_fd_resize(n, fds);
|
|
|
|
FD_SET(n, fds->fdset);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
rb_fd_clr(int n, rb_fdset_t *fds)
|
|
|
|
{
|
|
|
|
if (n >= fds->maxfd) return;
|
|
|
|
FD_CLR(n, fds->fdset);
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2007-02-05 15:21:01 +03:00
|
|
|
rb_fd_isset(int n, const rb_fdset_t *fds)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2007-02-05 15:21:01 +03:00
|
|
|
if (n >= fds->maxfd) return 0;
|
|
|
|
return FD_ISSET(n, fds->fdset) != 0; /* "!= 0" avoids FreeBSD PR 91421 */
|
|
|
|
}
|
|
|
|
|
2011-05-15 18:57:35 +04:00
|
|
|
void
|
|
|
|
rb_fd_copy(rb_fdset_t *dst, const fd_set *src, int max)
|
|
|
|
{
|
|
|
|
size_t size = howmany(max, NFDBITS) * sizeof(fd_mask);
|
|
|
|
|
|
|
|
if (size < sizeof(fd_set)) size = sizeof(fd_set);
|
|
|
|
dst->maxfd = max;
|
|
|
|
dst->fdset = xrealloc(dst->fdset, size);
|
|
|
|
memcpy(dst->fdset, src, size);
|
|
|
|
}
|
|
|
|
|
2007-02-05 15:21:01 +03:00
|
|
|
void
|
2011-05-15 18:51:09 +04:00
|
|
|
rb_fd_dup(rb_fdset_t *dst, const rb_fdset_t *src)
|
2007-02-05 15:21:01 +03:00
|
|
|
{
|
2011-04-30 15:18:14 +04:00
|
|
|
size_t size = howmany(rb_fd_max(src), NFDBITS) * sizeof(fd_mask);
|
2007-02-05 15:21:01 +03:00
|
|
|
|
2011-04-30 15:18:14 +04:00
|
|
|
if (size < sizeof(fd_set))
|
|
|
|
size = sizeof(fd_set);
|
|
|
|
dst->maxfd = src->maxfd;
|
2010-06-28 16:58:03 +04:00
|
|
|
dst->fdset = xrealloc(dst->fdset, size);
|
2011-05-04 02:37:07 +04:00
|
|
|
memcpy(dst->fdset, src->fdset, size);
|
2007-02-05 15:21:01 +03:00
|
|
|
}
|
|
|
|
|
2008-03-30 10:38:05 +04:00
|
|
|
int
|
|
|
|
rb_fd_select(int n, rb_fdset_t *readfds, rb_fdset_t *writefds, rb_fdset_t *exceptfds, struct timeval *timeout)
|
|
|
|
{
|
|
|
|
fd_set *r = NULL, *w = NULL, *e = NULL;
|
|
|
|
if (readfds) {
|
|
|
|
rb_fd_resize(n - 1, readfds);
|
|
|
|
r = rb_fd_ptr(readfds);
|
|
|
|
}
|
|
|
|
if (writefds) {
|
|
|
|
rb_fd_resize(n - 1, writefds);
|
|
|
|
w = rb_fd_ptr(writefds);
|
|
|
|
}
|
|
|
|
if (exceptfds) {
|
|
|
|
rb_fd_resize(n - 1, exceptfds);
|
|
|
|
e = rb_fd_ptr(exceptfds);
|
|
|
|
}
|
|
|
|
return select(n, r, w, e, timeout);
|
|
|
|
}
|
|
|
|
|
2017-02-15 08:53:01 +03:00
|
|
|
#define rb_fd_no_init(fds) ((void)((fds)->fdset = 0), (void)((fds)->maxfd = 0))
|
2015-09-06 05:04:40 +03:00
|
|
|
|
2007-02-05 15:21:01 +03:00
|
|
|
#undef FD_ZERO
|
|
|
|
#undef FD_SET
|
|
|
|
#undef FD_CLR
|
|
|
|
#undef FD_ISSET
|
|
|
|
|
|
|
|
#define FD_ZERO(f) rb_fd_zero(f)
|
2011-01-08 13:29:15 +03:00
|
|
|
#define FD_SET(i, f) rb_fd_set((i), (f))
|
|
|
|
#define FD_CLR(i, f) rb_fd_clr((i), (f))
|
|
|
|
#define FD_ISSET(i, f) rb_fd_isset((i), (f))
|
2007-02-05 15:21:01 +03:00
|
|
|
|
* include/ruby/intern.h, thread.c, win32/Makefile.sub (rb_fdset_t,
rb_fd_init, rb_fd_term, rb_fd_zero, rb_fd_set, rb_fd_clr, rb_fd_isset,
rb_fd_select, rb_fd_ptr, rb_fd_max, HAVE_RB_FD_INIT): new type,
functions, and macros for Windows.
* win32/win32.c (extract_fd, rb_w32_select): use rb_fdset_t to expand
fd_array if needed. [ruby-core:19946]
* win32/win32.c (copy_fd): new funcion for rb_w32_select().
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@21487 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2009-01-13 10:52:22 +03:00
|
|
|
#elif defined(_WIN32)
|
|
|
|
|
|
|
|
void
|
2011-05-07 17:40:56 +04:00
|
|
|
rb_fd_init(rb_fdset_t *set)
|
* include/ruby/intern.h, thread.c, win32/Makefile.sub (rb_fdset_t,
rb_fd_init, rb_fd_term, rb_fd_zero, rb_fd_set, rb_fd_clr, rb_fd_isset,
rb_fd_select, rb_fd_ptr, rb_fd_max, HAVE_RB_FD_INIT): new type,
functions, and macros for Windows.
* win32/win32.c (extract_fd, rb_w32_select): use rb_fdset_t to expand
fd_array if needed. [ruby-core:19946]
* win32/win32.c (copy_fd): new funcion for rb_w32_select().
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@21487 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2009-01-13 10:52:22 +03:00
|
|
|
{
|
|
|
|
set->capa = FD_SETSIZE;
|
|
|
|
set->fdset = ALLOC(fd_set);
|
|
|
|
FD_ZERO(set->fdset);
|
|
|
|
}
|
|
|
|
|
2011-05-07 17:36:08 +04:00
|
|
|
void
|
|
|
|
rb_fd_init_copy(rb_fdset_t *dst, rb_fdset_t *src)
|
|
|
|
{
|
|
|
|
rb_fd_init(dst);
|
2011-05-15 18:51:09 +04:00
|
|
|
rb_fd_dup(dst, src);
|
2011-05-07 17:36:08 +04:00
|
|
|
}
|
|
|
|
|
* include/ruby/intern.h, thread.c, win32/Makefile.sub (rb_fdset_t,
rb_fd_init, rb_fd_term, rb_fd_zero, rb_fd_set, rb_fd_clr, rb_fd_isset,
rb_fd_select, rb_fd_ptr, rb_fd_max, HAVE_RB_FD_INIT): new type,
functions, and macros for Windows.
* win32/win32.c (extract_fd, rb_w32_select): use rb_fdset_t to expand
fd_array if needed. [ruby-core:19946]
* win32/win32.c (copy_fd): new funcion for rb_w32_select().
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@21487 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2009-01-13 10:52:22 +03:00
|
|
|
void
|
|
|
|
rb_fd_term(rb_fdset_t *set)
|
|
|
|
{
|
|
|
|
xfree(set->fdset);
|
|
|
|
set->fdset = NULL;
|
|
|
|
set->capa = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
rb_fd_set(int fd, rb_fdset_t *set)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
SOCKET s = rb_w32_get_osfhandle(fd);
|
|
|
|
|
|
|
|
for (i = 0; i < set->fdset->fd_count; i++) {
|
|
|
|
if (set->fdset->fd_array[i] == s) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
* array.c, bignum.c, dln.c, error.c, gc.c, io.c, marshal.c,
numeric.c, pack.c, strftime.c, string.c, thread.c, transcode.c,
transcode_data.h, util.c, variable.c, vm_dump.c,
include/ruby/encoding.h, missing/crypt.c, missing/vsnprintf.c:
suppress VC type warnings. [ruby-core:22726]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@22914 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2009-03-12 12:16:15 +03:00
|
|
|
if (set->fdset->fd_count >= (unsigned)set->capa) {
|
* include/ruby/intern.h, thread.c, win32/Makefile.sub (rb_fdset_t,
rb_fd_init, rb_fd_term, rb_fd_zero, rb_fd_set, rb_fd_clr, rb_fd_isset,
rb_fd_select, rb_fd_ptr, rb_fd_max, HAVE_RB_FD_INIT): new type,
functions, and macros for Windows.
* win32/win32.c (extract_fd, rb_w32_select): use rb_fdset_t to expand
fd_array if needed. [ruby-core:19946]
* win32/win32.c (copy_fd): new funcion for rb_w32_select().
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@21487 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2009-01-13 10:52:22 +03:00
|
|
|
set->capa = (set->fdset->fd_count / FD_SETSIZE + 1) * FD_SETSIZE;
|
|
|
|
set->fdset = xrealloc(set->fdset, sizeof(unsigned int) + sizeof(SOCKET) * set->capa);
|
|
|
|
}
|
|
|
|
set->fdset->fd_array[set->fdset->fd_count++] = s;
|
|
|
|
}
|
|
|
|
|
|
|
|
#undef FD_ZERO
|
|
|
|
#undef FD_SET
|
|
|
|
#undef FD_CLR
|
|
|
|
#undef FD_ISSET
|
|
|
|
|
|
|
|
#define FD_ZERO(f) rb_fd_zero(f)
|
2011-01-08 13:29:15 +03:00
|
|
|
#define FD_SET(i, f) rb_fd_set((i), (f))
|
|
|
|
#define FD_CLR(i, f) rb_fd_clr((i), (f))
|
|
|
|
#define FD_ISSET(i, f) rb_fd_isset((i), (f))
|
* include/ruby/intern.h, thread.c, win32/Makefile.sub (rb_fdset_t,
rb_fd_init, rb_fd_term, rb_fd_zero, rb_fd_set, rb_fd_clr, rb_fd_isset,
rb_fd_select, rb_fd_ptr, rb_fd_max, HAVE_RB_FD_INIT): new type,
functions, and macros for Windows.
* win32/win32.c (extract_fd, rb_w32_select): use rb_fdset_t to expand
fd_array if needed. [ruby-core:19946]
* win32/win32.c (copy_fd): new funcion for rb_w32_select().
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@21487 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2009-01-13 10:52:22 +03:00
|
|
|
|
2017-02-15 08:53:01 +03:00
|
|
|
#define rb_fd_no_init(fds) (void)((fds)->fdset = 0)
|
2017-02-13 08:44:15 +03:00
|
|
|
|
2007-02-05 15:21:01 +03:00
|
|
|
#endif
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2015-09-06 05:04:40 +03:00
|
|
|
#ifndef rb_fd_no_init
|
|
|
|
#define rb_fd_no_init(fds) (void)(fds)
|
|
|
|
#endif
|
|
|
|
|
2018-05-18 11:01:07 +03:00
|
|
|
static int
|
2018-08-25 09:58:35 +03:00
|
|
|
wait_retryable(int *result, int errnum, rb_hrtime_t *rel, rb_hrtime_t end)
|
2015-07-21 15:21:35 +03:00
|
|
|
{
|
2018-05-18 11:01:07 +03:00
|
|
|
if (*result < 0) {
|
|
|
|
switch (errnum) {
|
|
|
|
case EINTR:
|
2015-07-21 15:21:35 +03:00
|
|
|
#ifdef ERESTART
|
2018-05-18 11:01:07 +03:00
|
|
|
case ERESTART:
|
2015-07-21 15:21:35 +03:00
|
|
|
#endif
|
2018-05-18 11:01:07 +03:00
|
|
|
*result = 0;
|
2018-08-25 09:58:35 +03:00
|
|
|
if (rel && hrtime_update_expire(rel, end)) {
|
|
|
|
*rel = 0;
|
2018-05-18 11:01:07 +03:00
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
else if (*result == 0) {
|
|
|
|
/* check for spurious wakeup */
|
2018-08-25 09:58:35 +03:00
|
|
|
if (rel) {
|
|
|
|
return !hrtime_update_expire(rel, end);
|
2018-05-18 11:01:07 +03:00
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
2015-07-21 15:21:35 +03:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2018-07-21 06:26:38 +03:00
|
|
|
struct select_set {
|
2018-08-14 00:34:20 +03:00
|
|
|
int max;
|
|
|
|
int sigwait_fd;
|
|
|
|
rb_thread_t *th;
|
|
|
|
rb_fdset_t *rset;
|
|
|
|
rb_fdset_t *wset;
|
|
|
|
rb_fdset_t *eset;
|
|
|
|
rb_fdset_t orig_rset;
|
|
|
|
rb_fdset_t orig_wset;
|
|
|
|
rb_fdset_t orig_eset;
|
|
|
|
struct timeval *timeout;
|
2018-07-21 06:26:38 +03:00
|
|
|
};
|
|
|
|
|
2018-08-14 00:34:20 +03:00
|
|
|
static VALUE
|
|
|
|
select_set_free(VALUE p)
|
2018-07-21 06:26:38 +03:00
|
|
|
{
|
2018-08-14 00:34:20 +03:00
|
|
|
struct select_set *set = (struct select_set *)p;
|
|
|
|
|
|
|
|
if (set->sigwait_fd >= 0) {
|
|
|
|
rb_sigwait_fd_put(set->th, set->sigwait_fd);
|
|
|
|
rb_sigwait_fd_migrate(set->th->vm);
|
|
|
|
}
|
|
|
|
|
|
|
|
rb_fd_term(&set->orig_rset);
|
|
|
|
rb_fd_term(&set->orig_wset);
|
|
|
|
rb_fd_term(&set->orig_eset);
|
|
|
|
|
|
|
|
return Qfalse;
|
thread_pthread: remove timer-thread by restructuring GVL
To reduce resource use and reduce CI failure; remove
timer-thread. Single-threaded Ruby processes (including forked
children) will never see extra thread overhead. This prevents
glibc and jemalloc from going into multi-threaded mode and
initializing locks or causing fragmentation via arena explosion.
The GVL is implements its own wait-queue as a ccan/list to
permit controlling wakeup order. Timeslice under contention is
handled by a designated timer thread (similar to choosing a
"patrol_thread" for current deadlock checking).
There is only one self-pipe, now, as wakeups for timeslice are
done independently using condition variables. This reduces FD
pressure slightly.
Signal handling is handled directly by a Ruby Thread (instead
of timer-thread) by exposing signal self-pipe to callers of
rb_thread_fd_select, native_sleep, rb_wait_for_single_fd, etc...
Acquiring, using, and releasing the self-pipe is exposed via 4
new internal functions:
1) rb_sigwait_fd_get - exclusively acquire timer_thread_pipe.normal[0]
2) rb_sigwait_fd_sleep - sleep and wait for signal (and no other FDs)
3) rb_sigwait_fd_put - release acquired result from rb_sigwait_fd_get
4) rb_sigwait_fd_migrate - migrate signal handling to another thread
after calling rb_sigwait_fd_put.
rb_sigwait_fd_migrate is necessary for waitpid callers because
only one thread can wait on self-pipe at a time, otherwise a
deadlock will occur if threads fight over the self-pipe.
TRAP_INTERRUPT_MASK is now set for the main thread directly in
signal handler via rb_thread_wakeup_timer_thread.
Originally, I wanted to use POSIX timers
(timer_create/timer_settime) for this. Unfortunately, this
proved unfeasible as Mutex#sleep resumes on spurious wakeups and
test/thread/test_cv.rb::test_condvar_timed_wait failed. Using
pthread_sigmask to mask out SIGVTALRM fixed that test, but
test/fiddle/test_function.rb::test_nogvl_poll proved there'd be
some unavoidable (and frequent) incompatibilities from that
approach.
Finally, this allows us to drop thread_destruct_lock and
interrupt current ec directly.
We don't need to rely on vm->thread_destruct_lock or a coherent
vm->running_thread on any platform. Separate timer-thread for
time slice and signal handling is relegated to thread_win32.c,
now.
[ruby-core:88088] [Misc #14937]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@64107 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-07-29 23:47:33 +03:00
|
|
|
}
|
2018-07-21 06:26:38 +03:00
|
|
|
|
2018-08-25 09:58:35 +03:00
|
|
|
static const rb_hrtime_t *
|
|
|
|
sigwait_timeout(rb_thread_t *th, int sigwait_fd, const rb_hrtime_t *orig,
|
2018-08-14 00:34:20 +03:00
|
|
|
int *drained_p)
|
2018-07-30 08:51:06 +03:00
|
|
|
{
|
2018-08-25 09:58:35 +03:00
|
|
|
static const rb_hrtime_t quantum = TIME_QUANTUM_USEC * 1000;
|
2018-07-30 08:51:06 +03:00
|
|
|
|
2018-08-14 00:34:20 +03:00
|
|
|
if (sigwait_fd >= 0 && (!ubf_threads_empty() || BUSY_WAIT_SIGNALS)) {
|
|
|
|
*drained_p = check_signals_nogvl(th, sigwait_fd);
|
2018-08-25 09:58:35 +03:00
|
|
|
if (!orig || *orig > quantum)
|
2018-08-14 00:34:20 +03:00
|
|
|
return &quantum;
|
|
|
|
}
|
2018-07-30 08:51:06 +03:00
|
|
|
|
2018-08-14 00:34:20 +03:00
|
|
|
return orig;
|
|
|
|
}
|
2018-08-06 08:22:00 +03:00
|
|
|
|
2018-08-14 00:34:20 +03:00
|
|
|
static VALUE
|
|
|
|
do_select(VALUE p)
|
2007-02-08 14:51:40 +03:00
|
|
|
{
|
2018-08-14 00:34:20 +03:00
|
|
|
struct select_set *set = (struct select_set *)p;
|
2018-08-27 08:32:18 +03:00
|
|
|
int result = 0;
|
2012-11-29 04:52:51 +04:00
|
|
|
int lerrno;
|
2018-09-09 15:42:48 +03:00
|
|
|
rb_hrtime_t *to, rel, end = 0;
|
2007-02-14 07:02:12 +03:00
|
|
|
|
2018-08-25 09:58:35 +03:00
|
|
|
timeout_prepare(&to, &rel, &end, set->timeout);
|
2018-08-14 00:34:20 +03:00
|
|
|
#define restore_fdset(dst, src) \
|
|
|
|
((dst) ? rb_fd_dup(dst, src) : (void)0)
|
2015-07-21 15:21:35 +03:00
|
|
|
#define do_select_update() \
|
2018-08-14 00:34:20 +03:00
|
|
|
(restore_fdset(set->rset, &set->orig_rset), \
|
|
|
|
restore_fdset(set->wset, &set->orig_wset), \
|
|
|
|
restore_fdset(set->eset, &set->orig_eset), \
|
2018-05-18 11:01:07 +03:00
|
|
|
TRUE)
|
2015-07-21 15:21:35 +03:00
|
|
|
|
|
|
|
do {
|
2018-08-14 00:34:20 +03:00
|
|
|
int drained;
|
2015-07-21 15:21:35 +03:00
|
|
|
lerrno = 0;
|
2007-02-14 07:02:12 +03:00
|
|
|
|
2018-08-14 00:34:20 +03:00
|
|
|
BLOCKING_REGION(set->th, {
|
2018-08-25 09:58:35 +03:00
|
|
|
const rb_hrtime_t *sto;
|
|
|
|
struct timeval tv;
|
|
|
|
|
|
|
|
sto = sigwait_timeout(set->th, set->sigwait_fd, to, &drained);
|
2018-08-27 08:32:18 +03:00
|
|
|
if (!RUBY_VM_INTERRUPTED(set->th->ec)) {
|
|
|
|
result = native_fd_select(set->max, set->rset, set->wset,
|
|
|
|
set->eset,
|
|
|
|
rb_hrtime2timeval(&tv, sto), set->th);
|
|
|
|
if (result < 0) lerrno = errno;
|
|
|
|
}
|
|
|
|
}, set->sigwait_fd >= 0 ? ubf_sigwait : ubf_select, set->th, TRUE);
|
2012-07-18 09:46:40 +04:00
|
|
|
|
2018-08-14 00:34:20 +03:00
|
|
|
if (set->sigwait_fd >= 0) {
|
|
|
|
if (result > 0 && rb_fd_isset(set->sigwait_fd, set->rset))
|
|
|
|
result--;
|
|
|
|
(void)check_signals_nogvl(set->th, set->sigwait_fd);
|
|
|
|
}
|
2018-07-21 06:26:38 +03:00
|
|
|
|
2018-08-14 00:34:20 +03:00
|
|
|
RUBY_VM_CHECK_INTS_BLOCKING(set->th->ec); /* may raise */
|
2018-08-25 09:58:35 +03:00
|
|
|
} while (wait_retryable(&result, lerrno, to, end) && do_select_update());
|
2018-08-06 08:22:00 +03:00
|
|
|
|
2018-05-18 11:01:07 +03:00
|
|
|
if (result < 0) {
|
|
|
|
errno = lerrno;
|
|
|
|
}
|
2011-07-01 06:08:48 +04:00
|
|
|
|
2018-08-14 00:34:20 +03:00
|
|
|
return (VALUE)result;
|
2007-02-08 14:51:40 +03:00
|
|
|
}
|
|
|
|
|
2007-02-05 15:21:01 +03:00
|
|
|
static void
|
2007-02-14 07:02:12 +03:00
|
|
|
rb_thread_wait_fd_rw(int fd, int read)
|
2007-02-05 15:21:01 +03:00
|
|
|
{
|
|
|
|
int result = 0;
|
2011-05-04 04:59:57 +04:00
|
|
|
int events = read ? RB_WAITFD_IN : RB_WAITFD_OUT;
|
|
|
|
|
2007-02-14 07:02:12 +03:00
|
|
|
thread_debug("rb_thread_wait_fd_rw(%d, %s)\n", fd, read ? "read" : "write");
|
2007-02-05 15:21:01 +03:00
|
|
|
|
2008-07-11 15:51:39 +04:00
|
|
|
if (fd < 0) {
|
|
|
|
rb_raise(rb_eIOError, "closed stream");
|
|
|
|
}
|
2007-06-26 11:30:01 +04:00
|
|
|
|
2012-12-18 14:07:05 +04:00
|
|
|
result = rb_wait_for_single_fd(fd, events, NULL);
|
|
|
|
if (result < 0) {
|
|
|
|
rb_sys_fail(0);
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
2007-02-05 15:21:01 +03:00
|
|
|
|
2007-02-14 07:02:12 +03:00
|
|
|
thread_debug("rb_thread_wait_fd_rw(%d, %s): done\n", fd, read ? "read" : "write");
|
2007-02-05 15:21:01 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
rb_thread_wait_fd(int fd)
|
|
|
|
{
|
2007-02-14 07:02:12 +03:00
|
|
|
rb_thread_wait_fd_rw(fd, 1);
|
2007-02-05 15:21:01 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
rb_thread_fd_writable(int fd)
|
|
|
|
{
|
2007-02-14 07:02:12 +03:00
|
|
|
rb_thread_wait_fd_rw(fd, 0);
|
* compile.c, cont.c, gc.c, insns.def, iseq.c, iseq.h, process.c,
thread.c, vm.c, vm_core.h, vm_dump.c, vm_eval.c,
vm_insnhelper.c, vm_method.c, template/insns_info.inc.tmpl,
tool/instruction.rb: fixed types.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@25030 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2009-09-22 00:58:26 +04:00
|
|
|
return TRUE;
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
2018-08-14 00:34:20 +03:00
|
|
|
static rb_fdset_t *
|
|
|
|
init_set_fd(int fd, rb_fdset_t *fds)
|
|
|
|
{
|
|
|
|
if (fd < 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
rb_fd_init(fds);
|
|
|
|
rb_fd_set(fd, fds);
|
|
|
|
|
|
|
|
return fds;
|
|
|
|
}
|
|
|
|
|
2009-04-01 08:56:51 +04:00
|
|
|
int
|
|
|
|
rb_thread_fd_select(int max, rb_fdset_t * read, rb_fdset_t * write, rb_fdset_t * except,
|
|
|
|
struct timeval *timeout)
|
|
|
|
{
|
2018-08-14 00:34:20 +03:00
|
|
|
struct select_set set;
|
|
|
|
|
|
|
|
set.th = GET_THREAD();
|
2018-08-27 08:32:18 +03:00
|
|
|
RUBY_VM_CHECK_INTS_BLOCKING(set.th->ec);
|
2018-08-14 00:34:20 +03:00
|
|
|
set.max = max;
|
|
|
|
set.rset = read;
|
|
|
|
set.wset = write;
|
|
|
|
set.eset = except;
|
|
|
|
set.timeout = timeout;
|
|
|
|
|
|
|
|
if (!set.rset && !set.wset && !set.eset) {
|
2018-11-14 06:52:31 +03:00
|
|
|
if (!timeout) {
|
|
|
|
rb_thread_sleep_forever();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
rb_thread_wait_for(*timeout);
|
|
|
|
return 0;
|
2009-04-01 08:56:51 +04:00
|
|
|
}
|
|
|
|
|
2018-11-14 06:52:28 +03:00
|
|
|
set.sigwait_fd = rb_sigwait_fd_get(set.th);
|
|
|
|
if (set.sigwait_fd >= 0) {
|
|
|
|
if (set.rset)
|
|
|
|
rb_fd_set(set.sigwait_fd, set.rset);
|
|
|
|
else
|
|
|
|
set.rset = init_set_fd(set.sigwait_fd, &set.orig_rset);
|
|
|
|
if (set.sigwait_fd >= set.max) {
|
|
|
|
set.max = set.sigwait_fd + 1;
|
|
|
|
}
|
|
|
|
}
|
2018-08-14 00:34:20 +03:00
|
|
|
#define fd_init_copy(f) do { \
|
|
|
|
if (set.f) { \
|
|
|
|
rb_fd_resize(set.max - 1, set.f); \
|
|
|
|
if (&set.orig_##f != set.f) { /* sigwait_fd */ \
|
|
|
|
rb_fd_init_copy(&set.orig_##f, set.f); \
|
|
|
|
} \
|
|
|
|
} \
|
|
|
|
else { \
|
|
|
|
rb_fd_no_init(&set.orig_##f); \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
fd_init_copy(rset);
|
|
|
|
fd_init_copy(wset);
|
|
|
|
fd_init_copy(eset);
|
|
|
|
#undef fd_init_copy
|
|
|
|
|
|
|
|
return (int)rb_ensure(do_select, (VALUE)&set, select_set_free, (VALUE)&set);
|
2009-04-01 08:56:51 +04:00
|
|
|
}
|
|
|
|
|
2018-07-30 03:31:08 +03:00
|
|
|
#ifdef USE_POLL
|
|
|
|
|
|
|
|
/* The same with linux kernel. TODO: make platform independent definition. */
|
|
|
|
#define POLLIN_SET (POLLRDNORM | POLLRDBAND | POLLIN | POLLHUP | POLLERR)
|
|
|
|
#define POLLOUT_SET (POLLWRBAND | POLLWRNORM | POLLOUT | POLLERR)
|
|
|
|
#define POLLEX_SET (POLLPRI)
|
|
|
|
|
|
|
|
#ifndef POLLERR_SET /* defined for FreeBSD for now */
|
|
|
|
# define POLLERR_SET (0)
|
|
|
|
#endif
|
|
|
|
|
2011-05-04 05:07:03 +04:00
|
|
|
/*
|
|
|
|
* returns a mask of events
|
|
|
|
*/
|
|
|
|
int
|
2018-02-03 22:59:21 +03:00
|
|
|
rb_wait_for_single_fd(int fd, int events, struct timeval *timeout)
|
2011-05-04 05:07:03 +04:00
|
|
|
{
|
2018-08-14 00:34:20 +03:00
|
|
|
struct pollfd fds[2];
|
2012-12-04 20:05:40 +04:00
|
|
|
int result = 0, lerrno;
|
2018-09-09 15:42:48 +03:00
|
|
|
rb_hrtime_t *to, rel, end = 0;
|
2018-08-14 00:34:20 +03:00
|
|
|
int drained;
|
|
|
|
nfds_t nfds;
|
|
|
|
rb_unblock_function_t *ubf;
|
2018-11-22 11:46:51 +03:00
|
|
|
struct waiting_fd wfd;
|
2018-11-24 01:01:35 +03:00
|
|
|
int state;
|
2011-05-06 18:58:38 +04:00
|
|
|
|
2018-11-22 11:46:51 +03:00
|
|
|
wfd.th = GET_THREAD();
|
|
|
|
wfd.fd = fd;
|
2018-11-24 01:01:35 +03:00
|
|
|
list_add(&wfd.th->vm->waiting_fds, &wfd.wfd_node);
|
|
|
|
EC_PUSH_TAG(wfd.th->ec);
|
|
|
|
if ((state = EC_EXEC_TAG()) == TAG_NONE) {
|
|
|
|
RUBY_VM_CHECK_INTS_BLOCKING(wfd.th->ec);
|
|
|
|
timeout_prepare(&to, &rel, &end, timeout);
|
|
|
|
fds[0].fd = fd;
|
|
|
|
fds[0].events = (short)events;
|
2018-11-07 01:55:57 +03:00
|
|
|
fds[0].revents = 0;
|
2018-11-24 01:01:35 +03:00
|
|
|
do {
|
|
|
|
fds[1].fd = rb_sigwait_fd_get(wfd.th);
|
2018-08-14 00:34:20 +03:00
|
|
|
|
2018-11-24 01:01:35 +03:00
|
|
|
if (fds[1].fd >= 0) {
|
|
|
|
fds[1].events = POLLIN;
|
|
|
|
fds[1].revents = 0;
|
|
|
|
nfds = 2;
|
|
|
|
ubf = ubf_sigwait;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
nfds = 1;
|
|
|
|
ubf = ubf_select;
|
2018-08-27 08:32:18 +03:00
|
|
|
}
|
2018-02-03 22:59:21 +03:00
|
|
|
|
2018-11-24 01:01:35 +03:00
|
|
|
lerrno = 0;
|
|
|
|
BLOCKING_REGION(wfd.th, {
|
|
|
|
const rb_hrtime_t *sto;
|
|
|
|
struct timespec ts;
|
|
|
|
|
|
|
|
sto = sigwait_timeout(wfd.th, fds[1].fd, to, &drained);
|
|
|
|
if (!RUBY_VM_INTERRUPTED(wfd.th->ec)) {
|
|
|
|
result = ppoll(fds, nfds, rb_hrtime2timespec(&ts, sto), 0);
|
|
|
|
if (result < 0) lerrno = errno;
|
|
|
|
}
|
|
|
|
}, ubf, wfd.th, TRUE);
|
|
|
|
|
|
|
|
if (fds[1].fd >= 0) {
|
|
|
|
if (result > 0 && fds[1].revents) {
|
|
|
|
result--;
|
|
|
|
}
|
|
|
|
(void)check_signals_nogvl(wfd.th, fds[1].fd);
|
|
|
|
rb_sigwait_fd_put(wfd.th, fds[1].fd);
|
|
|
|
rb_sigwait_fd_migrate(wfd.th->vm);
|
2018-08-14 00:34:20 +03:00
|
|
|
}
|
2018-11-24 01:01:35 +03:00
|
|
|
RUBY_VM_CHECK_INTS_BLOCKING(wfd.th->ec);
|
|
|
|
} while (wait_retryable(&result, lerrno, to, end));
|
|
|
|
}
|
|
|
|
EC_POP_TAG();
|
|
|
|
list_del(&wfd.wfd_node);
|
|
|
|
if (state) {
|
2018-11-24 01:01:38 +03:00
|
|
|
EC_JUMP_TAG(wfd.th->ec, state);
|
2018-11-24 01:01:35 +03:00
|
|
|
}
|
2018-08-14 00:34:20 +03:00
|
|
|
|
2018-05-18 11:01:07 +03:00
|
|
|
if (result < 0) {
|
|
|
|
errno = lerrno;
|
|
|
|
return -1;
|
|
|
|
}
|
2011-05-04 05:07:03 +04:00
|
|
|
|
2018-08-14 00:34:20 +03:00
|
|
|
if (fds[0].revents & POLLNVAL) {
|
2011-05-04 14:02:06 +04:00
|
|
|
errno = EBADF;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* POLLIN, POLLOUT have a different meanings from select(2)'s read/write bit.
|
2013-04-27 14:33:44 +04:00
|
|
|
* Therefore we need to fix it up.
|
2011-05-04 14:02:06 +04:00
|
|
|
*/
|
|
|
|
result = 0;
|
2018-08-14 00:34:20 +03:00
|
|
|
if (fds[0].revents & POLLIN_SET)
|
2011-05-04 14:02:06 +04:00
|
|
|
result |= RB_WAITFD_IN;
|
2018-08-14 00:34:20 +03:00
|
|
|
if (fds[0].revents & POLLOUT_SET)
|
2011-05-04 14:02:06 +04:00
|
|
|
result |= RB_WAITFD_OUT;
|
2018-08-14 00:34:20 +03:00
|
|
|
if (fds[0].revents & POLLEX_SET)
|
2011-05-04 14:02:06 +04:00
|
|
|
result |= RB_WAITFD_PRI;
|
|
|
|
|
2018-05-15 06:49:21 +03:00
|
|
|
/* all requested events are ready if there is an error */
|
2018-08-14 00:34:20 +03:00
|
|
|
if (fds[0].revents & POLLERR_SET)
|
2018-05-15 06:49:21 +03:00
|
|
|
result |= events;
|
|
|
|
|
2011-05-04 05:07:03 +04:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
#else /* ! USE_POLL - implement rb_io_poll_fd() using select() */
|
2011-05-04 04:59:57 +04:00
|
|
|
struct select_args {
|
|
|
|
union {
|
|
|
|
int fd;
|
|
|
|
int error;
|
|
|
|
} as;
|
|
|
|
rb_fdset_t *read;
|
|
|
|
rb_fdset_t *write;
|
|
|
|
rb_fdset_t *except;
|
2018-11-22 11:46:51 +03:00
|
|
|
struct waiting_fd wfd;
|
2011-05-04 04:59:57 +04:00
|
|
|
struct timeval *tv;
|
|
|
|
};
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
select_single(VALUE ptr)
|
|
|
|
{
|
|
|
|
struct select_args *args = (struct select_args *)ptr;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
r = rb_thread_fd_select(args->as.fd + 1,
|
|
|
|
args->read, args->write, args->except, args->tv);
|
|
|
|
if (r == -1)
|
|
|
|
args->as.error = errno;
|
|
|
|
if (r > 0) {
|
|
|
|
r = 0;
|
|
|
|
if (args->read && rb_fd_isset(args->as.fd, args->read))
|
|
|
|
r |= RB_WAITFD_IN;
|
|
|
|
if (args->write && rb_fd_isset(args->as.fd, args->write))
|
|
|
|
r |= RB_WAITFD_OUT;
|
|
|
|
if (args->except && rb_fd_isset(args->as.fd, args->except))
|
|
|
|
r |= RB_WAITFD_PRI;
|
|
|
|
}
|
|
|
|
return (VALUE)r;
|
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
select_single_cleanup(VALUE ptr)
|
|
|
|
{
|
|
|
|
struct select_args *args = (struct select_args *)ptr;
|
|
|
|
|
2018-11-22 11:46:51 +03:00
|
|
|
list_del(&args->wfd.wfd_node);
|
2011-05-04 04:59:57 +04:00
|
|
|
if (args->read) rb_fd_term(args->read);
|
|
|
|
if (args->write) rb_fd_term(args->write);
|
|
|
|
if (args->except) rb_fd_term(args->except);
|
|
|
|
|
|
|
|
return (VALUE)-1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
rb_wait_for_single_fd(int fd, int events, struct timeval *tv)
|
|
|
|
{
|
|
|
|
rb_fdset_t rfds, wfds, efds;
|
|
|
|
struct select_args args;
|
|
|
|
int r;
|
|
|
|
VALUE ptr = (VALUE)&args;
|
|
|
|
|
|
|
|
args.as.fd = fd;
|
|
|
|
args.read = (events & RB_WAITFD_IN) ? init_set_fd(fd, &rfds) : NULL;
|
|
|
|
args.write = (events & RB_WAITFD_OUT) ? init_set_fd(fd, &wfds) : NULL;
|
|
|
|
args.except = (events & RB_WAITFD_PRI) ? init_set_fd(fd, &efds) : NULL;
|
|
|
|
args.tv = tv;
|
2018-11-22 11:46:51 +03:00
|
|
|
args.wfd.fd = fd;
|
|
|
|
args.wfd.th = GET_THREAD();
|
2011-05-04 04:59:57 +04:00
|
|
|
|
2018-11-22 11:46:51 +03:00
|
|
|
list_add(&args.wfd.th->vm->waiting_fds, &args.wfd.wfd_node);
|
2011-05-04 04:59:57 +04:00
|
|
|
r = (int)rb_ensure(select_single, ptr, select_single_cleanup, ptr);
|
|
|
|
if (r == -1)
|
|
|
|
errno = args.as.error;
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
2011-05-04 05:07:03 +04:00
|
|
|
#endif /* ! USE_POLL */
|
2009-04-01 08:56:51 +04:00
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
/*
|
|
|
|
* for GC
|
|
|
|
*/
|
|
|
|
|
2007-07-14 11:19:59 +04:00
|
|
|
#ifdef USE_CONSERVATIVE_STACK_END
|
2006-12-31 18:02:22 +03:00
|
|
|
void
|
2007-02-05 15:21:01 +03:00
|
|
|
rb_gc_set_stack_end(VALUE **stack_end_p)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
|
|
|
VALUE stack_end;
|
|
|
|
*stack_end_p = &stack_end;
|
|
|
|
}
|
2007-07-14 11:19:59 +04:00
|
|
|
#endif
|
2006-12-31 18:02:22 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2010-04-27 16:27:13 +04:00
|
|
|
void
|
|
|
|
rb_threadptr_check_signal(rb_thread_t *mth)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2010-04-27 16:27:13 +04:00
|
|
|
/* mth must be main_thread */
|
2011-07-01 02:29:34 +04:00
|
|
|
if (rb_signal_buff_size() > 0) {
|
|
|
|
/* wakeup main thread */
|
2017-11-16 11:48:59 +03:00
|
|
|
threadptr_trap_interrupt(mth);
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
2010-04-27 16:27:13 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2018-08-14 00:34:20 +03:00
|
|
|
timer_thread_function(void)
|
2010-04-27 16:27:13 +04:00
|
|
|
{
|
2018-08-14 00:34:20 +03:00
|
|
|
volatile rb_execution_context_t *ec;
|
2010-04-27 16:27:13 +04:00
|
|
|
|
|
|
|
/* for time slice */
|
2018-08-14 00:34:20 +03:00
|
|
|
ec = ACCESS_ONCE(rb_execution_context_t *,
|
|
|
|
ruby_current_execution_context_ptr);
|
|
|
|
if (ec) RUBY_VM_SET_TIMER_INTERRUPT(ec);
|
|
|
|
}
|
2018-08-06 08:22:00 +03:00
|
|
|
|
2018-08-14 00:34:20 +03:00
|
|
|
static void
|
|
|
|
async_bug_fd(const char *mesg, int errno_arg, int fd)
|
|
|
|
{
|
|
|
|
char buff[64];
|
|
|
|
size_t n = strlcpy(buff, mesg, sizeof(buff));
|
|
|
|
if (n < sizeof(buff)-3) {
|
|
|
|
ruby_snprintf(buff+n, sizeof(buff)-n, "(%d)", fd);
|
|
|
|
}
|
|
|
|
rb_async_bug_errno(buff, errno_arg);
|
|
|
|
}
|
2018-08-06 08:22:00 +03:00
|
|
|
|
2018-08-14 00:34:20 +03:00
|
|
|
/* VM-dependent API is not available for this function */
|
|
|
|
static int
|
|
|
|
consume_communication_pipe(int fd)
|
|
|
|
{
|
2018-08-24 22:19:01 +03:00
|
|
|
#if USE_EVENTFD
|
|
|
|
uint64_t buff[1];
|
|
|
|
#else
|
2018-08-14 00:34:20 +03:00
|
|
|
/* buffer can be shared because no one refers to them. */
|
2018-08-24 22:19:01 +03:00
|
|
|
static char buff[1024];
|
|
|
|
#endif
|
2018-08-14 00:34:20 +03:00
|
|
|
ssize_t result;
|
|
|
|
int ret = FALSE; /* for rb_sigwait_sleep */
|
|
|
|
|
2018-08-16 12:16:11 +03:00
|
|
|
/*
|
|
|
|
* disarm UBF_TIMER before we read, because it can become
|
|
|
|
* re-armed at any time via sighandler and the pipe will refill
|
|
|
|
* We can disarm it because this thread is now processing signals
|
|
|
|
* and we do not want unnecessary SIGVTALRM
|
|
|
|
*/
|
|
|
|
ubf_timer_disarm();
|
|
|
|
|
2018-08-14 00:34:20 +03:00
|
|
|
while (1) {
|
|
|
|
result = read(fd, buff, sizeof(buff));
|
|
|
|
if (result > 0) {
|
|
|
|
ret = TRUE;
|
2018-08-24 22:19:01 +03:00
|
|
|
if (USE_EVENTFD || result < (ssize_t)sizeof(buff)) {
|
2018-08-14 00:34:20 +03:00
|
|
|
return ret;
|
|
|
|
}
|
2018-08-06 08:22:00 +03:00
|
|
|
}
|
2018-08-14 00:34:20 +03:00
|
|
|
else if (result == 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
else if (result < 0) {
|
|
|
|
int e = errno;
|
|
|
|
switch (e) {
|
|
|
|
case EINTR:
|
|
|
|
continue; /* retry */
|
|
|
|
case EAGAIN:
|
|
|
|
#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
|
|
|
|
case EWOULDBLOCK:
|
2018-08-06 08:22:00 +03:00
|
|
|
#endif
|
2018-08-14 00:34:20 +03:00
|
|
|
return ret;
|
|
|
|
default:
|
|
|
|
async_bug_fd("consume_communication_pipe: read", e, fd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
check_signals_nogvl(rb_thread_t *th, int sigwait_fd)
|
|
|
|
{
|
|
|
|
rb_vm_t *vm = GET_VM(); /* th may be 0 */
|
|
|
|
int ret = consume_communication_pipe(sigwait_fd);
|
|
|
|
ubf_wakeup_all_threads();
|
|
|
|
ruby_sigchld_handler(vm);
|
|
|
|
if (rb_signal_buff_size()) {
|
|
|
|
if (th == vm->main_thread)
|
|
|
|
/* no need to lock + wakeup if already in main thread */
|
|
|
|
RUBY_VM_SET_TRAP_INTERRUPT(th->ec);
|
|
|
|
else
|
|
|
|
threadptr_trap_interrupt(vm->main_thread);
|
|
|
|
ret = TRUE; /* for SIGCHLD_LOSSY && rb_sigwait_sleep */
|
|
|
|
}
|
|
|
|
return ret;
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
2007-01-07 12:47:52 +03:00
|
|
|
void
|
2015-08-14 12:44:10 +03:00
|
|
|
rb_thread_stop_timer_thread(void)
|
2007-01-07 12:47:52 +03:00
|
|
|
{
|
2015-08-14 12:44:10 +03:00
|
|
|
if (TIMER_THREAD_CREATED_P() && native_stop_timer_thread()) {
|
2009-11-02 06:58:25 +03:00
|
|
|
native_reset_timer_thread();
|
2007-01-07 12:47:52 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
rb_thread_reset_timer_thread(void)
|
|
|
|
{
|
2009-11-02 06:58:25 +03:00
|
|
|
native_reset_timer_thread();
|
2007-01-07 12:47:52 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
rb_thread_start_timer_thread(void)
|
|
|
|
{
|
2008-11-24 18:08:24 +03:00
|
|
|
system_working = 1;
|
2007-01-07 12:47:52 +03:00
|
|
|
rb_thread_create_timer_thread();
|
|
|
|
}
|
|
|
|
|
2008-07-01 20:55:30 +04:00
|
|
|
static int
|
|
|
|
clear_coverage_i(st_data_t key, st_data_t val, st_data_t dummy)
|
|
|
|
{
|
|
|
|
int i;
|
2017-09-03 17:26:06 +03:00
|
|
|
VALUE coverage = (VALUE)val;
|
|
|
|
VALUE lines = RARRAY_AREF(coverage, COVERAGE_INDEX_LINES);
|
2017-09-14 06:25:36 +03:00
|
|
|
VALUE branches = RARRAY_AREF(coverage, COVERAGE_INDEX_BRANCHES);
|
2008-07-01 20:55:30 +04:00
|
|
|
|
2017-09-03 17:26:06 +03:00
|
|
|
if (lines) {
|
2018-10-20 08:33:04 +03:00
|
|
|
if (GET_VM()->coverage_mode & COVERAGE_TARGET_ONESHOT_LINES) {
|
|
|
|
rb_ary_clear(lines);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < RARRAY_LEN(lines); i++) {
|
|
|
|
if (RARRAY_AREF(lines, i) != Qnil)
|
|
|
|
RARRAY_ASET(lines, i, INT2FIX(0));
|
|
|
|
}
|
|
|
|
}
|
2008-07-01 20:55:30 +04:00
|
|
|
}
|
2017-09-14 06:25:36 +03:00
|
|
|
if (branches) {
|
|
|
|
VALUE counters = RARRAY_AREF(branches, 1);
|
|
|
|
for (i = 0; i < RARRAY_LEN(counters); i++) {
|
|
|
|
RARRAY_ASET(counters, i, INT2FIX(0));
|
|
|
|
}
|
|
|
|
}
|
2017-09-03 17:26:06 +03:00
|
|
|
|
2008-07-01 20:55:30 +04:00
|
|
|
return ST_CONTINUE;
|
|
|
|
}
|
|
|
|
|
2018-10-20 08:33:04 +03:00
|
|
|
void
|
|
|
|
rb_clear_coverages(void)
|
2008-07-01 20:55:30 +04:00
|
|
|
{
|
2008-07-08 19:13:22 +04:00
|
|
|
VALUE coverages = rb_get_coverages();
|
2008-07-03 16:55:12 +04:00
|
|
|
if (RTEST(coverages)) {
|
2018-10-31 01:11:51 +03:00
|
|
|
rb_hash_foreach(coverages, clear_coverage_i, 0);
|
2008-07-01 20:55:30 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-20 08:44:14 +03:00
|
|
|
#if defined(HAVE_WORKING_FORK)
|
2008-12-27 13:30:04 +03:00
|
|
|
static void
|
2016-05-09 04:52:01 +03:00
|
|
|
rb_thread_atfork_internal(rb_thread_t *th, void (*atfork)(rb_thread_t *, const rb_thread_t *))
|
2008-12-27 13:30:04 +03:00
|
|
|
{
|
2014-05-31 16:24:28 +04:00
|
|
|
rb_thread_t *i = 0;
|
2008-12-27 13:30:04 +03:00
|
|
|
rb_vm_t *vm = th->vm;
|
|
|
|
vm->main_thread = th;
|
|
|
|
|
2010-11-27 23:15:59 +03:00
|
|
|
gvl_atfork(th->vm);
|
2018-08-21 00:34:39 +03:00
|
|
|
ubf_list_atfork();
|
2014-05-28 05:48:11 +04:00
|
|
|
|
|
|
|
list_for_each(&vm->living_threads, i, vmlt_node) {
|
|
|
|
atfork(i, th);
|
|
|
|
}
|
vm*: doubly-linked list from ccan to manage vm->living_threads
A doubly-linked list for tracking living threads guarantees
constant-time insert/delete performance with no corner cases of a
hash table. I chose this ccan implementation of doubly-linked
lists over the BSD sys/queue.h implementation since:
1) insertion and removal are both branchless
2) locality is improved if a struct may be a member of multiple lists
(0002 patch in Feature 9632 will introduce a secondary list
for waiting FDs)
This also increases cache locality during iteration: improving
performance in a new IO#close benchmark with many sleeping threads
while still scanning the same number of threads.
vm_thread_close 1.762
* vm_core.h (rb_vm_t): list_head and counter for living_threads
(rb_thread_t): vmlt_node for living_threads linkage
(rb_vm_living_threads_init): new function wrapper
(rb_vm_living_threads_insert): ditto
(rb_vm_living_threads_remove): ditto
* vm.c (rb_vm_living_threads_foreach): new function wrapper
* thread.c (terminate_i, thread_start_func_2, thread_create_core,
thread_fd_close_i, thread_fd_close): update to use new APIs
* vm.c (vm_mark_each_thread_func, rb_vm_mark, ruby_vm_destruct,
vm_memsize, vm_init2, Init_VM): ditto
* vm_trace.c (clear_trace_func_i, rb_clear_trace_func): ditto
* benchmark/bm_vm_thread_close.rb: added to show improvement
* ccan/build_assert/build_assert.h: added as a dependency of list.h
* ccan/check_type/check_type.h: ditto
* ccan/container_of/container_of.h: ditto
* ccan/licenses/BSD-MIT: ditto
* ccan/licenses/CC0: ditto
* ccan/str/str.h: ditto (stripped of unused macros)
* ccan/list/list.h: ditto
* common.mk: add CCAN_LIST_INCLUDES
[ruby-core:61871][Feature 9632 (part 1)]
Apologies for the size of this commit, but I think a good
doubly-linked list will be useful for future features, too.
This may be used to add ordering to a container_of-based hash
table to preserve compatibility if required (e.g. feature 9614).
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@45913 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2014-05-11 03:48:51 +04:00
|
|
|
rb_vm_living_threads_init(vm);
|
|
|
|
rb_vm_living_threads_insert(vm, th);
|
2018-07-05 06:02:33 +03:00
|
|
|
|
|
|
|
/* may be held by MJIT threads in parent */
|
|
|
|
rb_native_mutex_initialize(&vm->waitpid_lock);
|
2018-11-30 06:56:29 +03:00
|
|
|
rb_native_mutex_initialize(&vm->workqueue_lock);
|
2018-09-13 04:43:03 +03:00
|
|
|
|
|
|
|
/* may be held by any thread in parent */
|
|
|
|
rb_native_mutex_initialize(&th->interrupt_lock);
|
|
|
|
|
2018-04-20 06:22:26 +03:00
|
|
|
vm->fork_gen++;
|
2018-03-27 12:28:37 +03:00
|
|
|
|
2008-12-27 13:30:04 +03:00
|
|
|
vm->sleeper = 0;
|
2018-10-20 08:33:04 +03:00
|
|
|
rb_clear_coverages();
|
2008-12-27 13:30:04 +03:00
|
|
|
}
|
|
|
|
|
2014-05-28 05:48:11 +04:00
|
|
|
static void
|
|
|
|
terminate_atfork_i(rb_thread_t *th, const rb_thread_t *current_th)
|
2008-01-14 13:02:51 +03:00
|
|
|
{
|
2014-05-28 05:48:11 +04:00
|
|
|
if (th != current_th) {
|
2013-10-05 06:21:12 +04:00
|
|
|
rb_mutex_abandon_keeping_mutexes(th);
|
|
|
|
rb_mutex_abandon_locking_mutex(th);
|
2010-12-20 18:16:41 +03:00
|
|
|
thread_cleanup_func(th, TRUE);
|
2008-01-14 13:02:51 +03:00
|
|
|
}
|
|
|
|
}
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2018-08-29 11:04:09 +03:00
|
|
|
void rb_fiber_atfork(rb_thread_t *);
|
2006-12-31 18:02:22 +03:00
|
|
|
void
|
|
|
|
rb_thread_atfork(void)
|
|
|
|
{
|
2016-05-09 04:52:01 +03:00
|
|
|
rb_thread_t *th = GET_THREAD();
|
|
|
|
rb_thread_atfork_internal(th, terminate_atfork_i);
|
|
|
|
th->join_list = NULL;
|
2018-08-29 11:04:09 +03:00
|
|
|
rb_fiber_atfork(th);
|
2010-12-25 11:50:53 +03:00
|
|
|
|
|
|
|
/* We don't want reproduce CVE-2003-0900. */
|
2008-06-20 06:46:17 +04:00
|
|
|
rb_reset_random_seed();
|
2018-11-18 11:25:48 +03:00
|
|
|
|
2018-11-20 03:07:36 +03:00
|
|
|
/* For child, starting MJIT worker thread in this place which is safer than immediately after `after_fork_ruby`. */
|
2018-07-08 10:27:24 +03:00
|
|
|
mjit_child_after_fork();
|
2008-05-11 08:15:29 +04:00
|
|
|
}
|
|
|
|
|
2014-05-28 05:48:11 +04:00
|
|
|
static void
|
|
|
|
terminate_atfork_before_exec_i(rb_thread_t *th, const rb_thread_t *current_th)
|
2008-05-11 08:15:29 +04:00
|
|
|
{
|
2014-05-28 05:48:11 +04:00
|
|
|
if (th != current_th) {
|
2008-05-11 08:15:29 +04:00
|
|
|
thread_cleanup_func_before_exec(th);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
rb_thread_atfork_before_exec(void)
|
|
|
|
{
|
2016-05-09 04:52:01 +03:00
|
|
|
rb_thread_t *th = GET_THREAD();
|
|
|
|
rb_thread_atfork_internal(th, terminate_atfork_before_exec_i);
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
2016-05-09 04:46:37 +03:00
|
|
|
#else
|
|
|
|
void
|
|
|
|
rb_thread_atfork(void)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
rb_thread_atfork_before_exec(void)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
#endif
|
2006-12-31 18:02:22 +03:00
|
|
|
|
|
|
|
struct thgroup {
|
|
|
|
int enclosed;
|
|
|
|
VALUE group;
|
|
|
|
};
|
|
|
|
|
2009-09-09 07:21:14 +04:00
|
|
|
static size_t
|
|
|
|
thgroup_memsize(const void *ptr)
|
|
|
|
{
|
2015-12-09 03:38:32 +03:00
|
|
|
return sizeof(struct thgroup);
|
2009-09-09 07:21:14 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static const rb_data_type_t thgroup_data_type = {
|
|
|
|
"thgroup",
|
2010-07-18 11:31:54 +04:00
|
|
|
{NULL, RUBY_TYPED_DEFAULT_FREE, thgroup_memsize,},
|
2014-12-01 09:38:04 +03:00
|
|
|
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
|
2009-09-09 07:21:14 +04:00
|
|
|
};
|
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
/*
|
|
|
|
* Document-class: ThreadGroup
|
|
|
|
*
|
2013-02-28 00:36:59 +04:00
|
|
|
* ThreadGroup provides a means of keeping track of a number of threads as a
|
|
|
|
* group.
|
|
|
|
*
|
|
|
|
* A given Thread object can only belong to one ThreadGroup at a time; adding
|
|
|
|
* a thread to a new group will remove it from any previous group.
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2006-12-31 18:02:22 +03:00
|
|
|
* Newly created threads belong to the same group as the thread from which they
|
|
|
|
* were created.
|
|
|
|
*/
|
|
|
|
|
2013-02-24 09:29:06 +04:00
|
|
|
/*
|
|
|
|
* Document-const: Default
|
|
|
|
*
|
|
|
|
* The default ThreadGroup created when Ruby starts; all Threads belong to it
|
|
|
|
* by default.
|
|
|
|
*/
|
2006-12-31 18:02:22 +03:00
|
|
|
static VALUE
|
|
|
|
thgroup_s_alloc(VALUE klass)
|
|
|
|
{
|
|
|
|
VALUE group;
|
|
|
|
struct thgroup *data;
|
|
|
|
|
2009-09-09 07:21:14 +04:00
|
|
|
group = TypedData_Make_Struct(klass, struct thgroup, &thgroup_data_type, data);
|
2006-12-31 18:02:22 +03:00
|
|
|
data->enclosed = 0;
|
|
|
|
data->group = group;
|
|
|
|
|
|
|
|
return group;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* call-seq:
|
2010-05-18 01:07:33 +04:00
|
|
|
* thgrp.list -> array
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2013-02-28 00:36:59 +04:00
|
|
|
* Returns an array of all existing Thread objects that belong to this group.
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2006-12-31 18:02:22 +03:00
|
|
|
* ThreadGroup::Default.list #=> [#<Thread:0x401bdf4c run>]
|
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
thgroup_list(VALUE group)
|
|
|
|
{
|
|
|
|
VALUE ary = rb_ary_new();
|
2014-05-28 05:48:11 +04:00
|
|
|
rb_vm_t *vm = GET_THREAD()->vm;
|
2014-05-29 19:45:25 +04:00
|
|
|
rb_thread_t *th = 0;
|
2009-02-18 04:29:13 +03:00
|
|
|
|
2014-05-28 05:48:11 +04:00
|
|
|
list_for_each(&vm->living_threads, th, vmlt_node) {
|
|
|
|
if (th->thgroup == group) {
|
|
|
|
rb_ary_push(ary, th->self);
|
|
|
|
}
|
|
|
|
}
|
2006-12-31 18:02:22 +03:00
|
|
|
return ary;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* call-seq:
|
2010-05-18 01:07:33 +04:00
|
|
|
* thgrp.enclose -> thgrp
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2006-12-31 18:02:22 +03:00
|
|
|
* Prevents threads from being added to or removed from the receiving
|
2013-02-28 00:36:59 +04:00
|
|
|
* ThreadGroup.
|
|
|
|
*
|
|
|
|
* New threads can still be started in an enclosed ThreadGroup.
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2006-12-31 18:02:22 +03:00
|
|
|
* ThreadGroup::Default.enclose #=> #<ThreadGroup:0x4029d914>
|
2017-09-25 09:05:52 +03:00
|
|
|
* thr = Thread.new { Thread.stop } #=> #<Thread:0x402a7210 sleep>
|
|
|
|
* tg = ThreadGroup.new #=> #<ThreadGroup:0x402752d4>
|
2006-12-31 18:02:22 +03:00
|
|
|
* tg.add thr
|
2013-02-28 00:36:59 +04:00
|
|
|
* #=> ThreadError: can't move from the enclosed thread group
|
2006-12-31 18:02:22 +03:00
|
|
|
*/
|
|
|
|
|
2008-09-04 22:23:27 +04:00
|
|
|
static VALUE
|
2007-03-19 06:58:57 +03:00
|
|
|
thgroup_enclose(VALUE group)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
|
|
|
struct thgroup *data;
|
|
|
|
|
2009-09-09 07:21:14 +04:00
|
|
|
TypedData_Get_Struct(group, struct thgroup, &thgroup_data_type, data);
|
2006-12-31 18:02:22 +03:00
|
|
|
data->enclosed = 1;
|
|
|
|
|
|
|
|
return group;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* call-seq:
|
2010-05-18 01:07:33 +04:00
|
|
|
* thgrp.enclosed? -> true or false
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2013-02-28 00:36:59 +04:00
|
|
|
* Returns +true+ if the +thgrp+ is enclosed. See also ThreadGroup#enclose.
|
2006-12-31 18:02:22 +03:00
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
thgroup_enclosed_p(VALUE group)
|
|
|
|
{
|
|
|
|
struct thgroup *data;
|
|
|
|
|
2009-09-09 07:21:14 +04:00
|
|
|
TypedData_Get_Struct(group, struct thgroup, &thgroup_data_type, data);
|
2006-12-31 18:02:22 +03:00
|
|
|
if (data->enclosed)
|
|
|
|
return Qtrue;
|
|
|
|
return Qfalse;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* call-seq:
|
2010-05-18 01:07:33 +04:00
|
|
|
* thgrp.add(thread) -> thgrp
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2013-02-28 00:36:59 +04:00
|
|
|
* Adds the given +thread+ to this group, removing it from any other
|
|
|
|
* group to which it may have previously been a member.
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2006-12-31 18:02:22 +03:00
|
|
|
* puts "Initial group is #{ThreadGroup::Default.list}"
|
|
|
|
* tg = ThreadGroup.new
|
|
|
|
* t1 = Thread.new { sleep }
|
|
|
|
* t2 = Thread.new { sleep }
|
|
|
|
* puts "t1 is #{t1}"
|
|
|
|
* puts "t2 is #{t2}"
|
|
|
|
* tg.add(t1)
|
|
|
|
* puts "Initial group now #{ThreadGroup::Default.list}"
|
|
|
|
* puts "tg group now #{tg.list}"
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2013-02-28 00:36:59 +04:00
|
|
|
* This will produce:
|
* compile.c, dir.c, eval.c, eval_jump.h, eval_method.h, numeric.c,
pack.c, parse.y, re.c, thread.c, vm.c, vm_dump.c, call_cfunc.ci,
thread_pthread.ci, thread_win32.ci: fixed indentation.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12431 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-06-05 08:25:10 +04:00
|
|
|
*
|
2006-12-31 18:02:22 +03:00
|
|
|
* Initial group is #<Thread:0x401bdf4c>
|
|
|
|
* t1 is #<Thread:0x401b3c90>
|
|
|
|
* t2 is #<Thread:0x401b3c18>
|
|
|
|
* Initial group now #<Thread:0x401b3c18>#<Thread:0x401bdf4c>
|
|
|
|
* tg group now #<Thread:0x401b3c90>
|
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
thgroup_add(VALUE group, VALUE thread)
|
|
|
|
{
|
2017-06-28 07:49:30 +03:00
|
|
|
rb_thread_t *target_th = rb_thread_ptr(thread);
|
2006-12-31 18:02:22 +03:00
|
|
|
struct thgroup *data;
|
|
|
|
|
|
|
|
if (OBJ_FROZEN(group)) {
|
|
|
|
rb_raise(rb_eThreadError, "can't move to the frozen thread group");
|
|
|
|
}
|
2009-09-09 07:21:14 +04:00
|
|
|
TypedData_Get_Struct(group, struct thgroup, &thgroup_data_type, data);
|
2006-12-31 18:02:22 +03:00
|
|
|
if (data->enclosed) {
|
|
|
|
rb_raise(rb_eThreadError, "can't move to the enclosed thread group");
|
|
|
|
}
|
|
|
|
|
2017-06-28 07:49:30 +03:00
|
|
|
if (!target_th->thgroup) {
|
2006-12-31 18:02:22 +03:00
|
|
|
return Qnil;
|
|
|
|
}
|
|
|
|
|
2017-06-28 07:49:30 +03:00
|
|
|
if (OBJ_FROZEN(target_th->thgroup)) {
|
2006-12-31 18:02:22 +03:00
|
|
|
rb_raise(rb_eThreadError, "can't move from the frozen thread group");
|
|
|
|
}
|
2017-06-28 07:49:30 +03:00
|
|
|
TypedData_Get_Struct(target_th->thgroup, struct thgroup, &thgroup_data_type, data);
|
2006-12-31 18:02:22 +03:00
|
|
|
if (data->enclosed) {
|
|
|
|
rb_raise(rb_eThreadError,
|
|
|
|
"can't move from the enclosed thread group");
|
|
|
|
}
|
|
|
|
|
2017-06-28 07:49:30 +03:00
|
|
|
target_th->thgroup = group;
|
2006-12-31 18:02:22 +03:00
|
|
|
return group;
|
|
|
|
}
|
|
|
|
|
2007-05-03 17:19:11 +04:00
|
|
|
/*
|
2012-07-05 11:00:29 +04:00
|
|
|
* Document-class: ThreadShield
|
2007-05-03 17:19:11 +04:00
|
|
|
*/
|
2009-09-09 07:21:14 +04:00
|
|
|
static void
|
2012-07-05 11:00:29 +04:00
|
|
|
thread_shield_mark(void *ptr)
|
2009-09-09 07:21:14 +04:00
|
|
|
{
|
|
|
|
rb_gc_mark((VALUE)ptr);
|
|
|
|
}
|
|
|
|
|
2012-07-05 11:00:29 +04:00
|
|
|
static const rb_data_type_t thread_shield_data_type = {
|
|
|
|
"thread_shield",
|
|
|
|
{thread_shield_mark, 0, 0,},
|
2014-12-01 09:38:04 +03:00
|
|
|
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
|
2009-09-09 07:21:14 +04:00
|
|
|
};
|
|
|
|
|
2007-05-03 17:19:11 +04:00
|
|
|
static VALUE
|
2012-07-05 11:00:29 +04:00
|
|
|
thread_shield_alloc(VALUE klass)
|
2007-05-03 17:19:11 +04:00
|
|
|
{
|
2012-07-05 11:00:29 +04:00
|
|
|
return TypedData_Wrap_Struct(klass, &thread_shield_data_type, (void *)mutex_alloc(0));
|
2007-05-03 17:19:11 +04:00
|
|
|
}
|
|
|
|
|
2012-07-05 11:00:29 +04:00
|
|
|
#define GetThreadShieldPtr(obj) ((VALUE)rb_check_typeddata((obj), &thread_shield_data_type))
|
|
|
|
#define THREAD_SHIELD_WAITING_MASK (FL_USER0|FL_USER1|FL_USER2|FL_USER3|FL_USER4|FL_USER5|FL_USER6|FL_USER7|FL_USER8|FL_USER9|FL_USER10|FL_USER11|FL_USER12|FL_USER13|FL_USER14|FL_USER15|FL_USER16|FL_USER17|FL_USER18|FL_USER19)
|
|
|
|
#define THREAD_SHIELD_WAITING_SHIFT (FL_USHIFT)
|
|
|
|
#define rb_thread_shield_waiting(b) (int)((RBASIC(b)->flags&THREAD_SHIELD_WAITING_MASK)>>THREAD_SHIELD_WAITING_SHIFT)
|
2012-07-06 07:24:41 +04:00
|
|
|
|
|
|
|
static inline void
|
|
|
|
rb_thread_shield_waiting_inc(VALUE b)
|
|
|
|
{
|
2012-07-06 07:46:39 +04:00
|
|
|
unsigned int w = rb_thread_shield_waiting(b);
|
2012-07-06 07:24:41 +04:00
|
|
|
w++;
|
2012-07-06 07:46:39 +04:00
|
|
|
if (w > (unsigned int)(THREAD_SHIELD_WAITING_MASK>>THREAD_SHIELD_WAITING_SHIFT))
|
2012-07-06 07:24:41 +04:00
|
|
|
rb_raise(rb_eRuntimeError, "waiting count overflow");
|
|
|
|
RBASIC(b)->flags &= ~THREAD_SHIELD_WAITING_MASK;
|
|
|
|
RBASIC(b)->flags |= ((VALUE)w << THREAD_SHIELD_WAITING_SHIFT);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
rb_thread_shield_waiting_dec(VALUE b)
|
|
|
|
{
|
2012-07-06 07:46:39 +04:00
|
|
|
unsigned int w = rb_thread_shield_waiting(b);
|
2012-07-06 07:24:41 +04:00
|
|
|
if (!w) rb_raise(rb_eRuntimeError, "waiting count underflow");
|
|
|
|
w--;
|
|
|
|
RBASIC(b)->flags &= ~THREAD_SHIELD_WAITING_MASK;
|
|
|
|
RBASIC(b)->flags |= ((VALUE)w << THREAD_SHIELD_WAITING_SHIFT);
|
|
|
|
}
|
2009-09-09 07:21:14 +04:00
|
|
|
|
2007-05-03 17:19:11 +04:00
|
|
|
VALUE
|
2012-07-05 11:00:29 +04:00
|
|
|
rb_thread_shield_new(void)
|
2007-05-03 17:19:11 +04:00
|
|
|
{
|
2012-07-05 11:00:29 +04:00
|
|
|
VALUE thread_shield = thread_shield_alloc(rb_cThreadShield);
|
|
|
|
rb_mutex_lock((VALUE)DATA_PTR(thread_shield));
|
|
|
|
return thread_shield;
|
2008-11-11 21:28:47 +03:00
|
|
|
}
|
|
|
|
|
2011-12-14 05:20:11 +04:00
|
|
|
/*
|
2012-07-05 11:00:29 +04:00
|
|
|
* Wait a thread shield.
|
2011-12-14 05:20:11 +04:00
|
|
|
*
|
|
|
|
* Returns
|
2012-07-05 11:00:29 +04:00
|
|
|
* true: acquired the thread shield
|
|
|
|
* false: the thread shield was destroyed and no other threads waiting
|
|
|
|
* nil: the thread shield was destroyed but still in use
|
2011-12-14 05:20:11 +04:00
|
|
|
*/
|
2007-05-03 17:19:11 +04:00
|
|
|
VALUE
|
2012-07-05 11:00:29 +04:00
|
|
|
rb_thread_shield_wait(VALUE self)
|
2007-05-03 17:19:11 +04:00
|
|
|
{
|
2012-07-05 11:00:29 +04:00
|
|
|
VALUE mutex = GetThreadShieldPtr(self);
|
2011-06-16 04:12:55 +04:00
|
|
|
rb_mutex_t *m;
|
2007-05-03 17:19:11 +04:00
|
|
|
|
2008-11-13 00:08:47 +03:00
|
|
|
if (!mutex) return Qfalse;
|
2018-08-21 04:01:42 +03:00
|
|
|
m = mutex_ptr(mutex);
|
2011-12-14 05:20:11 +04:00
|
|
|
if (m->th == GET_THREAD()) return Qnil;
|
2012-07-05 11:00:29 +04:00
|
|
|
rb_thread_shield_waiting_inc(self);
|
2008-11-13 00:08:47 +03:00
|
|
|
rb_mutex_lock(mutex);
|
2012-07-05 11:00:29 +04:00
|
|
|
rb_thread_shield_waiting_dec(self);
|
2008-11-13 00:08:47 +03:00
|
|
|
if (DATA_PTR(self)) return Qtrue;
|
|
|
|
rb_mutex_unlock(mutex);
|
2012-07-05 11:00:29 +04:00
|
|
|
return rb_thread_shield_waiting(self) > 0 ? Qnil : Qfalse;
|
2008-11-11 21:28:47 +03:00
|
|
|
}
|
|
|
|
|
2015-08-01 07:47:53 +03:00
|
|
|
static VALUE
|
|
|
|
thread_shield_get_mutex(VALUE self)
|
|
|
|
{
|
|
|
|
VALUE mutex = GetThreadShieldPtr(self);
|
|
|
|
if (!mutex)
|
2015-08-03 07:35:24 +03:00
|
|
|
rb_raise(rb_eThreadError, "destroyed thread shield - %p", (void *)self);
|
2015-08-01 07:47:53 +03:00
|
|
|
return mutex;
|
|
|
|
}
|
|
|
|
|
2011-12-14 07:20:02 +04:00
|
|
|
/*
|
2012-07-05 18:10:48 +04:00
|
|
|
* Release a thread shield, and return true if it has waiting threads.
|
2011-12-14 07:20:02 +04:00
|
|
|
*/
|
2008-11-11 21:28:47 +03:00
|
|
|
VALUE
|
2012-07-05 11:00:29 +04:00
|
|
|
rb_thread_shield_release(VALUE self)
|
2008-11-11 21:28:47 +03:00
|
|
|
{
|
2015-08-01 07:47:53 +03:00
|
|
|
VALUE mutex = thread_shield_get_mutex(self);
|
2011-12-14 07:20:02 +04:00
|
|
|
rb_mutex_unlock(mutex);
|
2012-07-05 11:00:29 +04:00
|
|
|
return rb_thread_shield_waiting(self) > 0 ? Qtrue : Qfalse;
|
2008-11-11 21:28:47 +03:00
|
|
|
}
|
|
|
|
|
2011-12-14 07:20:02 +04:00
|
|
|
/*
|
2012-07-05 18:10:48 +04:00
|
|
|
* Release and destroy a thread shield, and return true if it has waiting threads.
|
2011-12-14 07:20:02 +04:00
|
|
|
*/
|
2008-11-11 21:28:47 +03:00
|
|
|
VALUE
|
2012-07-05 11:00:29 +04:00
|
|
|
rb_thread_shield_destroy(VALUE self)
|
2008-11-11 21:28:47 +03:00
|
|
|
{
|
2015-08-01 07:47:53 +03:00
|
|
|
VALUE mutex = thread_shield_get_mutex(self);
|
2008-11-13 00:08:47 +03:00
|
|
|
DATA_PTR(self) = 0;
|
2011-12-14 07:20:02 +04:00
|
|
|
rb_mutex_unlock(mutex);
|
2012-07-05 11:00:29 +04:00
|
|
|
return rb_thread_shield_waiting(self) > 0 ? Qtrue : Qfalse;
|
2011-12-13 11:13:31 +04:00
|
|
|
}
|
|
|
|
|
2014-11-27 04:56:38 +03:00
|
|
|
static VALUE
|
|
|
|
threadptr_recursive_hash(rb_thread_t *th)
|
|
|
|
{
|
2017-10-26 11:32:49 +03:00
|
|
|
return th->ec->local_storage_recursive_hash;
|
2014-11-27 04:56:38 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
threadptr_recursive_hash_set(rb_thread_t *th, VALUE hash)
|
|
|
|
{
|
2017-10-26 11:32:49 +03:00
|
|
|
th->ec->local_storage_recursive_hash = hash;
|
2014-11-27 04:56:38 +03:00
|
|
|
}
|
|
|
|
|
2014-12-09 04:16:27 +03:00
|
|
|
ID rb_frame_last_func(void);
|
|
|
|
|
2009-09-13 08:45:54 +04:00
|
|
|
/*
|
|
|
|
* Returns the current "recursive list" used to detect recursion.
|
|
|
|
* This list is a hash table, unique for the current thread and for
|
|
|
|
* the current __callee__.
|
|
|
|
*/
|
2007-02-05 15:21:01 +03:00
|
|
|
|
2007-12-07 06:22:28 +03:00
|
|
|
static VALUE
|
2014-12-10 03:38:43 +03:00
|
|
|
recursive_list_access(VALUE sym)
|
2009-09-13 09:09:15 +04:00
|
|
|
{
|
2014-11-27 04:56:38 +03:00
|
|
|
rb_thread_t *th = GET_THREAD();
|
|
|
|
VALUE hash = threadptr_recursive_hash(th);
|
2009-09-13 08:45:54 +04:00
|
|
|
VALUE list;
|
2011-09-29 15:07:45 +04:00
|
|
|
if (NIL_P(hash) || !RB_TYPE_P(hash, T_HASH)) {
|
2015-01-23 05:36:50 +03:00
|
|
|
hash = rb_ident_hash_new();
|
2014-11-27 04:56:38 +03:00
|
|
|
threadptr_recursive_hash_set(th, hash);
|
2007-02-05 15:21:01 +03:00
|
|
|
list = Qnil;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
list = rb_hash_aref(hash, sym);
|
|
|
|
}
|
2011-09-29 15:07:45 +04:00
|
|
|
if (NIL_P(list) || !RB_TYPE_P(list, T_HASH)) {
|
2016-05-18 15:31:53 +03:00
|
|
|
list = rb_hash_new();
|
2007-02-05 15:21:01 +03:00
|
|
|
rb_hash_aset(hash, sym, list);
|
|
|
|
}
|
2009-09-13 08:45:54 +04:00
|
|
|
return list;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2012-12-25 14:45:20 +04:00
|
|
|
* Returns Qtrue iff obj_id (or the pair <obj, paired_obj>) is already
|
2009-09-13 08:45:54 +04:00
|
|
|
* in the recursion list.
|
|
|
|
* Assumes the recursion list is valid.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
recursive_check(VALUE list, VALUE obj_id, VALUE paired_obj_id)
|
|
|
|
{
|
2012-12-25 14:14:12 +04:00
|
|
|
#if SIZEOF_LONG == SIZEOF_VOIDP
|
|
|
|
#define OBJ_ID_EQL(obj_id, other) ((obj_id) == (other))
|
|
|
|
#elif SIZEOF_LONG_LONG == SIZEOF_VOIDP
|
|
|
|
#define OBJ_ID_EQL(obj_id, other) (RB_TYPE_P((obj_id), T_BIGNUM) ? \
|
|
|
|
rb_big_eql((obj_id), (other)) : ((obj_id) == (other)))
|
|
|
|
#endif
|
|
|
|
|
2009-09-13 08:45:54 +04:00
|
|
|
VALUE pair_list = rb_hash_lookup2(list, obj_id, Qundef);
|
|
|
|
if (pair_list == Qundef)
|
|
|
|
return Qfalse;
|
|
|
|
if (paired_obj_id) {
|
2011-09-29 15:07:45 +04:00
|
|
|
if (!RB_TYPE_P(pair_list, T_HASH)) {
|
2012-12-25 14:14:12 +04:00
|
|
|
if (!OBJ_ID_EQL(paired_obj_id, pair_list))
|
|
|
|
return Qfalse;
|
2009-09-13 08:45:54 +04:00
|
|
|
}
|
|
|
|
else {
|
2012-12-25 14:14:12 +04:00
|
|
|
if (NIL_P(rb_hash_lookup(pair_list, paired_obj_id)))
|
|
|
|
return Qfalse;
|
2009-09-13 08:45:54 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return Qtrue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Pushes obj_id (or the pair <obj_id, paired_obj_id>) in the recursion list.
|
|
|
|
* For a single obj_id, it sets list[obj_id] to Qtrue.
|
|
|
|
* For a pair, it sets list[obj_id] to paired_obj_id if possible,
|
|
|
|
* otherwise list[obj_id] becomes a hash like:
|
|
|
|
* {paired_obj_id_1 => true, paired_obj_id_2 => true, ... }
|
|
|
|
* Assumes the recursion list is valid.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void
|
|
|
|
recursive_push(VALUE list, VALUE obj, VALUE paired_obj)
|
|
|
|
{
|
|
|
|
VALUE pair_list;
|
|
|
|
|
2009-05-24 17:48:23 +04:00
|
|
|
if (!paired_obj) {
|
|
|
|
rb_hash_aset(list, obj, Qtrue);
|
|
|
|
}
|
|
|
|
else if ((pair_list = rb_hash_lookup2(list, obj, Qundef)) == Qundef) {
|
|
|
|
rb_hash_aset(list, obj, paired_obj);
|
|
|
|
}
|
|
|
|
else {
|
2011-09-29 15:07:45 +04:00
|
|
|
if (!RB_TYPE_P(pair_list, T_HASH)){
|
2009-05-24 17:48:23 +04:00
|
|
|
VALUE other_paired_obj = pair_list;
|
|
|
|
pair_list = rb_hash_new();
|
|
|
|
rb_hash_aset(pair_list, other_paired_obj, Qtrue);
|
|
|
|
rb_hash_aset(list, obj, pair_list);
|
|
|
|
}
|
|
|
|
rb_hash_aset(pair_list, paired_obj, Qtrue);
|
|
|
|
}
|
2007-02-05 15:21:01 +03:00
|
|
|
}
|
|
|
|
|
2009-09-13 08:45:54 +04:00
|
|
|
/*
|
|
|
|
* Pops obj_id (or the pair <obj_id, paired_obj_id>) from the recursion list.
|
|
|
|
* For a pair, if list[obj_id] is a hash, then paired_obj_id is
|
|
|
|
* removed from the hash and no attempt is made to simplify
|
|
|
|
* list[obj_id] from {only_one_paired_id => true} to only_one_paired_id
|
|
|
|
* Assumes the recursion list is valid.
|
|
|
|
*/
|
|
|
|
|
2014-12-10 03:38:43 +03:00
|
|
|
static int
|
2009-09-13 08:45:54 +04:00
|
|
|
recursive_pop(VALUE list, VALUE obj, VALUE paired_obj)
|
2007-02-05 15:21:01 +03:00
|
|
|
{
|
2009-05-24 17:48:23 +04:00
|
|
|
if (paired_obj) {
|
2009-09-13 08:45:54 +04:00
|
|
|
VALUE pair_list = rb_hash_lookup2(list, obj, Qundef);
|
2009-05-24 17:48:23 +04:00
|
|
|
if (pair_list == Qundef) {
|
2014-12-10 03:38:43 +03:00
|
|
|
return 0;
|
2009-05-24 17:48:23 +04:00
|
|
|
}
|
2011-09-29 15:07:45 +04:00
|
|
|
if (RB_TYPE_P(pair_list, T_HASH)) {
|
2014-12-24 05:53:37 +03:00
|
|
|
rb_hash_delete_entry(pair_list, paired_obj);
|
2009-05-24 17:48:23 +04:00
|
|
|
if (!RHASH_EMPTY_P(pair_list)) {
|
2014-12-10 03:38:43 +03:00
|
|
|
return 1; /* keep hash until is empty */
|
2009-05-24 17:48:23 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-12-24 05:53:37 +03:00
|
|
|
rb_hash_delete_entry(list, obj);
|
2014-12-10 03:38:43 +03:00
|
|
|
return 1;
|
2007-02-05 15:21:01 +03:00
|
|
|
}
|
|
|
|
|
2009-09-16 01:30:50 +04:00
|
|
|
struct exec_recursive_params {
|
|
|
|
VALUE (*func) (VALUE, VALUE, int);
|
|
|
|
VALUE list;
|
|
|
|
VALUE obj;
|
|
|
|
VALUE objid;
|
|
|
|
VALUE pairid;
|
|
|
|
VALUE arg;
|
|
|
|
};
|
|
|
|
|
|
|
|
static VALUE
|
2013-11-29 11:59:14 +04:00
|
|
|
exec_recursive_i(RB_BLOCK_CALL_FUNC_ARGLIST(tag, data))
|
2009-09-16 01:30:50 +04:00
|
|
|
{
|
2013-11-29 06:26:48 +04:00
|
|
|
struct exec_recursive_params *p = (void *)data;
|
2013-12-13 19:22:18 +04:00
|
|
|
return (*p->func)(p->obj, p->arg, FALSE);
|
2009-09-16 01:30:50 +04:00
|
|
|
}
|
|
|
|
|
2009-09-13 08:45:54 +04:00
|
|
|
/*
|
|
|
|
* Calls func(obj, arg, recursive), where recursive is non-zero if the
|
|
|
|
* current method is called recursively on obj, or on the pair <obj, pairid>
|
2009-09-16 01:30:50 +04:00
|
|
|
* If outer is 0, then the innermost func will be called with recursive set
|
|
|
|
* to Qtrue, otherwise the outermost func will be called. In the latter case,
|
|
|
|
* all inner func are short-circuited by throw.
|
|
|
|
* Implementation details: the value thrown is the recursive list which is
|
2013-05-19 07:10:21 +04:00
|
|
|
* proper to the current method and unlikely to be caught anywhere else.
|
2009-09-16 01:30:50 +04:00
|
|
|
* list[recursive_key] is used as a flag for the outermost call.
|
2009-09-13 08:45:54 +04:00
|
|
|
*/
|
|
|
|
|
2009-05-24 17:48:23 +04:00
|
|
|
static VALUE
|
2009-09-16 01:30:50 +04:00
|
|
|
exec_recursive(VALUE (*func) (VALUE, VALUE, int), VALUE obj, VALUE pairid, VALUE arg, int outer)
|
2007-02-05 15:21:01 +03:00
|
|
|
{
|
2011-02-20 10:26:48 +03:00
|
|
|
VALUE result = Qundef;
|
2014-12-10 03:38:43 +03:00
|
|
|
const ID mid = rb_frame_last_func();
|
|
|
|
const VALUE sym = mid ? ID2SYM(mid) : ID2SYM(idNULL);
|
2009-09-16 01:30:50 +04:00
|
|
|
struct exec_recursive_params p;
|
|
|
|
int outermost;
|
2014-12-10 03:38:43 +03:00
|
|
|
p.list = recursive_list_access(sym);
|
2009-09-16 01:30:50 +04:00
|
|
|
p.objid = rb_obj_id(obj);
|
2011-02-20 10:26:48 +03:00
|
|
|
p.obj = obj;
|
|
|
|
p.pairid = pairid;
|
|
|
|
p.arg = arg;
|
2009-09-16 01:30:50 +04:00
|
|
|
outermost = outer && !recursive_check(p.list, ID2SYM(recursive_key), 0);
|
2007-12-07 06:22:28 +03:00
|
|
|
|
2009-09-16 01:30:50 +04:00
|
|
|
if (recursive_check(p.list, p.objid, pairid)) {
|
|
|
|
if (outer && !outermost) {
|
|
|
|
rb_throw_obj(p.list, p.list);
|
|
|
|
}
|
* compile.c, cont.c, gc.c, insns.def, iseq.c, iseq.h, process.c,
thread.c, vm.c, vm_core.h, vm_dump.c, vm_eval.c,
vm_insnhelper.c, vm_method.c, template/insns_info.inc.tmpl,
tool/instruction.rb: fixed types.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@25030 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2009-09-22 00:58:26 +04:00
|
|
|
return (*func)(obj, arg, TRUE);
|
2007-02-05 15:21:01 +03:00
|
|
|
}
|
|
|
|
else {
|
2017-06-23 11:24:54 +03:00
|
|
|
enum ruby_tag_type state;
|
|
|
|
|
2009-09-16 01:30:50 +04:00
|
|
|
p.func = func;
|
|
|
|
|
|
|
|
if (outermost) {
|
|
|
|
recursive_push(p.list, ID2SYM(recursive_key), 0);
|
2013-12-13 19:22:18 +04:00
|
|
|
recursive_push(p.list, p.objid, p.pairid);
|
|
|
|
result = rb_catch_protect(p.list, exec_recursive_i, (VALUE)&p, &state);
|
2014-12-10 03:38:43 +03:00
|
|
|
if (!recursive_pop(p.list, p.objid, p.pairid)) goto invalid;
|
|
|
|
if (!recursive_pop(p.list, ID2SYM(recursive_key), 0)) goto invalid;
|
2017-12-06 06:16:08 +03:00
|
|
|
if (state != TAG_NONE) EC_JUMP_TAG(GET_EC(), state);
|
2009-09-16 01:30:50 +04:00
|
|
|
if (result == p.list) {
|
* compile.c, cont.c, gc.c, insns.def, iseq.c, iseq.h, process.c,
thread.c, vm.c, vm_core.h, vm_dump.c, vm_eval.c,
vm_insnhelper.c, vm_method.c, template/insns_info.inc.tmpl,
tool/instruction.rb: fixed types.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@25030 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2009-09-22 00:58:26 +04:00
|
|
|
result = (*func)(obj, arg, TRUE);
|
2009-09-16 01:30:50 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2015-03-03 18:45:00 +03:00
|
|
|
volatile VALUE ret = Qundef;
|
2013-12-13 19:22:18 +04:00
|
|
|
recursive_push(p.list, p.objid, p.pairid);
|
2017-12-06 06:16:08 +03:00
|
|
|
EC_PUSH_TAG(GET_EC());
|
|
|
|
if ((state = EC_EXEC_TAG()) == TAG_NONE) {
|
2015-03-03 18:45:00 +03:00
|
|
|
ret = (*func)(obj, arg, FALSE);
|
2013-12-13 19:22:18 +04:00
|
|
|
}
|
2017-12-06 06:16:08 +03:00
|
|
|
EC_POP_TAG();
|
2014-12-10 03:38:43 +03:00
|
|
|
if (!recursive_pop(p.list, p.objid, p.pairid)) {
|
|
|
|
invalid:
|
|
|
|
rb_raise(rb_eTypeError, "invalid inspect_tbl pair_list "
|
|
|
|
"for %+"PRIsVALUE" in %+"PRIsVALUE,
|
|
|
|
sym, rb_thread_current());
|
|
|
|
}
|
2017-12-06 06:16:08 +03:00
|
|
|
if (state != TAG_NONE) EC_JUMP_TAG(GET_EC(), state);
|
2015-03-03 18:45:00 +03:00
|
|
|
result = ret;
|
2007-02-05 15:21:01 +03:00
|
|
|
}
|
|
|
|
}
|
2011-02-20 10:26:48 +03:00
|
|
|
*(volatile struct exec_recursive_params *)&p;
|
|
|
|
return result;
|
2007-02-05 15:21:01 +03:00
|
|
|
}
|
|
|
|
|
2009-09-13 08:45:54 +04:00
|
|
|
/*
|
|
|
|
* Calls func(obj, arg, recursive), where recursive is non-zero if the
|
|
|
|
* current method is called recursively on obj
|
|
|
|
*/
|
|
|
|
|
2009-05-24 17:48:23 +04:00
|
|
|
VALUE
|
|
|
|
rb_exec_recursive(VALUE (*func) (VALUE, VALUE, int), VALUE obj, VALUE arg)
|
|
|
|
{
|
2009-09-16 01:30:50 +04:00
|
|
|
return exec_recursive(func, obj, 0, arg, 0);
|
2009-05-24 17:48:23 +04:00
|
|
|
}
|
|
|
|
|
2009-09-13 08:45:54 +04:00
|
|
|
/*
|
|
|
|
* Calls func(obj, arg, recursive), where recursive is non-zero if the
|
2009-09-16 01:30:50 +04:00
|
|
|
* current method is called recursively on the ordered pair <obj, paired_obj>
|
2009-09-13 08:45:54 +04:00
|
|
|
*/
|
|
|
|
|
2009-05-24 17:48:23 +04:00
|
|
|
VALUE
|
|
|
|
rb_exec_recursive_paired(VALUE (*func) (VALUE, VALUE, int), VALUE obj, VALUE paired_obj, VALUE arg)
|
|
|
|
{
|
2009-09-16 01:30:50 +04:00
|
|
|
return exec_recursive(func, obj, rb_obj_id(paired_obj), arg, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If recursion is detected on the current method and obj, the outermost
|
|
|
|
* func will be called with (obj, arg, Qtrue). All inner func will be
|
|
|
|
* short-circuited using throw.
|
|
|
|
*/
|
|
|
|
|
|
|
|
VALUE
|
|
|
|
rb_exec_recursive_outer(VALUE (*func) (VALUE, VALUE, int), VALUE obj, VALUE arg)
|
|
|
|
{
|
|
|
|
return exec_recursive(func, obj, 0, arg, 1);
|
2009-05-24 17:48:23 +04:00
|
|
|
}
|
|
|
|
|
2013-10-09 08:53:18 +04:00
|
|
|
/*
|
|
|
|
* If recursion is detected on the current method, obj and paired_obj,
|
|
|
|
* the outermost func will be called with (obj, arg, Qtrue). All inner
|
|
|
|
* func will be short-circuited using throw.
|
|
|
|
*/
|
|
|
|
|
|
|
|
VALUE
|
|
|
|
rb_exec_recursive_paired_outer(VALUE (*func) (VALUE, VALUE, int), VALUE obj, VALUE paired_obj, VALUE arg)
|
|
|
|
{
|
|
|
|
return exec_recursive(func, obj, rb_obj_id(paired_obj), arg, 1);
|
|
|
|
}
|
|
|
|
|
2009-09-17 08:51:33 +04:00
|
|
|
/*
|
|
|
|
* call-seq:
|
2013-02-23 11:52:46 +04:00
|
|
|
* thread.backtrace -> array
|
|
|
|
*
|
|
|
|
* Returns the current backtrace of the target thread.
|
2009-09-17 08:51:33 +04:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2009-06-14 09:59:23 +04:00
|
|
|
static VALUE
|
2012-11-19 10:07:06 +04:00
|
|
|
rb_thread_backtrace_m(int argc, VALUE *argv, VALUE thval)
|
2009-06-14 09:59:23 +04:00
|
|
|
{
|
2013-03-06 10:30:03 +04:00
|
|
|
return rb_vm_thread_backtrace(argc, argv, thval);
|
2012-11-19 10:07:06 +04:00
|
|
|
}
|
|
|
|
|
2013-02-23 11:52:46 +04:00
|
|
|
/* call-seq:
|
|
|
|
* thread.backtrace_locations(*args) -> array or nil
|
|
|
|
*
|
|
|
|
* Returns the execution stack for the target thread---an array containing
|
|
|
|
* backtrace location objects.
|
|
|
|
*
|
|
|
|
* See Thread::Backtrace::Location for more information.
|
|
|
|
*
|
2013-02-24 09:23:51 +04:00
|
|
|
* This method behaves similarly to Kernel#caller_locations except it applies
|
|
|
|
* to a specific thread.
|
2013-02-23 11:52:46 +04:00
|
|
|
*/
|
2012-11-19 10:07:06 +04:00
|
|
|
static VALUE
|
|
|
|
rb_thread_backtrace_locations_m(int argc, VALUE *argv, VALUE thval)
|
|
|
|
{
|
2013-03-06 10:30:03 +04:00
|
|
|
return rb_vm_thread_backtrace_locations(argc, argv, thval);
|
2009-06-14 09:59:23 +04:00
|
|
|
}
|
|
|
|
|
2010-05-08 08:50:09 +04:00
|
|
|
/*
|
|
|
|
* Document-class: ThreadError
|
|
|
|
*
|
|
|
|
* Raised when an invalid operation is attempted on a thread.
|
|
|
|
*
|
|
|
|
* For example, when no other thread has been started:
|
|
|
|
*
|
|
|
|
* Thread.stop
|
|
|
|
*
|
2013-02-28 00:36:59 +04:00
|
|
|
* This will raises the following exception:
|
2010-05-08 08:50:09 +04:00
|
|
|
*
|
|
|
|
* ThreadError: stopping only thread
|
2013-02-28 00:36:59 +04:00
|
|
|
* note: use sleep to stop forever
|
2007-02-05 15:21:01 +03:00
|
|
|
*/
|
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
void
|
2007-02-05 15:21:01 +03:00
|
|
|
Init_Thread(void)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2008-06-09 13:25:32 +04:00
|
|
|
#undef rb_intern
|
2008-08-16 04:20:31 +04:00
|
|
|
#define rb_intern(str) rb_intern_const(str)
|
2008-06-09 13:25:32 +04:00
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
VALUE cThGroup;
|
2010-11-27 23:15:59 +03:00
|
|
|
rb_thread_t *th = GET_THREAD();
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2012-12-23 14:18:58 +04:00
|
|
|
sym_never = ID2SYM(rb_intern("never"));
|
|
|
|
sym_immediate = ID2SYM(rb_intern("immediate"));
|
|
|
|
sym_on_blocking = ID2SYM(rb_intern("on_blocking"));
|
2013-05-02 11:55:50 +04:00
|
|
|
id_locals = rb_intern("locals");
|
2012-12-23 14:18:58 +04:00
|
|
|
|
2007-12-05 10:18:52 +03:00
|
|
|
rb_define_singleton_method(rb_cThread, "new", thread_s_new, -1);
|
|
|
|
rb_define_singleton_method(rb_cThread, "start", thread_start, -2);
|
|
|
|
rb_define_singleton_method(rb_cThread, "fork", thread_start, -2);
|
2007-02-05 15:21:01 +03:00
|
|
|
rb_define_singleton_method(rb_cThread, "main", rb_thread_s_main, 0);
|
|
|
|
rb_define_singleton_method(rb_cThread, "current", thread_s_current, 0);
|
|
|
|
rb_define_singleton_method(rb_cThread, "stop", rb_thread_stop, 0);
|
|
|
|
rb_define_singleton_method(rb_cThread, "kill", rb_thread_s_kill, 1);
|
|
|
|
rb_define_singleton_method(rb_cThread, "exit", rb_thread_exit, 0);
|
|
|
|
rb_define_singleton_method(rb_cThread, "pass", thread_s_pass, 0);
|
|
|
|
rb_define_singleton_method(rb_cThread, "list", rb_thread_list, 0);
|
|
|
|
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);
|
2016-06-06 03:25:38 +03:00
|
|
|
rb_define_singleton_method(rb_cThread, "report_on_exception", rb_thread_s_report_exc, 0);
|
|
|
|
rb_define_singleton_method(rb_cThread, "report_on_exception=", rb_thread_s_report_exc_set, 1);
|
2007-02-24 12:39:25 +03:00
|
|
|
#if THREAD_DEBUG < 0
|
|
|
|
rb_define_singleton_method(rb_cThread, "DEBUG", rb_thread_s_debug, 0);
|
|
|
|
rb_define_singleton_method(rb_cThread, "DEBUG=", rb_thread_s_debug_set, 1);
|
|
|
|
#endif
|
2012-12-23 14:18:58 +04:00
|
|
|
rb_define_singleton_method(rb_cThread, "handle_interrupt", rb_thread_s_handle_interrupt, 1);
|
|
|
|
rb_define_singleton_method(rb_cThread, "pending_interrupt?", rb_thread_s_pending_interrupt_p, -1);
|
|
|
|
rb_define_method(rb_cThread, "pending_interrupt?", rb_thread_pending_interrupt_p, -1);
|
2007-02-05 15:21:01 +03:00
|
|
|
|
2007-12-05 10:18:52 +03:00
|
|
|
rb_define_method(rb_cThread, "initialize", thread_initialize, -2);
|
2007-02-05 15:21:01 +03:00
|
|
|
rb_define_method(rb_cThread, "raise", thread_raise_m, -1);
|
|
|
|
rb_define_method(rb_cThread, "join", thread_join_m, -1);
|
|
|
|
rb_define_method(rb_cThread, "value", thread_value, 0);
|
|
|
|
rb_define_method(rb_cThread, "kill", rb_thread_kill, 0);
|
|
|
|
rb_define_method(rb_cThread, "terminate", rb_thread_kill, 0);
|
|
|
|
rb_define_method(rb_cThread, "exit", rb_thread_kill, 0);
|
|
|
|
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, "[]", rb_thread_aref, 1);
|
|
|
|
rb_define_method(rb_cThread, "[]=", rb_thread_aset, 2);
|
2017-02-22 10:16:13 +03:00
|
|
|
rb_define_method(rb_cThread, "fetch", rb_thread_fetch, -1);
|
2007-02-05 15:21:01 +03:00
|
|
|
rb_define_method(rb_cThread, "key?", rb_thread_key_p, 1);
|
|
|
|
rb_define_method(rb_cThread, "keys", rb_thread_keys, 0);
|
|
|
|
rb_define_method(rb_cThread, "priority", rb_thread_priority, 0);
|
|
|
|
rb_define_method(rb_cThread, "priority=", rb_thread_priority_set, 1);
|
|
|
|
rb_define_method(rb_cThread, "status", rb_thread_status, 0);
|
2012-10-29 21:22:36 +04:00
|
|
|
rb_define_method(rb_cThread, "thread_variable_get", rb_thread_variable_get, 1);
|
|
|
|
rb_define_method(rb_cThread, "thread_variable_set", rb_thread_variable_set, 2);
|
|
|
|
rb_define_method(rb_cThread, "thread_variables", rb_thread_variables, 0);
|
|
|
|
rb_define_method(rb_cThread, "thread_variable?", rb_thread_variable_p, 1);
|
2007-02-05 15:21:01 +03:00
|
|
|
rb_define_method(rb_cThread, "alive?", rb_thread_alive_p, 0);
|
|
|
|
rb_define_method(rb_cThread, "stop?", rb_thread_stop_p, 0);
|
|
|
|
rb_define_method(rb_cThread, "abort_on_exception", rb_thread_abort_exc, 0);
|
|
|
|
rb_define_method(rb_cThread, "abort_on_exception=", rb_thread_abort_exc_set, 1);
|
2016-06-06 03:25:38 +03:00
|
|
|
rb_define_method(rb_cThread, "report_on_exception", rb_thread_report_exc, 0);
|
|
|
|
rb_define_method(rb_cThread, "report_on_exception=", rb_thread_report_exc_set, 1);
|
2007-02-05 15:21:01 +03:00
|
|
|
rb_define_method(rb_cThread, "safe_level", rb_thread_safe_level, 0);
|
|
|
|
rb_define_method(rb_cThread, "group", rb_thread_group, 0);
|
2012-11-19 10:07:06 +04:00
|
|
|
rb_define_method(rb_cThread, "backtrace", rb_thread_backtrace_m, -1);
|
|
|
|
rb_define_method(rb_cThread, "backtrace_locations", rb_thread_backtrace_locations_m, -1);
|
2007-02-05 15:21:01 +03:00
|
|
|
|
2015-06-13 11:39:30 +03:00
|
|
|
rb_define_method(rb_cThread, "name", rb_thread_getname, 0);
|
|
|
|
rb_define_method(rb_cThread, "name=", rb_thread_setname, 1);
|
2017-08-10 06:20:39 +03:00
|
|
|
rb_define_method(rb_cThread, "to_s", rb_thread_to_s, 0);
|
|
|
|
rb_define_alias(rb_cThread, "inspect", "to_s");
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2017-04-09 08:09:44 +03:00
|
|
|
rb_vm_register_special_exception(ruby_error_stream_closed, rb_eIOError,
|
|
|
|
"stream closed in another thread");
|
2011-02-12 08:44:23 +03:00
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
cThGroup = rb_define_class("ThreadGroup", rb_cObject);
|
|
|
|
rb_define_alloc_func(cThGroup, thgroup_s_alloc);
|
|
|
|
rb_define_method(cThGroup, "list", thgroup_list, 0);
|
|
|
|
rb_define_method(cThGroup, "enclose", thgroup_enclose, 0);
|
|
|
|
rb_define_method(cThGroup, "enclosed?", thgroup_enclosed_p, 0);
|
|
|
|
rb_define_method(cThGroup, "add", thgroup_add, 1);
|
2007-02-05 15:21:01 +03:00
|
|
|
|
|
|
|
{
|
|
|
|
th->thgroup = th->vm->thgroup_default = rb_obj_alloc(cThGroup);
|
|
|
|
rb_define_const(cThGroup, "Default", th->thgroup);
|
|
|
|
}
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2007-02-05 15:21:01 +03:00
|
|
|
recursive_key = rb_intern("__recursive_key__");
|
|
|
|
rb_eThreadError = rb_define_class("ThreadError", rb_eStandardError);
|
2007-02-07 16:44:11 +03:00
|
|
|
|
2007-04-19 14:37:08 +04:00
|
|
|
/* init thread core */
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
|
|
|
/* main thread setting */
|
|
|
|
{
|
2009-01-12 04:43:23 +03:00
|
|
|
/* acquire global vm lock */
|
2010-11-27 23:15:59 +03:00
|
|
|
gvl_init(th->vm);
|
|
|
|
gvl_acquire(th->vm, th);
|
2018-07-05 06:02:33 +03:00
|
|
|
rb_native_mutex_initialize(&th->vm->waitpid_lock);
|
2018-11-30 06:56:29 +03:00
|
|
|
rb_native_mutex_initialize(&th->vm->workqueue_lock);
|
mjit.c: merge MJIT infrastructure
that allows to JIT-compile Ruby methods by generating C code and
using C compiler. See the first comment of mjit.c to know what this
file does.
mjit.c is authored by Vladimir Makarov <vmakarov@redhat.com>.
After he invented great method JIT infrastructure for MRI as MJIT,
Lars Kanis <lars@greiz-reinsdorf.de> sent the patch to support MinGW
in MJIT. In addition to merging it, I ported pthread to Windows native
threads. Now this MJIT infrastructure can be compiled on Visual Studio.
This commit simplifies mjit.c to decrease code at initial merge. For
example, this commit does not provide multiple JIT threads support.
We can resurrect them later if we really want them, but I wanted to minimize
diff to make it easier to review this patch.
`/tmp/_mjitXXX` file is renamed to `/tmp/_ruby_mjitXXX` because non-Ruby
developers may not know the name "mjit" and the file name should make
sure it's from Ruby and not from some harmful programs. TODO: it may be
better to store this to some temporary directory which Ruby is already using
by Tempfile, if it's not bad for performance.
mjit.h: New. It has `mjit_exec` interface similar to `vm_exec`, which is
for triggering MJIT. This drops interface for AOT compared to the original
MJIT.
Makefile.in: define macros to let MJIT know the path of MJIT header.
Probably we can refactor this to reduce the number of macros (TODO).
win32/Makefile.sub: ditto.
common.mk: compile mjit.o and mjit_compile.o. Unlike original MJIT, this
commit separates MJIT infrastructure and JIT compiler code as independent
object files. As initial patch is NOT going to have ultra-fast JIT compiler,
it's likely to replace JIT compiler, e.g. original MJIT's compiler or some
future JIT impelementations which are not public now.
inits.c: define MJIT module. This is added because `MJIT.enabled?` was
necessary for testing.
test/lib/zombie_hunter.rb: skip if `MJIT.enabled?`. Obviously this
wouldn't work with current code when JIT is enabled.
test/ruby/test_io.rb: skip this too. This would make no sense with MJIT.
ruby.c: define MJIT CLI options. As major difference from original MJIT,
"-j:l"/"--jit:llvm" are renamed to "--jit-cc" because I want to support
not only gcc/clang but also cl.exe (Visual Studio) in the future. But it
takes only "--jit-cc=gcc", "--jit-cc=clang" for now. And only long "--jit"
options are allowed since some Ruby committers preferred it at Ruby
developers Meeting on January, and some of options are renamed.
This file also triggers to initialize MJIT thread and variables.
eval.c: finalize MJIT worker thread and variables.
test/ruby/test_rubyoptions.rb: fix number of CLI options for --jit.
thread_pthread.c: change for pthread abstraction in MJIT. Prefix rb_ for
functions which are used by other files.
thread_win32.c: ditto, for Windows. Those pthread porting is one of major
works that YARV-MJIT created, which is my fork of MJIT, in Feature 14235.
thread.c: follow rb_ prefix changes
vm.c: trigger MJIT call on VM invocation. Also trigger `mjit_mark` to avoid
SEGV by race between JIT and GC of ISeq. The improvement was provided by
wanabe <s.wanabe@gmail.com>.
In JIT compiler I created and am going to add in my next commit, I found
that having `mjit_exec` after `vm_loop_start:` is harmful because the
JIT-ed function doesn't proceed other ISeqs on RESTORE_REGS of leave insn.
Executing non-FINISH frame is unexpected for my JIT compiler and
`exception_handler` triggers executions of such ISeqs. So `mjit_exec`
here should be executed only when it directly comes from `vm_exec` call.
`RubyVM::MJIT` module and `.enabled?` method is added so that we can skip
some tests which don't expect JIT threads or compiler file descriptors.
vm_insnhelper.h: trigger MJIT on method calls during VM execution.
vm_core.h: add fields required for mjit.c. `bp` must be `cfp[6]` because
rb_control_frame_struct is likely to be casted to another struct. The
last position is the safest place to add the new field.
vm_insnhelper.c: save initial value of cfp->ep as cfp->bp. This is an
optimization which are done in both MJIT and YARV-MJIT. So this change
is added in this commit. Calculating bp from ep is a little heavy work,
so bp is kind of cache for it.
iseq.c: notify ISeq GC to MJIT. We should know which iseq in MJIT queue
is GCed to avoid SEGV. TODO: unload some GCed units in some safe way.
gc.c: add hooks so that MJIT can wait GC, and vice versa. Simultaneous
JIT and GC executions may cause SEGV and so we should synchronize them.
cont.c: save continuation information in MJIT worker. As MJIT shouldn't
unload JIT-ed code which is being used, MJIT wants to know full list of
saved execution contexts for continuation and detect ISeqs in use.
mjit_compile.c: added empty JIT compiler so that you can reuse this commit
to build your own JIT compiler. This commit tries to compile ISeqs but
all of them are considered as not supported in this commit. So you can't
use JIT compiler in this commit yet while we added --jit option now.
Patch author: Vladimir Makarov <vmakarov@redhat.com>.
Contributors:
Takashi Kokubun <takashikkbn@gmail.com>.
wanabe <s.wanabe@gmail.com>.
Lars Kanis <lars@greiz-reinsdorf.de>.
Part of Feature 12589 and 14235.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@62189 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-02-04 09:58:09 +03:00
|
|
|
rb_native_mutex_initialize(&th->interrupt_lock);
|
2012-07-18 09:46:40 +04:00
|
|
|
|
2012-12-23 14:18:58 +04:00
|
|
|
th->pending_interrupt_queue = rb_ary_tmp_new(0);
|
|
|
|
th->pending_interrupt_queue_checked = 0;
|
|
|
|
th->pending_interrupt_mask_stack = rb_ary_tmp_new(0);
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rb_thread_create_timer_thread();
|
2008-06-01 23:55:25 +04:00
|
|
|
|
2011-05-21 23:12:10 +04:00
|
|
|
/* suppress warnings on cygwin, mingw and mswin.*/
|
2008-06-01 23:55:25 +04:00
|
|
|
(void)native_mutex_trylock;
|
2015-08-22 02:36:23 +03:00
|
|
|
|
2015-09-01 12:08:42 +03:00
|
|
|
Init_thread_sync();
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
2007-07-22 11:07:12 +04:00
|
|
|
int
|
2007-12-25 10:28:51 +03:00
|
|
|
ruby_native_thread_p(void)
|
2007-02-08 14:51:40 +03:00
|
|
|
{
|
2007-08-18 12:40:13 +04:00
|
|
|
rb_thread_t *th = ruby_thread_from_native();
|
|
|
|
|
* compile.c, cont.c, gc.c, insns.def, iseq.c, iseq.h, process.c,
thread.c, vm.c, vm_core.h, vm_dump.c, vm_eval.c,
vm_insnhelper.c, vm_method.c, template/insns_info.inc.tmpl,
tool/instruction.rb: fixed types.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@25030 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2009-09-22 00:58:26 +04:00
|
|
|
return th != 0;
|
2007-02-08 14:51:40 +03:00
|
|
|
}
|
2008-06-12 17:01:38 +04:00
|
|
|
|
2014-05-28 05:48:11 +04:00
|
|
|
static void
|
2016-06-13 15:34:06 +03:00
|
|
|
debug_deadlock_check(rb_vm_t *vm, VALUE msg)
|
2008-06-12 17:01:38 +04:00
|
|
|
{
|
2014-05-29 19:45:25 +04:00
|
|
|
rb_thread_t *th = 0;
|
2016-06-13 15:34:06 +03:00
|
|
|
VALUE sep = rb_str_new_cstr("\n ");
|
2008-06-19 18:18:46 +04:00
|
|
|
|
2016-06-13 15:34:06 +03:00
|
|
|
rb_str_catf(msg, "\n%d threads, %d sleeps current:%p main thread:%p\n",
|
2018-01-02 09:41:40 +03:00
|
|
|
vm_living_thread_num(vm), vm->sleeper, (void *)GET_THREAD(), (void *)vm->main_thread);
|
2014-05-28 05:48:11 +04:00
|
|
|
list_for_each(&vm->living_threads, th, vmlt_node) {
|
2016-06-15 04:44:43 +03:00
|
|
|
rb_str_catf(msg, "* %+"PRIsVALUE"\n rb_thread_t:%p "
|
|
|
|
"native:%"PRI_THREAD_ID" int:%u",
|
2018-01-02 09:41:40 +03:00
|
|
|
th->self, (void *)th, thread_id_str(th), th->ec->interrupt_flag);
|
2014-05-28 05:48:11 +04:00
|
|
|
if (th->locking_mutex) {
|
2018-08-21 04:01:42 +03:00
|
|
|
rb_mutex_t *mutex = mutex_ptr(th->locking_mutex);
|
reduce rb_mutex_t size from 160 to 80 bytes on 64-bit
Instead of relying on a native condition variable and mutex for
every Ruby Mutex object, use a doubly linked-list to implement a
waiter queue in the Mutex. The immediate benefit of this is
reducing the size of every Mutex object, as some projects have
many objects requiring synchronization.
In the future, this technique using a linked-list and on-stack
list node (struct mutex_waiter) should allow us to easily
transition to M:N threading model, as we can avoid the native
thread dependency to implement Mutex.
We already do something similar for autoload in variable.c,
and this was inspired by the Linux kernel wait queue (as
ccan/list is inspired by the Linux kernel linked-list).
Finaly, there are big performance improvements for Mutex
benchmarks, especially in contended cases:
measure target: real
name |trunk |built
----------------|------:|------:
loop_whileloop2 | 0.149| 0.148
vm2_mutex* | 0.893| 0.651
vm_thread_mutex1| 0.809| 0.624
vm_thread_mutex2| 2.608| 0.628
vm_thread_mutex3| 28.227| 0.881
Speedup ratio: compare with the result of `trunk' (greater is better)
name |built
----------------|------:
loop_whileloop2 | 1.002
vm2_mutex* | 1.372
vm_thread_mutex1| 1.297
vm_thread_mutex2| 4.149
vm_thread_mutex3| 32.044
Tested on AMD FX-8320 8-core at 3.5GHz
* thread_sync.c (struct mutex_waiter): new on-stack struct
(struct rb_mutex_struct): remove native lock/cond, use ccan/list
(rb_mutex_num_waiting): new function for debug_deadlock_check
(mutex_free): remove native_*_destroy
(mutex_alloc): initialize waitq, remove native_*_initialize
(rb_mutex_trylock): remove native_mutex_{lock,unlock}
(lock_func): remove
(lock_interrupt): remove
(rb_mutex_lock): rewrite waiting path to use native_sleep + ccan/list
(rb_mutex_unlock_th): rewrite to wake up from native_sleep
using rb_threadptr_interrupt
(rb_mutex_abandon_all): empty waitq
* thread.c (debug_deadlock_check): update for new struct
(rb_check_deadlock): ditto
[ruby-core:80913] [Feature #13517]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@58604 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2017-05-08 03:18:53 +03:00
|
|
|
rb_str_catf(msg, " mutex:%p cond:%"PRIuSIZE,
|
2018-01-02 09:41:40 +03:00
|
|
|
(void *)mutex->th, rb_mutex_num_waiting(mutex));
|
2016-06-13 15:34:06 +03:00
|
|
|
}
|
|
|
|
{
|
|
|
|
rb_thread_list_t *list = th->join_list;
|
|
|
|
while (list) {
|
2018-01-02 09:41:40 +03:00
|
|
|
rb_str_catf(msg, "\n depended by: tb_thread_id:%p", (void *)list->th);
|
2016-06-13 15:34:06 +03:00
|
|
|
list = list->next;
|
|
|
|
}
|
|
|
|
}
|
2016-06-15 04:44:43 +03:00
|
|
|
rb_str_catf(msg, "\n ");
|
2017-10-28 16:22:04 +03:00
|
|
|
rb_str_concat(msg, rb_ary_join(rb_ec_backtrace_str_ary(th->ec, 0, 0), sep));
|
2016-06-13 15:34:06 +03:00
|
|
|
rb_str_catf(msg, "\n");
|
2014-05-28 05:48:11 +04:00
|
|
|
}
|
|
|
|
}
|
2008-06-19 18:18:46 +04:00
|
|
|
|
2008-06-12 17:01:38 +04:00
|
|
|
static void
|
|
|
|
rb_check_deadlock(rb_vm_t *vm)
|
|
|
|
{
|
|
|
|
int found = 0;
|
2014-05-29 19:45:25 +04:00
|
|
|
rb_thread_t *th = 0;
|
2008-06-12 17:01:38 +04:00
|
|
|
|
2008-06-19 18:18:46 +04:00
|
|
|
if (vm_living_thread_num(vm) > vm->sleeper) return;
|
|
|
|
if (vm_living_thread_num(vm) < vm->sleeper) rb_bug("sleeper must not be more than vm_living_thread_num(vm)");
|
2012-09-09 10:27:02 +04:00
|
|
|
if (patrol_thread && patrol_thread != GET_THREAD()) return;
|
2008-06-12 17:01:38 +04:00
|
|
|
|
2014-05-28 05:48:11 +04:00
|
|
|
list_for_each(&vm->living_threads, th, vmlt_node) {
|
2017-11-06 10:44:28 +03:00
|
|
|
if (th->status != THREAD_STOPPED_FOREVER || RUBY_VM_INTERRUPTED(th->ec)) {
|
2014-05-28 05:48:11 +04:00
|
|
|
found = 1;
|
|
|
|
}
|
|
|
|
else if (th->locking_mutex) {
|
2018-08-21 04:01:42 +03:00
|
|
|
rb_mutex_t *mutex = mutex_ptr(th->locking_mutex);
|
2014-05-28 05:48:11 +04:00
|
|
|
|
reduce rb_mutex_t size from 160 to 80 bytes on 64-bit
Instead of relying on a native condition variable and mutex for
every Ruby Mutex object, use a doubly linked-list to implement a
waiter queue in the Mutex. The immediate benefit of this is
reducing the size of every Mutex object, as some projects have
many objects requiring synchronization.
In the future, this technique using a linked-list and on-stack
list node (struct mutex_waiter) should allow us to easily
transition to M:N threading model, as we can avoid the native
thread dependency to implement Mutex.
We already do something similar for autoload in variable.c,
and this was inspired by the Linux kernel wait queue (as
ccan/list is inspired by the Linux kernel linked-list).
Finaly, there are big performance improvements for Mutex
benchmarks, especially in contended cases:
measure target: real
name |trunk |built
----------------|------:|------:
loop_whileloop2 | 0.149| 0.148
vm2_mutex* | 0.893| 0.651
vm_thread_mutex1| 0.809| 0.624
vm_thread_mutex2| 2.608| 0.628
vm_thread_mutex3| 28.227| 0.881
Speedup ratio: compare with the result of `trunk' (greater is better)
name |built
----------------|------:
loop_whileloop2 | 1.002
vm2_mutex* | 1.372
vm_thread_mutex1| 1.297
vm_thread_mutex2| 4.149
vm_thread_mutex3| 32.044
Tested on AMD FX-8320 8-core at 3.5GHz
* thread_sync.c (struct mutex_waiter): new on-stack struct
(struct rb_mutex_struct): remove native lock/cond, use ccan/list
(rb_mutex_num_waiting): new function for debug_deadlock_check
(mutex_free): remove native_*_destroy
(mutex_alloc): initialize waitq, remove native_*_initialize
(rb_mutex_trylock): remove native_mutex_{lock,unlock}
(lock_func): remove
(lock_interrupt): remove
(rb_mutex_lock): rewrite waiting path to use native_sleep + ccan/list
(rb_mutex_unlock_th): rewrite to wake up from native_sleep
using rb_threadptr_interrupt
(rb_mutex_abandon_all): empty waitq
* thread.c (debug_deadlock_check): update for new struct
(rb_check_deadlock): ditto
[ruby-core:80913] [Feature #13517]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@58604 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2017-05-08 03:18:53 +03:00
|
|
|
if (mutex->th == th || (!mutex->th && !list_empty(&mutex->waitq))) {
|
2014-05-28 05:48:11 +04:00
|
|
|
found = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (found)
|
|
|
|
break;
|
|
|
|
}
|
2008-06-12 17:01:38 +04:00
|
|
|
|
|
|
|
if (!found) {
|
|
|
|
VALUE argv[2];
|
|
|
|
argv[0] = rb_eFatal;
|
2012-04-23 19:27:00 +04:00
|
|
|
argv[1] = rb_str_new2("No live threads left. Deadlock?");
|
2016-06-13 15:34:06 +03:00
|
|
|
debug_deadlock_check(vm, argv[1]);
|
2009-08-21 19:14:29 +04:00
|
|
|
vm->sleeper--;
|
2009-06-08 20:14:06 +04:00
|
|
|
rb_threadptr_raise(vm->main_thread, 2, argv);
|
2008-06-12 17:01:38 +04:00
|
|
|
}
|
|
|
|
}
|
2008-07-03 16:55:12 +04:00
|
|
|
|
2017-12-06 09:39:05 +03:00
|
|
|
static void
|
|
|
|
update_line_coverage(VALUE data, const rb_trace_arg_t *trace_arg)
|
|
|
|
{
|
2018-10-20 08:33:04 +03:00
|
|
|
const rb_control_frame_t *cfp = GET_EC()->cfp;
|
|
|
|
VALUE coverage = rb_iseq_coverage(cfp->iseq);
|
2017-12-06 09:39:05 +03:00
|
|
|
if (RB_TYPE_P(coverage, T_ARRAY) && !RBASIC_CLASS(coverage)) {
|
|
|
|
VALUE lines = RARRAY_AREF(coverage, COVERAGE_INDEX_LINES);
|
|
|
|
if (lines) {
|
2018-08-22 14:09:47 +03:00
|
|
|
long line = rb_sourceline() - 1;
|
2017-12-06 09:39:05 +03:00
|
|
|
long count;
|
|
|
|
VALUE num;
|
2018-10-20 08:33:04 +03:00
|
|
|
void rb_iseq_clear_event_flags(const rb_iseq_t *iseq, size_t pos, rb_event_flag_t reset);
|
|
|
|
if (GET_VM()->coverage_mode & COVERAGE_TARGET_ONESHOT_LINES) {
|
|
|
|
rb_iseq_clear_event_flags(cfp->iseq, cfp->pc - cfp->iseq->body->iseq_encoded - 1, RUBY_EVENT_COVERAGE_LINE);
|
|
|
|
rb_ary_push(lines, LONG2FIX(line + 1));
|
|
|
|
return;
|
|
|
|
}
|
2017-12-06 09:39:05 +03:00
|
|
|
if (line >= RARRAY_LEN(lines)) { /* no longer tracked */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
num = RARRAY_AREF(lines, line);
|
|
|
|
if (!FIXNUM_P(num)) return;
|
|
|
|
count = FIX2LONG(num) + 1;
|
|
|
|
if (POSFIXABLE(count)) {
|
|
|
|
RARRAY_ASET(lines, line, LONG2FIX(count));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-07-03 16:55:12 +04:00
|
|
|
static void
|
2017-12-06 10:04:48 +03:00
|
|
|
update_branch_coverage(VALUE data, const rb_trace_arg_t *trace_arg)
|
2008-07-03 16:55:12 +04:00
|
|
|
{
|
2018-10-20 13:45:48 +03:00
|
|
|
const rb_control_frame_t *cfp = GET_EC()->cfp;
|
|
|
|
VALUE coverage = rb_iseq_coverage(cfp->iseq);
|
2016-03-10 08:32:48 +03:00
|
|
|
if (RB_TYPE_P(coverage, T_ARRAY) && !RBASIC_CLASS(coverage)) {
|
2017-12-06 10:04:48 +03:00
|
|
|
VALUE branches = RARRAY_AREF(coverage, COVERAGE_INDEX_BRANCHES);
|
|
|
|
if (branches) {
|
2018-10-20 13:45:48 +03:00
|
|
|
long pc = cfp->pc - cfp->iseq->body->iseq_encoded - 1;
|
|
|
|
long idx = FIX2INT(RARRAY_AREF(ISEQ_PC2BRANCHINDEX(cfp->iseq), pc)), count;
|
2017-12-06 10:04:48 +03:00
|
|
|
VALUE counters = RARRAY_AREF(branches, 1);
|
|
|
|
VALUE num = RARRAY_AREF(counters, idx);
|
|
|
|
count = FIX2LONG(num) + 1;
|
|
|
|
if (POSFIXABLE(count)) {
|
|
|
|
RARRAY_ASET(counters, idx, LONG2FIX(count));
|
2017-09-14 06:25:36 +03:00
|
|
|
}
|
2017-12-05 10:16:42 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const rb_method_entry_t *
|
2017-12-05 11:56:50 +03:00
|
|
|
rb_resolve_me_location(const rb_method_entry_t *me, VALUE resolved_location[5])
|
2017-12-05 10:16:42 +03:00
|
|
|
{
|
2018-01-09 11:45:35 +03:00
|
|
|
VALUE path, beg_pos_lineno, beg_pos_column, end_pos_lineno, end_pos_column;
|
2017-12-05 10:16:42 +03:00
|
|
|
|
|
|
|
retry:
|
|
|
|
switch (me->def->type) {
|
|
|
|
case VM_METHOD_TYPE_ISEQ: {
|
2017-12-05 11:56:50 +03:00
|
|
|
const rb_iseq_t *iseq = me->def->body.iseq.iseqptr;
|
|
|
|
rb_iseq_location_t *loc = &iseq->body->location;
|
|
|
|
path = rb_iseq_path(iseq);
|
2018-01-09 11:45:35 +03:00
|
|
|
beg_pos_lineno = INT2FIX(loc->code_location.beg_pos.lineno);
|
|
|
|
beg_pos_column = INT2FIX(loc->code_location.beg_pos.column);
|
|
|
|
end_pos_lineno = INT2FIX(loc->code_location.end_pos.lineno);
|
|
|
|
end_pos_column = INT2FIX(loc->code_location.end_pos.column);
|
2017-12-05 10:16:42 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case VM_METHOD_TYPE_BMETHOD: {
|
2018-11-26 21:16:54 +03:00
|
|
|
const rb_iseq_t *iseq = rb_proc_get_iseq(me->def->body.bmethod.proc, 0);
|
2017-12-05 10:16:42 +03:00
|
|
|
if (iseq) {
|
2017-12-05 11:56:50 +03:00
|
|
|
rb_iseq_location_t *loc;
|
2017-12-05 10:16:42 +03:00
|
|
|
rb_iseq_check(iseq);
|
|
|
|
path = rb_iseq_path(iseq);
|
2017-12-05 11:56:50 +03:00
|
|
|
loc = &iseq->body->location;
|
2018-01-09 11:45:35 +03:00
|
|
|
beg_pos_lineno = INT2FIX(loc->code_location.beg_pos.lineno);
|
|
|
|
beg_pos_column = INT2FIX(loc->code_location.beg_pos.column);
|
|
|
|
end_pos_lineno = INT2FIX(loc->code_location.end_pos.lineno);
|
|
|
|
end_pos_column = INT2FIX(loc->code_location.end_pos.column);
|
2017-09-14 08:12:34 +03:00
|
|
|
break;
|
2008-07-03 16:55:12 +04:00
|
|
|
}
|
2017-12-05 10:16:42 +03:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
case VM_METHOD_TYPE_ALIAS:
|
|
|
|
me = me->def->body.alias.original_me;
|
|
|
|
goto retry;
|
|
|
|
case VM_METHOD_TYPE_REFINED:
|
|
|
|
me = me->def->body.refined.orig_me;
|
|
|
|
if (!me) return NULL;
|
|
|
|
goto retry;
|
|
|
|
default:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* found */
|
|
|
|
if (RB_TYPE_P(path, T_ARRAY)) {
|
|
|
|
path = rb_ary_entry(path, 1);
|
|
|
|
if (!RB_TYPE_P(path, T_STRING)) return NULL; /* just for the case... */
|
|
|
|
}
|
|
|
|
if (resolved_location) {
|
|
|
|
resolved_location[0] = path;
|
2018-01-09 11:45:35 +03:00
|
|
|
resolved_location[1] = beg_pos_lineno;
|
|
|
|
resolved_location[2] = beg_pos_column;
|
|
|
|
resolved_location[3] = end_pos_lineno;
|
|
|
|
resolved_location[4] = end_pos_column;
|
2017-12-05 10:16:42 +03:00
|
|
|
}
|
|
|
|
return me;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
update_method_coverage(VALUE me2counter, rb_trace_arg_t *trace_arg)
|
|
|
|
{
|
|
|
|
const rb_control_frame_t *cfp = GET_EC()->cfp;
|
|
|
|
const rb_callable_method_entry_t *cme = rb_vm_frame_method_entry(cfp);
|
|
|
|
const rb_method_entry_t *me = (const rb_method_entry_t *)cme;
|
|
|
|
VALUE rcount;
|
|
|
|
long count;
|
|
|
|
|
|
|
|
me = rb_resolve_me_location(me, 0);
|
|
|
|
if (!me) return;
|
|
|
|
|
|
|
|
rcount = rb_hash_aref(me2counter, (VALUE) me);
|
|
|
|
count = FIXNUM_P(rcount) ? FIX2LONG(rcount) + 1 : 1;
|
|
|
|
if (POSFIXABLE(count)) {
|
|
|
|
rb_hash_aset(me2counter, (VALUE) me, LONG2FIX(count));
|
2008-07-03 16:55:12 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-07-08 19:13:22 +04:00
|
|
|
VALUE
|
|
|
|
rb_get_coverages(void)
|
2008-07-08 17:57:06 +04:00
|
|
|
{
|
2008-07-08 19:13:22 +04:00
|
|
|
return GET_VM()->coverages;
|
2008-07-08 17:57:06 +04:00
|
|
|
}
|
|
|
|
|
2018-10-20 08:33:04 +03:00
|
|
|
int
|
|
|
|
rb_get_coverage_mode(void)
|
|
|
|
{
|
|
|
|
return GET_VM()->coverage_mode;
|
|
|
|
}
|
|
|
|
|
2008-07-08 19:13:22 +04:00
|
|
|
void
|
2017-12-05 10:16:42 +03:00
|
|
|
rb_set_coverages(VALUE coverages, int mode, VALUE me2counter)
|
2008-07-08 17:57:06 +04:00
|
|
|
{
|
2008-07-08 19:13:22 +04:00
|
|
|
GET_VM()->coverages = coverages;
|
2017-09-03 17:26:06 +03:00
|
|
|
GET_VM()->coverage_mode = mode;
|
2017-12-20 07:24:14 +03:00
|
|
|
rb_add_event_hook2((rb_event_hook_func_t) update_line_coverage, RUBY_EVENT_COVERAGE_LINE, Qnil, RUBY_EVENT_HOOK_FLAG_SAFE | RUBY_EVENT_HOOK_FLAG_RAW_ARG);
|
2017-12-06 10:04:48 +03:00
|
|
|
if (mode & COVERAGE_TARGET_BRANCHES) {
|
2017-12-06 10:19:17 +03:00
|
|
|
rb_add_event_hook2((rb_event_hook_func_t) update_branch_coverage, RUBY_EVENT_COVERAGE_BRANCH, Qnil, RUBY_EVENT_HOOK_FLAG_SAFE | RUBY_EVENT_HOOK_FLAG_RAW_ARG);
|
2017-12-06 10:04:48 +03:00
|
|
|
}
|
2017-12-05 10:16:42 +03:00
|
|
|
if (mode & COVERAGE_TARGET_METHODS) {
|
|
|
|
rb_add_event_hook2((rb_event_hook_func_t) update_method_coverage, RUBY_EVENT_CALL, me2counter, RUBY_EVENT_HOOK_FLAG_SAFE | RUBY_EVENT_HOOK_FLAG_RAW_ARG);
|
|
|
|
}
|
2008-07-08 17:57:06 +04:00
|
|
|
}
|
|
|
|
|
2016-04-06 17:29:17 +03:00
|
|
|
/* Make coverage arrays empty so old covered files are no longer tracked. */
|
2008-07-03 16:55:12 +04:00
|
|
|
void
|
2008-07-08 19:13:22 +04:00
|
|
|
rb_reset_coverages(void)
|
2008-07-03 16:55:12 +04:00
|
|
|
{
|
2018-10-20 08:33:04 +03:00
|
|
|
rb_clear_coverages();
|
2018-08-22 08:24:50 +03:00
|
|
|
rb_iseq_remove_coverage_all();
|
2008-07-08 19:13:22 +04:00
|
|
|
GET_VM()->coverages = Qfalse;
|
2017-12-06 10:04:48 +03:00
|
|
|
rb_remove_event_hook((rb_event_hook_func_t) update_line_coverage);
|
|
|
|
if (GET_VM()->coverage_mode & COVERAGE_TARGET_BRANCHES) {
|
|
|
|
rb_remove_event_hook((rb_event_hook_func_t) update_branch_coverage);
|
2017-12-06 09:39:05 +03:00
|
|
|
}
|
2017-12-05 10:16:42 +03:00
|
|
|
if (GET_VM()->coverage_mode & COVERAGE_TARGET_METHODS) {
|
|
|
|
rb_remove_event_hook((rb_event_hook_func_t) update_method_coverage);
|
|
|
|
}
|
2008-07-03 16:55:12 +04:00
|
|
|
}
|
2012-12-05 23:37:49 +04:00
|
|
|
|
2017-09-03 17:26:06 +03:00
|
|
|
VALUE
|
|
|
|
rb_default_coverage(int n)
|
|
|
|
{
|
|
|
|
VALUE coverage = rb_ary_tmp_new_fill(3);
|
2017-12-05 10:16:42 +03:00
|
|
|
VALUE lines = Qfalse, branches = Qfalse;
|
2017-09-03 17:26:06 +03:00
|
|
|
int mode = GET_VM()->coverage_mode;
|
|
|
|
|
|
|
|
if (mode & COVERAGE_TARGET_LINES) {
|
|
|
|
lines = n > 0 ? rb_ary_tmp_new_fill(n) : rb_ary_tmp_new(0);
|
|
|
|
}
|
|
|
|
RARRAY_ASET(coverage, COVERAGE_INDEX_LINES, lines);
|
|
|
|
|
|
|
|
if (mode & COVERAGE_TARGET_BRANCHES) {
|
2017-09-14 06:25:36 +03:00
|
|
|
branches = rb_ary_tmp_new_fill(2);
|
|
|
|
/* internal data structures for branch coverage:
|
|
|
|
*
|
2017-11-23 14:52:36 +03:00
|
|
|
* [[base_type, base_first_lineno, base_first_column, base_last_lineno, base_last_column,
|
|
|
|
* target_type_1, target_first_lineno_1, target_first_column_1, target_last_lineno_1, target_last_column_1, target_counter_index_1,
|
|
|
|
* target_type_2, target_first_lineno_2, target_first_column_2, target_last_lineno_2, target_last_column_2, target_counter_index_2, ...],
|
2017-09-14 06:25:36 +03:00
|
|
|
* ...]
|
|
|
|
*
|
2017-11-23 14:52:36 +03:00
|
|
|
* Example: [[:case, 1, 0, 4, 3,
|
|
|
|
* :when, 2, 8, 2, 9, 0,
|
|
|
|
* :when, 3, 8, 3, 9, 1, ...],
|
2017-09-14 06:25:36 +03:00
|
|
|
* ...]
|
|
|
|
*/
|
|
|
|
RARRAY_ASET(branches, 0, rb_ary_tmp_new(0));
|
|
|
|
/* branch execution counters */
|
|
|
|
RARRAY_ASET(branches, 1, rb_ary_tmp_new(0));
|
2017-09-03 17:26:06 +03:00
|
|
|
}
|
|
|
|
RARRAY_ASET(coverage, COVERAGE_INDEX_BRANCHES, branches);
|
|
|
|
|
|
|
|
return coverage;
|
|
|
|
}
|
|
|
|
|
2012-12-05 23:37:49 +04:00
|
|
|
VALUE
|
|
|
|
rb_uninterruptible(VALUE (*b_proc)(ANYARGS), VALUE data)
|
|
|
|
{
|
2015-07-30 10:56:02 +03:00
|
|
|
VALUE interrupt_mask = rb_ident_hash_new();
|
2012-12-05 23:37:49 +04:00
|
|
|
rb_thread_t *cur_th = GET_THREAD();
|
|
|
|
|
2012-12-23 14:18:58 +04:00
|
|
|
rb_hash_aset(interrupt_mask, rb_cObject, sym_never);
|
2015-08-13 10:22:10 +03:00
|
|
|
OBJ_FREEZE_RAW(interrupt_mask);
|
2012-12-23 14:18:58 +04:00
|
|
|
rb_ary_push(cur_th->pending_interrupt_mask_stack, interrupt_mask);
|
2012-12-05 23:37:49 +04:00
|
|
|
|
2012-12-23 14:18:58 +04:00
|
|
|
return rb_ensure(b_proc, data, rb_ary_pop, cur_th->pending_interrupt_mask_stack);
|
2012-12-05 23:37:49 +04:00
|
|
|
}
|