Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve fiber_io_loop #279

Open
sidyhe opened this issue May 7, 2022 · 4 comments
Open

improve fiber_io_loop #279

sidyhe opened this issue May 7, 2022 · 4 comments

Comments

@sidyhe
Copy link
Contributor

sidyhe commented May 7, 2022

鉴于之前timer变量未更新的问题, 研究了一下io loop逻辑
发现即使需要更久的io超时, 也会把它限制在100毫秒以内
去掉此限制后, 程序却不会正常退出, 因为最后一个fiber无io无timer却会执行event_process且无期限(timeout == -1)
所以loop逻辑不够完善, 遂重写了一遍
添加了acl_fiber_nready函数 (所以之前fiber从创建到被调度是有额外延时的?)
重写之后会做最合适的超时判定, 不会再被无意义的唤醒, 减少了冗余代码
没有提PR的原因是刚写好, 尚未经过一定时间的验证, 也想请作者review一下

static void fiber_io_loop(ACL_FIBER *self fiber_unused, void *ctx) {
    EVENT *ev = (EVENT *) ctx;
    ACL_FIBER *timer;
    long long now;
    int timeout;

    fiber_system();

    for (;;) {
        // run any ready fiber
        while (acl_fiber_yield() > 0) { }

        if (__thread_fiber->io_stop) {
            break;
        }

        if (acl_fiber_nready() > 0) {
            timeout = 0; // will be check event no wait

            // maybe some timer expired
            timer = FIRST_FIBER(&__thread_fiber->ev_timer);
        } else {
            timer = FIRST_FIBER(&__thread_fiber->ev_timer);
            if (timer) {
                now = event_get_stamp(__thread_fiber->event);

                if (now >= timer->when) {
                    timeout = 0; // some timer expired
                } else {
                    long long diff = timer->when - now;

                    if (diff <= INT_MAX) {
                        timeout = (int)diff;
                    } else {
                        timeout = 1000;
                        msg_warn("%s(%d), too large timer! tid: %lu, ms: %lld",
                                 __FILE__, __LINE__, __pthread_self(), diff);
                    }
                }
            } else {
                // no timer, check any io exist
                if (ev->fdcount > 0 || ev->waiter > 0 || ring_size(&ev->events) > 0) {
                    timeout = -1;
                } else {
                    // nothing to go, finished
                    break; // for
                }
            }
        }

        event_process(ev, timeout);

        if (timer) {
            now = event_get_stamp(__thread_fiber->event);

            while (timer && now >= timer->when) {
                ring_detach(&timer->me);

                if (!timer->sys && --__thread_fiber->nsleeping == 0) {
                    fiber_count_dec();
                }

                timer->status = FIBER_STATUS_NONE;
                acl_fiber_ready(timer); // mark
                timer = FIRST_FIBER(&__thread_fiber->ev_timer);
            }
        }
    } // for

    msg_info("%s(%d), tid=%lu, IO fiber exit! fdcount=%d, waiter=%u, events=%d, io_count=%d",
             __FILE__, __LINE__, __pthread_self(),
             ev->fdcount, ev->waiter, ring_size(&ev->events), (int) __thread_fiber->io_count);

    // don't set ev_fiber NULL here, using fiber_io_clear() to set it NULL
    // in acl_fiber_schedule() after scheduling finished.
    //
    // __thread_fiber->ev_fiber = NULL;
}
int event_process(EVENT *ev, int timeout) {
    int ret;

    if (timeout < 0) {
        timeout = ev->timeout;
    } else if (ev->timeout >= 0) {
        if (ev->timeout < timeout) {
            timeout = ev->timeout;
        }
    }

#ifndef NDEBUG
    // make sure INFINITE
    assert(timeout >= 0 || timeout == -1);
#endif

    event_prepare(ev);

    // other code ...

    return ret;
}
@zhengshuxin
Copy link
Member

之所以没将将超时设为-1,就是防止没有句柄被监听的情况。另外,该超时值还会被动态地修改,比如在 hooked poll中。

@sidyhe
Copy link
Contributor Author

sidyhe commented May 7, 2022

是的, 一共有两个超时判定
io loop的timeout和ev->timeout

分层来看
io loop只关心io和timer综合后应当timeout的值
event只关心上层给的timeout和自己的timeout谁更小

整体来看
所以io loop提供的timeout只是一个建议的值
event_process会把两个timeout做比较, 用最小的那个

你所说的动态修改是指ev->timeout吧, 看到poll和fiber delay会改它
又review了一遍代码, 应该没啥问题?

@zhengshuxin
Copy link
Member

还需要拿一些例子去验证一下,另外,acl_fiber_nready 函数中需要把所有准备好的协程都得要包含进去。

@sidyhe
Copy link
Contributor Author

sidyhe commented May 7, 2022

unsigned acl_fiber_nready(void) {
    if (__thread_fiber == NULL) {
        return 0;
    }
    return (unsigned) ring_size(&__thread_fiber->ready);
}

是根据ndead仿照的, 是否有遗漏的情况呢

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants