# HG changeset patch # User Paul Boddie # Date 1625262793 -7200 # Node ID 36bfd4efefb38595dcc2de004d122701d8fcdbf9 # Parent adaea9880cce8c0849dfdf0a9d3a8e2432dfc2b4 Introduced support for subscribing to multiple file notification IRQs. diff -r adaea9880cce -r 36bfd4efefb3 libfsclient/include/fsclient/client.h --- a/libfsclient/include/fsclient/client.h Mon Jun 07 00:50:14 2021 +0200 +++ b/libfsclient/include/fsclient/client.h Fri Jul 02 23:53:13 2021 +0200 @@ -59,7 +59,9 @@ long client_set_blocking(file_t *file, notify_flags_t flags); long client_subscribe(file_t *file, notify_flags_t flags); long client_unsubscribe(file_t *file); -long client_wait(file_t *file); +long client_wait_init(file_t *file); +long client_wait_file(file_t *file); +long client_wait_files(file_t **file); EXTERN_C_END diff -r adaea9880cce -r 36bfd4efefb3 libfsclient/include/fsclient/file.h --- a/libfsclient/include/fsclient/file.h Mon Jun 07 00:50:14 2021 +0200 +++ b/libfsclient/include/fsclient/file.h Fri Jul 02 23:53:13 2021 +0200 @@ -115,7 +115,9 @@ long file_notify_subscribe(file_t *file, notify_flags_t flags); long file_notify_unsubscribe(file_t *file); -long file_notify_wait(file_t *file); +long file_notify_bind_file(file_t *file); +long file_notify_wait_file(file_t *file); +long file_notify_wait_files(file_t **file); diff -r adaea9880cce -r 36bfd4efefb3 libfsclient/lib/src/client.cc --- a/libfsclient/lib/src/client.cc Mon Jun 07 00:50:14 2021 +0200 +++ b/libfsclient/lib/src/client.cc Fri Jul 02 23:53:13 2021 +0200 @@ -20,6 +20,7 @@ */ #include +#include #include #include @@ -115,7 +116,7 @@ /* Handle an inability to access by blocking, exiting if waiting failed. */ - if (client_wait(file)) + if (client_wait_file(file)) return 0; } @@ -458,14 +459,39 @@ -/* Register for events concerning a file. */ +/* Bind and initialise files involved with notifications. */ -long client_wait(file_t *file) +long client_wait_init(file_t *file) { if (file == NULL) return -L4_EINVAL; - return file_notify_wait(file); + long err = file_notify_bind_file(file); + + if (err) + return err; + + ipc_init_irq(file->irq); + + return L4_EOK; +} + +/* Wait for events involving a specific file. */ + +long client_wait_file(file_t *file) +{ + if (file == NULL) + return -L4_EINVAL; + + return file_notify_wait_file(file); +} + +/* Wait for events concerning files, referencing a file object if an event is + delivered. */ + +long client_wait_files(file_t **file) +{ + return file_notify_wait_files(file); } diff -r adaea9880cce -r 36bfd4efefb3 libfsclient/lib/src/file.cc --- a/libfsclient/lib/src/file.cc Mon Jun 07 00:50:14 2021 +0200 +++ b/libfsclient/lib/src/file.cc Fri Jul 02 23:53:13 2021 +0200 @@ -387,24 +387,55 @@ long file_notify_unsubscribe(file_t *file) { + if (l4_is_invalid_cap(file->irq)) + return -L4_EINVAL; + + l4_irq_detach(file->irq); + client_Notification notify(file->ref); return notify.unsubscribe(); } -/* Wait for notification events on a file. */ +/* Bind a file IRQ to the current thread. */ + +long file_notify_bind_file(file_t *file) +{ + if (l4_is_invalid_cap(file->irq)) + return -L4_EINVAL; -long file_notify_wait(file_t *file) + return ipc_bind_irq(file->irq, (l4_umword_t) file, pthread_l4_cap(pthread_self())); +} + +/* Wait for a notification event on a file. */ + +long file_notify_wait_file(file_t *file) { - long err = ipc_bind_irq(file->irq, (l4_umword_t) &file->irq, pthread_l4_cap(pthread_self())); + long err = file_notify_bind_file(file); if (err) return err; - l4_msgtag_t tag = l4_irq_receive(file->irq, L4_IPC_NEVER); + return l4_error(l4_irq_receive(file->irq, L4_IPC_NEVER)); +} + +/* Wait for notification events on files. */ - l4_irq_detach(file->irq); - return l4_error(tag); +long file_notify_wait_files(file_t **file) +{ + l4_umword_t label; + l4_msgtag_t tag = l4_ipc_wait(l4_utcb(), &label, L4_IPC_NEVER); + long err = l4_error(tag); + + if (err) + *file = NULL; + else + { + label = label & ~3UL; + *file = reinterpret_cast(label); + } + + return err; } diff -r adaea9880cce -r 36bfd4efefb3 tests/dstest_pipe_client.cc --- a/tests/dstest_pipe_client.cc Mon Jun 07 00:50:14 2021 +0200 +++ b/tests/dstest_pipe_client.cc Fri Jul 02 23:53:13 2021 +0200 @@ -27,6 +27,7 @@ #include #include #include +#include /* sleep */ #include #include @@ -55,34 +56,88 @@ printf("Written %ld/%ld in #%d of %d/%d to pipe...\n", nwritten, size, region, loop, 2); } + + sleep(1); } /* Flush to make the final output available. */ client_flush(writer); + + file_close(writer); } /* Use the reader to obtain data from the pipe. */ -static void read_pipe(file_t *reader) +static void read_pipes(file_t *reader1, file_t *reader2) { - offset_t size = 600; + offset_t size = 600, totals[] = {0, 0}; + bool active[] = {true, true}; + int num_active = 2; char buffer[size]; offset_t nread; + file_t *reader; + long err; - do + if ((err = client_wait_init(reader1)) || (err = client_wait_init(reader2))) + { + printf("Could not initialise waiting for files: %s\n", l4sys_errtostr(err)); + return; + } + + while (1) { + /* Wait for notification of content. */ + + printf("Waiting...\n"); + long err = client_wait_files(&reader); + + if (err) + { + printf("Error waiting for notifications: %s\n", l4sys_errtostr(err)); + return; + } + + if ((reader != reader1) && (reader != reader2)) + { + printf("Spurious notification received for %p versus %p, %p.\n", reader, reader1, reader2); + continue; + } + nread = client_read(reader, buffer, size); - printf("Read %ld/%ld from pipe...\n", nread, size); + // NOTE: Should really be testing for the condition somehow. - for (offset_t i = 0; i < nread; i += 60) + int p = reader == reader1 ? 0 : 1; + + if (!nread) { - fwrite(buffer + i, sizeof(char), nread - i > 60 ? 60 : nread - i, stdout); - fputs("\n", stdout); + if (active[p]) + { + active[p] = false; + num_active--; + + if (!num_active) + break; + } } + + do + { + totals[p] += nread; + + printf("Read %ld/%ld, total %ld, from pipe #%d...\n", nread, size, totals[p], p + 1); +#if 0 + for (offset_t i = 0; i < nread; i += 60) + { + fwrite(buffer + i, sizeof(char), nread - i > 60 ? 60 : nread - i, stdout); + fputs("\n", stdout); + } +#endif + nread = client_read(reader, buffer, size); + } + while (nread); } - while (nread); printf("Data shown.\n"); } @@ -95,19 +150,29 @@ /* Invoke the open method to receive the file reference. */ - file_t reader, writer; - long err = pipe_open(page(PIPE_PAGES), &reader, &writer, server); + file_t reader1, reader2, writer1, writer2; + long err = pipe_open(page(PIPE_PAGES), &reader1, &writer1, server) || + pipe_open(page(PIPE_PAGES), &reader2, &writer2, server); if (err) { - printf("Could not obtain pipe: %s\n", l4sys_errtostr(err)); + printf("Could not obtain pipes: %s\n", l4sys_errtostr(err)); return 1; } - /* Make the reader and writer blocking to permit synchronisation. */ + /* Register the readers for notification. */ - if ((err = client_set_blocking(&reader, NOTIFY_CONTENT_AVAILABLE)) || - (err = client_set_blocking(&writer, NOTIFY_SPACE_AVAILABLE))) + if ((err = client_subscribe(&reader1, NOTIFY_CONTENT_AVAILABLE | NOTIFY_PEER_CLOSED)) || + (err = client_subscribe(&reader2, NOTIFY_CONTENT_AVAILABLE | NOTIFY_PEER_CLOSED))) + { + printf("Could not subscribe to notifications: %s\n", l4sys_errtostr(err)); + return 1; + } + + /* Make the writers blocking to permit synchronisation. */ + + if ((err = client_set_blocking(&writer1, NOTIFY_SPACE_AVAILABLE)) || + (err = client_set_blocking(&writer2, NOTIFY_SPACE_AVAILABLE))) { printf("Could not set as blocking: %s\n", l4sys_errtostr(err)); return 1; @@ -115,12 +180,13 @@ /* Schedule reader and writer threads. */ - std::thread *activities[2]; + std::thread *activities[3]; - activities[0] = new std::thread(read_pipe, &reader); - activities[1] = new std::thread(write_pipe, &writer); + activities[0] = new std::thread(read_pipes, &reader1, &reader2); + activities[1] = new std::thread(write_pipe, &writer1); + activities[2] = new std::thread(write_pipe, &writer2); - for (int i = 0; i < 2; i++) + for (int i = 0; i < 3; i++) activities[i]->join(); }