Beispiel #1
0
static VALUE thread_spec_rb_thread_alone() {
  return rb_thread_alone() ? Qtrue : Qfalse;
}
Beispiel #2
0
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;
}
Beispiel #4
0
static VALUE thread_spec_rb_thread_alone() {
  return INT2NUM(rb_thread_alone());
}