static VALUE thread_spec_rb_thread_alone() { return rb_thread_alone() ? Qtrue : Qfalse; }
static int NIO_Selector_run(struct NIO_Selector *selector, VALUE timeout) { int result; selector->selecting = 1; #if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL) || defined(HAVE_RB_THREAD_ALONE) /* Implement the optional timeout (if any) as a ev_timer */ if(timeout != Qnil) { /* It seems libev is not a fan of timers being zero, so fudge a little */ selector->timer.repeat = NUM2DBL(timeout) + 0.0001; ev_timer_again(selector->ev_loop, &selector->timer); } else { ev_timer_stop(selector->ev_loop, &selector->timer); } #else /* Store when we started the loop so we can calculate the timeout */ ev_tstamp started_at = ev_now(selector->ev_loop); #endif #if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL) /* libev is patched to release the GIL when it makes its system call */ ev_loop(selector->ev_loop, EVLOOP_ONESHOT); #elif defined(HAVE_RB_THREAD_ALONE) /* If we're the only thread we can make a blocking system call */ if(rb_thread_alone()) { #else /* If we don't have rb_thread_alone() we can't block */ if(0) { #endif /* defined(HAVE_RB_THREAD_BLOCKING_REGION) */ #if !defined(HAVE_RB_THREAD_BLOCKING_REGION) && !defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL) TRAP_BEG; ev_loop(selector->ev_loop, EVLOOP_ONESHOT); TRAP_END; } else { /* We need to busy wait as not to stall the green thread scheduler Ruby 1.8: just say no! :( */ ev_timer_init(&selector->timer, NIO_Selector_timeout_callback, BUSYWAIT_INTERVAL, BUSYWAIT_INTERVAL); ev_timer_start(selector->ev_loop, &selector->timer); /* Loop until we receive events */ while(selector->selecting && !selector->ready_count) { TRAP_BEG; ev_loop(selector->ev_loop, EVLOOP_ONESHOT); TRAP_END; /* Run the next green thread */ rb_thread_schedule(); /* Break if the timeout has elapsed */ if(timeout != Qnil && ev_now(selector->ev_loop) - started_at >= NUM2DBL(timeout)) break; } ev_timer_stop(selector->ev_loop, &selector->timer); } #endif /* defined(HAVE_RB_THREAD_BLOCKING_REGION) */ result = selector->ready_count; selector->selecting = selector->ready_count = 0; return result; } /* Wake the selector up from another thread */ static VALUE NIO_Selector_wakeup(VALUE self) { struct NIO_Selector *selector; Data_Get_Struct(self, struct NIO_Selector, selector); if(selector->closed) { rb_raise(rb_eIOError, "selector is closed"); } write(selector->wakeup_writer, "\0", 1); return Qnil; } /* Close the selector and free system resources */ static VALUE NIO_Selector_close(VALUE self) { VALUE args[1] = {self}; return NIO_Selector_synchronize(self, NIO_Selector_close_synchronized, args); }
static VALUE loop_run_poll(VALUE argp) { lp_arg *args = (lp_arg*)argp; rb_mt_loop *loop = args->loop; hrtime_t now, next_time; if (loop->events.count) { uint32_t i; args->fds = calloc(loop->events.count, sizeof(struct pollfd)); if (args->fds == NULL) { rb_raise(cb_eClientNoMemoryError, "failed to allocate memory for pollfd"); } for(i = 0; i < loop->events.count; i++) { rb_mt_socket_list *list = &loop->events.sockets[i]; args->fds[i].fd = list->socket; args->fds[i].events = (list->flags & LCB_READ_EVENT ? POLLIN : 0) | (list->flags & LCB_WRITE_EVENT ? POLLOUT : 0); } args->nfd = loop->events.count; } retry: next_time = timers_minimum(&loop->timers); if (next_time) { now = gethrtime(); args->ts = next_time <= now ? 0 : next_time - now; } else { args->ts = HRTIME_INFINITY; } #ifdef HAVE_RB_THREAD_BLOCKING_REGION rb_thread_blocking_region(loop_blocking_poll, args, RUBY_UBF_PROCESS, NULL); #else if (rb_thread_alone()) { TRAP_BEG; args->result = xpoll(args->fds, args->nfd, args->ts); if (args->result < 0) args->lerrno = errno; TRAP_END; } else { /* 5 millisecond pause */ hrtime_t mini_pause = 5000000; int exact = 0; if (args->ts != HRTIME_INFINITY && args->ts < mini_pause) { mini_pause = args->ts; exact = 1; } TRAP_BEG; args->result = xpoll(args->fds, args->nfd, mini_pause); if (args->result < 0) args->lerrno = errno; TRAP_END; if (args->result == 0 && !exact) { args->result = -1; args->lerrno = EINTR; } } #endif if (args->result < 0) { errno = args->lerrno; switch (errno) { case EINTR: #ifdef ERESTART case ERESTART: #endif #ifndef HAVE_RB_THREAD_BLOCKING_REGION rb_thread_schedule(); #endif goto retry; } rb_sys_fail("poll"); return Qnil; } if (next_time) { now = gethrtime(); } if (args->result > 0) { uint32_t cnt = args->result; uint32_t fd_n = 0, ev_n = 0; while (cnt && fd_n < args->nfd && ev_n < loop->events.count) { struct pollfd *res = args->fds + fd_n; rb_mt_socket_list *list = loop->events.sockets + ev_n; rb_mt_event *sock = list->first; /* if plugin used correctly, this checks are noop */ if (res->fd < list->socket) { fd_n++; continue; } else if (res->fd > list->socket) { ev_n++; continue; } if (res->revents) { short flags = ((res->revents & POLLIN_SET) ? LCB_READ_EVENT : 0) | ((res->revents & POLLOUT_SET) ? LCB_WRITE_EVENT : 0); cnt--; loop_enque_events(&loop->callbacks, sock, flags); } fd_n++; ev_n++; } callbacks_run(&loop->callbacks); } if (next_time) { timers_run(&loop->timers, now); } if (loop->events.count == 0 && loop->timers.count == 0) { loop->run = 0; } return Qnil; }
static VALUE thread_spec_rb_thread_alone() { return INT2NUM(rb_thread_alone()); }