1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 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 29 /* 30 * Copyright 2018 Joyent, Inc. 31 */ 32 33 /* 34 * Test program for the micro event library. Set up a simple TCP echo 35 * service. 36 * 37 * cc mevent_test.c mevent.c -lpthread 38 */ 39 40 #include <sys/types.h> 41 #include <sys/stdint.h> 42 #ifdef __FreeBSD__ 43 #include <sys/sysctl.h> 44 #endif 45 #include <sys/socket.h> 46 #include <netinet/in.h> 47 #ifdef __FreeBSD__ 48 #include <machine/cpufunc.h> 49 #endif 50 51 #include <stdio.h> 52 #include <stdlib.h> 53 #include <pthread.h> 54 #include <unistd.h> 55 56 #include "mevent.h" 57 58 #define TEST_PORT 4321 59 60 static pthread_mutex_t accept_mutex = PTHREAD_MUTEX_INITIALIZER; 61 static pthread_cond_t accept_condvar = PTHREAD_COND_INITIALIZER; 62 63 static struct mevent *tevp; 64 65 66 #define MEVENT_ECHO 67 68 /* Number of timer events to capture */ 69 #define TEVSZ 4096 70 uint64_t tevbuf[TEVSZ]; 71 72 static void 73 timer_print(void) 74 { 75 uint64_t min, max, diff, sum; 76 #ifdef __FreeBSD__ 77 uint64_t tsc_freq; 78 size_t len; 79 #endif 80 int j; 81 82 min = UINT64_MAX; 83 max = 0; 84 sum = 0; 85 86 #ifdef __FreeBSD__ 87 len = sizeof(tsc_freq); 88 sysctlbyname("machdep.tsc_freq", &tsc_freq, &len, NULL, 0); 89 #endif 90 91 for (j = 1; j < TEVSZ; j++) { 92 #ifdef __FreeBSD__ 93 /* Convert a tsc diff into microseconds */ 94 diff = (tevbuf[j] - tevbuf[j-1]) * 1000000 / tsc_freq; 95 #else 96 diff = (tevbuf[j] - tevbuf[j-1]) / 1000; 97 #endif 98 sum += diff; 99 if (min > diff) 100 min = diff; 101 if (max < diff) 102 max = diff; 103 } 104 105 printf("timers done: usecs, min %ld, max %ld, mean %ld\n", min, max, 106 sum/(TEVSZ - 1)); 107 } 108 109 static void 110 timer_callback(int fd, enum ev_type type, void *param) 111 { 112 static int i; 113 114 if (i >= TEVSZ) 115 abort(); 116 117 #ifdef __FreeBSD__ 118 tevbuf[i++] = rdtsc(); 119 #else 120 tevbuf[i++] = gethrtime(); 121 #endif 122 123 if (i == TEVSZ) { 124 mevent_delete(tevp); 125 timer_print(); 126 } 127 } 128 129 130 #ifdef MEVENT_ECHO 131 struct esync { 132 pthread_mutex_t e_mt; 133 pthread_cond_t e_cond; 134 }; 135 136 static void 137 echoer_callback(int fd, enum ev_type type, void *param) 138 { 139 struct esync *sync = param; 140 141 pthread_mutex_lock(&sync->e_mt); 142 pthread_cond_signal(&sync->e_cond); 143 pthread_mutex_unlock(&sync->e_mt); 144 } 145 146 static void * 147 echoer(void *param) 148 { 149 struct esync sync; 150 struct mevent *mev; 151 char buf[128]; 152 int fd = (int)(uintptr_t) param; 153 int len; 154 155 pthread_mutex_init(&sync.e_mt, NULL); 156 pthread_cond_init(&sync.e_cond, NULL); 157 158 pthread_mutex_lock(&sync.e_mt); 159 160 mev = mevent_add(fd, EVF_READ, echoer_callback, &sync); 161 if (mev == NULL) { 162 printf("Could not allocate echoer event\n"); 163 exit(4); 164 } 165 166 while (!pthread_cond_wait(&sync.e_cond, &sync.e_mt)) { 167 len = read(fd, buf, sizeof(buf)); 168 if (len > 0) { 169 write(fd, buf, len); 170 write(0, buf, len); 171 } else { 172 break; 173 } 174 } 175 176 mevent_delete_close(mev); 177 178 pthread_mutex_unlock(&sync.e_mt); 179 pthread_mutex_destroy(&sync.e_mt); 180 pthread_cond_destroy(&sync.e_cond); 181 182 return (NULL); 183 } 184 185 #else 186 187 static void * 188 echoer(void *param) 189 { 190 char buf[128]; 191 int fd = (int)(uintptr_t) param; 192 int len; 193 194 while ((len = read(fd, buf, sizeof(buf))) > 0) { 195 write(1, buf, len); 196 } 197 198 return (NULL); 199 } 200 #endif /* MEVENT_ECHO */ 201 202 static void 203 acceptor_callback(int fd, enum ev_type type, void *param) 204 { 205 pthread_mutex_lock(&accept_mutex); 206 pthread_cond_signal(&accept_condvar); 207 pthread_mutex_unlock(&accept_mutex); 208 } 209 210 static void * 211 acceptor(void *param) 212 { 213 struct sockaddr_in sin; 214 pthread_t tid; 215 int news; 216 int s; 217 218 if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { 219 perror("cannot create socket"); 220 exit(4); 221 } 222 223 #ifdef __FreeBSD__ 224 sin.sin_len = sizeof(sin); 225 #endif 226 sin.sin_family = AF_INET; 227 sin.sin_addr.s_addr = htonl(INADDR_ANY); 228 sin.sin_port = htons(TEST_PORT); 229 230 if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) { 231 perror("cannot bind socket"); 232 exit(4); 233 } 234 235 if (listen(s, 1) < 0) { 236 perror("cannot listen socket"); 237 exit(4); 238 } 239 240 (void) mevent_add(s, EVF_READ, acceptor_callback, NULL); 241 242 pthread_mutex_lock(&accept_mutex); 243 244 while (!pthread_cond_wait(&accept_condvar, &accept_mutex)) { 245 news = accept(s, NULL, NULL); 246 if (news < 0) { 247 perror("accept error"); 248 } else { 249 static int first = 1; 250 251 if (first) { 252 /* 253 * Start a timer 254 */ 255 first = 0; 256 tevp = mevent_add(1, EVF_TIMER, timer_callback, 257 NULL); 258 } 259 260 printf("incoming connection, spawning thread\n"); 261 pthread_create(&tid, NULL, echoer, 262 (void *)(uintptr_t)news); 263 } 264 } 265 266 return (NULL); 267 } 268 269 int 270 main() 271 { 272 pthread_t tid; 273 274 pthread_create(&tid, NULL, acceptor, NULL); 275 276 mevent_dispatch(); 277 return (0); 278 } 279