Add shared memory hit counter example to redbean

This commit is contained in:
Justine Tunney 2022-10-08 08:39:13 -07:00
parent 38e3ab57a6
commit 8aca94f951
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
6 changed files with 114 additions and 29 deletions

View file

@ -29,7 +29,7 @@ assert(mem:read() == 'hello')
mem:write('hi') mem:write('hi')
assert(mem:read() == 'hi') assert(mem:read() == 'hi')
assert(mem:read(0, 5) == 'hi\0lo') 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(0, 5) == 'Hi\0lo')
assert(mem:read(1, 1) == 'i') assert(mem:read(1, 1) == 'i')

View file

@ -2696,19 +2696,29 @@ static int LuaUnixMemoryRead(lua_State *L) {
return 1; 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) { static int LuaUnixMemoryWrite(lua_State *L) {
int b;
const char *s; const char *s;
size_t i, n, j; size_t i, n, j;
struct Memory *m; struct Memory *m;
m = luaL_checkudata(L, 1, "unix.Memory"); m = luaL_checkudata(L, 1, "unix.Memory");
s = luaL_checklstring(L, 2, &n); if (!lua_isnumber(L, 2)) {
i = luaL_optinteger(L, 3, 0); // 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) { if (i > m->size) {
luaL_error(L, "out of range"); luaL_error(L, "out of range");
unreachable; unreachable;
} }
if (lua_isnoneornil(L, 4)) { if (lua_isnoneornil(L, b)) {
// unix.Memory:write(data:str[, offset:int]) // unix.Memory:write(data:str[, offset:int])
// writes binary data, plus a nul terminator // writes binary data, plus a nul terminator
if (i < n < m->size) { if (i < n < m->size) {
@ -2723,9 +2733,9 @@ static int LuaUnixMemoryWrite(lua_State *L) {
} else { } else {
// unix.Memory:write(data:str, offset:int, bytes:int]) // unix.Memory:write(data:str, offset:int, bytes:int])
// writes binary data without including nul-terminator // writes binary data without including nul-terminator
j = luaL_checkinteger(L, 4); j = luaL_checkinteger(L, b);
if (j > n) { 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; unreachable;
} }
n = j; n = j;

View file

@ -20,35 +20,73 @@ db:exec[[
INSERT INTO test (content) VALUES ('Hello Sqlite3'); 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) function OnServerListen(fd, ip, port)
unix.setsockopt(fd, unix.SOL_TCP, unix.TCP_SAVE_SYN, true) unix.setsockopt(fd, unix.SOL_TCP, unix.TCP_SAVE_SYN, true)
return false return false
end end
function OnClientConnection(ip, port, serverip, serverport) 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 end
-- this intercepts all requests if it's defined -- this intercepts all requests if it's defined
function OnHttpRequest() function OnHttpRequest()
if GetHeader('User-Agent') then UpdateHitCounter()
Log(kLogInfo, "client is running %s and reports %s" % { if GetHeader('User-Agent') then
finger.GetSynFingerOs(finger.FingerSyn(syn)), Log(kLogInfo, "client is running %s and reports %s" % {
VisualizeControlCodes(GetHeader('User-Agent'))}) finger.GetSynFingerOs(finger.FingerSyn(syn)),
end VisualizeControlCodes(GetHeader('User-Agent'))})
if HasParam('magic') then end
Write('<p>\r\n') if HasParam('magic') then
Write('OnHttpRequest() has intercepted your request<br>\r\n') Write('<p>\r\n')
Write('because you specified the magic parameter\r\n') Write('OnHttpRequest() has intercepted your request<br>\r\n')
Write('<pre>\r\n') Write('because you specified the magic parameter\r\n')
Write(EscapeHtml(LoadAsset('/.init.lua'))) Write('<pre>\r\n')
Write('</pre>\r\n') Write(EscapeHtml(LoadAsset('/.init.lua')))
else Write('</pre>\r\n')
Route() -- this asks redbean to do the default thing else
end Route() -- this asks redbean to do the default thing
SetHeader('Server', 'redbean!') end
SetHeader('Server', 'redbean!')
end end
function Adder(x, y) function Adder(x, y)
return x + y return x + y
end end

View file

@ -0,0 +1,35 @@
Write([[<!doctype html>
<title>redbean mapshared demo</title>
<style>
body { padding: 1em; }
h1 a { color: inherit; text-decoration: none; }
h1 img { border: none; vertical-align: middle; }
input { margin: 1em; padding: .5em; }
pre { margin-left: 2em; }
p { word-break: break-word; max-width: 650px; }
dt { font-weight: bold; }
dd { margin-top: 1em; margin-bottom: 1em; }
.hdr { text-indent: -1em; padding-left: 1em; }
</style>
<h1>
<a href="/"><img src="/redbean.png"></a>
<a href="fetch.lua">redbean mapshared demo</a>
</h1>
<p>
This page displays a <code>unix.mapshared()</code> hit counter of
the <code>GetPath()</code>.
</p>
<dl>
]])
Lock()
s = shm:read(SHM_JSON)
if s == '' then s = '{}' end
t = DecodeJson(s)
Unlock()
for k,v in pairs(t) do
Write('<dt>%s<dd>%d\n' % {EscapeHtml(k), v})
end
Write('</dl>')

View file

@ -4509,7 +4509,7 @@ UNIX MODULE
end end
end end
function Unlock() function Unlock()
old = mem:add(LOCK, -1) old = mem:fetch_add(LOCK, -1)
if old == 2 then if old == 2 then
mem:store(LOCK, 0) mem:store(LOCK, 0)
mem:wake(LOCK, 1) mem:wake(LOCK, 1)
@ -4568,7 +4568,7 @@ UNIX MODULE
single lock which is used to synchronize reads and writes to single lock which is used to synchronize reads and writes to
that specific map. To make it scale, create additional maps. 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. Writes bytes to memory region.

View file

@ -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/maxmind.lua.zip.o \
o/$(MODE)/tool/net/demo/redbean.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/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/binarytrees.lua.zip.o \
o/$(MODE)/tool/net/demo/crashreport.lua.zip.o \ o/$(MODE)/tool/net/demo/crashreport.lua.zip.o \
o/$(MODE)/tool/net/demo/closedsource.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/redbean.lua.zip.o \
o/$(MODE)/tool/net/demo/maxmind.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/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/binarytrees.lua.zip.o \
o/$(MODE)/tool/net/demo/crashreport.lua.zip.o \ o/$(MODE)/tool/net/demo/crashreport.lua.zip.o \
o/$(MODE)/tool/net/demo/closedsource.lua.zip.o \ o/$(MODE)/tool/net/demo/closedsource.lua.zip.o \