diff --git a/kernel/poll.cpp b/kernel/poll.cpp index f4a324d5..952a1e2e 100644 --- a/kernel/poll.cpp +++ b/kernel/poll.cpp @@ -25,14 +25,17 @@ #include #include +#include #include #include -#include +#include +#include #include #include #include +#include #include #include #include @@ -41,6 +44,8 @@ #include #include #include +#include +#include #include "poll.h" @@ -177,6 +182,21 @@ static bool FetchTimespec(struct timespec* dest, const struct timespec* user) return true; } +struct poll_timeout +{ + kthread_mutex_t* wake_mutex; + kthread_cond_t* wake_cond; + bool* woken; +}; + +static void poll_timeout_callback(Clock*, Timer*, void* ctx) +{ + struct poll_timeout* pts = (struct poll_timeout*) ctx; + ScopedLock lock(pts->wake_mutex); + *pts->woken = true; + kthread_cond_signal(pts->wake_cond); +} + int sys_ppoll(struct pollfd* user_fds, size_t nfds, const struct timespec* user_timeout_ts, const sigset_t* user_sigmask) @@ -187,7 +207,7 @@ int sys_ppoll(struct pollfd* user_fds, size_t nfds, if ( !FetchTimespec(&timeout_ts, user_timeout_ts) ) return -1; - if ( 0 < timeout_ts.tv_sec || timeout_ts.tv_nsec || user_sigmask ) + if ( user_sigmask ) return errno = ENOSYS, -1; struct pollfd* fds = CopyFdsFromUser(user_fds, nfds); @@ -205,9 +225,23 @@ int sys_ppoll(struct pollfd* user_fds, size_t nfds, int ret = -1; bool self_woken = false; - volatile bool remote_woken = false; + bool remote_woken = false; bool unexpected_error = false; + Timer timer; + struct poll_timeout pts; + if ( timespec_le(timespec_make(0, 1), timeout_ts) ) + { + timer.Attach(Time::GetClock(CLOCK_MONOTONIC)); + struct itimerspec its; + its.it_interval = timespec_nul(); + its.it_value = timeout_ts; + pts.wake_mutex = &wakeup_mutex; + pts.wake_cond = &wakeup_cond; + pts.woken = &remote_woken; + timer.Set(&its, NULL, 0, poll_timeout_callback, &pts); + } + size_t reqs; for ( reqs = 0; !unexpected_error && reqs < nfds; ) { @@ -229,7 +263,7 @@ int sys_ppoll(struct pollfd* user_fds, size_t nfds, node->revents = 0; node->wake_mutex = &wakeup_mutex; node->wake_cond = &wakeup_cond; - node->woken = (bool*) &remote_woken; + node->woken = &remote_woken; reqs++; // TODO: How should errors be handled? if ( desc->poll(&ctx, node) == 0 ) @@ -240,7 +274,7 @@ int sys_ppoll(struct pollfd* user_fds, size_t nfds, unexpected_error = self_woken = true; } - if ( timeout_ts.tv_sec == 0 ) + if ( timeout_ts.tv_sec == 0 && timeout_ts.tv_nsec == 0 ) self_woken = true; while ( !(self_woken || remote_woken) ) @@ -256,6 +290,12 @@ int sys_ppoll(struct pollfd* user_fds, size_t nfds, if ( 0 <= fds[i].fd ) nodes[i].Cancel(); + if ( timespec_le(timespec_make(0, 1), timeout_ts) ) + { + timer.Cancel(); + timer.Detach(); + } + if ( !unexpected_error ) { int num_events = 0;