1 /* 2 * Test pipe operations. 3 * 4 * Copyright (C) 2020, 2021, 2022, 2023 Paul Boddie <paul@boddie.org.uk> 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License as 8 * published by the Free Software Foundation; either version 2 of 9 * the License, or (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA 20 */ 21 22 #include <l4/re/env.h> 23 #include <l4/sys/err.h> 24 25 #include <thread> 26 27 #include <stdio.h> 28 #include <string.h> 29 #include <stdlib.h> 30 #include <unistd.h> /* sleep */ 31 32 #include <fsclient/client.h> 33 #include <systypes/fcntl.h> 34 #include <systypes/format.h> 35 36 #define FMToffsetd "%" pFMToffset "d" 37 38 39 40 /* Common size and iteration details. */ 41 42 const offset_t TO_TRANSFER = 600; 43 const int NUMBER_OF_ITERATIONS = 3; 44 45 46 47 /* Use the writer to fill the pipe with data. */ 48 49 static void write_pipe(file_t *writer, int number) 50 { 51 char buffer[TO_TRANSFER]; 52 long err; 53 54 /* Make writers blocking to permit synchronisation. */ 55 56 if ((err = client_set_blocking(writer, NOTIFY_SPACE_AVAILABLE))) 57 { 58 printf("Could not set pipe #%d as blocking: %s\n", number, l4sys_errtostr(err)); 59 return; 60 } 61 62 for (int loop = 0; loop < NUMBER_OF_ITERATIONS; loop++) 63 { 64 for (int region = 0; region < 26; region++) 65 { 66 memset(buffer, (int) 'a' + region, TO_TRANSFER); 67 68 offset_t nwritten = client_write(writer, buffer, TO_TRANSFER); 69 70 printf("Written " FMToffsetd "/" FMToffsetd " in #%d of %d/%d to pipe #%d...\n", nwritten, TO_TRANSFER, region, loop, 2, number); 71 } 72 73 sleep(1); 74 } 75 76 /* Flush to make the final output available. */ 77 78 client_close(writer); 79 } 80 81 /* Use the reader to obtain data from the pipe. */ 82 83 static void read_pipes(file_t *reader1, file_t *reader2) 84 { 85 offset_t totals[] = {0, 0}; 86 unsigned int active = 2; 87 long err; 88 file_t *reader; 89 90 /* Use a local notifier to wait for pipe events. */ 91 92 file_notifier_t *notifier = client_notifier_local(); 93 94 /* Register the readers for notification. */ 95 96 if ((err = client_subscribe(reader1, NOTIFY_CONTENT_AVAILABLE | NOTIFY_PEER_CLOSED, notifier)) || 97 (err = client_subscribe(reader2, NOTIFY_CONTENT_AVAILABLE | NOTIFY_PEER_CLOSED, notifier))) 98 { 99 printf("Could not subscribe to notifications: %s\n", l4sys_errtostr(err)); 100 return; 101 } 102 103 while (1) 104 { 105 char buffer[TO_TRANSFER]; 106 offset_t nread; 107 108 /* Wait for notification of content. */ 109 110 err = client_wait_files(&reader, notifier); 111 112 if (err) 113 { 114 printf("Error waiting for notifications: %s\n", l4sys_errtostr(err)); 115 client_notifier_close(notifier); 116 return; 117 } 118 119 if ((reader != reader1) && (reader != reader2)) 120 { 121 printf("Spurious notification received for %p versus %p, %p.\n", reader, reader1, reader2); 122 continue; 123 } 124 125 int p = reader == reader1 ? 0 : 1; 126 127 printf("Pipe #%d notified with conditions:%s%s\n", p + 1, file_notifications(reader) & NOTIFY_PEER_CLOSED ? " closed" : "", 128 file_notifications(reader) & NOTIFY_CONTENT_AVAILABLE ? " content" : ""); 129 130 nread = client_read(reader, buffer, TO_TRANSFER); 131 132 while (nread) 133 { 134 totals[p] += nread; 135 136 printf("Read " FMToffsetd "/" FMToffsetd ", total " FMToffsetd ", first %c, last %c, from pipe #%d...\n", nread, TO_TRANSFER, totals[p], *buffer, *(buffer + nread - 1), p + 1); 137 nread = client_read(reader, buffer, TO_TRANSFER); 138 } 139 140 /* Without any more content, a peer closed event should terminate reading 141 from the pipe. */ 142 143 if (file_notifications(reader) & NOTIFY_PEER_CLOSED) 144 { 145 active--; 146 if (!active) 147 break; 148 } 149 } 150 151 client_close(reader1); 152 client_close(reader2); 153 client_notifier_close(notifier); 154 155 for (int p = 0; p < 2; p++) 156 { 157 printf("Data received for pipe #%d: " FMToffsetd "\n", p, totals[p]); 158 printf("Data shown for pipe #%d: %s\n", p, totals[p] == TO_TRANSFER * 26 * NUMBER_OF_ITERATIONS ? "True" : "False"); 159 } 160 } 161 162 int main(void) 163 { 164 /* Obtain pipe endpoint references. Blocking will be set up manually. */ 165 166 printf("Obtaining pipes...\n"); 167 168 file_t *reader1, *reader2, *writer1, *writer2; 169 long err = client_pipe(&reader1, &writer1, O_NONBLOCK) || 170 client_pipe(&reader2, &writer2, O_NONBLOCK); 171 172 if (err) 173 { 174 printf("Could not obtain pipes: %s\n", l4sys_errtostr(err)); 175 return 1; 176 } 177 178 printf("Starting activities...\n"); 179 180 /* Schedule reader and writer threads. */ 181 182 std::thread *activities[3]; 183 184 activities[0] = new std::thread(read_pipes, reader1, reader2); 185 activities[1] = new std::thread(write_pipe, writer1, 1); 186 activities[2] = new std::thread(write_pipe, writer2, 2); 187 188 for (int i = 0; i < 3; i++) 189 activities[i]->join(); 190 191 printf("End of test.\n"); 192 return 0; 193 } 194 195 // vim: tabstop=2 expandtab shiftwidth=2