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