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
munch(int fd,enum ev_type ev,void * arg)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
tick(int ms,enum ev_type ev,void * arg)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
main(int argc,const char * argv[])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