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");
if (!lua_isnumber(L, 2)) {
// unix.Memory:write(data:str[, bytes:int])
i = 0;
s = luaL_checklstring(L, 2, &n); s = luaL_checklstring(L, 2, &n);
i = luaL_optinteger(L, 3, 0); 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,6 +20,30 @@ 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
@ -29,8 +53,22 @@ 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 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 -- this intercepts all requests if it's defined
function OnHttpRequest() function OnHttpRequest()
UpdateHitCounter()
if GetHeader('User-Agent') then if GetHeader('User-Agent') then
Log(kLogInfo, "client is running %s and reports %s" % { Log(kLogInfo, "client is running %s and reports %s" % {
finger.GetSynFingerOs(finger.FingerSyn(syn)), finger.GetSynFingerOs(finger.FingerSyn(syn)),

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 \