diff --git a/libc/Makefile b/libc/Makefile index b8d67ff5..20dcddb5 100644 --- a/libc/Makefile +++ b/libc/Makefile @@ -189,6 +189,8 @@ on_exit.o \ openat.o \ open.o \ pipe.o \ +poll.o \ +ppoll.o \ print.o \ putc.o \ raise.o \ diff --git a/libc/include/poll.h b/libc/include/poll.h new file mode 100644 index 00000000..77717bfb --- /dev/null +++ b/libc/include/poll.h @@ -0,0 +1,46 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2012. + + This file is part of the Sortix C Library. + + The Sortix C Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + The Sortix C Library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with the Sortix C Library. If not, see . + + poll.h + Input/output multiplexing. + +*******************************************************************************/ + +#ifndef _POLL_H +#define _POLL_H 1 + +#include + +__BEGIN_DECLS + +@include(time_t.h) + +__END_DECLS +#include +#include +#include +__BEGIN_DECLS + +int poll(struct pollfd* fds, nfds_t nfds, int timeout); +int ppoll(struct pollfd* fds, nfds_t nfds, const struct timespec* timeout, + const sigset_t* sigmask); + +__END_DECLS + +#endif diff --git a/libc/poll.cpp b/libc/poll.cpp new file mode 100644 index 00000000..d9048840 --- /dev/null +++ b/libc/poll.cpp @@ -0,0 +1,34 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2012. + + This file is part of the Sortix C Library. + + The Sortix C Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + The Sortix C Library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with the Sortix C Library. If not, see . + + poll.cpp + Input/output multiplexing. + +*******************************************************************************/ + +#include +#include + +extern "C" int poll(struct pollfd* fds, nfds_t nfds, int timeout) +{ + struct timespec ts; + ts.tv_sec = timeout; + ts.tv_nsec = 0; + return ppoll(fds, nfds, &ts, NULL); +} diff --git a/libc/ppoll.cpp b/libc/ppoll.cpp new file mode 100644 index 00000000..ddbf9fdd --- /dev/null +++ b/libc/ppoll.cpp @@ -0,0 +1,36 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2012. + + This file is part of the Sortix C Library. + + The Sortix C Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + The Sortix C Library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with the Sortix C Library. If not, see . + + ppoll.cpp + Input/output multiplexing. + +*******************************************************************************/ + +#include + +#include + +DEFN_SYSCALL4(int, sys_ppoll, SYSCALL_PPOLL, struct pollfd*, nfds_t, + const struct timespec*, const sigset_t*); + +extern "C" int ppoll(struct pollfd* fds, nfds_t nfds, + const struct timespec* timeout, const sigset_t* sigmask) +{ + return sys_ppoll(fds, nfds, timeout, sigmask); +} diff --git a/sortix/Makefile b/sortix/Makefile index 466198a6..9119a1f1 100644 --- a/sortix/Makefile +++ b/sortix/Makefile @@ -106,6 +106,7 @@ mtable.o \ panic.o \ pci.o \ pipe.o \ +poll.o \ process.o \ refcount.o \ scheduler.o \ diff --git a/sortix/descriptor.cpp b/sortix/descriptor.cpp index ce2b7078..cfe0fc10 100644 --- a/sortix/descriptor.cpp +++ b/sortix/descriptor.cpp @@ -416,4 +416,9 @@ int Descriptor::gettermmode(ioctx_t* ctx, unsigned* mode) return vnode->gettermmode(ctx, mode); } +int Descriptor::poll(ioctx_t* ctx, PollNode* node) +{ + return vnode->poll(ctx, node); +} + } // namespace Sortix diff --git a/sortix/fs/user.cpp b/sortix/fs/user.cpp index b7eabccf..18822222 100644 --- a/sortix/fs/user.cpp +++ b/sortix/fs/user.cpp @@ -213,6 +213,7 @@ public: virtual int tcgetwinsize(ioctx_t* ctx, struct winsize* ws); virtual int settermmode(ioctx_t* ctx, unsigned mode); virtual int gettermmode(ioctx_t* ctx, unsigned* mode); + virtual int poll(ioctx_t* ctx, PollNode* node); private: bool SendMessage(Channel* channel, size_t type, void* ptr, size_t size, @@ -1115,6 +1116,11 @@ int Unode::gettermmode(ioctx_t* ctx, unsigned* mode) return ret; } +int Unode::poll(ioctx_t* /*ctx*/, PollNode* /*node*/) +{ + return errno = ENOTSUP, -1; +} + // // Initialization. // diff --git a/sortix/include/sortix/kernel/descriptor.h b/sortix/include/sortix/kernel/descriptor.h index fb61b45b..e66e1faf 100644 --- a/sortix/include/sortix/kernel/descriptor.h +++ b/sortix/include/sortix/kernel/descriptor.h @@ -41,6 +41,7 @@ struct kernel_dirent; namespace Sortix { +class PollNode; class Inode; class Vnode; struct ioctx_struct; @@ -77,6 +78,7 @@ public: int tcgetwinsize(ioctx_t* ctx, struct winsize* ws); int settermmode(ioctx_t* ctx, unsigned mode); int gettermmode(ioctx_t* ctx, unsigned* mode); + int poll(ioctx_t* ctx, PollNode* node); private: Ref open_elem(ioctx_t* ctx, const char* filename, int flags, diff --git a/sortix/include/sortix/kernel/inode.h b/sortix/include/sortix/kernel/inode.h index 3cfd96f0..2dacdb55 100644 --- a/sortix/include/sortix/kernel/inode.h +++ b/sortix/include/sortix/kernel/inode.h @@ -41,6 +41,7 @@ struct kernel_dirent; namespace Sortix { +class PollNode; struct ioctx_struct; typedef struct ioctx_struct ioctx_t; @@ -87,6 +88,7 @@ public: virtual int tcgetwinsize(ioctx_t* ctx, struct winsize* ws) = 0; virtual int settermmode(ioctx_t* ctx, unsigned mode) = 0; virtual int gettermmode(ioctx_t* ctx, unsigned* mode) = 0; + virtual int poll(ioctx_t* ctx, PollNode* node) = 0; }; @@ -151,6 +153,7 @@ public: virtual int tcgetwinsize(ioctx_t* ctx, struct winsize* ws); virtual int settermmode(ioctx_t* ctx, unsigned mode); virtual int gettermmode(ioctx_t* ctx, unsigned* mode); + virtual int poll(ioctx_t* ctx, PollNode* node); }; diff --git a/sortix/include/sortix/kernel/poll.h b/sortix/include/sortix/kernel/poll.h new file mode 100644 index 00000000..7da30245 --- /dev/null +++ b/sortix/include/sortix/kernel/poll.h @@ -0,0 +1,83 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2012. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . + + sortix/kernel/poll.h + Kernel declarations for event polling. + +*******************************************************************************/ + +#ifndef INCLUDE_SORTIX_KERNEL_POLL_H +#define INCLUDE_SORTIX_KERNEL_POLL_H + +#include + +namespace Sortix { + +class PollChannel; +class PollNode; + +class PollChannel +{ +public: + PollChannel(); + ~PollChannel(); + void Signal(short events); + void Register(PollNode* node); + void Unregister(PollNode* node); + +private: + void SignalUnlocked(short events); + +private: + struct PollNode* first; + struct PollNode* last; + kthread_mutex_t channel_lock; + kthread_cond_t no_pending_cond; + +}; + +class PollNode +{ + friend class PollChannel; + +public: + PollNode() { next = NULL; prev = NULL; channel = NULL; } + +private: + PollNode* next; + PollNode* prev; + +public: + PollChannel* channel; + +public: + kthread_mutex_t* wake_mutex; + kthread_cond_t* wake_cond; + short events; + short revents; + bool* woken; + +public: + void Cancel(); + +}; + +} // namespace Sortix + +#endif diff --git a/sortix/include/sortix/kernel/vnode.h b/sortix/include/sortix/kernel/vnode.h index a18a3cc7..42a09b2f 100644 --- a/sortix/include/sortix/kernel/vnode.h +++ b/sortix/include/sortix/kernel/vnode.h @@ -34,6 +34,7 @@ struct kernel_dirent; namespace Sortix { +class PollNode; class Inode; struct ioctx_struct; typedef struct ioctx_struct ioctx_t; @@ -74,6 +75,7 @@ public: int tcgetwinsize(ioctx_t* ctx, struct winsize* ws); int settermmode(ioctx_t* ctx, unsigned mode); int gettermmode(ioctx_t* ctx, unsigned* mode); + int poll(ioctx_t* ctx, PollNode* node); public /*TODO: private*/: Ref inode; diff --git a/sortix/include/sortix/poll.h b/sortix/include/sortix/poll.h new file mode 100644 index 00000000..b79ae372 --- /dev/null +++ b/sortix/include/sortix/poll.h @@ -0,0 +1,57 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2012. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . + + sortix/poll.h + Interface for waiting on file descriptor events. + +*******************************************************************************/ + +#ifndef INCLUDE_SORTIX_POLL_H +#define INCLUDE_SORTIX_POLL_H + +#include + +__BEGIN_DECLS + +typedef unsigned long int nfds_t; + +struct pollfd +{ + int fd; + short events; + short revents; +}; + +#define POLLERR (1<<0) +#define POLLHUP (1<<1) +#define POLLNVAL (1<<2) + +#define POLLIN (1<<3) +#define POLLRDNORM (1<<4) +#define POLLRDBAND (1<<5) +#define POLLPRI (1<<6) +#define POLLOUT (1<<7) +#define POLLWRNORM (1<<8) +#define POLLWRBAND (1<<9) + +#define POLL__ONLY_REVENTS (POLLERR | POLLHUP | POLLNVAL) + +__END_DECLS + +#endif diff --git a/sortix/include/sortix/syscallnum.h b/sortix/include/sortix/syscallnum.h index b8e77f09..fab1d7c2 100644 --- a/sortix/include/sortix/syscallnum.h +++ b/sortix/include/sortix/syscallnum.h @@ -93,6 +93,7 @@ #define SYSCALL_FCHMODAT 69 #define SYSCALL_LINKAT 70 #define SYSCALL_FSM_FSBIND 71 -#define SYSCALL_MAX_NUM 72 /* index of highest constant + 1 */ +#define SYSCALL_PPOLL 72 +#define SYSCALL_MAX_NUM 73 /* index of highest constant + 1 */ #endif diff --git a/sortix/inode.cpp b/sortix/inode.cpp index 95c31d6f..7afa5c43 100644 --- a/sortix/inode.cpp +++ b/sortix/inode.cpp @@ -267,4 +267,18 @@ int AbstractInode::gettermmode(ioctx_t* /*ctx*/, unsigned* /*mode*/) return errno = ENOTTY, -1; } +int AbstractInode::poll(ioctx_t* /*ctx*/, PollNode* /*node*/) +{ +#if 0 // TODO: Support poll on regular files as per POSIX. + if ( inode_type == INODE_TYPE_FILE ) + { + // TODO: Correct bits? + node->revents |= (POLLIN | POLLOUT) & node->events; + // TODO: What if not listening on events (POLLIN | POLLOUT)? + return 0; + } +#endif + return errno = ENOTSUP, -1; +} + } // namespace Sortix diff --git a/sortix/kernel.cpp b/sortix/kernel.cpp index 16c2a196..c5d27229 100644 --- a/sortix/kernel.cpp +++ b/sortix/kernel.cpp @@ -73,6 +73,7 @@ #include "io.h" #include "pipe.h" #include "interrupt.h" +#include "poll.h" #include "dispmsg.h" #include "fs/kram.h" #include "fs/user.h" @@ -396,6 +397,9 @@ static void BootThread(void* /*user*/) // Initialize the pipe system. Pipe::Init(); + // Initialize poll system call. + Poll::Init(); + // Initialize the kernel information query syscall. Info::Init(); diff --git a/sortix/poll.cpp b/sortix/poll.cpp new file mode 100644 index 00000000..56000198 --- /dev/null +++ b/sortix/poll.cpp @@ -0,0 +1,263 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2012. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . + + poll.cpp + Interface for waiting on file descriptor events. + +*******************************************************************************/ + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "syscall.h" +#include "process.h" +#include "poll.h" + +namespace Sortix { + +PollChannel::PollChannel() +{ + first = NULL; + last = NULL; + channel_lock = KTHREAD_MUTEX_INITIALIZER; + no_pending_cond = KTHREAD_COND_INITIALIZER; +} + +PollChannel::~PollChannel() +{ + ScopedLock lock(&channel_lock); + // TODO: Is this the correct error to signal with? + SignalUnlocked(POLLHUP); + // Note: We can't stop early in case of a signal, because that would mean + // other threads are still using our data, and since this is the destructor, + // leaving early _will_ cause data corruption. Luckily, this loop will + // terminate because everyone is now woken up and will cancel, which is what + // we wait for to finish. No new requests can come, since we are the + // destructor - whoever owns this object is no longer using it. + while ( first ) + kthread_cond_wait(&no_pending_cond, &channel_lock); +} + +void PollChannel::Signal(short events) +{ + ScopedLock lock(&channel_lock); + SignalUnlocked(events); +} + +void PollChannel::SignalUnlocked(short events) +{ + for ( PollNode* node = first; node; node = node->next ) + if ( node->revents |= events & (node->events | POLL__ONLY_REVENTS) ) + { + ScopedLock node_lock(node->wake_mutex); + if ( !*node->woken ) + { + *node->woken = true; + kthread_cond_signal(node->wake_cond); + } + } +} + +void PollChannel::Register(PollNode* node) +{ + ScopedLock lock(&channel_lock); + node->channel = this; + if ( !first ) + first = last = node, + node->next = node->prev = NULL; + else + node->next = NULL, + node->prev = last, + last->next = node, + last = node; +} + +void PollChannel::Unregister(PollNode* node) +{ + ScopedLock lock(&channel_lock); + node->channel = NULL; + if ( node->prev ) + node->prev->next = node->next; + else + first = node->next; + if ( node->next ) + node->next->prev = node->prev; + else + last = node->prev; + if ( !first ) + kthread_cond_signal(&no_pending_cond); +} + +void PollNode::Cancel() +{ + if ( channel ) + channel->Unregister(this); +} + +namespace Poll { + +static struct pollfd* CopyFdsFromUser(struct pollfd* user_fds, nfds_t nfds) +{ + size_t size = sizeof(struct pollfd) * nfds; + struct pollfd* fds = new struct pollfd[nfds]; + if ( !fds ) + return NULL; + if ( !CopyFromUser(fds, user_fds, size) ) + { + delete[] fds; + return NULL; + } + return fds; +} + +static bool CopyFdsToUser(struct pollfd* user_fds, + const struct pollfd* kernel_fds, nfds_t nfds) +{ + size_t size = sizeof(struct pollfd) * nfds; + return CopyToUser(user_fds, kernel_fds, size); +} + +static bool FetchTimespec(struct timespec* dest, const struct timespec* user) +{ + if ( !user ) + dest->tv_sec = -1, + dest->tv_nsec = 0; + else if ( !CopyFromUser(dest, user, sizeof(*dest)) ) + return false; + return true; +} + +static int sys_ppoll(struct pollfd* user_fds, nfds_t nfds, + const struct timespec* user_timeout_ts, + const sigset_t* user_sigmask) +{ + ioctx_t ctx; SetupKernelIOCtx(&ctx); + + struct timespec timeout_ts; + if ( !FetchTimespec(&timeout_ts, user_timeout_ts) ) + return -1; + + if ( 0 < timeout_ts.tv_sec || timeout_ts.tv_nsec || user_sigmask ) + return errno = ENOSYS, -1; + + struct pollfd* fds = CopyFdsFromUser(user_fds, nfds); + if ( !fds ) { return -1; } + + PollNode* nodes = new PollNode[nfds]; + if ( !nodes ) { delete[] fds; return -1; } + + Process* process = CurrentProcess(); + + kthread_mutex_t wakeup_mutex = KTHREAD_MUTEX_INITIALIZER; + kthread_cond_t wakeup_cond = KTHREAD_COND_INITIALIZER; + + kthread_mutex_lock(&wakeup_mutex); + + int ret = -1; + bool self_woken = false; + volatile bool remote_woken = false; + bool unexpected_error = false; + + nfds_t reqs = nfds; + for ( reqs = 0; !unexpected_error && reqs < nfds; reqs++ ) + { + PollNode* node = nodes + reqs; + if ( fds[reqs].fd < 0 ) + { + fds[reqs].revents = POLLNVAL; + // TODO: Should we set POLLNVAL in node->revents too? Should this + // system call ignore this error and keep polling, or return to + // user-space immediately? What if conditions are already true on + // some of the file descriptors (those we have processed so far?)? + node->revents = 0; + continue; + } + Ref desc = process->GetDescriptor(fds[reqs].fd); + if ( !desc ) { unexpected_error = true; break; } + node->events = fds[reqs].events; + node->revents = 0; + node->wake_mutex = &wakeup_mutex; + node->wake_cond = &wakeup_cond; + node->woken = (bool*) &remote_woken; + // TODO: How should erors be handled? + if ( desc->poll(&ctx, node) == 0 ) + self_woken = true; + else if ( errno != EAGAIN ) + unexpected_error = self_woken = true; + } + + if ( timeout_ts.tv_sec < 0 ) + self_woken = true; + + while ( !(self_woken || remote_woken) ) + { + if ( !kthread_cond_wait_signal(&wakeup_cond, &wakeup_mutex) ) + errno = -EINTR, + self_woken = true; + } + + kthread_mutex_unlock(&wakeup_mutex); + + for ( nfds_t i = 0; i < reqs; i++ ) + nodes[i].Cancel(); + + if ( !unexpected_error ) + { + int num_events = 0; + for ( nfds_t i = 0; i < reqs; i++ ) + { + if ( fds[i].fd < -1 ) + continue; + if ( (fds[i].revents = nodes[i].revents) ) + num_events++; + } + + if ( CopyFdsToUser(user_fds, fds, nfds) ) + ret = num_events; + } + + delete[] nodes; + delete[] fds; + return ret; +} + +void Init() +{ + Syscall::Register(SYSCALL_PPOLL, (void*) sys_ppoll); +} + +} // namespace Poll + +} // namespace Sortix diff --git a/sortix/poll.h b/sortix/poll.h new file mode 100644 index 00000000..4cb5ea67 --- /dev/null +++ b/sortix/poll.h @@ -0,0 +1,36 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2012. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . + + poll.h + Interface for waiting on file descriptor events. + +*******************************************************************************/ + +#ifndef SORTIX_POLL_H +#define SORTIX_POLL_H + +namespace Sortix { +namespace Poll { + +void Init(); + +} // nanmespace Poll +} // namespace Sortix + +#endif diff --git a/sortix/vnode.cpp b/sortix/vnode.cpp index 24549b29..b8dfa870 100644 --- a/sortix/vnode.cpp +++ b/sortix/vnode.cpp @@ -213,4 +213,9 @@ int Vnode::gettermmode(ioctx_t* ctx, unsigned* mode) return inode->gettermmode(ctx, mode); } +int Vnode::poll(ioctx_t* ctx, PollNode* node) +{ + return inode->poll(ctx, node); +} + } // namespace Sortix