1/* 2 * lws-api-test-lws_smd 3 * 4 * Written in 2020 by Andy Green <andy@warmcat.com> 5 * 6 * This file is made available under the Creative Commons CC0 1.0 7 * Universal Public Domain Dedication. 8 * 9 * This api test confirms lws_smd System Message Distribution 10 */ 11 12#include <libwebsockets.h> 13#define HAVE_STRUCT_TIMESPEC 14#include <pthread.h> 15#include <signal.h> 16 17static int interrupted, ok, fail, _exp = 111; 18static unsigned int how_many_msg = 100, usec_interval = 1000; 19static lws_sorted_usec_list_t sul, sul_initial_drain; 20struct lws_context *context; 21static pthread_t thread_spam; 22 23static void 24timeout_cb(lws_sorted_usec_list_t *sul) 25{ 26 /* We should have completed the test before this fires */ 27 lwsl_notice("%s: test period finished\n", __func__); 28 interrupted = 1; 29 lws_cancel_service(context); 30} 31 32static int 33smd_cb1int(void *opaque, lws_smd_class_t _class, lws_usec_t timestamp, 34 void *buf, size_t len) 35{ 36#if 0 37 lwsl_notice("%s: ts %llu, len %d\n", __func__, 38 (unsigned long long)timestamp, (int)len); 39 lwsl_hexdump_notice(buf, len); 40#endif 41 ok++; 42 43 return 0; 44} 45 46static int 47smd_cb2int(void *opaque, lws_smd_class_t _class, lws_usec_t timestamp, 48 void *buf, size_t len) 49{ 50#if 0 51 lwsl_notice("%s: ts %llu, len %d\n", __func__, 52 (unsigned long long)timestamp, (int)len); 53 lwsl_hexdump_notice(buf, len); 54#endif 55 ok++; 56 57 return 0; 58} 59 60/* 61 * This is used in an smd participant that is deregistered before the message 62 * can be delivered, it should never see any message 63 */ 64 65static int 66smd_cb3int(void *opaque, lws_smd_class_t _class, lws_usec_t timestamp, 67 void *buf, size_t len) 68{ 69 lwsl_err("%s: Countermanded ts %llu, len %d\n", __func__, 70 (unsigned long long)timestamp, (int)len); 71 lwsl_hexdump_err(buf, len); 72 73 fail++; 74 75 return 0; 76} 77 78static void * 79_thread_spam(void *d) 80{ 81#if defined(WIN32) 82 unsigned int mypid = 0; 83#else 84 unsigned int mypid = (unsigned int)getpid(); 85#endif 86 unsigned int n = 0, atm = 0; 87 88 while (n++ < how_many_msg) { 89 90 atm++; 91 if (lws_smd_msg_printf(context, LWSSMDCL_SYSTEM_STATE, 92 "{\"s\":\"state\"," 93 "\"pid\":%u," 94 "\"msg\":%d}", 95 mypid, (unsigned int)n)) { 96 lwsl_err("%s: send attempt %d failed\n", __func__, atm); 97 n--; 98 fail++; 99 if (fail >= 3) { 100 interrupted = 1; 101 lws_cancel_service(context); 102 break; 103 } 104 } 105#if defined(WIN32) 106 Sleep(3); 107#else 108 usleep(usec_interval); 109#endif 110 } 111#if !defined(WIN32) 112 pthread_exit(NULL); 113#endif 114 115 return NULL; 116} 117 118void sigint_handler(int sig) 119{ 120 interrupted = 1; 121} 122 123static void 124drained_cb(lws_sorted_usec_list_t *sul) 125{ 126 /* 127 * spawn the test thread, it's going to spam 100 messages at 3ms 128 * intervals... check we got everything 129 */ 130 131 if (pthread_create(&thread_spam, NULL, _thread_spam, NULL)) 132 lwsl_err("%s: failed to create the spamming thread\n", __func__); 133} 134 135static int 136system_notify_cb(lws_state_manager_t *mgr, lws_state_notify_link_t *link, 137 int current, int target) 138{ 139 // struct lws_context *context = mgr->parent; 140 int n; 141 142 if (current != LWS_SYSTATE_OPERATIONAL || target != LWS_SYSTATE_OPERATIONAL) 143 return 0; 144 145 /* 146 * Overflow the message queue too see if it handles it well, both 147 * as overflowing and in recovery. These are all still going into the 148 * smd buffer dll2, since we don't break for the event loop to have a 149 * chance to deliver them. 150 */ 151 152 n = 0; 153 while (n++ < 100) 154 if (lws_smd_msg_printf(context, LWSSMDCL_SYSTEM_STATE, 155 "{\"s\":\"state\",\"test\":\"overflow\"}")) 156 break; 157 158 lwsl_notice("%s: overflow test added %d messages\n", __func__, n); 159 if (n == 100) { 160 lwsl_err("%s: didn't overflow\n", __func__); 161 interrupted = 1; 162 return 1; 163 } 164 165 /* 166 * So we have some normal messages from earlier and now the rest of the 167 * smd buffer filled with junk overflow messages. Before we start the 168 * actual spamming test from another thread, we need to return to the 169 * event loop so these can be cleared first. 170 */ 171 172 lws_sul_schedule(context, 0, &sul_initial_drain, drained_cb, 173 5 * LWS_US_PER_MS); 174 175 176 lwsl_info("%s: operational\n", __func__); 177 178 return 0; 179} 180 181int 182main(int argc, const char **argv) 183{ 184 lws_state_notify_link_t notifier = { { NULL, NULL, NULL }, 185 system_notify_cb, "app" }; 186 lws_state_notify_link_t *na[] = { ¬ifier, NULL }; 187 int logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; 188 struct lws_context_creation_info info; 189 struct lws_smd_peer *userreg; 190 const char *p; 191 void *retval; 192 193 /* the normal lws init */ 194 195 signal(SIGINT, sigint_handler); 196 197 if ((p = lws_cmdline_option(argc, argv, "-d"))) 198 logs = atoi(p); 199 200 if ((p = lws_cmdline_option(argc, argv, "--count"))) 201 how_many_msg = (unsigned int)atol(p); 202 203 if ((p = lws_cmdline_option(argc, argv, "--interval"))) 204 usec_interval = (unsigned int)atol(p); 205 206 lws_set_log_level(logs, NULL); 207 lwsl_user("LWS API selftest: lws_smd: %u msgs at %uus interval\n", 208 how_many_msg, usec_interval); 209 210 memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ 211 info.port = CONTEXT_PORT_NO_LISTEN; 212 info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; 213 info.register_notifier_list = na; 214 215 context = lws_create_context(&info); 216 if (!context) { 217 lwsl_err("lws init failed\n"); 218 return 1; 219 } 220 221 /* game over after this long */ 222 223 lws_sul_schedule(context, 0, &sul, timeout_cb, 224 (how_many_msg * (usec_interval + 1000)) + (4 * LWS_US_PER_SEC)); 225 226 /* register a messaging participant to hear INTERACTION class */ 227 228 if (!lws_smd_register(context, NULL, 0, LWSSMDCL_INTERACTION, 229 smd_cb1int)) { 230 lwsl_err("%s: smd register 1 failed\n", __func__); 231 goto bail; 232 } 233 234 /* register a messaging participant to hear SYSTEM_STATE class */ 235 236 if (!lws_smd_register(context, NULL, 0, LWSSMDCL_SYSTEM_STATE, 237 smd_cb2int)) { 238 lwsl_err("%s: smd register 2 failed\n", __func__); 239 goto bail; 240 } 241 242 /* temporarily register a messaging participant to hear a user class */ 243 244 userreg = lws_smd_register(context, NULL, 0, 1 << LWSSMDCL_USER_BASE_BITNUM, 245 smd_cb3int); 246 if (!userreg) { 247 lwsl_err("%s: smd register userclass failed\n", __func__); 248 goto bail; 249 } 250 251 /* 252 * The event loop isn't started yet, so these smd messages are getting 253 * buffered. Later we will deliberately overrun the buffer and wait 254 * for that to be cleared before the spam thread test. 255 */ 256 257 /* generate an INTERACTION class message */ 258 259 if (lws_smd_msg_printf(context, LWSSMDCL_INTERACTION, 260 "{\"s\":\"interaction\"}")) { 261 lwsl_err("%s: problem sending smd\n", __func__); 262 goto bail; 263 } 264 265 /* generate a SYSTEM_STATE class message */ 266 267 if (lws_smd_msg_printf(context, LWSSMDCL_SYSTEM_STATE, 268 "{\"s\":\"state\"}")) { 269 lwsl_err("%s: problem sending smd\n", __func__); 270 goto bail; 271 } 272 273 /* no participant listens for this class, so it should be skipped */ 274 275 if (lws_smd_msg_printf(context, LWSSMDCL_NETWORK, "{\"s\":\"network\"}")) { 276 lwsl_err("%s: problem sending smd\n", __func__); 277 goto bail; 278 } 279 280 /* generate a user class message... */ 281 282 if (lws_smd_msg_printf(context, 1 << LWSSMDCL_USER_BASE_BITNUM, 283 "{\"s\":\"userclass\"}")) { 284 lwsl_err("%s: problem sending smd\n", __func__); 285 goto bail; 286 } 287 288 /* 289 * ... and screw that user class message up by deregistering the only 290 * handler before it can deliver it... it should not get delivered 291 * and cleanly discarded 292 */ 293 294 lws_smd_unregister(userreg); 295 296 /* the usual lws event loop */ 297 298 while (!interrupted && lws_service(context, 0) >= 0) 299 ; 300 301 pthread_join(thread_spam, &retval); 302 303bail: 304 lws_context_destroy(context); 305 306 if (fail || ok >= _exp) 307 lwsl_user("Completed: PASS: %d / %d, FAIL: %d\n", ok, _exp, 308 fail); 309 else 310 lwsl_user("Completed: ALL PASS: %d / %d\n", ok, _exp); 311 312 return !(ok >= _exp && !fail); 313} 314