From 7ad8332838c8e699bdeae5e8d9991af7b38bfc0e Mon Sep 17 00:00:00 2001 From: Andy Green <andy@warmcat.com> Date: Tue, 13 Mar 2018 13:13:23 +0800 Subject: [PATCH] minimal-ws-server-threads --- lib/context.c | 2 +- lib/libwebsockets.h | 3 +- .../minimal-ws-server-threads/CMakeLists.txt | 17 + .../minimal-ws-server-threads/README.md | 25 ++ .../minimal-ws-server.c | 117 ++++++ .../mount-origin/favicon.ico | Bin 0 -> 1406 bytes .../mount-origin/index.html | 90 +++++ .../mount-origin/libwebsockets.org-logo.png | Bin 0 -> 7029 bytes .../protocol_lws_minimal.c | 341 ++++++++++++++++++ 9 files changed, 593 insertions(+), 2 deletions(-) create mode 100644 minimal-examples/minimal-ws-server-threads/CMakeLists.txt create mode 100644 minimal-examples/minimal-ws-server-threads/README.md create mode 100644 minimal-examples/minimal-ws-server-threads/minimal-ws-server.c create mode 100644 minimal-examples/minimal-ws-server-threads/mount-origin/favicon.ico create mode 100644 minimal-examples/minimal-ws-server-threads/mount-origin/index.html create mode 100644 minimal-examples/minimal-ws-server-threads/mount-origin/libwebsockets.org-logo.png create mode 100644 minimal-examples/minimal-ws-server-threads/protocol_lws_minimal.c diff --git a/lib/context.c b/lib/context.c index d309906a..c78684a8 100644 --- a/lib/context.c +++ b/lib/context.c @@ -909,7 +909,7 @@ lws_cancel_service(struct lws_context *context) struct lws_context_per_thread *pt = &context->pt[0]; short m = context->count_threads; - lwsl_notice("%s\n", __func__); + lwsl_info("%s\n", __func__); while (m--) { if (pt->pipe_wsi) diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h index ea483f23..a5833475 100644 --- a/lib/libwebsockets.h +++ b/lib/libwebsockets.h @@ -2354,7 +2354,7 @@ lws_finalize_startup(struct lws_context *context); * * Returns NULL, or a pointer to the name pvo in the linked-list */ -const struct lws_protocol_vhost_options * +LWS_VISIBLE LWS_EXTERN const struct lws_protocol_vhost_options * lws_pvo_search(const struct lws_protocol_vhost_options *pvo, const char *name); LWS_VISIBLE LWS_EXTERN int @@ -6554,6 +6554,7 @@ struct lejp_ctx; #ifndef ARRAY_SIZE #define ARRAY_SIZE(_x) (sizeof(_x) / sizeof(_x[0])) #endif +#define LWS_ARRAY_SIZE(_x) (sizeof(_x) / sizeof(_x[0])) #define LEJP_FLAG_WS_KEEP 64 #define LEJP_FLAG_WS_COMMENTLINE 32 diff --git a/minimal-examples/minimal-ws-server-threads/CMakeLists.txt b/minimal-examples/minimal-ws-server-threads/CMakeLists.txt new file mode 100644 index 00000000..0849c579 --- /dev/null +++ b/minimal-examples/minimal-ws-server-threads/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 2.8) +include(CheckIncludeFile) + +set(SAMP lws-minimal-ws-server-threads) +set(SRCS minimal-ws-server.c) + +if (UNIX) + set(CMAKE_C_FLAGS "-Wall -Wsign-compare -Wignored-qualifiers -Wtype-limits -Wuninitialized -Werror -Wundef ${CMAKE_C_FLAGS}" ) +endif() + +CHECK_INCLUDE_FILE(pthread.h LWS_HAVE_PTHREAD_H) +if (NOT LWS_HAVE_PTHREAD_H) + message(FATAL_ERROR "threading support requires pthreads") +endif() + +add_executable(${SAMP} ${SRCS}) +target_link_libraries(${SAMP} -lwebsockets -pthread) diff --git a/minimal-examples/minimal-ws-server-threads/README.md b/minimal-examples/minimal-ws-server-threads/README.md new file mode 100644 index 00000000..123b7bbf --- /dev/null +++ b/minimal-examples/minimal-ws-server-threads/README.md @@ -0,0 +1,25 @@ +# lws minimal ws server (threads) + +## build + +``` + $ cmake . && make +``` + +Pthreads is required on your system. + +## usage + +``` + $ ./lws-minimal-ws-server-threads +[2018/03/13 13:09:52:2208] USER: LWS minimal ws server + threads | visit http://localhost:7681 +[2018/03/13 13:09:52:2365] NOTICE: Creating Vhost 'default' port 7681, 2 protocols, IPv6 off +``` + +Visit http://localhost:7681 on multiple browser windows + +Two asynchronous threads generate strings and add them to a ringbuffer, +signalling lws to send new entries to all the browser windows. + +This demonstrates how to safely manage asynchronously generated content +and hook it up to the lws service thread. diff --git a/minimal-examples/minimal-ws-server-threads/minimal-ws-server.c b/minimal-examples/minimal-ws-server-threads/minimal-ws-server.c new file mode 100644 index 00000000..90773454 --- /dev/null +++ b/minimal-examples/minimal-ws-server-threads/minimal-ws-server.c @@ -0,0 +1,117 @@ +/* + * lws-minimal-ws-server + * + * Copyright (C) 2018 Andy Green <andy@warmcat.com> + * + * This file is made available under the Creative Commons CC0 1.0 + * Universal Public Domain Dedication. + * + * This demonstrates a minimal ws server that can cooperate with + * other threads cleanly. Two other threads are started, which fill + * a ringbuffer with strings at 10Hz. + * + * The actual work and thread spawning etc are done in the protocol + * implementation in protocol_lws_minimal.c. + * + * To keep it simple, it serves stuff in the subdirectory "./mount-origin" of + * the directory it was started in. + * You can change that by changing mount.origin. + */ + +#include <libwebsockets.h> +#include <string.h> +#include <signal.h> + +#define LWS_PLUGIN_STATIC +#include "protocol_lws_minimal.c" + +static struct lws_protocols protocols[] = { + { "http", lws_callback_http_dummy, 0, 0 }, + LWS_PLUGIN_PROTOCOL_MINIMAL, + { NULL, NULL, 0, 0 } /* terminator */ +}; + +static int interrupted; + +static const struct lws_http_mount mount = { + /* .mount_next */ NULL, /* linked-list "next" */ + /* .mountpoint */ "/", /* mountpoint URL */ + /* .origin */ "./mount-origin", /* serve from dir */ + /* .def */ "index.html", /* default filename */ + /* .protocol */ NULL, + /* .cgienv */ NULL, + /* .extra_mimetypes */ NULL, + /* .interpret */ NULL, + /* .cgi_timeout */ 0, + /* .cache_max_age */ 0, + /* .auth_mask */ 0, + /* .cache_reusable */ 0, + /* .cache_revalidate */ 0, + /* .cache_intermediaries */ 0, + /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ + /* .mountpoint_len */ 1, /* char count */ + /* .basic_auth_login_file */ NULL, +}; + +/* + * This demonstrates how to pass a pointer into a specific protocol handler + * running on a specific vhost. In this case, it's our default vhost and + * we pass the pvo named "config" with the value a const char * "myconfig". + * + * This is the preferred way to pass configuration into a specific vhost + + * protocol instance. + */ + +static const struct lws_protocol_vhost_options pvo_ops = { + NULL, + NULL, + "config", /* pvo name */ + (void *)"myconfig" /* pvo value */ +}; + +static const struct lws_protocol_vhost_options pvo = { + NULL, /* "next" pvo linked-list */ + &pvo_ops, /* "child" pvo linked-list */ + "lws-minimal", /* protocol name we belong to on this vhost */ + "" /* ignored */ +}; + +void sigint_handler(int sig) +{ + interrupted = 1; +} + +int main(int argc, char **argv) +{ + struct lws_context_creation_info info; + struct lws_context *context; + + signal(SIGINT, sigint_handler); + + memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ + info.port = 7681; + info.mounts = &mount; + info.protocols = protocols; + info.pvo = &pvo; /* per-vhost options */ + + lws_set_log_level(LLL_ERR | LLL_WARN | LLL_NOTICE | LLL_USER + /* | LLL_INFO */ /* | LLL_DEBUG */, NULL); + + lwsl_user("LWS minimal ws server + threads | visit http://localhost:7681\n"); + + context = lws_create_context(&info); + if (!context) { + lwsl_err("lws init failed\n"); + return 1; + } + + /* start the threads that create content */ + + while (!interrupted) + if (lws_service(context, 1000)) + interrupted = 1; + + lws_context_destroy(context); + + return 0; +} diff --git a/minimal-examples/minimal-ws-server-threads/mount-origin/favicon.ico b/minimal-examples/minimal-ws-server-threads/mount-origin/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c0cc2e3dff34012ba3d4a7848a7ed17579788ec5 GIT binary patch literal 1406 zcmZQzU}Ruq5D;JhgA7&%1~CQ(1`P%V29S^f14y0&EXTkIp}-`Ry|Mg1n7g4Wf+79t z2ZrqYe+;>20vPVjzRB=<LjuF5*B2OyS3%T(6^;_4Aut*O!zKh6Nd`<zOpI)7Y@qT3 ztd|fUtO$wV;NW0nWo2b#XJ-eC65@juArUMrER39-oQ%xO%wSPMe6S*%#Q*;c3^07e Soq+*{L1HjAh{nbTi2(qS_AI0T literal 0 HcmV?d00001 diff --git a/minimal-examples/minimal-ws-server-threads/mount-origin/index.html b/minimal-examples/minimal-ws-server-threads/mount-origin/index.html new file mode 100644 index 00000000..5f6b28e4 --- /dev/null +++ b/minimal-examples/minimal-ws-server-threads/mount-origin/index.html @@ -0,0 +1,90 @@ +<meta charset="UTF-8"> +<html> + <body> + <img src="libwebsockets.org-logo.png"><br> + + <b>Minimal ws server threads example</b>.<br> + Strings generated by server threads are sent to + all browsers open on this page.<br> + The textarea show the last 50 lines received. + <br> + <br> + <textarea id=r readonly cols=40 rows=50></textarea><br> + </body> + + +<script> + +var head = 0, tail = 0, ring = new Array(); + +function get_appropriate_ws_url(extra_url) +{ + var pcol; + var u = document.URL; + + /* + * We open the websocket encrypted if this page came on an + * https:// url itself, otherwise unencrypted + */ + + if (u.substring(0, 5) == "https") { + pcol = "wss://"; + u = u.substr(8); + } else { + pcol = "ws://"; + if (u.substring(0, 4) == "http") + u = u.substr(7); + } + + u = u.split('/'); + + /* + "/xxx" bit is for IE10 workaround */ + + return pcol + u[0] + "/" + extra_url; +} + +function new_ws(urlpath, protocol) +{ + if (typeof MozWebSocket != "undefined") + return new MozWebSocket(urlpath, protocol); + + return new WebSocket(urlpath, protocol); +} + +ws = new_ws(get_appropriate_ws_url(""), "lws-minimal"); +try { + ws.onopen = function() { + document.getElementById("m").disabled = 0; + document.getElementById("b").disabled = 0; + } + + ws.onmessage =function got_packet(msg) { + var n, s = ""; + + ring[head] = msg.data + "\n"; + head = (head + 1) % 50; + if (tail == head) + tail = (tail + 1) % 50; + + n = tail; + do { + s = s + ring[n]; + n = (n + 1) % 50; + } while (n != head); + + document.getElementById("r").value = s; + document.getElementById("r").scrollTop = + document.getElementById("r").scrollHeight; + } + + ws.onclose = function(){ + document.getElementById("m").disabled = 1; + document.getElementById("b").disabled = 1; + } +} catch(exception) { + alert('<p>Error' + exception); +} + +</script> +</html> + diff --git a/minimal-examples/minimal-ws-server-threads/mount-origin/libwebsockets.org-logo.png b/minimal-examples/minimal-ws-server-threads/mount-origin/libwebsockets.org-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..2060a10c936a0959f2a5c3a6b7fa60ac324f1a95 GIT binary patch literal 7029 zcmeAS@N?(olHy`uVBq!ia0y~yV6<jnU~uMOV_;x-d~4Bt1_lO}VkgfK4h{~E8jh3> z1_lPs0*}aI1_n7j5N7;4S4f?KfkCpwHKHUqKdq!Zu_%?Hyu4g5GcUV1Ik6yBFTW^# z_B$IX1_r55o-U3d6?5LsE#DD#^y6`RXLnYS00&mlrAHE+Cm!*-a^#PWWADZV8;|Na z2K4auO6=AsTimH}P%N1t+C=J&l<%S&8=FNK9UW#VocQx^hV7o}@3zl%zsKc0eD}Tb z+~;}a>Ax-0%gW2r!a}BYbaZrVmF>;i%_Jx&C}`Sv;*FA$k`#;5=I)M;41ta_dP+)* z92JaqcXV{Hp-E;0-4@kgSGpu)A@PD?3!@#=yx^<rv$iG)3JR9W1<6n8a$O==AXi|s zA=Dv`>kjjrwX05N?Z^@o6#U9kI`5{XTe)~a%)}M{+4#b*W@hgR0!i?FbxbQ){n?Yp zXTWhoUV^cP`HzIfez$BfLBYWMfaU3BN|(ec<X=ck&<nkKf42n_59>NcF(&I(ua<{u zY*xPz9C|$5tiPk9V^{kwyUo^3HE!noJ3L*Yx=%14xOyOW#+!Af8)`*Q$H?E<(^cu> z;&R1s%CR>t?;KoK|E%JlIALOI#!R7WG7(I3It&l%H!f~S>-*=M>D%AYk>Q~c`t=FV z0`3o=vvhv9AK>nI)BEa6k45I0ir9qkgo(fFc`sbKd!F5H<uNNa7ne1v8of7Hu5)=} z@SrZ?u-3Gj5;soXEoYB&Nzo5kzU0W%9-D7RLjw7jzZE8SHyE4R&Nt-}%w&vVU9hT| z`Br{IZo}`8ulwUF;vaZC$PL}Qn{9RUTY-tajrS`ZjYGcPy0z;3thNpr#y=;xs~ke( z*RTEMGKC{_?N9DDtrg|Ip*OU8*m)RCp8gWEICk`=sD;dp4OXw(_8w_I?Q{9o>e(z? zKI;9sq41|d+-_UgJV(>G1KSRMuH3!t^_I5V)!`TJ31@A1W7}AFGW2$B;QV#3HZKs! zWPkI<?$6Ex-7D64^scsInz-zMsNND|v%|NguX@IvIo`l}V_9Rr*nwE3P5K2}c5PQs zTEzI^=Y#rduhf+su9hdeN+}j`eh~e@|G@D<`~edezB4|{t5gd*#V>JAewaJEdr`MR z>O&KggK2f=!xnxsZCu>IYar<0;^Lya&?>~=jcLFC$<@ps43zet@L6d!=gu2GAGseg zO4gN${$iT@=XczRsVdDDTeZt2R6OIFj=7T3BDa98scSs=AC%}nDNK;qAXvbV_Vk0f z%Qx4rKd;z?=({YDTqePDLF|XP((i@8v<kEfG&Z<5&f*c77`mz|Ep*mqH3g+~=Guna zAzzO&ee`3hd-bS}a}QI@vQ^xBEqWU-KACshCe-%sV)Nr~S|%?&Tf6Gl4EOu;KemRy z(Fm=zTHzj@prF?Ny6>!)J(C?n-u14ewxrzq%(HL4d)4%h`_7_O`6);4vs&=J=;WPt zc7yD<^$pt_%p2H4zwVy0Wv`+4>~{|Q53IxAdq&)^4_vaYHa2$m&TQE;LC<v`1cv^t zmVEn;d5-HlOO1lPVRshX60*3ubu0U}LUYav_8pGQ&&B+fo8+I=QsQTG+#h<?vTvK= zx7u%q_nqW5IQqMC-5T?Tk4Jy0Ej?p)<NGc48&Wf#zN($R>e<>OE-Bn+gtE4+ny0B$ z&A9)_nt4`?_Fk_Plwuo-4JY3Cb>MZxe|r_B>CDkd_m5UTy&}2d5gTjF<6NVj%MKRn z3#Kl%jdzMOXPGV5E%L48(T-SyySuiW<@zrb8LFtZb%P^g%=358ndetl?q$yrTT-o? zQLNs-&E26|!{fJu!~4lW%Pm=dl}nn<Fy*@RG&g`jok2V3Qv}x^t)=|>S`U9}T~PG6 z&K0cnf1krF)dQ`Gdt-m~HW(gQ#q?60J=4fs!@#%sC*xn8{(CGE?|vDr-m~+-M8}y+ zzw|QA-u+{*=G)mnb5-~CYwEq#O4xLL+SCIZrkL!N{cG&goV4`!|K94nyD@uWH#X{? z%oaK}M^vJRcN*85;FbBiz0NTHduO=(vC*TR(l2GIPt32K&fi(J+xNgs*SHf;cCFLO zUG!|`-JqQEN{3H|OwkO&vVT`9+<od;&Hp*$Fw?C!3U7J(&EzxQT>R$$^7PUJ&ov)1 zH`h3)ZQpakIBH(a6usK>Q_`(@lividIAP+PCT=wEiL+;QcpFP5YsEST*~z~r&5+%o zEGl{T`+<{=w?0T}9=nsqnz*{*?&<???r|OxPdJk#v~1bbmg#5SZ0J3hsdv}D@tfWr zOM?~}VQYovo3-6%cdTbJE84<q@VGJU$&43=9kSi?S>`;McRVO-;!WKTGlIqS{|K}m z7cC2sc=STH`^nbXhm!R7FAlP*uIS|dbF1pen@@I5?)NY3mEn5xto6O<mal33-+a8{ zzm#mrbNo@EA1j?u<si?s=J|_irf%C?N(+h^)0W@R*r&ZgZiBUf>4N@mm*k}{@bUgy zBzdgIoiTUshxx28!xBz!E8U=aVJl-T=Z2-z>k1tH%LNDBKXTDJsriEN(HXMe9M5yV zGE(rk-reO>!g@h|_FGfldz`FxEVHW5FNpWD|IWQmY{sL#_Lu)=pZdL3wm`F>K=S?1 zfa&+e9X5;nTH5!|PL%!9y|~!CvnPI=xc_hYBKadVPHyX=H_xiitehxr7o6DRl-#;p z^x3NF49TBgLL**0Elq6XvsZuGxIFfH$SUWo^t9_*%J-{d=Dgm!LF~zl1&>N@-C>_> zpfZ2soL4W@+5Q@zIQ==C@$K9xWj&mac)n$1p2>T!@RyOXR&wXeH=K-b`9-8<bnnJD zl`IZ^v`6xY<?+R>o99P+)!b9uyj${zRmN>c&6n$VF8x(iJy-2G$Cjz8weH-Ccx|n7 zIq^qCZZDnkO*G>>x8DBE#xLG(oH?7xc2B|lo4GS5n*H?q#8&72{>9Tk&9v~VcW=0^ zejgHE`EWk7d$Rmp|Hjz?vGZm%pWr(Xe1rLrsoLf2&RJ>yDpjw%mDz3?aQ)-VSzW$T zYP;_1PGT@+U%I}cu&VRKuNy*HvEpUS-!3(t<@j@-MR?C|d6q5D4p<0E&X2jVriCf9 zMknwpr{fLb8$QSMlY`6u-&c5Nkg%+y=HaRR5iWIeg8a@usGqfbmiy&P9Uj6t$p?<> zYQFS7=#Z1loAbdc;PT#QEnnY0{J!z^^U`LE=;_gt-|cFju)ggx{Pt7nQ&UHe$VBBM zW^AV&!@tkob#Te5;*FwAH{6^$QYG>|oImx|&zSgKC&yu>$*NT$bCMI3q#u<vZb&_O z_r_@(j@yA}+dcGiUOv_4<t~s{IvXH6d74k?`ZDXu)5TZbH&flRaNERGaUL~!#^7Dw z3>k0SzH81@w)m0zN0Du}w{q`{&6$7mY}nQBd;i~U`m=lYY5^y=hmTJiIE03}JY$fq zJ{Zjq9Qrj@a3^PVcuh%Rczwc-4xa;QKHajmU!rW4m+Eb9{_h!WuKwJHsf%Y*YJ$<v zGuArR2hXWK34ddmlBlpu_TnM#?B>1vC#?;AUD#H<I(^irxcRW!yZnXa-@M=K%$w#k zduiURX-!Y|YLpx~<g*}lVU?Iu=*+t-Z*<(1neiYe=6TY;CuRCGF8w*?s}TOv^2T9H zx!Y~~*(W|*@y}9s=c>4e68Dyd<~ZjnZ{hp6vGMm@`Ih$0%kCN;=So<7UnS?{M(zz8 zJO1=^@fe33;gdWhKI6b10XuDn?8$=nw0`WbS`rrQDbcUH{dYo-PRQ09(!nnNSEf9h z@LJj9{pk&7);HZ}Y0^F1wbh#U{-n<rK07~{ue8bkR8Oth&gU%4&P_I5?H@4n*Me2T zE=#1<rr+V6d~qwsqEfbF9Vezm6{b#`cHc`dvcvYa)BBuHZ=Nr`z%|v7`}O~CtdB}G zLXIz5aY`yKG|<8$ap~l(d)MZ*FY}nCE1FhewCg9=ZR=a1aX++LFL$31%u|WZEB9A@ zB4nz&d3LD6eHV@Dbb)&kw`09e)!hAUf9~kb>x=ixd^z1IS?am&_UWAZS!dQ23#6ZJ z*vFL-sc`=5hW?V23GaOM&nYv$ZJJW^j{i~2vx|p(ColHS$=BFyJ-g=TeeVOi9Cyub zvQQ6w^p__z=aU(i(IwM^(=Mt97b>d;_f4DRuJqr=rup89YX0(1hr(CS6FFgBIc??c z!xF-4LTAd($WanB$nQS;Vbk(iO0yG<yHjrMV!6S8b$d+R^F*es4Hv(KxwMI_ICz9< z%dZALrA_`4FV5Ywxb*k$eE;C9?^k9h?=;>iXc`z(C;Hn!VfmHW3UhAmbvqjP-DCNe zS?q#0qg?Vz<>qoL&a71TUa*?^SM;i1YZsS`POMvZmSM%Kq^(b{E`9Q(KcG<Ud)9>K zl1iJZ*j`Vm=I4F%BhyLsz4lQT?bV7(ue(lkuGBq~XlJ(L$EOu{8{Fr*&sp<}PjKSb zOGY9SZC#!$6bZ6W_BB0OVzl$>=BUuvm8;}ey_&1}HN!j8W8c=tMlaYC-#-+3@L1@x z&xfAhwnr=F`3s|F{(QRFNXF=EljFUfH@d!;S2z2L*nNIfyLv;oTixM36{`yB*pBm; ze|mNMjNiuT8H*d{t+?r<nyN1SBiC*AU%{{TnqN*z=I&7I*e)=;b57sR{9nr33@2SE z|8Qy9**7B5%kK8;u6Dk~824-b?hP+*@O_crFumF$P5SNf>KoqIWrB-8yeu|+`m*rJ zmrawhYc4$Fd!ij!-(Fi$p0up1_>i4)^!at#YMVc0`ae3m??gENtiF@;{EwQYxjlS) zVwuhI@DuM(SI$^dFh@LpVfKIJ8<x7whO7Gu!v9(?Xq~=}ec5|6*5z+rU)-j&{dQG} z!)=*h>A-{v2kq3f>vQh-RGZveBfmK((#tP*xmbw*C&?>E<%(ymnCCaeiM_So<`>u2 z*ws!y=SmywIs8eAVZUePkFT9K_qNn-xV%yIr|XS1zJ{u2osCc3v0A#9d*zDzCQ7R3 zVkLiW{cuY6sn(G<c1P3tBQxdsPW`LwOx0tmV$Uo;#B%-UrMian+(%3A_t$i{f0!k+ zL^H+lV)viaN6HSt?(d!YG)49Xy}G^M;m`Fw72MuBj}IyQkCS`buK6_n(M9>gZyx=3 z$-7V?zI^`6rJASS#O#@0aX!h?jC028fBc8TEV?$Y(fWORol*1Y^Z;esXJ3{%MYn$A zdSCf))jUq$iGSKEGC$1_`1vdOU#R?yyAeL^FGRIYC8sglYX{|7baSlL<@0$t*(UX^ z!dXFy&0eq1tG;8kzgxf1(_B2e?d+wR%`2YXC^9oKfBo{)S)<VCPQ#vktiS42(&|K( z&F#(2Fwwp6?Awf!ky6bOY<J|3=)X9BZm)B9vjzV{Hl^$4&qXrc$2_nzO6i!<^!4+N zcbnJyKR4cOZD`u3vwCrPV{p(Xy%TR@=KOo2qxq+9SFebrj;fKh`1ak|ztwL{s+D<n zThaXU?L9_&R{ruA-~9EPM$zkEoipC(+Hj<6ZLnSy?0w=hk5A!eosfKvE^E%}4Ve;a zR~=8iVAM59KC$hjxkzu!*#e_!_YO>76}9@^Q`eN(oXBmQX2CN#&xBl^n)>WA&uz|- z2eQ6~kN6*zAA1veJ5m0XPr|NM(@M5=+}NqMT9$D|)~$633%|&&c){_&xpdP}Q%1p? z47c}W_8UH%IU_4zYt0Nl_RYrE=Ia`=83l)azS68UvE03=kHJpw`(2IAYrdYid1+h7 z<ml<GuZr)@bO<$U%*%ctkigzQi{qel;^m*59d8<6ol&a1*L8yH)An<Tx<zp-maY2W zlzW}oA#{4e;{Urt&pfx@^!41ONB+vo<0EE%N(=NX<(*z<_H&g*HqR=n`3cOcW_^xt zcspTT=bqJ9uBSe->B-*{AnC@Q{Yt}i^_ht?g#G20PmC^W;<Qm$2>KJlecM%Ps#9e4 zj93>J=e7<Wfr;HNOJ;YNh#gK-GHRN0^$P3Mv-M}^N@pwoa-0#xe86?Zy-r!Z$JwX; zGgPq6J|}*<xX>#j*8aDdlF}lRl0zZAcE?1viYM<rl%8yKuyNI|sI*r~EB>Xf;lHZd z@rHd)!>iJ`AG$MqUTBGLlKtTC^6g-8SftXUGkQu%Zx7VYh{+T83A-Z|ymDrX<)X8j zR_k(JJ@#hKrxbIa8Gm^<goK7C8g;(@B(x%~ms7TV=LEY0!sk|hyj0Y+F6ykZlF}=t zIXoZEzgtxnlF6x|^t=0nsnRF?9O*x@KJhtr2GK6>1SaMr*Iey3;FskrW0qUG=vw8) ztSRxIJ32br+Pn;{1z&0^)gLZdeC{C6mfFda)oz6SI(a}@$;igo_^RTJx^pHz+m|sf z-B}P7J9m@TelbUvcY+iB-qmdhX4Md>j}@%6X})H1a`miNWh;WYwZz^h3I6nCTbrzU zM<A5*=@d<+{{j=IX+Avbs#2mGn)P#b#|clRqSlT%N@Z#uQG1pjt`>~+caih%mJj4i zQ%_s|%P6flqUWN{u^HEozT41KvSMCimUzhS#k+V{O`j6H)acQ=+!xO0ZXffW5wKlg zR>J1zCSQ(-l&|Iv);Mcdl<l)#kN5G47W?g5NjjpolSK}_n-Sb5(0g*byB?R|PkSHV z^x`wC(t<aIPW;2XLg-ldi=So{Q&cw>FK3GFyt#2|Q`(c7wpaxv-;l3&icaY;zGo~? zxv=TnoLK3JH*}a*`wQI`+#!C$KQ#YiM(oDmWA~2v<wdc|^jNA&^t|+|W!MsUb^4q~ zuQwi;A6mMxnm=vjD|Wx1KUjP+OM6zSt((8^xQ_fJBlE)|XYU_5v+r5fyOtHl&fVCj zTd;lAIYH~UtAz`ubbL9?a;0<s5t|O#n?E_XoeDUklQ(m*Y0=xat#if7_ODvik;C=n zs7_#A<6ix_4U9^g)YS~~n|R;MPAXfc&%(8TuIrQOGY*x!$~kxTU*B<^)OVp*_c$In zx>NYa)~Y^#*OZj7*|s{|pI&uF9<o`!N`J?Oz8U_9&l~D?2Hx0yQ$Bb>T}0^p!out2 z4}}f&ujwbfKXf=wl0EsHjN0^o{Z@@d>1E*oR*QqRtlU|C%<XuR9~Q#6>erf8^JW$H zt~<BsmBqQ^okA{8>>fzGdAwiHc&XO~q1y((WQ^_hpJ+XQ?99$(2~mI68tisA=$8vm zNSAr=Zg!_faL%;*yt9w?icLHmGso62{EfwWJ^#eIPjy|Ai+|5+KY#Sx9fpd-m3t!| zy!yO!b+F@U?l%F`_9v!WmayCtwT=?<c~Hw^X27TNeBusiH7Cg~!+$$9Q}fylCx;uz zUYy-jlwbJP>QZN!)`7~pO1WYcYt9|PE`6RW|Mwjz;<d4vp228zXm@_6?fd?Q&#q6Z z#P>-)bNTMHMt)l2`Gi%!>o<4EJb#qeve$fivRe9OS=-9}QOUwRT9th33a4a+x9ye4 zJbu--bMBu~!=K0hq{f<u9ox26O;0OPI=Z{GR{q7Scj}3!=TDP(?8S4woH1<YZ?kp( zCog`zjrHH-ciTPR-Tl98MrL4fPh<>p%==F>dOP{~*ynw`l*4cH=p8$A_^#j94as{d z-ao9_$;i*7Y;$XW;XdBKzlDB1GJLb=>XZK!J)YYSr_DI&88P>6^&Kf=oA{>Zp=`zV zTbw6sPuP5@w$Xp@2iF~&)i!iG8+oi@u6cXtH?w|qW)*|~@+<$ZPbqqmq4#go2f5-^ zD-ZPD`!BifU#;P@yILofYpU^C-mEUW8*BH^aIW&?_e|0%?pMG4j(-<=&S;yyXnGU( z#^gu;UbV(*DTyb}6XZH8#cnB|p8cz*W9lxpw_jgrY-6+wzxrfP`*!AEPG@i4t!te5 z_NilNc*hB8iO=^Xt*^fE%qjamY1OJ3=Xc8~ecChK<m~5n=R!)(%KuXMa(3yy18Iu+ zORuix__JB?U%TP2n=@X2-<j|%L&->Z_TNTJW?y@GR)LAk8yamcn!M0ZlFrKW{A)k` zy_R65{O3cTc1?E;Px!o`|I(@1&#tc2is-C<wrZDyi`Zmq{ztK2_o#QDc)#K1_uCT6 zUwp0Avt5nP*)!E2-=gk!XpZ9*?J0-fI4%3=a_Geop0B$T*pAf;Ow^olyxWQAeaW?& z_On70=dZ|mr6`xJd5>vVmq+n;!S9?eBH5mu-WYiA_~skkw!WdyyKbt>`<?swT;xoq z<9?;mD#_%E_dEVu2VX2q3!6J_qCS^q%i9gAMMd^fvu~CNPW*cJ_<h02r)yg^*RA9D z#Bth&+afsh|Jx^h`}rT`^ZS&0zh8SY{YkLF&y@X2rS;4i+Df${rCW73c5J!wNk-7* zpjpJdvOd+QWTo!1irmu4y~Z0Cn~8UxP`tr-jZ^DO_o_M-rOhrW0vB9DYun=V1Nb&` z^BYUDD=0A=_N(mpZyWN}L|sJA_3f<UQkHq8Jnt7T+IXF5#jA3qPvH+dm$z+@|H&LG zWyg5SqI6>aRhHZ5lfv{)&g4IzwCUiJJN+AP`2Fs^;rFiU#?ONfHDmc1%DX+PbG}_@ z$zgl`*fn86M^ABTK0i<B)sXjl4*bh2yDAplDYJT&w`+=yn*Sc|=%(Jn9(~>Z=?4AB zrFR?IA2sugVoXo)Q>o=C>&dJ4N!ah;a_!@b{^WMnyKQHCqL`vHAA}w^oMXH(`0l!6 zW*t>Yv#lOBgmT<xoWApu-+^3**4$9*;3TnvJsoEbq|7{8V69dr5#v_-isw|K%m0H< zoH!>vJ<m2H!NSt&orK`a727O7NOQ?=3KV|9Vxz7!)pqGhy~kli?H}(nea>6Bs@dT0 z@zfiZVx8jssZV^qO!}`JBXyK-Zl2dWm(`s!wm;bT?tkrpqbGeLbGL8i-^OAs6};VJ z+NAXlWgJ($S}pN8^1&<1sdqS2_>+xn_WVET^Cv1N;4WLT&|Slw5n_6WpG?zj$Tdmt zEH*i|gYO7mhu57o{>Rel?bemP^*y!cz|q_Cw`WMUo%~tw|NOCOKh{qwp16<cdE32C z;kq7PHRcVyv)3%<kUuc*;4i&A>o-$&tl8G~{qzRa@2NKw%Ga-&_bB7?$?ZNCGPiqf za3()FnRe`Yg|Gzw`6TUzYU5WS+XMR=_$8e3CO=H|skkdWdE*bcX8w<tRCv};>FqYH zJj<|a`h{avn;ioe@YWhe2I}W1>{>hR^<}=xp068pyZ0Xokvw%aUE`pmo!nbPoACD^ zSA9#r!Is<>e!VPKt9oj-bhWmb(bwZ{AH_EYZqVF$@l*Dm#s5OS>Z!}`n0qUf;hx0o zhhHxr>N6A%toRlne?cxf&+K5!)2plA1$)Z$zg*>e_?&>oNy+8>p|XJufAqfkiY}^4 zR9UvAt}r)z$tI24!5e4Xebp>+nA?!w!awx?lu7SOHu0Ta7kP%^*UGuM(t?5)9Rf7B zzwuM3R<QIx!fm+r_i0m0Aw%1{3O<n=w+TwdxwtSpf1U3LnjH#c7u@^D&Beup!zE2z zQ1GIHz|2?|7nf#-lrlj<L0+bbH(kk;{LddRFz59D`dT9f1_lOCS3j3^P6<r_^iEsv literal 0 HcmV?d00001 diff --git a/minimal-examples/minimal-ws-server-threads/protocol_lws_minimal.c b/minimal-examples/minimal-ws-server-threads/protocol_lws_minimal.c new file mode 100644 index 00000000..f648c888 --- /dev/null +++ b/minimal-examples/minimal-ws-server-threads/protocol_lws_minimal.c @@ -0,0 +1,341 @@ +/* + * ws protocol handler plugin for "lws-minimal" demonstrating multithread + * + * Copyright (C) 2010-2018 Andy Green <andy@warmcat.com> + * + * This file is made available under the Creative Commons CC0 1.0 + * Universal Public Domain Dedication. + */ + +#if !defined (LWS_PLUGIN_STATIC) +#define LWS_DLL +#define LWS_INTERNAL +#include <libwebsockets.h> +#endif + +#include <string.h> + +/* one of these created for each message in the ringbuffer */ + +struct msg { + void *payload; /* is malloc'd */ + size_t len; +}; + +/* + * One of these is created for each client connecting to us. + * + * It is ONLY read or written from the lws service thread context. + */ + +struct per_session_data__minimal { + struct per_session_data__minimal *pss_list; + struct lws *wsi; + uint32_t tail; +}; + +/* one of these is created for each vhost our protocol is used with */ + +struct per_vhost_data__minimal { + struct lws_context *context; + struct lws_vhost *vhost; + const struct lws_protocols *protocol; + + struct per_session_data__minimal *pss_list; /* linked-list of live pss*/ + pthread_t pthread_spam[2]; + + pthread_mutex_t lock_ring; /* serialize access to the ring buffer */ + struct lws_ring *ring; /* {lock_ring} ringbuffer holding unsent content */ + + const char *config; + char finished; +}; + +/* + * This runs under both lws service and "spam threads" contexts. + * Access is serialized by vhd->lock_ring. + */ + +static void +__minimal_destroy_message(void *_msg) +{ + struct msg *msg = _msg; + + free(msg->payload); + msg->payload = NULL; + msg->len = 0; +} + +/* + * This runs under the "spam thread" thread context only. + * + * We spawn two threads that generate messages with this. + * + */ + +static void * +thread_spam(void *d) +{ + struct per_vhost_data__minimal *vhd = + (struct per_vhost_data__minimal *)d; + struct msg amsg; + int len = 128, index = 1, n; + + do { + /* don't generate output if nobody connected */ + if (!vhd->pss_list) + goto wait; + + pthread_mutex_lock(&vhd->lock_ring); /* --------- ring lock { */ + + /* only create if space in ringbuffer */ + n = (int)lws_ring_get_count_free_elements(vhd->ring); + if (!n) { + lwsl_user("dropping!\n"); + goto wait_unlock; + } + + amsg.payload = malloc(LWS_PRE + len); + if (!amsg.payload) { + lwsl_user("OOM: dropping\n"); + goto wait_unlock; + } + n = lws_snprintf((char *)amsg.payload + LWS_PRE, len, + "%s: tid: %p, msg: %d", vhd->config, + (void *)pthread_self(), index++); + amsg.len = n; + n = lws_ring_insert(vhd->ring, &amsg, 1); + if (n != 1) { + __minimal_destroy_message(&amsg); + lwsl_user("dropping!\n"); + } else + /* + * This will cause a LWS_CALLBACK_EVENT_WAIT_CANCELLED + * in the lws service thread context. + */ + lws_cancel_service(vhd->context); + +wait_unlock: + pthread_mutex_unlock(&vhd->lock_ring); /* } ring lock ------- */ + +wait: + usleep(100000); + + } while (!vhd->finished); + + lwsl_notice("thread_spam %p exiting\n", (void *)pthread_self()); + + pthread_exit(NULL); +} + +/* this runs under the lws service thread context only */ + +static int +callback_minimal(struct lws *wsi, enum lws_callback_reasons reason, + void *user, void *in, size_t len) +{ + struct per_session_data__minimal *pss = + (struct per_session_data__minimal *)user; + struct per_vhost_data__minimal *vhd = + (struct per_vhost_data__minimal *) + lws_protocol_vh_priv_get(lws_get_vhost(wsi), + lws_get_protocol(wsi)); + const struct lws_protocol_vhost_options *pvo; + const struct msg *pmsg; + uint32_t oldest; + void *retval; + int n, m, r = 0; + + switch (reason) { + case LWS_CALLBACK_PROTOCOL_INIT: + /* create our per-vhost struct */ + vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), + lws_get_protocol(wsi), + sizeof(struct per_vhost_data__minimal)); + if (!vhd) + return 1; + + pthread_mutex_init(&vhd->lock_ring, NULL); + + /* recover the pointer to the globals struct */ + pvo = lws_pvo_search( + (const struct lws_protocol_vhost_options *)in, + "config"); + if (!pvo || !pvo->value) { + lwsl_err("%s: Can't find \"config\" pvo\n", __func__); + return 1; + } + vhd->config = pvo->value; + + vhd->context = lws_get_context(wsi); + vhd->protocol = lws_get_protocol(wsi); + vhd->vhost = lws_get_vhost(wsi); + + vhd->ring = lws_ring_create(sizeof(struct msg), 8, + __minimal_destroy_message); + if (!vhd->ring) { + lwsl_err("%s: failed to create ring\n", __func__); + return 1; + } + + /* start the content-creating threads */ + + for (n = 0; n < (int)LWS_ARRAY_SIZE(vhd->pthread_spam); n++) + if (pthread_create(&vhd->pthread_spam[n], NULL, + thread_spam, vhd)) { + lwsl_err("thread creation failed\n"); + r = 1; + goto init_fail; + } + break; + + case LWS_CALLBACK_PROTOCOL_DESTROY: +init_fail: + vhd->finished = 1; + for (n = 0; n < (int)LWS_ARRAY_SIZE(vhd->pthread_spam); n++) + if (vhd->pthread_spam[n]) + pthread_join(vhd->pthread_spam[n], &retval); + + if (vhd->ring) + lws_ring_destroy(vhd->ring); + + pthread_mutex_destroy(&vhd->lock_ring); + break; + + case LWS_CALLBACK_ESTABLISHED: + /* add ourselves to the list of live pss held in the vhd */ + pss->pss_list = vhd->pss_list; + vhd->pss_list = pss; + pss->tail = lws_ring_get_oldest_tail(vhd->ring); + pss->wsi = wsi; + break; + + case LWS_CALLBACK_CLOSED: + /* remove our closing pss from the list of live pss */ + lws_start_foreach_llp(struct per_session_data__minimal **, + ppss, vhd->pss_list) { + if (*ppss == pss) { + *ppss = pss->pss_list; + break; + } + } lws_end_foreach_llp(ppss, pss_list); + break; + + case LWS_CALLBACK_SERVER_WRITEABLE: + pthread_mutex_lock(&vhd->lock_ring); /* --------- ring lock { */ + + pmsg = lws_ring_get_element(vhd->ring, &pss->tail); + if (!pmsg) { + pthread_mutex_unlock(&vhd->lock_ring); /* } ring lock ------- */ + break; + } + + /* notice we allowed for LWS_PRE in the payload already */ + m = lws_write(wsi, pmsg->payload + LWS_PRE, pmsg->len, + LWS_WRITE_TEXT); + if (m < (int)pmsg->len) { + pthread_mutex_unlock(&vhd->lock_ring); /* } ring lock ------- */ + lwsl_err("ERROR %d writing to di socket\n", n); + return -1; + } + + n = lws_ring_get_oldest_tail(vhd->ring) == pss->tail; + lws_ring_consume(vhd->ring, &pss->tail, NULL, 1); + + if (n) { /* we may have been the oldest tail */ + n = 0; + oldest = pss->tail; + lws_start_foreach_llp( + struct per_session_data__minimal **, + ppss, vhd->pss_list) { + m = lws_ring_get_count_waiting_elements( + vhd->ring, &(*ppss)->tail); + if (m > n) { + n = m; + oldest = (*ppss)->tail; + } + } lws_end_foreach_llp(ppss, pss_list); + + /* this will delete any entries behind the new oldest */ + lws_ring_update_oldest_tail(vhd->ring, oldest); + } + + /* more to do? */ + if (lws_ring_get_element(vhd->ring, &pss->tail)) + /* come back as soon as we can write more */ + lws_callback_on_writable(pss->wsi); + + pthread_mutex_unlock(&vhd->lock_ring); /* } ring lock ------- */ + break; + + case LWS_CALLBACK_RECEIVE: + break; + + case LWS_CALLBACK_EVENT_WAIT_CANCELLED: + /* + * When the "spam" threads add a message to the ringbuffer, + * they create this event in the lws service thread context + * using lws_cancel_service(). + * + * We respond by scheduling a writable callback for all + * connected clients. + */ + lws_start_foreach_llp(struct per_session_data__minimal **, + ppss, vhd->pss_list) { + lws_callback_on_writable((*ppss)->wsi); + } lws_end_foreach_llp(ppss, pss_list); + break; + + case LWS_CALLBACK_TIMER: + lwsl_notice("%s: LWS_CALLBACK_TIMER\n", __func__); + lws_set_timer(wsi, 3); + break; + + default: + break; + } + + return r; +} + +#define LWS_PLUGIN_PROTOCOL_MINIMAL \ + { \ + "lws-minimal", \ + callback_minimal, \ + sizeof(struct per_session_data__minimal), \ + 128, \ + 0, NULL, 0 \ + } + +#if !defined (LWS_PLUGIN_STATIC) + +/* boilerplate needed if we are built as a dynamic plugin */ + +static const struct lws_protocols protocols[] = { + LWS_PLUGIN_PROTOCOL_MINIMAL +}; + +LWS_EXTERN LWS_VISIBLE int +init_protocol_minimal(struct lws_context *context, + struct lws_plugin_capability *c) +{ + if (c->api_magic != LWS_PLUGIN_API_MAGIC) { + lwsl_err("Plugin API %d, library API %d", LWS_PLUGIN_API_MAGIC, + c->api_magic); + return 1; + } + + c->protocols = protocols; + c->count_protocols = ARRAY_SIZE(protocols); + c->extensions = NULL; + c->count_extensions = 0; + + return 0; +} + +LWS_EXTERN LWS_VISIBLE int +destroy_protocol_minimal(struct lws_context *context) +{ + return 0; +} +#endif -- GitLab