xref: /freebsd/usr.sbin/bhyve/mevent_test.c (revision 9768746b)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2011 NetApp, Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  * $FreeBSD$
29  */
30 
31 /*
32  * Test program for the micro event library. Set up a simple TCP echo
33  * service.
34  *
35  *  cc mevent_test.c mevent.c -lpthread
36  */
37 
38 #include <sys/types.h>
39 #include <sys/stdint.h>
40 #include <sys/sysctl.h>
41 #include <sys/socket.h>
42 #include <netinet/in.h>
43 #include <machine/cpufunc.h>
44 
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <pthread.h>
48 #include <unistd.h>
49 
50 #include "mevent.h"
51 
52 #define TEST_PORT	4321
53 
54 static pthread_mutex_t accept_mutex = PTHREAD_MUTEX_INITIALIZER;
55 static pthread_cond_t accept_condvar = PTHREAD_COND_INITIALIZER;
56 
57 static struct mevent *tevp;
58 
59 
60 #define MEVENT_ECHO
61 
62 /* Number of timer events to capture */
63 #define TEVSZ	4096
64 uint64_t tevbuf[TEVSZ];
65 
66 static void
67 timer_print(void)
68 {
69 	uint64_t min, max, diff, sum, tsc_freq;
70 	size_t len;
71 	int j;
72 
73 	min = UINT64_MAX;
74 	max = 0;
75 	sum = 0;
76 
77 	len = sizeof(tsc_freq);
78 	sysctlbyname("machdep.tsc_freq", &tsc_freq, &len, NULL, 0);
79 
80 	for (j = 1; j < TEVSZ; j++) {
81 		/* Convert a tsc diff into microseconds */
82 		diff = (tevbuf[j] - tevbuf[j-1]) * 1000000 / tsc_freq;
83 		sum += diff;
84 		if (min > diff)
85 			min = diff;
86 		if (max < diff)
87 			max = diff;
88 	}
89 
90 	printf("timers done: usecs, min %ld, max %ld, mean %ld\n", min, max,
91 	    sum/(TEVSZ - 1));
92 }
93 
94 static void
95 timer_callback(int fd, enum ev_type type, void *param)
96 {
97 	static int i;
98 
99 	if (i >= TEVSZ)
100 		abort();
101 
102 	tevbuf[i++] = rdtsc();
103 
104 	if (i == TEVSZ) {
105 		mevent_delete(tevp);
106 		timer_print();
107 	}
108 }
109 
110 
111 #ifdef MEVENT_ECHO
112 struct esync {
113 	pthread_mutex_t	e_mt;
114 	pthread_cond_t	e_cond;
115 };
116 
117 static void
118 echoer_callback(int fd, enum ev_type type, void *param)
119 {
120 	struct esync *sync = param;
121 
122 	pthread_mutex_lock(&sync->e_mt);
123 	pthread_cond_signal(&sync->e_cond);
124 	pthread_mutex_unlock(&sync->e_mt);
125 }
126 
127 static void *
128 echoer(void *param)
129 {
130 	struct esync sync;
131 	struct mevent *mev;
132 	char buf[128];
133 	int fd = (int)(uintptr_t) param;
134 	int len;
135 
136 	pthread_mutex_init(&sync.e_mt, NULL);
137 	pthread_cond_init(&sync.e_cond, NULL);
138 
139 	pthread_mutex_lock(&sync.e_mt);
140 
141 	mev = mevent_add(fd, EVF_READ, echoer_callback, &sync);
142 	if (mev == NULL) {
143 		printf("Could not allocate echoer event\n");
144 		exit(4);
145 	}
146 
147 	while (!pthread_cond_wait(&sync.e_cond, &sync.e_mt)) {
148 		len = read(fd, buf, sizeof(buf));
149 		if (len > 0) {
150 			write(fd, buf, len);
151 			write(0, buf, len);
152 		} else {
153 			break;
154 		}
155 	}
156 
157 	mevent_delete_close(mev);
158 
159 	pthread_mutex_unlock(&sync.e_mt);
160 	pthread_mutex_destroy(&sync.e_mt);
161 	pthread_cond_destroy(&sync.e_cond);
162 
163 	return (NULL);
164 }
165 
166 #else
167 
168 static void *
169 echoer(void *param)
170 {
171 	char buf[128];
172 	int fd = (int)(uintptr_t) param;
173 	int len;
174 
175 	while ((len = read(fd, buf, sizeof(buf))) > 0) {
176 		write(1, buf, len);
177 	}
178 
179 	return (NULL);
180 }
181 #endif /* MEVENT_ECHO */
182 
183 static void
184 acceptor_callback(int fd, enum ev_type type, void *param)
185 {
186 	pthread_mutex_lock(&accept_mutex);
187 	pthread_cond_signal(&accept_condvar);
188 	pthread_mutex_unlock(&accept_mutex);
189 }
190 
191 static void *
192 acceptor(void *param)
193 {
194 	struct sockaddr_in sin;
195 	pthread_t tid;
196 	int news;
197 	int s;
198 	static int first;
199 
200 	if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
201 		perror("cannot create socket");
202 		exit(4);
203 	}
204 
205 	sin.sin_len = sizeof(sin);
206 	sin.sin_family = AF_INET;
207 	sin.sin_addr.s_addr = htonl(INADDR_ANY);
208 	sin.sin_port = htons(TEST_PORT);
209 
210 	if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
211 		perror("cannot bind socket");
212 		exit(4);
213 	}
214 
215 	if (listen(s, 1) < 0) {
216 		perror("cannot listen socket");
217 		exit(4);
218 	}
219 
220 	(void) mevent_add(s, EVF_READ, acceptor_callback, NULL);
221 
222 	pthread_mutex_lock(&accept_mutex);
223 
224 	while (!pthread_cond_wait(&accept_condvar, &accept_mutex)) {
225 		news = accept(s, NULL, NULL);
226 		if (news < 0) {
227 			perror("accept error");
228 		} else {
229 			static int first = 1;
230 
231 			if (first) {
232 				/*
233 				 * Start a timer
234 				 */
235 				first = 0;
236 				tevp = mevent_add(1, EVF_TIMER, timer_callback,
237 						  NULL);
238 			}
239 
240 			printf("incoming connection, spawning thread\n");
241 			pthread_create(&tid, NULL, echoer,
242 				       (void *)(uintptr_t)news);
243 		}
244 	}
245 
246 	return (NULL);
247 }
248 
249 main()
250 {
251 	pthread_t tid;
252 
253 	pthread_create(&tid, NULL, acceptor, NULL);
254 
255 	mevent_dispatch();
256 }
257