1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2018 Joyent, Inc. 14 */ 15 16 /* 17 * Test: read.cancel 18 * Assertion: A read is not requeued if mevent_disable() is called while it is 19 * being handled. 20 * 21 * Strategy: 1. Create a pipe 22 * 2. Call mevent_add() to be notified of writes to the pipe. The 23 * callback will signal a cv. 24 * 3. Write to the pipe then wait for a wakeup. 25 * 4. From the read event callback, disable the event then awaken 26 * the main thread. 27 * 5. In the main thread, add a timer event that will awaken the 28 * main thread after a short delay. 29 * 5. Write to the pipe and wait to be awoken. The wakeup should 30 * come from the timer event, not the read event. 31 */ 32 33 #include <errno.h> 34 #include <fcntl.h> 35 #include <pthread.h> 36 #include <signal.h> 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <strings.h> 40 #include <unistd.h> 41 42 #include <sys/types.h> 43 #include <sys/stat.h> 44 45 #include "testlib.h" 46 #include "mevent.h" 47 48 typedef enum { 49 CB_NONE, 50 CB_READ, 51 CB_TIMER, 52 } lastwake_t; 53 54 static lastwake_t lastwake = CB_NONE; 55 56 static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; 57 static pthread_cond_t cv = PTHREAD_COND_INITIALIZER; 58 59 static struct mevent *read_event; 60 61 static void 62 munch(int fd, enum ev_type ev, void *arg) 63 { 64 ssize_t nbytes; 65 char buf[32] = { 0 }; 66 int err; 67 68 if ((nbytes = read(fd, buf, sizeof (buf))) < 0) { 69 FAIL_ERRNO("bad read"); 70 } 71 VERBOSE(("read %ld bytes '%s'", nbytes, buf)); 72 73 err = mevent_disable(read_event); 74 ASSERT_INT_EQ(("mevent_disable: ", strerror(err)), err, 0); 75 76 pthread_mutex_lock(&mtx); 77 78 ASSERT_INT_EQ(("wrong lastwake"), lastwake, CB_NONE); 79 lastwake = CB_READ; 80 81 pthread_cond_signal(&cv); 82 VERBOSE(("wakeup")); 83 84 pthread_mutex_unlock(&mtx); 85 } 86 87 static void 88 tick(int ms, enum ev_type ev, void *arg) 89 { 90 pthread_mutex_lock(&mtx); 91 92 ASSERT_INT_EQ(("wrong lastwake"), lastwake, CB_READ); 93 lastwake = CB_TIMER; 94 95 pthread_cond_signal(&cv); 96 VERBOSE(("wakeup")); 97 98 pthread_mutex_unlock(&mtx); 99 } 100 101 int 102 main(int argc, const char *argv[]) 103 { 104 int pipefds[2]; 105 struct mevent *timer; 106 ssize_t written; 107 char *msgs[] = { "first", "second" }; 108 char *msg; 109 110 start_test(argv[0], 5); 111 start_event_thread(); 112 113 if (pipe(pipefds) != 0) { 114 FAIL_ERRNO("pipe"); 115 } 116 if (fcntl(pipefds[0], F_SETFL, O_NONBLOCK) != 0) { 117 FAIL_ERRNO("set pipe nonblocking"); 118 } 119 120 /* 121 * First write 122 */ 123 msg = msgs[0]; 124 read_event = mevent_add(pipefds[0], EVF_READ, munch, msg); 125 ASSERT_PTR_NEQ(("mevent_add pipefd"), read_event, NULL); 126 127 pthread_mutex_lock(&mtx); 128 written = write(pipefds[1], msg, strlen(msg)); 129 if (written < 0) { 130 FAIL_ERRNO("bad write"); 131 } 132 ASSERT_INT64_EQ(("write '%s' failed", msg), written, strlen(msg)); 133 134 /* 135 * Wait for it to be read 136 */ 137 pthread_cond_wait(&cv, &mtx); 138 ASSERT_INT_EQ(("wrong lastwake"), lastwake, CB_READ); 139 pthread_mutex_unlock(&mtx); 140 141 /* 142 * Add timer, second write. 143 */ 144 msg = msgs[1]; 145 timer = mevent_add(50, EVF_TIMER, tick, msg); 146 ASSERT_PTR_NEQ(("mevent_add timer"), timer, NULL); 147 148 pthread_mutex_lock(&mtx); 149 written = write(pipefds[1], msg, strlen(msg)); 150 if (written < 0) { 151 FAIL_ERRNO("bad write"); 152 } 153 ASSERT_INT64_EQ(("write '%s' failed", msg), written, strlen(msg)); 154 155 /* 156 * Wait for timer to expire 157 */ 158 pthread_cond_wait(&cv, &mtx); 159 ASSERT_INT_EQ(("wrong lastwake"), lastwake, CB_TIMER); 160 pthread_mutex_unlock(&mtx); 161 162 PASS(); 163 } 164