From 8aca94f951f5e625503e6d70a53f4935499af2c4 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Sat, 8 Oct 2022 08:39:13 -0700 Subject: [PATCH] Add shared memory hit counter example to redbean --- test/tool/net/mapshared_test.lua | 2 +- third_party/lua/lunix.c | 22 ++++++--- tool/net/demo/.init.lua | 78 ++++++++++++++++++++++++-------- tool/net/demo/hitcounter.lua | 35 ++++++++++++++ tool/net/help.txt | 4 +- tool/net/net.mk | 2 + 6 files changed, 114 insertions(+), 29 deletions(-) create mode 100644 tool/net/demo/hitcounter.lua diff --git a/test/tool/net/mapshared_test.lua b/test/tool/net/mapshared_test.lua index 8d4f2ca6f..e29e5cda9 100644 --- a/test/tool/net/mapshared_test.lua +++ b/test/tool/net/mapshared_test.lua @@ -29,7 +29,7 @@ assert(mem:read() == 'hello') mem:write('hi') assert(mem:read() == 'hi') assert(mem:read(0, 5) == 'hi\0lo') -mem:write('H', 0, 1) +mem:write(0, 'H', 1) assert(mem:read(0, 5) == 'Hi\0lo') assert(mem:read(1, 1) == 'i') diff --git a/third_party/lua/lunix.c b/third_party/lua/lunix.c index a36b2921a..803735489 100644 --- a/third_party/lua/lunix.c +++ b/third_party/lua/lunix.c @@ -2696,19 +2696,29 @@ static int LuaUnixMemoryRead(lua_State *L) { return 1; } -// unix.Memory:write(data:str[, offset:int[, bytes:int]]) +// unix.Memory:write([offset:int,] data:str[, bytes:int]) static int LuaUnixMemoryWrite(lua_State *L) { + int b; const char *s; size_t i, n, j; struct Memory *m; m = luaL_checkudata(L, 1, "unix.Memory"); - s = luaL_checklstring(L, 2, &n); - i = luaL_optinteger(L, 3, 0); + if (!lua_isnumber(L, 2)) { + // unix.Memory:write(data:str[, bytes:int]) + i = 0; + s = luaL_checklstring(L, 2, &n); + b = 3; + } else { + // unix.Memory:write(offset:int, data:str[, bytes:int]) + i = luaL_checkinteger(L, 2); + s = luaL_checklstring(L, 3, &n); + b = 4; + } if (i > m->size) { luaL_error(L, "out of range"); unreachable; } - if (lua_isnoneornil(L, 4)) { + if (lua_isnoneornil(L, b)) { // unix.Memory:write(data:str[, offset:int]) // writes binary data, plus a nul terminator if (i < n < m->size) { @@ -2723,9 +2733,9 @@ static int LuaUnixMemoryWrite(lua_State *L) { } else { // unix.Memory:write(data:str, offset:int, bytes:int]) // writes binary data without including nul-terminator - j = luaL_checkinteger(L, 4); + j = luaL_checkinteger(L, b); if (j > n) { - luaL_argerror(L, 4, "bytes is more than what's in data"); + luaL_argerror(L, b, "bytes is more than what's in data"); unreachable; } n = j; diff --git a/tool/net/demo/.init.lua b/tool/net/demo/.init.lua index 40ff1e195..b6dbf1658 100644 --- a/tool/net/demo/.init.lua +++ b/tool/net/demo/.init.lua @@ -20,35 +20,73 @@ db:exec[[ INSERT INTO test (content) VALUES ('Hello Sqlite3'); ]] +-- shared memory hit counter +SHM_LOCK = 0 -- index zero (first eight bytes) will hold mutex +SHM_JSON = 8 -- store json payload starting at the index eight +shm = unix.mapshared(65520) +function Lock() + local ok, old = shm:cmpxchg(SHM_LOCK, 0, 1) + if not ok then + if old == 1 then + old = shm:xchg(SHM_LOCK, 2) + end + while old > 0 do + shm:wait(SHM_LOCK, 2) + old = shm:xchg(SHM_LOCK, 2) + end + end +end +function Unlock() + old = shm:fetch_add(SHM_LOCK, -1) + if old == 2 then + shm:store(SHM_LOCK, 0) + shm:wake(SHM_LOCK, 1) + end +end + function OnServerListen(fd, ip, port) - unix.setsockopt(fd, unix.SOL_TCP, unix.TCP_SAVE_SYN, true) - return false + unix.setsockopt(fd, unix.SOL_TCP, unix.TCP_SAVE_SYN, true) + return false end function OnClientConnection(ip, port, serverip, serverport) - syn, synerr = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_SAVED_SYN) + syn, synerr = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_SAVED_SYN) +end + +function UpdateHitCounter() + local s, t, k + Lock() + s = shm:read(SHM_JSON) + if s == '' then s = '{}' end + t = DecodeJson(s) + k = GetPath() + if not t[k] then t[k] = 0 end + t[k] = t[k] + 1 + shm:write(SHM_JSON, EncodeJson(t)) + Unlock() end -- this intercepts all requests if it's defined function OnHttpRequest() - if GetHeader('User-Agent') then - Log(kLogInfo, "client is running %s and reports %s" % { - finger.GetSynFingerOs(finger.FingerSyn(syn)), - VisualizeControlCodes(GetHeader('User-Agent'))}) - end - if HasParam('magic') then - Write('

\r\n') - Write('OnHttpRequest() has intercepted your request
\r\n') - Write('because you specified the magic parameter\r\n') - Write('

\r\n')
-      Write(EscapeHtml(LoadAsset('/.init.lua')))
-      Write('
\r\n') - else - Route() -- this asks redbean to do the default thing - end - SetHeader('Server', 'redbean!') + UpdateHitCounter() + if GetHeader('User-Agent') then + Log(kLogInfo, "client is running %s and reports %s" % { + finger.GetSynFingerOs(finger.FingerSyn(syn)), + VisualizeControlCodes(GetHeader('User-Agent'))}) + end + if HasParam('magic') then + Write('

\r\n') + Write('OnHttpRequest() has intercepted your request
\r\n') + Write('because you specified the magic parameter\r\n') + Write('

\r\n')
+        Write(EscapeHtml(LoadAsset('/.init.lua')))
+        Write('
\r\n') + else + Route() -- this asks redbean to do the default thing + end + SetHeader('Server', 'redbean!') end function Adder(x, y) - return x + y + return x + y end diff --git a/tool/net/demo/hitcounter.lua b/tool/net/demo/hitcounter.lua new file mode 100644 index 000000000..657ca3ccb --- /dev/null +++ b/tool/net/demo/hitcounter.lua @@ -0,0 +1,35 @@ +Write([[ +redbean mapshared demo + +

+ + redbean mapshared demo +

+

+ This page displays a unix.mapshared() hit counter of + the GetPath(). +

+
+]]) + +Lock() +s = shm:read(SHM_JSON) +if s == '' then s = '{}' end +t = DecodeJson(s) +Unlock() + +for k,v in pairs(t) do + Write('
%s
%d\n' % {EscapeHtml(k), v}) +end + +Write('
') diff --git a/tool/net/help.txt b/tool/net/help.txt index 82407b4af..b44cdc333 100644 --- a/tool/net/help.txt +++ b/tool/net/help.txt @@ -4509,7 +4509,7 @@ UNIX MODULE end end function Unlock() - old = mem:add(LOCK, -1) + old = mem:fetch_add(LOCK, -1) if old == 2 then mem:store(LOCK, 0) mem:wake(LOCK, 1) @@ -4568,7 +4568,7 @@ UNIX MODULE single lock which is used to synchronize reads and writes to that specific map. To make it scale, create additional maps. - unix.Memory:write(data:str[, offset:int[, bytes:int]]) + unix.Memory:write([offset:int,] data:str[, bytes:int]]) Writes bytes to memory region. diff --git a/tool/net/net.mk b/tool/net/net.mk index 36b79a141..4afa51bf1 100644 --- a/tool/net/net.mk +++ b/tool/net/net.mk @@ -154,6 +154,7 @@ o/$(MODE)/tool/net/demo/store-asset.lua.zip.o \ o/$(MODE)/tool/net/demo/maxmind.lua.zip.o \ o/$(MODE)/tool/net/demo/redbean.lua.zip.o \ o/$(MODE)/tool/net/demo/opensource.lua.zip.o \ +o/$(MODE)/tool/net/demo/hitcounter.lua.zip.o \ o/$(MODE)/tool/net/demo/binarytrees.lua.zip.o \ o/$(MODE)/tool/net/demo/crashreport.lua.zip.o \ o/$(MODE)/tool/net/demo/closedsource.lua.zip.o \ @@ -205,6 +206,7 @@ o/$(MODE)/tool/net/redbean-demo.com.dbg: \ o/$(MODE)/tool/net/demo/redbean.lua.zip.o \ o/$(MODE)/tool/net/demo/maxmind.lua.zip.o \ o/$(MODE)/tool/net/demo/opensource.lua.zip.o \ + o/$(MODE)/tool/net/demo/hitcounter.lua.zip.o \ o/$(MODE)/tool/net/demo/binarytrees.lua.zip.o \ o/$(MODE)/tool/net/demo/crashreport.lua.zip.o \ o/$(MODE)/tool/net/demo/closedsource.lua.zip.o \