First pass at a select-like fs wait function

This commit is contained in:
Kevin Lange 2017-01-06 18:30:17 +09:00
parent 9345487324
commit 665343e636
10 changed files with 312 additions and 141 deletions

View File

@ -71,6 +71,20 @@ static inline void pipe_increment_write_by(pipe_device_t * pipe, size_t amount)
pipe->write_ptr = (pipe->write_ptr + amount) % pipe->size;
}
static void pipe_alert_waiters(fs_node_t * fs_node) {
pipe_device_t * pipe = (pipe_device_t *)fs_node->device;
if (pipe->alert_waiters) {
while (pipe->alert_waiters->head) {
node_t * node = list_dequeue(pipe->alert_waiters);
process_t * p = node->value;
process_alert_node(p, fs_node);
free(node);
}
}
}
uint32_t read_pipe(fs_node_t *node, uint32_t offset, uint32_t size, uint8_t *buffer) {
assert(node->device != 0 && "Attempted to read from a fully-closed pipe.");
@ -166,6 +180,7 @@ uint32_t write_pipe(fs_node_t *node, uint32_t offset, uint32_t size, uint8_t *bu
spin_unlock(pipe->lock_write);
wakeup_queue(pipe->wait_queue_readers);
pipe_alert_waiters(node);
if (written < size) {
sleep_on(pipe->wait_queue_writers);
}
@ -211,10 +226,39 @@ void close_pipe(fs_node_t * node) {
return;
}
static int pipe_check(fs_node_t * node) {
pipe_device_t * pipe = (pipe_device_t *)node->device;
if (pipe_unread(pipe) > 0) {
return 0;
}
return 1;
}
static int pipe_wait(fs_node_t * node, void * process) {
pipe_device_t * pipe = (pipe_device_t *)node->device;
if (!pipe->alert_waiters) {
pipe->alert_waiters = list_create();
}
list_insert(pipe->alert_waiters, process);
list_insert(((process_t *)process)->node_waits, pipe);
return 0;
}
static int pipe_match(fs_node_t * node, void * value) {
pipe_device_t * pipe = (pipe_device_t *)node->device;
return pipe == value;
}
fs_node_t * make_pipe(size_t size) {
fs_node_t * fnode = malloc(sizeof(fs_node_t));
pipe_device_t * pipe = malloc(sizeof(pipe_device_t));
memset(fnode, 0, sizeof(fs_node_t));
memset(pipe, 0, sizeof(pipe_device_t));
fnode->device = 0;
fnode->name[0] = '\0';
@ -232,6 +276,10 @@ fs_node_t * make_pipe(size_t size) {
fnode->ioctl = NULL; /* TODO ioctls for pipes? maybe */
fnode->get_size = pipe_size;
fnode->selectcheck = pipe_check;
fnode->selectwait = pipe_wait;
fnode->match = pipe_match;
fnode->atime = now();
fnode->mtime = fnode->atime;
fnode->ctime = fnode->atime;

View File

@ -89,6 +89,41 @@ static fs_node_t * vfs_mapper(void) {
return fnode;
}
/**
* selectcheck_fs: Check if a read from this file would block.
*/
int selectcheck_fs(fs_node_t * node) {
if (!node) return -1;
if (node->selectcheck) {
return node->selectcheck(node);
}
return -1;
}
/**
* selectwait_fs: Inform a node that it should alert the current_process.
*/
int selectwait_fs(fs_node_t * node, void * process) {
if (!node) return -1;
if (node->selectwait) {
return node->selectwait(node, process);
}
return -1;
}
/**
* fsnode_matches: Compare a node internally against a magic value to determine if the value belongs to that node.
*/
int fsnode_matches(fs_node_t * node, void * value) {
if (!node) return 0;
if (!node->match) return 0;
return node->match(node, value);
}
/**
* read_fs: Read a file system node based on its underlying type.

View File

@ -51,6 +51,9 @@ typedef int (*get_size_type_t) (struct fs_node *);
typedef int (*chmod_type_t) (struct fs_node *, int mode);
typedef void (*symlink_type_t) (struct fs_node *, char * name, char * value);
typedef int (*readlink_type_t) (struct fs_node *, char * buf, size_t size);
typedef int (*selectcheck_type_t) (struct fs_node *);
typedef int (*selectwait_type_t) (struct fs_node *, void * process);
typedef int (*match_type_t) (struct fs_node *, void * value);
typedef struct fs_node {
char name[256]; /* The filename. */
@ -89,6 +92,10 @@ typedef struct fs_node {
uint32_t offset; /* Offset for read operations XXX move this to new "file descriptor" entry */
int32_t refcount;
uint32_t nlink;
selectcheck_type_t selectcheck;
selectwait_type_t selectwait;
match_type_t match;
} fs_node_t;
struct dirent {
@ -140,6 +147,9 @@ int chmod_fs(fs_node_t *node, int mode);
int unlink_fs(char * name);
int symlink_fs(char * value, char * name);
int readlink_fs(fs_node_t * node, char * buf, size_t size);
int selectcheck_fs(fs_node_t * node);
int selectwait_fs(fs_node_t * node, void * process);
int fsnode_matches(fs_node_t * node, void * value);
void vfs_install(void);
void * vfs_mount(char * path, fs_node_t * local_root);

View File

@ -19,6 +19,7 @@ typedef struct _pipe_device {
list_t * wait_queue_readers;
list_t * wait_queue_writers;
int dead;
list_t * alert_waiters;
} pipe_device_t;
fs_node_t * make_pipe(size_t size);

View File

@ -103,6 +103,8 @@ typedef struct process {
node_t * timed_sleep_node;
uint8_t is_tasklet;
volatile uint8_t sleep_interrupted;
list_t * node_waits;
int awoken_index;
} process_t;
typedef struct {
@ -134,6 +136,9 @@ extern volatile process_t * current_process;
extern process_t * kernel_idle_task;
extern list_t * process_list;
extern int process_wait_nodes(process_t * process,fs_node_t * nodes[]);
extern int process_alert_node(process_t * process, fs_node_t * fs_node);
typedef void (*tasklet_t) (void *, char *);
extern int create_kernel_tasklet(tasklet_t tasklet, char * name, void * argp);

View File

@ -814,3 +814,59 @@ int waitpid(int pid, int * status, int options) {
} while (1);
}
int process_wait_nodes(process_t * process,fs_node_t * nodes[]) {
assert(!process->node_waits && "Tried to wait on nodes while already waiting on nodes.");
assert(nodes[0] && "Empty wait list.");
fs_node_t ** n = nodes;
int index = 0;
do {
int result = selectcheck_fs(*n);
if (result < 0) {
debug_print(NOTICE, "An invalid descriptor was specified: %d (0x%x)", index, *n);
return -1;
}
if (result == 0) {
return index;
}
n++;
index++;
} while (*n);
n = nodes;
process->node_waits = list_create();
do {
if (selectwait_fs(*n, process) < 0) {
debug_print(NOTICE, "Bad selectwait? 0x%x", *n);
}
n++;
} while (*n);
process->awoken_index = -1;
/* Wait. */
switch_task(0);
return process->awoken_index;
}
int process_alert_node(process_t * process, fs_node_t * fs_node) {
if (!process->node_waits) {
return 0; /* Possibly already returned. Wait for another call. */
}
int index = 0;
foreach(node, process->node_waits) {
if (fsnode_matches(fs_node, node->value)) {
process->awoken_index = index;
list_free(process->node_waits);
free(process->node_waits);
process->node_waits = NULL;
make_process_ready(process);
return 0;
}
index++;
}
return -1;
}

View File

@ -787,6 +787,22 @@ static int sys_lstat(char * file, uintptr_t st) {
return result;
}
static int sys_fswait(int c, int fds[]) {
PTR_VALIDATE(fds);
for (int i = 0; i < c; ++i) {
if (!FD_CHECK(fds[i])) return -1;
}
fs_node_t ** nodes = malloc(sizeof(fs_node_t *)*(c+1));
for (int i = 0; i < c; ++i) {
nodes[i] = FD_ENTRY(fds[i]);
}
nodes[c] = NULL;
int result = process_wait_nodes((process_t *)current_process, nodes);
free(nodes);
return result;
}
/*
* System Call Internals
*/
@ -839,6 +855,7 @@ static int (*syscalls[])() = {
[SYS_SYMLINK] = sys_symlink,
[SYS_READLINK] = sys_readlink,
[SYS_LSTAT] = sys_lstat,
[SYS_FSWAIT] = sys_fswait,
};
uint32_t num_syscalls = sizeof(syscalls) / sizeof(*syscalls);

View File

@ -238,6 +238,32 @@ static void close_client(fs_node_t * node) {
free(c);
}
static int match_server(fs_node_t * node, void * value) {
pex_ex_t * p = (pex_ex_t *)node->device;
return fsnode_matches(p->server_pipe,value);
}
static int wait_server(fs_node_t * node, void * process) {
pex_ex_t * p = (pex_ex_t *)node->device;
return selectwait_fs(p->server_pipe, process);
}
static int check_server(fs_node_t * node) {
pex_ex_t * p = (pex_ex_t *)node->device;
return selectcheck_fs(p->server_pipe);
}
static int match_client(fs_node_t * node, void * value) {
pex_client_t * c = (pex_client_t *)node->inode;
return fsnode_matches(c->pipe, value);
}
static int wait_client(fs_node_t * node, void * process) {
pex_client_t * c = (pex_client_t *)node->inode;
return selectwait_fs(c->pipe, process);
}
static int check_client(fs_node_t * node) {
pex_client_t * c = (pex_client_t *)node->inode;
return selectcheck_fs(c->pipe);
}
static void open_pex(fs_node_t * node, unsigned int flags) {
pex_ex_t * t = (pex_ex_t *)(node->device);
@ -250,6 +276,9 @@ static void open_pex(fs_node_t * node, unsigned int flags) {
node->read = read_server;
node->write = write_server;
node->ioctl = ioctl_server;
node->selectcheck = check_server;
node->selectwait = wait_server;
node->match = match_server;
debug_print(INFO, "[pex] Server launched: %s", t->name);
debug_print(INFO, "fs_node = 0x%x", node);
} else if (!(flags & O_CREAT)) {
@ -261,6 +290,10 @@ static void open_pex(fs_node_t * node, unsigned int flags) {
node->ioctl = ioctl_client;
node->close = close_client;
node->selectcheck = check_client;
node->selectwait = wait_client;
node->match = match_client;
list_insert(t->clients, client);
/* XXX: Send plumbing message to server for new client connection */
@ -272,7 +305,6 @@ static void open_pex(fs_node_t * node, unsigned int flags) {
return;
}
static struct dirent * readdir_packetfs(fs_node_t *node, uint32_t index) {
pex_t * p = (pex_t *)node->device;
unsigned int i = 0;

View File

@ -45,3 +45,4 @@
#define SYS_SYMLINK 56
#define SYS_READLINK 57
#define SYS_LSTAT 58
#define SYS_FSWAIT 59

View File

@ -476,106 +476,10 @@ static void server_window_resize_finish(yutani_globals_t * yg, yutani_server_win
mark_window(yg, win);
}
/**
* Nested Yutani input thread
*
* Handles keyboard and mouse events, as well as
* other Yutani events from the nested window.
*/
void * nested_input(void * _yg) {
yutani_globals_t * yg = _yg;
yutani_t * y = yutani_init();
while (1) {
yutani_msg_t * m = yutani_poll(yg->host_context);
if (m) {
switch (m->type) {
case YUTANI_MSG_KEY_EVENT:
{
struct yutani_msg_key_event * ke = (void*)m->data;
yutani_msg_t * m_ = yutani_msg_build_key_event(0, &ke->event, &ke->state);
int result = yutani_msg_send(y, m_);
free(m_);
}
break;
case YUTANI_MSG_WINDOW_MOUSE_EVENT:
{
struct yutani_msg_window_mouse_event * me = (void*)m->data;
mouse_device_packet_t packet;
packet.buttons = me->buttons;
packet.x_difference = me->new_x;
packet.y_difference = me->new_y;
yutani_msg_t * m_ = yutani_msg_build_mouse_event(0, &packet, YUTANI_MOUSE_EVENT_TYPE_ABSOLUTE);
int result = yutani_msg_send(y, m_);
free(m_);
}
break;
case YUTANI_MSG_RESIZE_OFFER:
{
struct yutani_msg_window_resize * wr = (void*)m->data;
TRACE("Resize request from host compositor for size %dx%d", wr->width, wr->height);
yutani_window_resize_accept(yg->host_context, yg->host_window, wr->width, wr->height);
yg->resize_on_next = 1;
}
break;
case YUTANI_MSG_SESSION_END:
TRACE("Host session ended. Should exit.");
break;
default:
break;
}
}
free(m);
}
}
/**
* Mouse input thread
*
* Reads the kernel mouse device and converts
* mouse clicks and movements into event objects
* to send to the core compositor.
*/
static uint32_t last_mouse_buttons = 0;
void * mouse_input(void * garbage) {
int mfd = open("/dev/mouse", O_RDONLY);
yutani_t * y = yutani_init();
mouse_device_packet_t packet;
while (1) {
int r = read(mfd, (char *)&packet, sizeof(mouse_device_packet_t));
if (r > 0) {
last_mouse_buttons = packet.buttons;
yutani_msg_t * m = yutani_msg_build_mouse_event(0, &packet, YUTANI_MOUSE_EVENT_TYPE_RELATIVE);
int result = yutani_msg_send(y, m);
free(m);
}
}
}
void * mouse_input_abs(void * garbage) {
int mfd = open("/dev/absmouse", O_RDONLY);
if (mfd == -1) return 0;
yutani_t * y = yutani_init();
mouse_device_packet_t packet;
while (1) {
int r = read(mfd, (char *)&packet, sizeof(mouse_device_packet_t));
if (r > 0) {
packet.buttons = last_mouse_buttons & 0xF;
yutani_msg_t * m = yutani_msg_build_mouse_event(0, &packet, YUTANI_MOUSE_EVENT_TYPE_ABSOLUTE);
int result = yutani_msg_send(y, m);
free(m);
}
}
}
#ifndef syscall_fswait
/* TODO: This isn't in our newlib syscall bindings yet. */
DEFN_SYSCALL2(fswait,59,int,int*);
#endif
void * timer_tick(void * _server) {
(void)_server;
@ -589,32 +493,6 @@ void * timer_tick(void * _server) {
}
}
/**
* Keyboard input thread
*
* Reads the kernel keyboard device and converts
* key presses into event objects to send to the
* core compositor.
*/
void * keyboard_input(void * garbage) {
int kfd = open("/dev/kbd", O_RDONLY);
yutani_t * y = yutani_init();
key_event_t event;
key_event_state_t state = {0};
while (1) {
char buf[1];
int r = read(kfd, buf, 1);
if (r > 0) {
kbd_scancode(&state, buf[0], &event);
yutani_msg_t * m = yutani_msg_build_key_event(0, &event, &state);
int result = yutani_msg_send(y, m);
free(m);
}
}
}
#define FONT_PATH "/usr/share/fonts/"
#define FONT(a,b) {a, FONT_PATH b}
@ -2109,6 +1987,7 @@ int main(int argc, char * argv[]) {
yg->host_context = yutani_init();
yg->host_window = yutani_window_create(yg->host_context, yutani_options.nest_width, yutani_options.nest_height);
yutani_window_move(yg->host_context, yg->host_window, 50, 50);
yutani_window_advertise_icon(yg->host_context, yg->host_window, "Compositor", "compositor");
yg->backend_ctx = init_graphics_yutani_double_buffer(yg->host_window);
} else {
_static_yg = yg;
@ -2178,23 +2057,9 @@ int main(int argc, char * argv[]) {
yutani_cairo_init(yg);
pthread_t mouse_thread;
pthread_t absmouse_thread;
pthread_t keyboard_thread;
pthread_t render_thread;
pthread_t nested_thread;
pthread_t timer_thread;
if (yutani_options.nested) {
/* Nested Yutani-Yutani mouse+keyboard */
pthread_create(&nested_thread, NULL, nested_input, yg);
} else {
/* Toaru mouse+keyboard driver */
pthread_create(&mouse_thread, NULL, mouse_input, NULL);
pthread_create(&absmouse_thread, NULL, mouse_input_abs, NULL);
pthread_create(&keyboard_thread, NULL, keyboard_input, NULL);
}
pthread_create(&render_thread, NULL, redraw, yg);
pthread_create(&timer_thread, NULL, timer_tick, yg);
@ -2208,7 +2073,108 @@ int main(int argc, char * argv[]) {
}
}
int fds[4], mfd, kfd, amfd;
mouse_device_packet_t packet;
key_event_t event;
key_event_state_t state = {0};
uint32_t last_mouse_buttons = 0;
fds[0] = fileno(server);
if (yutani_options.nested) {
fds[1] = fileno(yg->host_context->sock);
} else {
mfd = open("/dev/mouse", O_RDONLY);
kfd = open("/dev/kbd", O_RDONLY);
amfd = open("/dev/absmouse", O_RDONLY);
fds[1] = mfd;
fds[2] = kfd;
fds[3] = amfd;
}
while (1) {
if (yutani_options.nested) {
int index = syscall_fswait(2, fds);
if (index == 1) {
yutani_msg_t * m = yutani_poll(yg->host_context);
if (m) {
switch (m->type) {
case YUTANI_MSG_KEY_EVENT:
{
struct yutani_msg_key_event * ke = (void*)m->data;
yutani_msg_t * m_ = yutani_msg_build_key_event(0, &ke->event, &ke->state);
handle_key_event(yg, (struct yutani_msg_key_event *)m_->data);
free(m_);
}
break;
case YUTANI_MSG_WINDOW_MOUSE_EVENT:
{
struct yutani_msg_window_mouse_event * me = (void*)m->data;
mouse_device_packet_t packet;
packet.buttons = me->buttons;
packet.x_difference = me->new_x;
packet.y_difference = me->new_y;
yutani_msg_t * m_ = yutani_msg_build_mouse_event(0, &packet, YUTANI_MOUSE_EVENT_TYPE_ABSOLUTE);
handle_mouse_event(yg, (struct yutani_msg_mouse_event *)m_->data);
free(m_);
}
break;
case YUTANI_MSG_RESIZE_OFFER:
{
struct yutani_msg_window_resize * wr = (void*)m->data;
TRACE("Resize request from host compositor for size %dx%d", wr->width, wr->height);
yutani_window_resize_accept(yg->host_context, yg->host_window, wr->width, wr->height);
yg->resize_on_next = 1;
}
break;
case YUTANI_MSG_SESSION_END:
TRACE("Host session ended. Should exit.");
break;
default:
break;
}
}
free(m);
continue;
}
} else {
int index = syscall_fswait(amfd == -1 ? 3 : 4, fds);
if (index == 2) {
char buf[1];
int r = read(kfd, buf, 1);
if (r > 0) {
kbd_scancode(&state, buf[0], &event);
yutani_msg_t * m = yutani_msg_build_key_event(0, &event, &state);
handle_key_event(yg, (struct yutani_msg_key_event *)m->data);
free(m);
}
continue;
} else if (index == 1) {
int r = read(mfd, (char *)&packet, sizeof(mouse_device_packet_t));
if (r > 0) {
last_mouse_buttons = packet.buttons;
yutani_msg_t * m = yutani_msg_build_mouse_event(0, &packet, YUTANI_MOUSE_EVENT_TYPE_RELATIVE);
handle_mouse_event(yg, (struct yutani_msg_mouse_event *)m->data);
free(m);
}
continue;
} else if (index == 3) {
int r = read(amfd, (char *)&packet, sizeof(mouse_device_packet_t));
if (r > 0) {
packet.buttons = last_mouse_buttons & 0xF;
yutani_msg_t * m = yutani_msg_build_mouse_event(0, &packet, YUTANI_MOUSE_EVENT_TYPE_ABSOLUTE);
handle_mouse_event(yg, (struct yutani_msg_mouse_event *)m->data);
free(m);
}
continue;
}
}
pex_packet_t * p = calloc(PACKET_SIZE, 1);
pex_listen(server, p);