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