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: lists.delete 18 * Assertion: mevent_delete() causes the total number of events to decrease 19 * 20 * Strategy: 1. Create a pipe. 21 * 2. Call mevent_add() to be notified of writes to the pipe. The 22 * callback will do nothing other than generate an error if it 23 * is called. 24 * 3. Create another pipe and add a read event watcher to it. The 25 * callback will signal a cv when called. A write to the pipe 26 * followed by a wait on the cv will ensure that async 27 * operations in mevent.c are complete. See flush_and_wait(). 28 * 4. Call flush_and_wait(), then get event count. 29 * 5. Delete the event created in step 2. 30 * 6. Call flush_and_wait(), then get event count. 31 * 7. Verify result in step 6 is one less than result in step 4. 32 */ 33 34 #include <errno.h> 35 #include <fcntl.h> 36 #include <pthread.h> 37 #include <signal.h> 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <strings.h> 41 #include <unistd.h> 42 43 #include <sys/types.h> 44 #include <sys/stat.h> 45 46 #include "testlib.h" 47 #include "mevent.h" 48 49 static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; 50 static pthread_cond_t cv = PTHREAD_COND_INITIALIZER; 51 52 static int 53 get_count(void) 54 { 55 int global = -1, change = -1, del_pending = -1; 56 int total; 57 58 test_mevent_count_lists(&global, &change, &del_pending); 59 ASSERT_INT_NEQ(("count not set"), global, -1); 60 ASSERT_INT_NEQ(("count not set"), change, -1); 61 ASSERT_INT_NEQ(("count not set"), change, -1); 62 ASSERT_INT_EQ(("pending delete not processed"), del_pending, 0); 63 64 total = global + change + del_pending; 65 66 VERBOSE(("count = %d (%d + %d + %d)", total, global, change, 67 del_pending)); 68 69 return (total); 70 } 71 72 static void 73 not_called_cb(int fd, enum ev_type ev, void *arg) 74 { 75 FAIL(("this callback should never be called")); 76 } 77 78 static void 79 flush_cb(int fd, enum ev_type ev, void *arg) 80 { 81 char buf[32]; 82 83 /* Drain the pipe */ 84 while (read(fd, buf, sizeof (buf)) > 0) 85 ; 86 87 pthread_mutex_lock(&mtx); 88 pthread_cond_signal(&cv); 89 pthread_mutex_unlock(&mtx); 90 } 91 92 void 93 flush_and_wait(int fd) 94 { 95 uint8_t msg = 42; 96 97 /* 98 * Lock taken ahead of waking flush_cb so this thread doesn't race 99 * with the event thread. 100 */ 101 pthread_mutex_lock(&mtx); 102 if (write(fd, &msg, sizeof (msg)) != sizeof (msg)) { 103 FAIL(("bad write")); 104 } 105 106 /* Wait for it to be read */ 107 pthread_cond_wait(&cv, &mtx); 108 pthread_mutex_unlock(&mtx); 109 } 110 111 int 112 main(int argc, const char *argv[]) 113 { 114 int unused_pipe[2]; 115 int flush_pipe[2]; 116 struct mevent *unused_evp, *flush_evp; 117 int count1, count2; 118 119 start_test(argv[0], 5); 120 start_event_thread(); 121 122 /* 123 * Create first pipe and related event 124 */ 125 if (pipe(unused_pipe) != 0) { 126 FAIL_ERRNO("pipe"); 127 } 128 VERBOSE(("unused_pipe[] = { %d, %d }", unused_pipe[0], unused_pipe[1])); 129 if (fcntl(unused_pipe[0], F_SETFL, O_NONBLOCK) != 0) { 130 FAIL_ERRNO("set pipe nonblocking"); 131 } 132 unused_evp = mevent_add(unused_pipe[0], EVF_READ, not_called_cb, NULL); 133 ASSERT_PTR_NEQ(("mevent_add"), unused_evp, NULL); 134 135 /* 136 * Create flush pipe and related event 137 */ 138 if (pipe(flush_pipe) != 0) { 139 FAIL_ERRNO("pipe"); 140 } 141 VERBOSE(("flush_pipe[] = { %d, %d }", flush_pipe[0], 142 flush_pipe[1])); 143 if (fcntl(flush_pipe[0], F_SETFL, O_NONBLOCK) != 0) { 144 FAIL_ERRNO("set pipe nonblocking"); 145 } 146 flush_evp = mevent_add(flush_pipe[0], EVF_READ, flush_cb, NULL); 147 ASSERT_PTR_NEQ(("mevent_add"), flush_evp, NULL); 148 149 /* Get count before delete. */ 150 flush_and_wait(flush_pipe[1]); 151 count1 = get_count(); 152 153 /* 154 * Delete the first event and flush a read after the delete is 155 * complete. 156 */ 157 if (mevent_delete(unused_evp) != 0) { 158 FAIL_ERRNO("mevent_delete"); 159 } 160 161 /* 162 * Verify count decreased. 163 */ 164 flush_and_wait(flush_pipe[1]); 165 count2 = get_count(); 166 if (count1 - 1 != count2) { 167 FAIL(("mevent_delete() did not decrease count by 1: " 168 "was %d, now %d", count1, count2)); 169 } 170 171 PASS(); 172 } 173