mirror of
https://github.com/ruby/ruby.git
synced 2026-01-27 04:24:23 +00:00
Before this patch, the MN scheduler waits for the IO with the
following steps:
1. `poll(fd, timeout=0)` to check fd is ready or not.
2. if fd is not ready, waits with MN thread scheduler
3. call `func` to issue the blocking I/O call
The advantage of advanced `poll()` is we can wait for the
IO ready for any fds. However `poll()` becomes overhead
for already ready fds.
This patch changes the steps like:
1. call `func` to issue the blocking I/O call
2. if the `func` returns `EWOULDBLOCK` the fd is `O_NONBLOCK`
and we need to wait for fd is ready so that waits with MN
thread scheduler.
In this case, we can wait only for `O_NONBLOCK` fds. Otherwise
it waits with blocking operations such as `read()` system call.
However we don't need to call `poll()` to check fd is ready
in advance.
With this patch we can observe performance improvement
on microbenchmark which repeats blocking I/O (not
`O_NONBLOCK` fd) with and without MN thread scheduler.
```ruby
require 'benchmark'
f = open('/dev/null', 'w')
f.sync = true
TN = 1
N = 1_000_000 / TN
Benchmark.bm{|x|
x.report{
TN.times.map{
Thread.new{
N.times{f.print '.'}
}
}.each(&:join)
}
}
__END__
TN = 1
user system total real
ruby32 0.393966 0.101122 0.495088 ( 0.495235)
ruby33 0.493963 0.089521 0.583484 ( 0.584091)
ruby33+MN 0.639333 0.200843 0.840176 ( 0.840291) <- Slow
this+MN 0.512231 0.099091 0.611322 ( 0.611074) <- Good
```
80 lines
3.0 KiB
C
80 lines
3.0 KiB
C
#ifndef INTERNAL_THREAD_H /*-*-C-*-vi:se ft=c:*/
|
|
#define INTERNAL_THREAD_H
|
|
/**
|
|
* @author Ruby developers <ruby-core@ruby-lang.org>
|
|
* @copyright This file is a part of the programming language Ruby.
|
|
* Permission is hereby granted, to either redistribute and/or
|
|
* modify this file, provided that the conditions mentioned in the
|
|
* file COPYING are met. Consult the file for details.
|
|
* @brief Internal header for Thread.
|
|
*/
|
|
#include "ruby/ruby.h" /* for VALUE */
|
|
#include "ruby/intern.h" /* for rb_blocking_function_t */
|
|
#include "ccan/list/list.h" /* for list in rb_io_close_wait_list */
|
|
|
|
struct rb_thread_struct; /* in vm_core.h */
|
|
|
|
#define RB_VM_SAVE_MACHINE_CONTEXT(th) \
|
|
do { \
|
|
FLUSH_REGISTER_WINDOWS; \
|
|
setjmp((th)->ec->machine.regs); \
|
|
SET_MACHINE_STACK_END(&(th)->ec->machine.stack_end); \
|
|
} while (0)
|
|
|
|
/* thread.c */
|
|
#define COVERAGE_INDEX_LINES 0
|
|
#define COVERAGE_INDEX_BRANCHES 1
|
|
#define COVERAGE_TARGET_LINES 1
|
|
#define COVERAGE_TARGET_BRANCHES 2
|
|
#define COVERAGE_TARGET_METHODS 4
|
|
#define COVERAGE_TARGET_ONESHOT_LINES 8
|
|
#define COVERAGE_TARGET_EVAL 16
|
|
|
|
#define RUBY_FATAL_THREAD_KILLED INT2FIX(0)
|
|
#define RUBY_FATAL_THREAD_TERMINATED INT2FIX(1)
|
|
#define RUBY_FATAL_FIBER_KILLED RB_INT2FIX(2)
|
|
|
|
VALUE rb_obj_is_mutex(VALUE obj);
|
|
VALUE rb_suppress_tracing(VALUE (*func)(VALUE), VALUE arg);
|
|
void rb_thread_execute_interrupts(VALUE th);
|
|
VALUE rb_get_coverages(void);
|
|
int rb_get_coverage_mode(void);
|
|
VALUE rb_default_coverage(int);
|
|
VALUE rb_thread_shield_new(void);
|
|
bool rb_thread_shield_owned(VALUE self);
|
|
VALUE rb_thread_shield_wait(VALUE self);
|
|
VALUE rb_thread_shield_release(VALUE self);
|
|
VALUE rb_thread_shield_destroy(VALUE self);
|
|
int rb_thread_to_be_killed(VALUE thread);
|
|
void rb_mutex_allow_trap(VALUE self, int val);
|
|
VALUE rb_uninterruptible(VALUE (*b_proc)(VALUE), VALUE data);
|
|
VALUE rb_mutex_owned_p(VALUE self);
|
|
VALUE rb_exec_recursive_outer_mid(VALUE (*f)(VALUE g, VALUE h, int r), VALUE g, VALUE h, ID mid);
|
|
void ruby_mn_threads_params(void);
|
|
|
|
int rb_thread_wait_for_single_fd(int fd, int events, struct timeval * timeout);
|
|
|
|
struct rb_io_close_wait_list {
|
|
struct ccan_list_head pending_fd_users;
|
|
VALUE closing_thread;
|
|
VALUE wakeup_mutex;
|
|
};
|
|
int rb_notify_fd_close(int fd, struct rb_io_close_wait_list *busy);
|
|
void rb_notify_fd_close_wait(struct rb_io_close_wait_list *busy);
|
|
|
|
RUBY_SYMBOL_EXPORT_BEGIN
|
|
|
|
/* Temporary. This API will be removed (renamed). */
|
|
VALUE rb_thread_io_blocking_region(rb_blocking_function_t *func, void *data1, int fd);
|
|
VALUE rb_thread_io_blocking_call(rb_blocking_function_t *func, void *data1, int fd, int events);
|
|
|
|
/* thread.c (export) */
|
|
int ruby_thread_has_gvl_p(void); /* for ext/fiddle/closure.c */
|
|
|
|
RUBY_SYMBOL_EXPORT_END
|
|
|
|
int rb_threadptr_execute_interrupts(struct rb_thread_struct *th, int blocking_timing);
|
|
bool rb_thread_mn_schedulable(VALUE thread);
|
|
|
|
#endif /* INTERNAL_THREAD_H */
|