Implement basic support for POSIX-style select().

Now we can block on both the PTY *and* the GUI event stream in Terminal.
This commit is contained in:
Andreas Kling 2019-01-15 23:12:20 +01:00
parent 46181cf023
commit 10387beda7
Notes: sideshowbarker 2024-07-19 16:01:56 +09:00
8 changed files with 164 additions and 30 deletions

View File

@ -1909,3 +1909,78 @@ DisplayInfo Process::get_display_info()
info.framebuffer = m_display_framebuffer_region->linearAddress.asPtr();
return info;
}
int Process::sys$select(const Syscall::SC_select_params* params)
{
if (!validate_read_typed(params))
return -EFAULT;
if (params->writefds && !validate_read_typed(params->writefds))
return -EFAULT;
if (params->readfds && !validate_read_typed(params->readfds))
return -EFAULT;
if (params->exceptfds && !validate_read_typed(params->exceptfds))
return -EFAULT;
if (params->timeout && !validate_read_typed(params->timeout))
return -EFAULT;
int nfds = params->nfds;
fd_set* writefds = params->writefds;
fd_set* readfds = params->readfds;
fd_set* exceptfds = params->exceptfds;
auto* timeout = params->timeout;
// FIXME: Implement exceptfds support.
ASSERT(!exceptfds);
// FIXME: Implement timeout support.
ASSERT(!timeout);
if (nfds < 0)
return -EINVAL;
// FIXME: Return -EBADF if one of the fd sets contains an invalid fd.
// FIXME: Return -EINTR if a signal is caught.
// FIXME: Return -EINVAL if timeout is invalid.
auto transfer_fds = [nfds] (fd_set* set, auto& vector) {
if (!set)
return;
vector.clear_with_capacity();
auto bitmap = Bitmap::wrap((byte*)set, FD_SETSIZE);
for (int i = 0; i < nfds; ++i) {
if (bitmap.get(i))
vector.append(i);
}
};
transfer_fds(writefds, m_select_write_fds);
transfer_fds(readfds, m_select_read_fds);
block(BlockedSelect);
Scheduler::yield();
int markedfds = 0;
if (readfds) {
memset(readfds, 0, sizeof(fd_set));
auto bitmap = Bitmap::wrap((byte*)readfds, FD_SETSIZE);
for (int fd : m_select_read_fds) {
if (m_fds[fd].descriptor->has_data_available_for_reading(*this)) {
bitmap.set(fd, true);
++markedfds;
}
}
}
if (writefds) {
memset(writefds, 0, sizeof(fd_set));
auto bitmap = Bitmap::wrap((byte*)writefds, FD_SETSIZE);
for (int fd : m_select_write_fds) {
if (m_fds[fd].descriptor->can_write(*this)) {
bitmap.set(fd, true);
++markedfds;
}
}
}
return markedfds;
}

View File

@ -69,6 +69,7 @@ public:
BlockedRead,
BlockedWrite,
BlockedSignal,
BlockedSelect,
};
enum RingLevel {
@ -81,7 +82,7 @@ public:
bool is_blocked() const
{
return m_state == BlockedSleep || m_state == BlockedWait || m_state == BlockedRead || m_state == BlockedSignal;
return m_state == BlockedSleep || m_state == BlockedWait || m_state == BlockedRead || m_state == BlockedSignal || m_state == BlockedSelect;
}
PageDirectory& page_directory() { return *m_page_directory; }
@ -156,6 +157,7 @@ public:
void* sys$mmap(const Syscall::SC_mmap_params*);
int sys$munmap(void*, size_t size);
int sys$set_mmap_name(void*, size_t, const char*);
int sys$select(const Syscall::SC_select_params*);
ssize_t sys$get_dir_entries(int fd, void*, size_t);
int sys$getcwd(char*, size_t);
int sys$chdir(const char*);
@ -304,6 +306,8 @@ private:
dword m_timesScheduled { 0 };
pid_t m_waitee_pid { -1 };
int m_blocked_fd { -1 };
Vector<int> m_select_read_fds;
Vector<int> m_select_write_fds;
size_t m_max_open_file_descriptors { 16 };
SignalActionData m_signal_action_data[32];
dword m_pending_signals { 0 };
@ -394,6 +398,7 @@ static inline const char* toString(Process::State state)
case Process::BlockedRead: return "Read";
case Process::BlockedWrite: return "Write";
case Process::BlockedSignal: return "Signal";
case Process::BlockedSelect: return "Select";
case Process::BeingInspected: return "Inspect";
}
ASSERT_NOT_REACHED();

View File

@ -64,6 +64,22 @@ bool Scheduler::pick_next()
return true;
}
if (process.state() == Process::BlockedSelect) {
for (int fd : process.m_select_read_fds) {
if (process.m_fds[fd].descriptor->has_data_available_for_reading(process)) {
process.unblock();
return true;
}
}
for (int fd : process.m_select_write_fds) {
if (process.m_fds[fd].descriptor->can_write(process)) {
process.unblock();
return true;
}
}
return true;
}
if (process.state() == Process::Skip1SchedulerPass) {
process.set_state(Process::Skip0SchedulerPasses);
return true;

View File

@ -98,6 +98,8 @@ static dword handle(RegisterDump& regs, dword function, dword arg1, dword arg2,
return current->sys$waitpid((pid_t)arg1, (int*)arg2, (int)arg3);
case Syscall::SC_mmap:
return (dword)current->sys$mmap((const SC_mmap_params*)arg1);
case Syscall::SC_select:
return current->sys$select((const SC_select_params*)arg1);
case Syscall::SC_munmap:
return current->sys$munmap((void*)arg1, (size_t)arg2);
case Syscall::SC_gethostname:

View File

@ -71,8 +71,11 @@
__ENUMERATE_SYSCALL(gui_destroy_window) \
__ENUMERATE_SYSCALL(gui_get_window_backing_store) \
__ENUMERATE_SYSCALL(gui_invalidate_window) \
__ENUMERATE_SYSCALL(select) \
struct fd_set;
namespace Syscall {
enum Function {
@ -102,6 +105,14 @@ struct SC_mmap_params {
int32_t offset; // FIXME: 64-bit off_t?
};
struct SC_select_params {
int nfds;
fd_set* readfds;
fd_set* writefds;
fd_set* exceptfds;
struct timeval* timeout;
};
void initialize();
int sync();

View File

@ -41,6 +41,7 @@ LIBC_OBJS = \
math.o \
utime.o \
gui.o \
sys/select.o \
entry.o
OBJS = $(AK_OBJS) $(WIDGETS_OBJS) $(LIBC_OBJS)

View File

@ -9,6 +9,7 @@
#include <Widgets/GraphicsBitmap.h>
#include <Widgets/Painter.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <gui.h>
#include "Terminal.h"
@ -53,9 +54,14 @@ static void make_shell(int ptm_fd)
}
}
static int max(int a, int b)
{
return a > b ? a : b;
}
int main(int, char**)
{
int ptm_fd = open("/dev/ptm0", O_RDWR | O_NONBLOCK);
int ptm_fd = open("/dev/ptm0", O_RDWR);
if (ptm_fd < 0) {
perror("open");
return 1;
@ -63,7 +69,7 @@ int main(int, char**)
make_shell(ptm_fd);
int event_fd = open("/dev/gui_events", O_RDONLY | O_NONBLOCK);
int event_fd = open("/dev/gui_events", O_RDONLY);
if (event_fd < 0) {
perror("open");
return 1;
@ -74,38 +80,53 @@ int main(int, char**)
terminal.paint();
for (;;) {
byte buffer[1024];
ssize_t ptm_nread = read(ptm_fd, buffer, sizeof(buffer));
if (ptm_nread > 0) {
for (ssize_t i = 0; i < ptm_nread; ++i)
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(ptm_fd, &rfds);
FD_SET(event_fd, &rfds);
int nfds = select(max(ptm_fd, event_fd) + 1, &rfds, nullptr, nullptr, nullptr);
if (nfds < 0) {
dbgprintf("Terminal(%u) select() failed :( errno=%d\n", getpid(), errno);
}
if (FD_ISSET(ptm_fd, &rfds)) {
byte buffer[1024];
ssize_t nread = read(ptm_fd, buffer, sizeof(buffer));
if (nread < 0) {
perror("read(ptm)");
continue;
}
assert(nread != 0);
for (ssize_t i = 0; i < nread; ++i)
terminal.on_char(buffer[i]);
terminal.paint();
}
GUI_Event event;
ssize_t nread = read(event_fd, &event, sizeof(event));
if (nread < 0) {
perror("read");
return 1;
}
if (nread == 0)
continue;
assert(nread == sizeof(event));
dbgprintf("(Terminal:%d) ", getpid());
switch (event.type) {
case GUI_Event::Type::Paint: dbgprintf("WID=%x Paint [%d,%d %dx%d]\n", event.window_id, event.paint.rect.location.x, event.paint.rect.location.y, event.paint.rect.size.width, event.paint.rect.size.height); break;
case GUI_Event::Type::MouseDown: dbgprintf("WID=%x MouseDown %d,%d\n", event.window_id, event.mouse.position.x, event.mouse.position.y); break;
case GUI_Event::Type::MouseUp: dbgprintf("WID=%x MouseUp %d,%d\n", event.window_id, event.mouse.position.x, event.mouse.position.y); break;
case GUI_Event::Type::MouseMove: dbgprintf("WID=%x MouseMove %d,%d\n", event.window_id, event.mouse.position.x, event.mouse.position.y); break;
case GUI_Event::Type::KeyDown: dbgprintf("WID=%x KeyDown 0x%b (%c)\n", event.window_id, event.key.character, event.key.character); break;
default:
ASSERT_NOT_REACHED();
}
if (FD_ISSET(event_fd, &rfds)) {
GUI_Event event;
ssize_t nread = read(event_fd, &event, sizeof(event));
if (nread < 0) {
perror("read(event)");
return 1;
}
assert(nread != 0);
assert(nread == sizeof(event));
dbgprintf("(Terminal:%d) ", getpid());
switch (event.type) {
case GUI_Event::Type::Paint: dbgprintf("WID=%x Paint [%d,%d %dx%d]\n", event.window_id, event.paint.rect.location.x, event.paint.rect.location.y, event.paint.rect.size.width, event.paint.rect.size.height); break;
case GUI_Event::Type::MouseDown: dbgprintf("WID=%x MouseDown %d,%d\n", event.window_id, event.mouse.position.x, event.mouse.position.y); break;
case GUI_Event::Type::MouseUp: dbgprintf("WID=%x MouseUp %d,%d\n", event.window_id, event.mouse.position.x, event.mouse.position.y); break;
case GUI_Event::Type::MouseMove: dbgprintf("WID=%x MouseMove %d,%d\n", event.window_id, event.mouse.position.x, event.mouse.position.y); break;
case GUI_Event::Type::KeyDown: dbgprintf("WID=%x KeyDown 0x%b (%c)\n", event.window_id, event.key.character, event.key.character); break;
default:
ASSERT_NOT_REACHED();
}
if (event.type == GUI_Event::Type::Paint) {
terminal.paint();
} else if (event.type == GUI_Event::Type::KeyDown) {
write(ptm_fd, &event.key.character, 1);
if (event.type == GUI_Event::Type::Paint) {
terminal.paint();
} else if (event.type == GUI_Event::Type::KeyDown) {
write(ptm_fd, &event.key.character, 1);
}
}
}
return 0;

View File

@ -2,6 +2,9 @@
extern "C" {
#define FD_SETSIZE 64
struct fd_set { unsigned char bits[FD_SETSIZE / 8]; };
namespace Unix {
#define WNOHANG 1