* thread.c (rb_thread_call_without_gvl2): added.
it can skip last CHECK_INTS. See document for more details. Document about it was updated a bit. * include/ruby/thread.h (decl. of rb_thread_call_without_gvl2): added. * thread.c (rb_thread_call_with_gvl): remove "EXPERIMENTAL!" warning from a document. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@36433 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
6684ade9a3
commit
e9a91d2c95
11
ChangeLog
11
ChangeLog
@ -1,3 +1,14 @@
|
|||||||
|
Wed Jul 18 15:33:21 2012 Koichi Sasada <ko1@atdot.net>
|
||||||
|
|
||||||
|
* thread.c (rb_thread_call_without_gvl2): added.
|
||||||
|
it can skip last CHECK_INTS. See document for more details.
|
||||||
|
Document about it was updated a bit.
|
||||||
|
|
||||||
|
* include/ruby/thread.h (decl. of rb_thread_call_without_gvl2): added.
|
||||||
|
|
||||||
|
* thread.c (rb_thread_call_with_gvl): remove "EXPERIMENTAL!"
|
||||||
|
warning from a document.
|
||||||
|
|
||||||
Wed Jul 18 14:53:21 2012 Nobuyoshi Nakada <nobu@ruby-lang.org>
|
Wed Jul 18 14:53:21 2012 Nobuyoshi Nakada <nobu@ruby-lang.org>
|
||||||
|
|
||||||
* configure.in (EXTDLDFLAGS): split options for each extension
|
* configure.in (EXTDLDFLAGS): split options for each extension
|
||||||
|
@ -28,6 +28,9 @@ extern "C" {
|
|||||||
void *rb_thread_call_with_gvl(void *(*func)(void *), void *data1);
|
void *rb_thread_call_with_gvl(void *(*func)(void *), void *data1);
|
||||||
void *rb_thread_call_without_gvl(void *(*func)(void *), void *data1,
|
void *rb_thread_call_without_gvl(void *(*func)(void *), void *data1,
|
||||||
rb_unblock_function_t *ubf, void *data2);
|
rb_unblock_function_t *ubf, void *data2);
|
||||||
|
void *rb_thread_call_without_gvl2(void *(*func)(void *, int *), void *data1,
|
||||||
|
rb_unblock_function_t *ubf, void *data2);
|
||||||
|
|
||||||
|
|
||||||
#if defined __GNUC__ && __GNUC__ >= 4
|
#if defined __GNUC__ && __GNUC__ >= 4
|
||||||
#pragma GCC visibility pop
|
#pragma GCC visibility pop
|
||||||
|
88
thread.c
88
thread.c
@ -1081,26 +1081,65 @@ rb_thread_blocking_region_end(struct rb_blocking_region_buffer *region)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* rb_thread_blocking_region - permit concurrent/parallel execution.
|
* rb_thread_call_without_gvl - permit concurrent/parallel execution.
|
||||||
|
* rb_thread_call_without_gvl2 - permit concurrent/parallel execution with care of interrupt checking.
|
||||||
*
|
*
|
||||||
* This function does:
|
* rb_thread_call_without_gvl() does:
|
||||||
* (1) release GVL.
|
* (1) release GVL.
|
||||||
* Other Ruby threads may run in parallel.
|
* Other Ruby threads may run in parallel.
|
||||||
* (2) call func with data1.
|
* (2) call func with data1
|
||||||
* (3) acquire GVL.
|
* (3) acquire GVL.
|
||||||
* Other Ruby threads can not run in parallel any more.
|
* Other Ruby threads can not run in parallel any more.
|
||||||
|
* (4) Check interrupts.
|
||||||
|
*
|
||||||
|
* rb_thread_call_without_gvl2() does:
|
||||||
|
* (1) release GVL.
|
||||||
|
* (2) call func with data1 with pointer of skip_interrupt flag.
|
||||||
|
* (3) acquire GVL.
|
||||||
|
* (4) Check interrupts if skip_interrupt flag is not set.
|
||||||
*
|
*
|
||||||
* If another thread interrupts this thread (Thread#kill, signal delivery,
|
* If another thread interrupts this thread (Thread#kill, signal delivery,
|
||||||
* VM-shutdown request, and so on), `ubf()' is called (`ubf()' means
|
* VM-shutdown request, and so on), `ubf()' is called (`ubf()' means
|
||||||
* "un-blocking function"). `ubf()' should interrupt `func()' execution.
|
* "un-blocking function"). `ubf()' should interrupt `func()' execution.
|
||||||
*
|
*
|
||||||
* There are built-in ubfs and you can specify these ubfs.
|
* There are built-in ubfs and you can specify these ubfs.
|
||||||
* However, we can not guarantee our built-in ubfs interrupt
|
|
||||||
* your `func()' correctly. Be careful to use rb_thread_blocking_region().
|
|
||||||
*
|
*
|
||||||
* * RUBY_UBF_IO: ubf for IO operation
|
* * RUBY_UBF_IO: ubf for IO operation
|
||||||
* * RUBY_UBF_PROCESS: ubf for process operation
|
* * RUBY_UBF_PROCESS: ubf for process operation
|
||||||
*
|
*
|
||||||
|
* 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 do not stop with Control+C.
|
||||||
|
*
|
||||||
|
* "Check interrupts" on above list (4) means that check asynchronous
|
||||||
|
* interrupt events (such as Thread#kill, signal delivery, VM-shutdown
|
||||||
|
* request, and so on) and call corresponding procedures
|
||||||
|
* (such as `trap' for signals, raise an exception for Thread#raise).
|
||||||
|
* If `func()' finished and receive interrupts, you may skip interrupt
|
||||||
|
* checking. For example, assume the following func() it read data from file.
|
||||||
|
*
|
||||||
|
* read_func(...) {
|
||||||
|
* // (a) before read
|
||||||
|
* read(buffer); // (b) reading
|
||||||
|
* // (c) after read
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* If interrupts are occure on (a) and (b), then `ubf()' cancels this `read_func()'
|
||||||
|
* and interrupts are checked. No problem on it.
|
||||||
|
* However, the interrupts are occure on (c), after *read* operation is completed,
|
||||||
|
* check intterrupts is harmful because it causes irrevocable side-effect,
|
||||||
|
* especially read data will be vanished. To avoid such problem, the `read_func()'
|
||||||
|
* should be:
|
||||||
|
*
|
||||||
|
* read_func(void *data, int *skip_check_flag) {
|
||||||
|
* // (a) before read
|
||||||
|
* read(buffer); // (b) reading
|
||||||
|
* // (c) after read
|
||||||
|
* if (read was cpmpleted) {
|
||||||
|
* *skip_check_flag = 1;
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
* NOTE: You can not execute most of Ruby C API and touch Ruby
|
* NOTE: You can not execute most of Ruby C API and touch Ruby
|
||||||
* objects in `func()' and `ubf()', including raising an
|
* objects in `func()' and `ubf()', including raising an
|
||||||
* exception, because current thread doesn't acquire GVL
|
* exception, because current thread doesn't acquire GVL
|
||||||
@ -1111,6 +1150,10 @@ rb_thread_blocking_region_end(struct rb_blocking_region_buffer *region)
|
|||||||
* use other ways if you have. We lack experiences to use this API.
|
* use other ways if you have. We lack experiences to use this API.
|
||||||
* Please report your problem related on it.
|
* Please report your problem related on it.
|
||||||
*
|
*
|
||||||
|
* NOTE: Releasing GVL and re-acquiring GVL are costful operation
|
||||||
|
* for short running `func()'.
|
||||||
|
* Use this mechanism if `func()' consumes long time enough.
|
||||||
|
*
|
||||||
* Safe C API:
|
* Safe C API:
|
||||||
* * rb_thread_interrupted() - check interrupt flag
|
* * rb_thread_interrupted() - check interrupt flag
|
||||||
* * ruby_xmalloc(), ruby_xrealloc(), ruby_xfree() -
|
* * ruby_xmalloc(), ruby_xrealloc(), ruby_xfree() -
|
||||||
@ -1118,12 +1161,13 @@ rb_thread_blocking_region_end(struct rb_blocking_region_buffer *region)
|
|||||||
* when GC is needed.
|
* when GC is needed.
|
||||||
*/
|
*/
|
||||||
void *
|
void *
|
||||||
rb_thread_call_without_gvl(void *(*func)(void *), void *data1,
|
rb_thread_call_without_gvl2(void *(*func)(void *data, int *skip_checkints), void *data1,
|
||||||
rb_unblock_function_t *ubf, void *data2)
|
rb_unblock_function_t *ubf, void *data2)
|
||||||
{
|
{
|
||||||
void *val;
|
void *val;
|
||||||
rb_thread_t *th = GET_THREAD();
|
rb_thread_t *th = GET_THREAD();
|
||||||
int saved_errno = 0;
|
int saved_errno = 0;
|
||||||
|
int skip_checkints = 0;
|
||||||
|
|
||||||
th->waiting_fd = -1;
|
th->waiting_fd = -1;
|
||||||
if (ubf == RUBY_UBF_IO || ubf == RUBY_UBF_PROCESS) {
|
if (ubf == RUBY_UBF_IO || ubf == RUBY_UBF_PROCESS) {
|
||||||
@ -1132,18 +1176,41 @@ rb_thread_call_without_gvl(void *(*func)(void *), void *data1,
|
|||||||
}
|
}
|
||||||
|
|
||||||
BLOCKING_REGION({
|
BLOCKING_REGION({
|
||||||
val = func(data1);
|
val = func(data1, &skip_checkints);
|
||||||
saved_errno = errno;
|
saved_errno = errno;
|
||||||
}, ubf, data2);
|
}, ubf, data2);
|
||||||
|
|
||||||
/* TODO: check */
|
if (!skip_checkints) {
|
||||||
RUBY_VM_CHECK_INTS();
|
RUBY_VM_CHECK_INTS();
|
||||||
|
}
|
||||||
|
|
||||||
errno = saved_errno;
|
errno = saved_errno;
|
||||||
|
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct without_gvl_wrapper_arg {
|
||||||
|
void *(*func)(void *data);
|
||||||
|
void *data;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void *
|
||||||
|
without_gvl_wrapper(void *data, int *skip_checkints)
|
||||||
|
{
|
||||||
|
struct without_gvl_wrapper_arg *arg = (struct without_gvl_wrapper_arg*)data;
|
||||||
|
return arg->func(arg->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
rb_thread_call_without_gvl(void *(*func)(void *data), void *data1,
|
||||||
|
rb_unblock_function_t *ubf, void *data2)
|
||||||
|
{
|
||||||
|
struct without_gvl_wrapper_arg arg;
|
||||||
|
arg.func = func;
|
||||||
|
arg.data = data1;
|
||||||
|
return rb_thread_call_without_gvl2(without_gvl_wrapper, &arg, ubf, data2);
|
||||||
|
}
|
||||||
|
|
||||||
VALUE
|
VALUE
|
||||||
rb_thread_io_blocking_region(rb_blocking_function_t *func, void *data1, int fd)
|
rb_thread_io_blocking_region(rb_blocking_function_t *func, void *data1, int fd)
|
||||||
{
|
{
|
||||||
@ -1189,11 +1256,6 @@ rb_thread_blocking_region(
|
|||||||
/*
|
/*
|
||||||
* rb_thread_call_with_gvl - re-enter into Ruby world while releasing GVL.
|
* rb_thread_call_with_gvl - re-enter into Ruby world while releasing GVL.
|
||||||
*
|
*
|
||||||
***
|
|
||||||
*** This API is EXPERIMENTAL!
|
|
||||||
*** We do not guarantee that this API remains in ruby 1.9.2 or later.
|
|
||||||
***
|
|
||||||
*
|
|
||||||
* While releasing GVL using rb_thread_blocking_region() or
|
* While releasing GVL using rb_thread_blocking_region() or
|
||||||
* rb_thread_call_without_gvl(), you can not access Ruby values or invoke methods.
|
* rb_thread_call_without_gvl(), you can not access Ruby values or invoke methods.
|
||||||
* If you need to access it, you must use this function rb_thread_call_with_gvl().
|
* If you need to access it, you must use this function rb_thread_call_with_gvl().
|
||||||
|
Loading…
x
Reference in New Issue
Block a user