├── VERSION ├── src ├── .gitignore ├── isa │ └── isapi │ │ ├── isapi_dll.def │ │ ├── isapi_dll.c │ │ └── isapi_ecb.c ├── luasys.h ├── event │ ├── select.h │ ├── poll.h │ ├── epoll.h │ ├── kqueue.h │ ├── timeout.h │ ├── signal.h │ ├── evq.c │ ├── win32.h │ ├── evq.h │ ├── win32sig.c │ ├── timeout.c │ ├── select.c │ ├── poll.c │ ├── epoll.c │ ├── win32thr.c │ ├── kqueue.c │ ├── signal.c │ └── win32iocr.c ├── thread │ ├── thread_affin_mach.c │ ├── thread_affin.c │ ├── thread_dpool.c │ └── thread_sync.c ├── msvcbuild.bat ├── sys_unix.c ├── sys_env.c ├── sys_rand.c ├── Makefile ├── win32 │ ├── win32_utf8.c │ ├── getloadavg.c │ ├── sys_win32.c │ └── win32_svc.c ├── sys_log.c ├── luasys.c └── mem │ └── membuf.c ├── test ├── sock │ ├── trace.sh │ ├── unixsend.lua │ ├── unixrecv.lua │ ├── udpsend.lua │ ├── mcastrecv.lua │ ├── udprecv.lua │ ├── echoclnt.lua │ ├── mcastsend.lua │ ├── daytimeclnt.lua │ ├── connect.lua │ ├── udpsendraw.lua │ ├── manyclnt.lua │ ├── echosrvr.lua │ └── echosrvr_co.lua ├── isa │ ├── isapi.lua │ └── fcgi.lua ├── libuv │ ├── benchmark-million-timers.lua │ └── benchmark-thread.lua ├── thread │ ├── test_thread.lua │ ├── dinner.lua │ ├── dpool.lua │ ├── affin.lua │ ├── sched │ │ ├── bench.lua │ │ ├── test_sched.lua │ │ └── test_evq.lua │ ├── stdin.lua │ └── vms.lua ├── win32 │ ├── test_win32.lua │ └── beep_svc.lua ├── event │ ├── test_time.lua │ ├── bench.lua │ ├── bench_cascade.lua │ └── test_evq.lua ├── test_pid.lua ├── mem │ ├── bench.lua │ └── test_mem.lua └── test_sys.lua ├── README └── COPYRIGHT /VERSION: -------------------------------------------------------------------------------- 1 | 1.8 -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | *.a 2 | *.o 3 | *.dll 4 | *.so 5 | -------------------------------------------------------------------------------- /test/sock/trace.sh: -------------------------------------------------------------------------------- 1 | 2 | strace -r -T -x -e raw=poll,select -o trace.log ./echosrvr.lua 3 | -------------------------------------------------------------------------------- /src/isa/isapi/isapi_dll.def: -------------------------------------------------------------------------------- 1 | EXPORTS 2 | GetExtensionVersion 3 | TerminateExtension 4 | HttpExtensionProc 5 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | LuaSys is a portable Lua library providing access to 2 | system and networking functions. 3 | 4 | Also includes: event notification mechanism, 5 | win32 specifics (registry, service), serial communication, 6 | signals, threading and much more. 7 | 8 | -- 9 | Nodir Temirkhodjaev, 10 | -------------------------------------------------------------------------------- /src/luasys.h: -------------------------------------------------------------------------------- 1 | #ifndef LUASYS_H 2 | #define LUASYS_H 3 | 4 | #define LUA_SYSLIBNAME "sys" 5 | LUALIB_API int luaopen_sys (lua_State *L); 6 | 7 | #define LUA_SOCKLIBNAME "sys.sock" 8 | LUALIB_API int luaopen_sys_sock (lua_State *L); 9 | 10 | #define LUA_ISAPILIBNAME "sys.isapi" 11 | LUALIB_API int luaopen_sys_isapi (lua_State *L); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /test/sock/unixsend.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | local sys = require"sys" 4 | local sock = require"sys.sock" 5 | 6 | 7 | local path = "/tmp/test_sock_lua" 8 | 9 | local fd = sock.handle() 10 | assert(fd:socket("dgram", "unix")) 11 | 12 | local saddr = assert(sock.addr():file(path)) 13 | assert(fd:connect(saddr)) 14 | 15 | fd:send(sys.stdin:read(13)) 16 | -------------------------------------------------------------------------------- /test/sock/unixrecv.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | local sys = require"sys" 4 | local sock = require"sys.sock" 5 | 6 | 7 | local path = "/tmp/test_sock_lua" 8 | 9 | local fd = sock.handle() 10 | assert(fd:socket("dgram", "unix")) 11 | 12 | local saddr = assert(sock.addr():file(path)) 13 | assert(fd:bind(saddr)) 14 | 15 | print(fd:recv(13)) 16 | 17 | sys.remove(path) 18 | -------------------------------------------------------------------------------- /test/sock/udpsend.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | local sys = require"sys" 4 | local sock = require"sys.sock" 5 | 6 | 7 | local port = 1313 8 | local host = "127.0.0.1" 9 | 10 | local fd = sock.handle() 11 | assert(fd:socket("dgram")) 12 | 13 | local saddr = assert(sock.addr()) 14 | assert(saddr:inet(port, sock.inet_pton(host))) 15 | 16 | assert(fd:connect(saddr)) 17 | 18 | assert(fd:send(sys.stdin:read(13))) 19 | -------------------------------------------------------------------------------- /test/sock/mcastrecv.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | local sys = require"sys" 4 | local sock = require"sys.sock" 5 | 6 | 7 | local MCAST_ADDR = "234.5.6.7" 8 | local MCAST_PORT = 25000 9 | 10 | local fd = sock.handle() 11 | assert(fd:socket("dgram")) 12 | 13 | assert(fd:sockopt("reuseaddr", 1)) 14 | assert(fd:bind(sock.addr():inet(MCAST_PORT))) 15 | 16 | assert(fd:membership(sock.inet_pton(MCAST_ADDR))) 17 | 18 | while true do 19 | sys.stdout:write(fd:recv()) 20 | end 21 | -------------------------------------------------------------------------------- /test/sock/udprecv.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | local sys = require"sys" 4 | local sock = require"sys.sock" 5 | 6 | 7 | local port = 1313 8 | local host = "127.0.0.1" 9 | 10 | local fd = sock.handle() 11 | assert(fd:socket("dgram")) 12 | 13 | local saddr = assert(sock.addr()) 14 | assert(saddr:inet(port, sock.inet_pton(host))) 15 | 16 | assert(fd:bind(saddr)) 17 | 18 | while true do 19 | local line = fd:recv(13) 20 | print(line or "== NO DATA/PAYLOAD ==") 21 | end 22 | -------------------------------------------------------------------------------- /test/sock/echoclnt.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | local sys = require"sys" 4 | local sock = require"sys.sock" 5 | 6 | 7 | local host, port = "127.0.0.1", 8080 8 | 9 | local stdin, stdout = sys.stdin, sys.stdout 10 | 11 | local fd = sock.handle() 12 | assert(fd:socket()) 13 | 14 | assert(fd:connect(sock.addr():inet(port, sock.inet_pton(host)))) 15 | 16 | while true do 17 | local line = stdin:read() 18 | if not fd:write(line) then break end 19 | line = fd:read() 20 | if not line then break end 21 | stdout:write(line) 22 | end 23 | -------------------------------------------------------------------------------- /test/sock/mcastsend.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | local sys = require"sys" 4 | local sock = require"sys.sock" 5 | 6 | 7 | local MCAST_ADDR = "234.5.6.7" 8 | local MCAST_PORT = 25000 9 | 10 | local fd = sock.handle() 11 | assert(fd:socket("dgram")) 12 | 13 | assert(fd:sockopt("reuseaddr", 1)) 14 | 15 | local saddr = sock.addr():inet(MCAST_PORT + 1) 16 | assert(fd:bind(saddr)) 17 | 18 | sys.stdout:write("Enter text: ") 19 | local line = sys.stdin:read() 20 | 21 | saddr:inet(MCAST_PORT, sock.inet_pton(MCAST_ADDR)) 22 | assert(fd:send(line, saddr)) 23 | -------------------------------------------------------------------------------- /src/event/select.h: -------------------------------------------------------------------------------- 1 | #ifndef EVQ_SELECT_H 2 | #define EVQ_SELECT_H 3 | 4 | #define EVQ_SOURCE "select.c" 5 | 6 | #define EVENT_EXTRA \ 7 | struct event_queue *evq; \ 8 | unsigned int index; 9 | 10 | #define EVQ_EXTRA \ 11 | struct timeout_queue *tq; \ 12 | pthread_mutex_t sig_cs; \ 13 | int volatile sig_ready; /* triggered signals */ \ 14 | fd_t sig_fd[2]; /* pipe to interrupt the loop */ \ 15 | unsigned int npolls, max_fd; \ 16 | struct event *events[FD_SETSIZE]; \ 17 | fd_set readset, writeset; 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /test/sock/daytimeclnt.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | local sys = require"sys" 4 | local sock = require"sys.sock" 5 | 6 | 7 | local host, port = "127.0.0.1", 13 8 | local MAX_MSG_LEN = 100 9 | 10 | local fd = sock.handle() 11 | assert(fd:socket("dgram")) 12 | 13 | local saddr = sock.addr() 14 | assert(saddr:inet(port, sock.inet_pton(host))) 15 | 16 | fd:nonblocking(true) 17 | if fd:send("get time", saddr) then 18 | local data = assert(fd:recv(MAX_MSG_LEN, saddr)) 19 | port, host = saddr:inet() 20 | print(sock.inet_ntop(host) .. ":" .. port .. "> " .. data) 21 | end 22 | -------------------------------------------------------------------------------- /src/event/poll.h: -------------------------------------------------------------------------------- 1 | #ifndef EVQ_POLL_H 2 | #define EVQ_POLL_H 3 | 4 | #include 5 | 6 | #define EVQ_SOURCE "poll.c" 7 | 8 | #define NEVENT 64 9 | 10 | #define EVENT_EXTRA \ 11 | struct event_queue *evq; \ 12 | unsigned int index; 13 | 14 | #define EVQ_EXTRA \ 15 | struct timeout_queue *tq; \ 16 | pthread_mutex_t sig_cs; \ 17 | int volatile sig_ready; /* triggered signals */ \ 18 | fd_t sig_fd[2]; /* pipe to interrupt the loop */ \ 19 | unsigned int npolls, max_polls; \ 20 | struct event **events; \ 21 | struct pollfd *fdset; 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /test/isa/isapi.lua: -------------------------------------------------------------------------------- 1 | -- Lua Internet Server Application: ISAPI Extension Launcher 2 | 3 | local root = ... 4 | 5 | -- Set C-modules placement 6 | do 7 | local cpath = root:gsub([[^\\%?\]], "") 8 | 9 | package.cpath = package.cpath 10 | .. cpath .. "\\?.dll;" 11 | .. cpath .. "\\?51.dll;" 12 | .. cpath .. "\\lua5.1.dll;" 13 | end 14 | 15 | 16 | local sys = require("sys") 17 | 18 | sys.thread.init() 19 | 20 | 21 | local function process(ecb) 22 | local path = ecb:getvar"PATH_TRANSLATED" 23 | local chunk, err = loadfile(path) 24 | if err then error(err) end 25 | chunk(ecb) 26 | end 27 | 28 | return process 29 | -------------------------------------------------------------------------------- /src/event/epoll.h: -------------------------------------------------------------------------------- 1 | #ifndef EVQ_EPOLL_H 2 | #define EVQ_EPOLL_H 3 | 4 | #include 5 | #include 6 | 7 | #ifdef USE_EVENTFD 8 | #include 9 | #define NSIG_FD 1 10 | #else 11 | #define NSIG_FD 2 12 | #endif 13 | 14 | #define EVQ_SOURCE "epoll.c" 15 | 16 | #define NEVENT 64 17 | 18 | #define EVENT_EXTRA \ 19 | struct event_queue *evq; 20 | 21 | #define EVQ_EXTRA \ 22 | struct timeout_queue *tq; \ 23 | pthread_mutex_t sig_cs; \ 24 | int volatile sig_ready; /* triggered signals */ \ 25 | fd_t sig_fd[NSIG_FD]; /* eventfd or pipe to interrupt the loop */ \ 26 | int epoll_fd; /* epoll descriptor */ 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /src/event/kqueue.h: -------------------------------------------------------------------------------- 1 | #ifndef EVQ_KQUEUE_H 2 | #define EVQ_KQUEUE_H 3 | 4 | #include 5 | 6 | #define EVQ_SOURCE "kqueue.c" 7 | 8 | #define NEVENT 64 9 | 10 | #define EVENT_EXTRA \ 11 | struct event_queue *evq; 12 | 13 | #define EVQ_EXTRA \ 14 | struct timeout_queue *tq; \ 15 | pthread_mutex_t sig_cs; \ 16 | int volatile sig_ready; /* triggered signals */ \ 17 | fd_t sig_fd[2]; /* pipe to interrupt the loop */ \ 18 | int kqueue_fd; /* kqueue descriptor */ \ 19 | unsigned int nchanges; \ 20 | struct kevent kev_list[NEVENT]; 21 | 22 | #if defined (__NetBSD__) 23 | typedef intptr_t kev_udata_t; 24 | #else 25 | typedef void * kev_udata_t; 26 | #endif 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /src/event/timeout.h: -------------------------------------------------------------------------------- 1 | #ifndef EVQ_TIMEOUT_H 2 | #define EVQ_TIMEOUT_H 3 | 4 | /* 5 | * Timer values are spread in small range (usually several minutes) 6 | * and overflow each 49.7 days. 7 | */ 8 | 9 | #define MIN_TIMEOUT 10 /* milliseconds */ 10 | #define MAX_TIMEOUT (~0U >> 1) /* milliseconds */ 11 | 12 | struct timeout_queue { 13 | struct timeout_queue *tq_prev, *tq_next; 14 | struct event *ev_head, *ev_tail; 15 | msec_t msec; 16 | }; 17 | 18 | /* Helper function to fast lookup */ 19 | typedef struct timeout_queue *(*timeout_map_fn) (struct event_queue *evq, 20 | struct timeout_queue *tq, 21 | const msec_t msec, 22 | const int is_remove); 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /src/thread/thread_affin_mach.c: -------------------------------------------------------------------------------- 1 | /* Lua System: Threading: CPU Affinity: Mac OS X */ 2 | 3 | #include 4 | 5 | #define USE_MACH_AFFIN 6 | 7 | 8 | static int 9 | affin_cpu_set (mach_port_t tid, int cpu) 10 | { 11 | struct thread_affinity_policy ap; 12 | int res; 13 | 14 | ap.affinity_tag = cpu; 15 | 16 | res = thread_policy_set(tid, THREAD_AFFINITY_POLICY, 17 | (thread_policy_t) &ap, THREAD_AFFINITY_POLICY_COUNT); 18 | 19 | return (res == KERN_SUCCESS) ? 0 : -1; 20 | } 21 | 22 | /* 23 | * Returns: number_of_processors (number) 24 | */ 25 | static int 26 | affin_nprocs (lua_State *L) 27 | { 28 | const int n = sysconf(_SC_NPROCESSORS_ONLN); 29 | if (n < 1) return 0; 30 | lua_pushinteger(L, n); 31 | return 1; 32 | } 33 | 34 | 35 | #define AFFIN_METHODS \ 36 | {"nprocs", affin_nprocs} 37 | -------------------------------------------------------------------------------- /test/libuv/benchmark-million-timers.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | local sys = require("sys") 4 | 5 | 6 | local NUM_TIMERS = 1000 * 1000 7 | 8 | local timer_cb_called = 0 9 | 10 | 11 | local function timer_cb(evq) 12 | timer_cb_called = timer_cb_called + 1 13 | end 14 | 15 | 16 | local function million_timers() 17 | local evq = assert(sys.event_queue()) 18 | local period = sys.period() 19 | 20 | local timeout = 0 21 | for i = 1, NUM_TIMERS do 22 | if (i % 1000 == 0) then timeout = timeout + 1 end 23 | if not evq:add_timer(timer_cb, timeout, true) then 24 | error(SYS_ERR) 25 | end 26 | end 27 | 28 | period:start() 29 | assert(evq:loop()) 30 | local duration = period:get() / 1e6 31 | 32 | assert(timer_cb_called == NUM_TIMERS) 33 | 34 | print(duration .. " seconds") 35 | 36 | return 0 37 | end 38 | 39 | 40 | return million_timers() 41 | -------------------------------------------------------------------------------- /test/sock/connect.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | local sys = require"sys" 4 | local sock = require"sys.sock" 5 | 6 | 7 | local host, port = "127.0.0.1", 8080 8 | 9 | local stdin, stdout = sys.stdin, sys.stdout 10 | 11 | local fd = sock.handle() 12 | assert(fd:socket()) 13 | assert(fd:nonblocking(true)) 14 | 15 | local addr = assert(sock.getaddrinfo(host)) 16 | 17 | local res, err = fd:connect(sock.addr():inet(port, addr)) 18 | if res == nil then error(err) end 19 | 20 | if not res then 21 | local function on_connect(evq, evid, fd, ev) 22 | if ev == 't' then error"Timeout" end 23 | 24 | local errno = fd:sockopt("error") 25 | if errno ~= 0 then 26 | error(sys.strerror(errno)) 27 | end 28 | end 29 | 30 | local evq = assert(sys.event_queue()) 31 | 32 | assert(evq:add_socket(fd, "connect", on_connect, 5000)) 33 | 34 | evq:loop() 35 | end 36 | 37 | print("OK") 38 | -------------------------------------------------------------------------------- /test/thread/test_thread.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | local sys = require"sys" 4 | 5 | local thread = sys.thread 6 | 7 | thread.init() 8 | 9 | 10 | print"-- Thread:Coroutine interruption" 11 | do 12 | local function work() 13 | while true do 14 | thread.sleep(5) 15 | end 16 | end 17 | 18 | local function test() 19 | local co = assert(coroutine.create(work)) 20 | local _, err = coroutine.resume(co) 21 | if err and not (thread.self():interrupted() 22 | and err == thread.interrupt_error()) then 23 | print("Error:", err) 24 | err = nil 25 | end 26 | if not err then 27 | error("Thread Interrupt Error expected") 28 | end 29 | end 30 | 31 | local td = assert(thread.run(test)) 32 | 33 | thread.switch() 34 | td:interrupt() 35 | assert(td:wait() == 0) 36 | print"OK" 37 | end 38 | 39 | 40 | assert(thread.self():wait()) 41 | -------------------------------------------------------------------------------- /test/win32/test_win32.lua: -------------------------------------------------------------------------------- 1 | 2 | local sys = require("sys") 3 | 4 | local win32 = assert(sys.win32, "Windows 9x/NT required") 5 | 6 | 7 | print"-- Mailslot" 8 | do 9 | local fd = sys.handle() 10 | assert(fd:mailslot([[\\.\mailslot\luasys]])) 11 | print(fd:get_mailslot_info()) 12 | fd:close() 13 | print"OK" 14 | end 15 | 16 | 17 | print"-- Registry" 18 | do 19 | local r = win32.registry() 20 | assert(r:open("HKEY_LOCAL_MACHINE", 21 | [[Software\Microsoft\Windows\CurrentVersion\Setup]])) 22 | for i, k, v in r:values() do 23 | sys.stdout:write(k, ' = "', v, '"\n') 24 | end 25 | if r:set("TEST_SET", 666) then 26 | error("'Access denied.' expected") 27 | end 28 | r:close() 29 | print("OK") 30 | end 31 | 32 | 33 | print"-- Drive DOS Names List" 34 | do 35 | for drive, type in sys.dir("/") do 36 | print(drive, type, win32.drive_dosname(drive)) 37 | end 38 | print("OK") 39 | end 40 | 41 | 42 | -------------------------------------------------------------------------------- /test/libuv/benchmark-thread.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | local sys = require("sys") 4 | 5 | local thread = sys.thread 6 | thread.init() 7 | 8 | 9 | local NUM_THREADS = 20 * 1000 10 | 11 | local num_threads = 0 12 | 13 | 14 | local function thread_entry(arg) 15 | assert(arg == 42) 16 | num_threads = num_threads + 1 17 | end 18 | 19 | 20 | local function thread_create() 21 | local period = sys.period() 22 | 23 | period:start() 24 | 25 | for i = 1, NUM_THREADS do 26 | local tid = thread.run(thread_entry, 42) 27 | if not tid then 28 | error(SYS_ERR) 29 | end 30 | 31 | if not tid:wait() then 32 | error(SYS_ERR) 33 | end 34 | end 35 | 36 | local duration = period:get() / 1e6 37 | 38 | assert(num_threads == NUM_THREADS) 39 | 40 | print(NUM_THREADS .. " threads created in " .. duration 41 | .. " seconds (" .. (NUM_THREADS / duration) .. "/s)") 42 | 43 | return 0 44 | end 45 | 46 | return thread_create() 47 | -------------------------------------------------------------------------------- /src/event/signal.h: -------------------------------------------------------------------------------- 1 | #ifndef EVQ_SIGNAL_H 2 | #define EVQ_SIGNAL_H 3 | 4 | #ifdef _WIN32 5 | 6 | #define EVQ_SIGINT CTRL_C_EVENT 7 | #define EVQ_SIGQUIT CTRL_BREAK_EVENT 8 | #define EVQ_SIGHUP CTRL_LOGOFF_EVENT 9 | #define EVQ_SIGTERM CTRL_SHUTDOWN_EVENT 10 | #define EVQ_NSIG 4 11 | 12 | #define EVQ_SIGEVQ (EVQ_SIGTERM + 1) 13 | 14 | #else 15 | 16 | #define EVQ_SIGINT SIGINT 17 | #define EVQ_SIGQUIT SIGQUIT 18 | #define EVQ_SIGHUP SIGHUP 19 | #define EVQ_SIGTERM SIGTERM 20 | #define EVQ_SIGCHLD SIGCHLD 21 | #define EVQ_NSIG 5 22 | 23 | #define EVQ_SIGEVQ SIGPIPE 24 | 25 | typedef void (*sig_handler_t) (const int); 26 | 27 | EVQ_API int signal_set (const int signo, sig_handler_t func); 28 | 29 | #endif 30 | 31 | EVQ_API void signal_init (void); 32 | 33 | EVQ_API int evq_signal (struct event_queue *evq, const int signo); 34 | EVQ_API int evq_ignore_signal (struct event_queue *evq, const int signo, 35 | const int ignore); 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /src/msvcbuild.bat: -------------------------------------------------------------------------------- 1 | @rem Open "Visual Studio .NET Command Prompt" to run this script 2 | 3 | @setlocal 4 | @set LUA=../../luajit-2.0 5 | @set LSCOMPILE=cl /nologo /c /LD /MD /O2 /W3 /D_CRT_SECURE_NO_DEPRECATE /D_CRT_SECURE_NO_WARNINGS /D_SCL_SECURE_NO_WARNINGS /I%LUA%/src 6 | @set LSLINK=link /nologo 7 | 8 | @rem Check Windows Version is Vista+ 9 | @ver | findstr /i "Version 6\." > NUL 10 | @if errorlevel 0 goto VERSION_VISTA 11 | @set WIN32_VERSION=WIN32_COMMON 12 | @goto VERSION_END 13 | :VERSION_VISTA 14 | @set WIN32_VERSION=WIN32_VISTA 15 | @set LSCOMPILE=%LSCOMPILE% /arch:SSE2 16 | :VERSION_END 17 | 18 | %LSCOMPILE% /DWIN32 /DLUA_BUILD_AS_DLL /D%WIN32_VERSION% luasys.c sock/sys_sock.c isa/isapi/isapi_dll.c 19 | @if errorlevel 1 goto :END 20 | %LSLINK% /DLL /OUT:sys.dll /DEF:isa/isapi/isapi_dll.def *.obj %LUA%/src/lua*.lib kernel32.lib user32.lib winmm.lib shell32.lib advapi32.lib ws2_32.lib psapi.lib 21 | @if errorlevel 1 goto :END 22 | 23 | :END 24 | 25 | @del *.obj *.manifest *.lib *.exp 26 | -------------------------------------------------------------------------------- /test/thread/dinner.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | local sys = require"sys" 4 | 5 | local thread = sys.thread 6 | 7 | thread.init() 8 | 9 | 10 | local NFORKS = 5 11 | local NPHILOSOPHERS = NFORKS 12 | local NSPAGHETTI = 3 13 | 14 | -- Forks 15 | local forks 16 | do 17 | forks = assert(thread.data_pool()) 18 | for i = 1, NFORKS do 19 | forks:put(1) 20 | end 21 | end 22 | 23 | -- Philosophers 24 | do 25 | local n = NPHILOSOPHERS 26 | local rand = assert(sys.random()) 27 | 28 | local function philosopher(num) 29 | print(num, "begin") 30 | for i = 1, NSPAGHETTI do 31 | forks:get() 32 | if forks:get(0) then 33 | print(num, "eat") 34 | forks:put(1) 35 | else 36 | print(num, "skip") 37 | end 38 | forks:put(1) 39 | thread.sleep(rand(10)) 40 | end 41 | print(num, "end.") 42 | 43 | n = n - 1 44 | if n == 0 then 45 | print"The End." 46 | end 47 | end 48 | 49 | for i = 1, NPHILOSOPHERS do 50 | assert(thread.run(philosopher, i)) 51 | end 52 | end 53 | 54 | assert(thread.self():wait()) 55 | -------------------------------------------------------------------------------- /test/event/test_time.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | -- libevent/test/test-time.c 4 | 5 | 6 | local sys = require"sys" 7 | 8 | 9 | local NEVENT = 20000 10 | 11 | local called = 0 12 | 13 | local timers = {} 14 | 15 | local rand_int = math.random 16 | math.randomseed(os.time()) 17 | 18 | local function time_cb(evq, evid, idx) 19 | evq:del(evid) 20 | timers[idx] = nil 21 | 22 | called = called + 1 23 | if called < 10 * NEVENT then 24 | for i = 1, 10 do 25 | local j = rand_int(NEVENT) 26 | local msec = rand_int(50) - 1 27 | 28 | evid = timers[j] 29 | if msec % 2 == 0 then 30 | if evid then 31 | evq:del(evid) 32 | timers[j] = nil 33 | end 34 | elseif evid then 35 | evq:timeout(evid, msec) 36 | else 37 | timers[j] = evq:add_timer(time_cb, msec, nil, j) 38 | end 39 | end 40 | end 41 | end 42 | 43 | do 44 | local evq = assert(sys.event_queue()) 45 | 46 | for i = 1, NEVENT do 47 | timers[i] = evq:add_timer(time_cb, rand_int(50) - 1, nil, i) 48 | end 49 | 50 | evq:loop() 51 | 52 | print("called", called) 53 | return (called < NEVENT) 54 | end 55 | 56 | -------------------------------------------------------------------------------- /test/thread/dpool.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | local sys = require"sys" 4 | 5 | local thread = sys.thread 6 | 7 | thread.init() 8 | 9 | 10 | -- Data Pool 11 | local dpool 12 | do 13 | local function on_full(dpool, ...) 14 | print("put on full>", ...) 15 | return ... 16 | end 17 | 18 | local function on_empty() 19 | print("get on empty>") 20 | end 21 | 22 | dpool = assert(thread.data_pool()) 23 | dpool:callbacks(on_full, on_empty) 24 | dpool:max(2) -- set maximum watermark 25 | end 26 | 27 | -- Consumer Thread 28 | local consumer 29 | do 30 | local function consume() 31 | while true do 32 | local i, s = dpool:get(200) 33 | if not i then break end 34 | print(i, s) 35 | thread.sleep(200) 36 | end 37 | end 38 | 39 | consumer = assert(thread.run(consume)) 40 | end 41 | 42 | -- Producer Thread 43 | local producer 44 | do 45 | local function produce() 46 | for i = 1, 10 do 47 | dpool:put(i, (i % 2 == 0) and "even" or "odd") 48 | thread.sleep(100) 49 | end 50 | end 51 | 52 | producer = assert(thread.run(produce)) 53 | end 54 | 55 | -- Wait threads termination 56 | assert(consumer:wait()) 57 | assert(producer:wait()) 58 | -------------------------------------------------------------------------------- /test/thread/affin.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | local sys = require"sys" 4 | 5 | local thread = sys.thread 6 | 7 | thread.init() 8 | 9 | print" to start (You may set CPU affinity for process)..." 10 | sys.stdin:read() 11 | 12 | local COUNT = 1000000 13 | local NPROCS = thread.nprocs() or sys.nprocs() 14 | 15 | if NPROCS > COUNT then 16 | NPROCS = COUNT 17 | end 18 | 19 | local period = sys.period() 20 | period:start() 21 | 22 | -- Pipe 23 | local work_pipe = thread.pipe() 24 | 25 | -- Worker 26 | local function process(work_pipe, from, count) 27 | local sum = 0 28 | for i = from, from + count do 29 | sum = sum + i 30 | end 31 | work_pipe:put(sum) 32 | end 33 | 34 | -- Start workers 35 | do 36 | local step = COUNT / NPROCS 37 | local func = string.dump(process) 38 | 39 | for i = 1, NPROCS do 40 | assert(thread.runvm({cpu = i}, func, work_pipe, 41 | (i - 1) * step + 1, step - 1)) 42 | end 43 | end 44 | 45 | -- Wait result 46 | do 47 | local sum = 0 48 | 49 | for i = 1, NPROCS do 50 | local _, res = work_pipe:get() 51 | sum = sum + res 52 | end 53 | 54 | print("time:", period:get() / 1000) 55 | print("nprocs:", NPROCS) 56 | print("count:", COUNT) 57 | print("sum:", sum) 58 | end 59 | -------------------------------------------------------------------------------- /test/thread/sched/bench.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | local sys = require"sys" 4 | 5 | local thread = sys.thread 6 | assert(thread.init()) 7 | 8 | 9 | local DEBUG = false 10 | local NWORKERS = 3 11 | local NTASKS = 100000 12 | 13 | -- Scheduler 14 | local sched 15 | do 16 | local function controller(co, err) 17 | if not co then return end 18 | print("task end", co, err or "") 19 | end 20 | 21 | sched = assert(thread.scheduler( 22 | DEBUG and controller or nil)) 23 | end 24 | 25 | -- Put tasks to scheduler 26 | do 27 | local sum = 0 28 | 29 | local function process() 30 | if DEBUG then 31 | print("task", coroutine.running()) 32 | end 33 | coroutine.yield() 34 | sum = sum + 1 35 | if sum == NTASKS then 36 | print("ntasks:", NTASKS) 37 | end 38 | end 39 | 40 | for i = 1, NTASKS do 41 | assert(sched:put(process)) 42 | end 43 | end 44 | 45 | print(" to start...") 46 | sys.stdin:read() 47 | 48 | -- Scheduler workers 49 | do 50 | local function loop() 51 | local _, err = sched:loop(0) 52 | if err then error(err) end 53 | end 54 | 55 | for i = 1, NWORKERS do 56 | assert(thread.run(loop)) 57 | end 58 | end 59 | 60 | -- Wait Threads termination 61 | assert(thread.self():wait()) 62 | 63 | -------------------------------------------------------------------------------- /src/event/evq.c: -------------------------------------------------------------------------------- 1 | /* Event queue */ 2 | 3 | #include "evq.h" 4 | 5 | 6 | #include "timeout.c" 7 | 8 | #ifdef _WIN32 9 | 10 | #include "win32sig.c" 11 | 12 | #else 13 | 14 | #include "signal.c" 15 | 16 | EVQ_API int 17 | evq_set_timeout (struct event *ev, const msec_t msec) 18 | { 19 | struct event_queue *evq = ev->evq; 20 | 21 | if (ev->tq) { 22 | if (ev->tq->msec == msec) { 23 | timeout_reset(ev, evq->now); 24 | return 0; 25 | } 26 | timeout_del(ev); 27 | } 28 | 29 | return (msec == TIMEOUT_INFINITE) ? 0 30 | : timeout_add(ev, msec, evq->now); 31 | } 32 | 33 | EVQ_API int 34 | evq_add_timer (struct event_queue *evq, struct event *ev, const msec_t msec) 35 | { 36 | ev->evq = evq; 37 | if (!evq_set_timeout(ev, msec)) { 38 | evq->nevents++; 39 | return 0; 40 | } 41 | return -1; 42 | } 43 | 44 | #endif /* !WIN32 */ 45 | 46 | EVQ_API struct event * 47 | evq_process_active (struct event *ev, struct event *ev_ready, 48 | const msec_t now) 49 | { 50 | ev->flags |= EVENT_READ_RES; 51 | if (ev->flags & EVENT_ACTIVE) 52 | return ev_ready; 53 | 54 | ev->flags |= EVENT_ACTIVE; 55 | if (ev->flags & EVENT_ONESHOT) 56 | evq_del(ev, 1); 57 | else if (ev->tq && !(ev->flags & EVENT_TIMEOUT_MANUAL)) 58 | timeout_reset(ev, now); 59 | 60 | ev->next_ready = ev_ready; 61 | return ev; 62 | } 63 | 64 | 65 | #include EVQ_SOURCE 66 | 67 | -------------------------------------------------------------------------------- /test/test_pid.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | local sys = require"sys" 4 | 5 | 6 | local evq = assert(sys.event_queue()) 7 | 8 | -- Timer 9 | local timer_id 10 | do 11 | local function on_timer() 12 | print"Working..." 13 | end 14 | 15 | timer_id = assert(evq:add_timer(on_timer, 500)) 16 | end 17 | 18 | -- Subprocess 19 | do 20 | local pid = sys.pid() 21 | local fdi, fdo = sys.handle(), sys.handle() 22 | assert(fdi:pipe(fdo)) 23 | do 24 | local sleep_msec = 1000 25 | local subprocess = [[ 26 | sys.thread.sleep(]] .. sleep_msec .. [[) 27 | sys.stdout:write"Exited normally." 28 | ]] 29 | print("Subprocess sleep:", sleep_msec) 30 | 31 | local args = {"-l", "sys", "-e", subprocess} 32 | if not sys.spawn("lua", args, pid, nil, fdo, nil) then 33 | error(SYS_ERR) 34 | end 35 | fdo:close() 36 | end 37 | 38 | local function on_child(evq, evid, pid, ev, err) 39 | evq:del(timer_id) 40 | if ev == 't' then 41 | print("Timeout: Kill the subprocess") 42 | assert(pid:kill()) 43 | assert(evq:add_pid(pid, on_child)) 44 | else 45 | print("Status:", err or 0) 46 | if err and err ~= 0 then 47 | print("Subprocess killed.") 48 | else 49 | print("Subprocess output:", fdi:read()) 50 | end 51 | end 52 | end 53 | 54 | assert(evq:add_pid(pid, on_child, 1014)) 55 | end 56 | 57 | evq:loop() 58 | 59 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | =============================================================================== 2 | LuaSys is Copyright (c) 2011 Nodir Temirkhodjaev. 3 | 4 | You may use, distribute and copy the LuaSys under the terms of MIT license, 5 | that is displayed below. 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | 25 | [ MIT license: http://www.opensource.org/licenses/mit-license.php ] 26 | 27 | =============================================================================== 28 | -------------------------------------------------------------------------------- /src/sys_unix.c: -------------------------------------------------------------------------------- 1 | /* Lua System: Unix specifics */ 2 | 3 | /* 4 | * Arguments: path (string) 5 | * Returns: [boolean] 6 | */ 7 | static int 8 | sys_chroot (lua_State *L) 9 | { 10 | const char *path = luaL_checkstring(L, 1); 11 | 12 | if (!chroot(path)) { 13 | lua_pushboolean(L, 1); 14 | return 1; 15 | } 16 | return sys_seterror(L, 0); 17 | } 18 | 19 | /* 20 | * Returns: [boolean] 21 | */ 22 | static int 23 | sys_daemonize (lua_State *L) 24 | { 25 | lua_pushboolean(L, 1); 26 | if (getppid() == 1) 27 | return 1; /* already a daemon */ 28 | 29 | switch (fork()) { 30 | case -1: goto err; 31 | case 0: break; 32 | default: _exit(0); 33 | } 34 | if (setsid() == -1 || chdir("/") == -1) 35 | goto err; 36 | umask(0); 37 | /* standard files */ 38 | { 39 | int fd = open("/dev/null", O_RDWR, 0); 40 | dup2(fd, 0); 41 | dup2(fd, 1); 42 | dup2(fd, 2); 43 | if (fd > 2) close(fd); 44 | } 45 | return 1; 46 | err: 47 | return sys_seterror(L, 0); 48 | } 49 | 50 | /* 51 | * Arguments: path (string), [permissions (number)] 52 | * Returns: [boolean] 53 | */ 54 | static int 55 | sys_mkfifo (lua_State *L) 56 | { 57 | const char *path = luaL_checkstring(L, 1); 58 | mode_t perm = (mode_t) luaL_optinteger(L, 2, SYS_FILE_PERMISSIONS); 59 | 60 | if (!mkfifo(path, perm)) { 61 | lua_pushboolean(L, 1); 62 | return 1; 63 | } 64 | return sys_seterror(L, 0); 65 | } 66 | 67 | 68 | #define UNIX_METHODS \ 69 | {"chroot", sys_chroot}, \ 70 | {"daemonize", sys_daemonize}, \ 71 | {"mkfifo", sys_mkfifo} 72 | -------------------------------------------------------------------------------- /test/thread/stdin.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | local sys = require"sys" 4 | 5 | local thread = sys.thread 6 | 7 | thread.init() 8 | 9 | 10 | -- Usage notes 11 | print[[ 12 | Press to quit or any chars for feedback... 13 | ]] 14 | 15 | 16 | local stdin = sys.stdin 17 | 18 | -- Event Queue 19 | local evq = assert(sys.event_queue()) 20 | 21 | local worker 22 | 23 | -- Controller 24 | local controller 25 | do 26 | local function on_event(evq, evid) 27 | print("Controller:", "Close stdin") 28 | worker:interrupt() 29 | stdin:close(true) -- Win32 workaround 30 | assert(worker:wait() == -1) 31 | evq:del(evid) 32 | end 33 | 34 | controller = assert(evq:add_timer(on_event, 30000)) 35 | end 36 | 37 | -- Worker Thread 38 | do 39 | local function read_stdin() 40 | while true do 41 | local line = stdin:read() 42 | if #line <= 2 then 43 | print("Worker:", "Notify controller") 44 | evq:sync(evq.notify, evq, controller) 45 | else 46 | sys.stdout:write("Worker:\tInput: ", line) 47 | end 48 | end 49 | end 50 | 51 | local function start() 52 | local _, err = pcall(read_stdin) 53 | if err and not (thread.self():interrupted() 54 | and err == thread.interrupt_error()) then 55 | print("Error:", err) 56 | err = nil 57 | end 58 | if not err then 59 | error("Thread Interrupt Error expected") 60 | end 61 | print("Worker:", "Terminated") 62 | return -1 63 | end 64 | 65 | worker = assert(thread.run(start)) 66 | end 67 | 68 | assert(evq:loop()) 69 | -------------------------------------------------------------------------------- /test/mem/bench.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | local sys = require("sys") 4 | 5 | local mem = sys.mem 6 | 7 | 8 | -- Copy file 9 | local infile = ... 10 | local outfile = "bench.out" 11 | 12 | if not infile then 13 | print[[Usage: bench.lua ]] 14 | return 15 | end 16 | 17 | 18 | local BUFSIZ = 65536 19 | 20 | local period = sys.period() 21 | 22 | period:start() 23 | do 24 | local fi = assert(io.open(infile, "rb")) 25 | local fo = assert(io.open(outfile, "wb")) 26 | while true do 27 | local data = fi:read(BUFSIZ) 28 | if not (data and fo:write(data)) or #data < BUFSIZ then 29 | break 30 | end 31 | end 32 | fi:close() 33 | fo:close() 34 | os.remove(outfile) 35 | end 36 | print("os.io=", period:get()) 37 | 38 | period:start() 39 | do 40 | local fi = assert(sys.handle():open(infile)) 41 | local fo = assert(sys.handle():create(outfile)) 42 | while true do 43 | local data = fi:read(BUFSIZ) 44 | if not (data and fo:write(data)) or #data < BUFSIZ then 45 | break 46 | end 47 | end 48 | fi:close() 49 | fo:close() 50 | sys.remove(outfile) 51 | end 52 | print("sys.io=", period:get()) 53 | 54 | period:start() 55 | do 56 | local fi = assert(sys.handle():open(infile)) 57 | local fo = assert(sys.handle():create(outfile)) 58 | local buf = assert(mem.pointer(BUFSIZ)) 59 | while true do 60 | local len = fi:read(buf, BUFSIZ) 61 | if not (len and fo:write(buf)) or len < BUFSIZ then 62 | break 63 | end 64 | end 65 | fi:close() 66 | fo:close() 67 | sys.remove(outfile) 68 | end 69 | print("sys.io.buf=", period:get()) 70 | 71 | -------------------------------------------------------------------------------- /test/sock/udpsendraw.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | local sys = require"sys" 4 | local sock = require"sys.sock" 5 | 6 | -- http://www.inf.puc-rio.br/~roberto/struct/ 7 | local struct = require"struct" 8 | 9 | 10 | local port = 1313 11 | local host = "127.0.0.1" 12 | 13 | local fd = sock.handle() 14 | assert(fd:socket("raw")) 15 | assert(fd:sockopt("hdrincl", 1)) 16 | 17 | local saddr = assert(sock.addr()) 18 | assert(saddr:inet(port, sock.inet_pton(host))) 19 | 20 | -- ip header fields 21 | local ip_header 22 | do 23 | local ip_ihl = 5 24 | local ip_ver = 4 25 | local ip_tos = 16 -- low delay 26 | local ip_tot_len = 0 -- kernel will fill the correct total length 27 | local ip_id = 54321 -- Id of this packet 28 | local ip_frag_off = 0 29 | local ip_ttl = 64 -- hops 30 | local ip_proto = 17 -- IPPROTO_UDP 31 | local ip_check = 0 -- kernel will fill the correct checksum 32 | local ip_saddr = sock.inet_pton(host) -- Spoof the source ip address if you want to 33 | local ip_daddr = sock.inet_pton(host) 34 | 35 | local ip_ihl_ver = bit32.lshift(ip_ver, 4) + ip_ihl 36 | 37 | ip_header = struct.pack('>BBHHHBBHc4c4', ip_ihl_ver, ip_tos, ip_tot_len, ip_id, 38 | ip_frag_off, ip_ttl, ip_proto, ip_check, ip_saddr, ip_daddr) 39 | end 40 | 41 | -- udp header fields 42 | local udp_header 43 | do 44 | local udp_srcport = 0 45 | local udp_destport = port 46 | local udp_len = 8 47 | local udp_chksum = 0 48 | 49 | udp_header = struct.pack('>HHHH', udp_srcport, udp_destport, udp_len, udp_chksum) 50 | end 51 | 52 | assert(fd:send(ip_header .. udp_header, saddr)) 53 | -------------------------------------------------------------------------------- /test/sock/manyclnt.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | local sys = require"sys" 4 | local sock = require"sys.sock" 5 | 6 | 7 | local host, port = "127.0.0.1", 8080 8 | local nclnt = 2000 9 | 10 | local stdin, stdout, stderr = sys.stdin, sys.stdout, sys.stderr 11 | 12 | 13 | local askt, iskt = {}, 0 14 | 15 | local function ev_cb(evq, evid, fd, ev) 16 | local s = tostring(fd) 17 | if ev == 'w' then 18 | assert(fd:write(s)) 19 | assert(evq:mod_socket(evid, 'r')) 20 | else 21 | local line 22 | if ev == 'r' then 23 | line = fd:read() 24 | end 25 | if not (line or askt[evid]) then 26 | evq:del(evid) 27 | fd:close() 28 | return 29 | end 30 | if line ~= s then 31 | error("got: " .. tostring(line) 32 | .. ", expected: " .. s) 33 | end 34 | 35 | iskt = iskt + 1 36 | if nclnt - iskt < 1 then 37 | -- close all sockets 38 | for evid, fd in pairs(askt) do 39 | askt[evid] = nil 40 | fd:shutdown() 41 | end 42 | end 43 | end 44 | end 45 | 46 | 47 | local start_time = sys.msec() 48 | 49 | local evq = assert(sys.event_queue()) 50 | 51 | local saddr = sock.addr():inet(port, sock.inet_pton(host)) 52 | for i = 1, nclnt do 53 | local fd = sock.handle() 54 | assert(fd:socket()) 55 | assert(fd:connect(saddr)) 56 | 57 | local evid = evq:add_socket(fd, 'w', ev_cb) 58 | if not evid then 59 | error(SYS_ERR) 60 | end 61 | askt[evid] = fd 62 | end 63 | 64 | 65 | stdout:write(nclnt, " sessions opened in ", sys.msec() - start_time, 66 | " msec\nPress any key to send data...\n") 67 | stdin:read() 68 | 69 | local start_time = sys.msec() 70 | 71 | evq:loop() 72 | 73 | stdout:write("Sessions closed in ", sys.msec() - start_time, " msec\n") 74 | -------------------------------------------------------------------------------- /test/thread/sched/test_sched.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | local coroutine = require("coroutine") 4 | 5 | local sys = require("sys") 6 | local sock = require"sys.sock" 7 | 8 | local thread = sys.thread 9 | assert(thread.init()) 10 | 11 | 12 | -- Scheduler 13 | local sched, sched_stop 14 | do 15 | local function controller(co) 16 | if co and sched_stop and sched:size() == 0 then 17 | sched:stop() 18 | end 19 | end 20 | 21 | sched = assert(thread.scheduler(controller)) 22 | 23 | -- Workers 24 | assert(thread.run(sched.loop, sched)) 25 | end 26 | 27 | 28 | print("-- Add coroutine as task") 29 | do 30 | local msg = "test" 31 | 32 | local function test(s) 33 | assert(s == msg, msg .. " expected, got " .. tostring(s)) 34 | end 35 | 36 | local co = assert(coroutine.create(test)) 37 | local task = assert(sched:put(co, msg)) 38 | end 39 | 40 | 41 | print("-- Suspend/Resume") 42 | do 43 | local msg = "test" 44 | 45 | local function test() 46 | local s = sched:suspend() 47 | assert(s == msg, msg .. " expected, got " .. tostring(s)) 48 | end 49 | 50 | local task = assert(sched:put(test)) 51 | 52 | thread.sleep(10) 53 | sched:resume(task, msg) 54 | end 55 | 56 | 57 | print("-- Preemptive multi-tasking") 58 | do 59 | assert(thread.run(sched.preempt_tasks, sched, 50)) 60 | thread.sleep(10) 61 | 62 | local condition 63 | 64 | local function test1() 65 | local i = 0 66 | while not condition do 67 | i = i + 1 68 | end 69 | end 70 | 71 | local function test2() 72 | coroutine.yield() 73 | condition = true 74 | end 75 | 76 | assert(sched:put(test1)) 77 | assert(sched:put(test2)) 78 | end 79 | 80 | 81 | sched_stop = true 82 | 83 | assert(sched:loop()) 84 | print("OK") 85 | 86 | -------------------------------------------------------------------------------- /test/thread/vms.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | local sys = require"sys" 4 | 5 | local thread = sys.thread 6 | 7 | thread.init() 8 | 9 | 10 | local COUNT = 10000 11 | 12 | local period = sys.period() 13 | period:start() 14 | 15 | -- Pipe 16 | local work_pipe = thread.pipe() 17 | 18 | -- Producer VM-Thread 19 | do 20 | local function produce(work_pipe, from, to) 21 | local sys = require"sys" 22 | 23 | local rep = string.rep 24 | local thread = sys.thread 25 | local rand = assert(sys.random()) 26 | 27 | for i = from, to do 28 | local d = i % 10 29 | local r = rand(460) 30 | local data = rep(d, r + 1) 31 | work_pipe:put(i, r, data) 32 | end 33 | end 34 | 35 | local func = string.dump(produce) 36 | assert(thread.runvm(nil, func, work_pipe, 1, COUNT/2)) 37 | assert(thread.runvm(nil, func, work_pipe, COUNT/2 + 1, COUNT)) 38 | end 39 | 40 | -- Consumer VM-Thread 41 | do 42 | local function consume(work_pipe, from, to) 43 | local sys = require"sys" 44 | 45 | local rep = string.rep 46 | local thread = sys.thread 47 | 48 | local expect = to - from + 1 49 | local count = 0 50 | for i = from, to do 51 | local _, num, r, s = work_pipe:get() 52 | if num == false then break end 53 | local d = num % 10 54 | local data = rep(d, r + 1) 55 | if s ~= data then 56 | error(data .. " string expected, got " .. s) 57 | end 58 | count = count + 1 59 | end 60 | assert(count == expect, expect .. " messages expected, got " .. count) 61 | end 62 | 63 | local func = string.dump(consume) 64 | assert(thread.runvm(nil, func, work_pipe, 1, COUNT/2)) 65 | assert(thread.runvm(nil, func, work_pipe, COUNT/2 + 1, COUNT)) 66 | end 67 | 68 | -- Wait VM-Threads termination 69 | assert(thread.self():wait()) 70 | 71 | print("time:", period:get() / 1000) 72 | print("count:", COUNT) 73 | -------------------------------------------------------------------------------- /test/thread/sched/test_evq.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | local sys = require("sys") 4 | local sock = require"sys.sock" 5 | 6 | local thread = sys.thread 7 | assert(thread.init()) 8 | 9 | 10 | local evq = assert(sys.event_queue()) 11 | 12 | -- Scheduler 13 | local sched 14 | do 15 | sched = assert(thread.scheduler()) 16 | 17 | -- Workers 18 | assert(thread.run(sched.loop, sched)) 19 | end 20 | 21 | 22 | print("-- Timer event") 23 | local task 24 | do 25 | local timeout = 10 26 | 27 | local function on_timeout() 28 | local ev = sched:wait_timer(evq, timeout) 29 | assert(ev == 't', timeout .. " msec timeout expected") 30 | end 31 | 32 | assert(sched:put(on_timeout)) 33 | end 34 | 35 | 36 | print("-- Socket event") 37 | do 38 | local msg = "test" 39 | 40 | local fdi, fdo = sock.handle(), sock.handle() 41 | assert(fdi:socket(fdo)) 42 | 43 | local function on_read() 44 | local ev = sched:wait_socket(evq, fdi, "r") 45 | assert(ev == 'r') 46 | local s = assert(fdi:read()) 47 | assert(s == msg, msg .. " expected, got " .. tostring(s)) 48 | fdi:close() 49 | fdo:close() 50 | end 51 | 52 | assert(sched:put(on_read)) 53 | 54 | thread.sleep(100) 55 | assert(fdo:write(msg)) 56 | end 57 | 58 | 59 | print("-- Terminate task, waiting on event") 60 | do 61 | local timeout = 100 62 | 63 | local fdi, fdo = sock.handle(), sock.handle() 64 | assert(fdi:socket(fdo)) 65 | 66 | local function on_timeout(task) 67 | local ev = sched:wait_timer(evq, timeout) 68 | assert(ev == 't', timeout .. " msec timeout expected") 69 | assert(sched:terminate(task)) 70 | fdi:close() 71 | fdo:close() 72 | end 73 | 74 | local function on_read(fd) 75 | sched:wait_socket(evq, fd, "r") 76 | assert(false, "Termination expected") 77 | end 78 | 79 | local task = assert(sched:put(on_read, fdi)) 80 | assert(sched:put(on_timeout, task)) 81 | 82 | thread.sleep(100) 83 | end 84 | 85 | 86 | assert(evq:loop()) 87 | print("OK") 88 | 89 | sched:stop() 90 | -------------------------------------------------------------------------------- /src/sys_env.c: -------------------------------------------------------------------------------- 1 | /* Lua System: Process Environment */ 2 | 3 | #ifndef _WIN32 4 | extern char **environ; 5 | #endif 6 | 7 | 8 | /* 9 | * Arguments: name (string) 10 | * Returns: [value (string)] 11 | */ 12 | static int 13 | sys_getenv (lua_State *L) 14 | { 15 | const char *name = luaL_checkstring(L, 1); 16 | 17 | #ifndef _WIN32 18 | lua_pushstring(L, getenv(name)); 19 | return 1; 20 | #else 21 | unsigned int len; 22 | 23 | len = GetEnvironmentVariableA(name, NULL, 0); 24 | if (len) { 25 | char *buf = malloc(len); 26 | if (buf && GetEnvironmentVariableA(name, buf, len) == len - 1) { 27 | lua_pushlstring(L, buf, len - 1); 28 | free(buf); 29 | return 1; 30 | } 31 | free(buf); 32 | } 33 | return sys_seterror(L, 0); 34 | #endif 35 | } 36 | 37 | /* 38 | * Arguments: name (string), [value (string)] 39 | * Returns: [boolean] 40 | */ 41 | static int 42 | sys_setenv (lua_State *L) 43 | { 44 | const char *name = luaL_checkstring(L, 1); 45 | const char *value = lua_tostring(L, 2); 46 | 47 | #ifndef _WIN32 48 | if (!(value ? setenv(name, value, 1) 49 | #if defined(__linux__) 50 | : unsetenv(name))) { 51 | #else 52 | : ((void) unsetenv(name), 0))) { 53 | #endif 54 | #else 55 | if (SetEnvironmentVariableA(name, value)) { 56 | #endif 57 | lua_pushboolean(L, 1); 58 | return 1; 59 | } 60 | return sys_seterror(L, 0); 61 | } 62 | 63 | /* 64 | * Returns: environment (table) 65 | */ 66 | static int 67 | sys_env (lua_State *L) 68 | { 69 | const char *name, *value, *end; 70 | #ifndef _WIN32 71 | char **env = environ; 72 | #else 73 | char *env = GetEnvironmentStringsA(); 74 | #endif 75 | 76 | if (!env) return 0; 77 | lua_newtable(L); 78 | #ifndef _WIN32 79 | for (; (name = *env); ++env) { 80 | #else 81 | for (name = env; *name != '\0'; name = end + 1) { 82 | #endif 83 | value = strchr(name, '=') + 1; 84 | end = strchr(value, '\0'); 85 | 86 | lua_pushlstring(L, name, value - name - 1); 87 | lua_pushlstring(L, value, end - value); 88 | lua_rawset(L, -3); 89 | } 90 | #ifdef _WIN32 91 | FreeEnvironmentStringsA(env); 92 | #endif 93 | return 1; 94 | } 95 | 96 | 97 | #define ENV_METHODS \ 98 | {"getenv", sys_getenv}, \ 99 | {"setenv", sys_setenv}, \ 100 | {"env", sys_env} 101 | -------------------------------------------------------------------------------- /test/event/bench.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | -- libevent/test/bench.c 4 | 5 | 6 | local sys = require"sys" 7 | local sock = require"sys.sock" 8 | 9 | 10 | local num_pipes, num_active, num_writes 11 | local count, writes, fired 12 | 13 | local pipes = {} 14 | local events, events_idx = {}, {} 15 | 16 | local period = sys.period() 17 | 18 | 19 | local function read_cb(evq, evid, fd) 20 | count = count + (fd:read(1) and 1 or 0) 21 | 22 | if writes ~= 0 then 23 | local widx = events_idx[evid] + 1 24 | if widx > num_pipes then 25 | widx = widx - num_pipes 26 | end 27 | 28 | local fdo = pipes[widx][2] 29 | fdo:write("e") 30 | writes = writes - 1 31 | fired = fired + 1 32 | end 33 | end 34 | 35 | local function run_once(evq) 36 | for i = 1, num_pipes do 37 | local fdi = pipes[i][1] 38 | local evid = events[i] 39 | if evid then 40 | evq:del(evid, true) 41 | end 42 | evid = evq:add_socket(fdi, "r", read_cb) 43 | if not evid then 44 | error(SYS_ERR) 45 | end 46 | events[i], events_idx[evid] = evid, i 47 | end 48 | 49 | evq:loop(0, true, true) 50 | 51 | fired = 0 52 | local space = math.floor(num_pipes / num_active) 53 | for i = 0, num_active - 1 do 54 | local fdo = pipes[i * space + 1][2] 55 | fdo:write("e") 56 | fired = fired + 1 57 | end 58 | 59 | count = 0 60 | writes = num_writes 61 | 62 | local xcount = 0 63 | period:start() 64 | repeat 65 | evq:loop(0, true, true) 66 | xcount = xcount + 1 67 | until (count == fired) 68 | local res = period:get() 69 | 70 | if xcount ~= count then 71 | sys.stderr:write("Xcount: ", xcount, ", Rcount: ", count, "\n") 72 | end 73 | return res 74 | end 75 | 76 | local function main(npipes, nactives, nwrites) 77 | num_pipes = tonumber(npipes) or 100 78 | num_active = tonumber(nactives) or 1 79 | num_writes = tonumber(nwrites) or 100 80 | 81 | assert(sys.limit_nfiles(num_pipes * 2 + 50)) 82 | 83 | for i = 1, num_pipes do 84 | local fdi, fdo = sock.handle(), sock.handle() 85 | if not fdi:socket(fdo) then 86 | error(SYS_ERR) 87 | end 88 | pipes[i] = {fdi, fdo} 89 | end 90 | 91 | local evq = assert(sys.event_queue()) 92 | 93 | for i = 1, 25 do 94 | print(run_once(evq)) 95 | end 96 | 97 | sys.exit(0) 98 | end 99 | 100 | main(...) 101 | -------------------------------------------------------------------------------- /test/event/bench_cascade.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | -- libevent/test/bench_cascade.c 4 | 5 | --[[ 6 | * This benchmark tests how quickly we can propagate a write down a chain 7 | * of socket pairs. We start by writing to the first socket pair and all 8 | * events will fire subsequently until the last socket pair has been reached 9 | * and the benchmark terminates. 10 | --]] 11 | 12 | 13 | local sys = require"sys" 14 | local sock = require"sys.sock" 15 | 16 | 17 | local num_pipes, fired 18 | 19 | local pipes, events, events_idx 20 | 21 | local period = sys.period() 22 | 23 | 24 | local function read_cb(evq, evid, fd) 25 | fd:read(1) 26 | local idx = events_idx[evid] 27 | if idx < num_pipes then 28 | pipes[idx + 1][2]:write("e") 29 | end 30 | fired = fired + 1 31 | end 32 | 33 | local function run_once(evq) 34 | pipes, events, events_idx = {}, {}, {} 35 | 36 | for i = 1, num_pipes do 37 | local fdi, fdo = sock.handle(), sock.handle() 38 | if not fdi:socket(fdo) then 39 | error(SYS_ERR) 40 | end 41 | pipes[i] = {fdi, fdo} 42 | end 43 | 44 | -- measurements includes event setup 45 | period:start() 46 | 47 | -- provide a default timeout for events 48 | local timeout = 60000 49 | 50 | for i = 1, num_pipes do 51 | local fdi = pipes[i][1] 52 | evid = evq:add_socket(fdi, "r", read_cb, timeout) 53 | if not evid then 54 | error(SYS_ERR) 55 | end 56 | events[i], events_idx[evid] = evid, i 57 | end 58 | 59 | fired = 0; 60 | 61 | -- kick everything off with a single write 62 | pipes[1][2]:write("e") 63 | 64 | local xcount = 0 65 | while true do 66 | evq:loop(0) 67 | if fired < num_pipes then 68 | xcount = xcount + 1 69 | else break end 70 | end 71 | if xcount ~= 0 then 72 | sys.stderr:write("Xcount: ", xcount, "\n") 73 | end 74 | 75 | local res = period:get() 76 | 77 | for i = 1, num_pipes do 78 | evq:del(events[i]) 79 | local pipe = pipes[i] 80 | pipe[1]:close() 81 | pipe[2]:close() 82 | end 83 | 84 | return res 85 | end 86 | 87 | local function main(npipes) 88 | num_pipes = tonumber(npipes) or 100 89 | 90 | assert(sys.limit_nfiles(num_pipes * 2 + 50)) 91 | 92 | local evq = assert(sys.event_queue()) 93 | 94 | for i = 1, 25 do 95 | print(run_once(evq)) 96 | end 97 | 98 | sys.exit(0) 99 | end 100 | 101 | main(...) 102 | -------------------------------------------------------------------------------- /test/win32/beep_svc.lua: -------------------------------------------------------------------------------- 1 | -- Win32: Test Beeper Service 2 | 3 | local sys = require("sys") 4 | 5 | local win32 = assert(sys.win32, "Windows 9x/NT required") 6 | 7 | 8 | local TIMEOUT = 3000 -- milliseconds 9 | 10 | local FILENAME = [[C:\tmp\lua_msvc.52\bin\beep_svc.lua]] -- EDIT PATH !!! 11 | 12 | local USE_FOREVER_LOOP = true 13 | 14 | 15 | local svc_name = "Beeper.Lua" 16 | 17 | while true do 18 | local action = ... 19 | if action == "service" then 20 | break 21 | elseif action == "install" then 22 | if not win32.service.install(svc_name, FILENAME .. " service", true) then 23 | error(SYS_ERR) 24 | end 25 | -- Set Description 26 | local r = win32.registry() 27 | if r:open("HKEY_LOCAL_MACHINE", 28 | [[System\CurrentControlSet\Services\]] .. svc_name, "w") then 29 | r:set("Description", "Beeper - Lua Test Service") 30 | r:close() 31 | end 32 | elseif action == "uninstall" then 33 | local res, working = win32.service.uninstall(svc_name) 34 | if not res then 35 | error(working and "Stop the service first." or SYS_ERR) 36 | end 37 | else 38 | return print("Usage: lua.exe beep_svc.lua (un)install") 39 | end 40 | return print("Service '" .. svc_name .. "' " .. action .. "ed.") 41 | end 42 | 43 | 44 | local svc = assert(win32.service.handle(svc_name, true)) 45 | 46 | if USE_FOREVER_LOOP then 47 | -- Simple Loop 48 | local timeout = TIMEOUT 49 | while true do 50 | local res = svc:wait(timeout) 51 | if not res then 52 | if res == nil then 53 | break -- Fail 54 | end 55 | win32.beep() -- Timeout 56 | else 57 | res = svc:status() 58 | if res == "stop" then 59 | break 60 | elseif res == "pause" then 61 | timeout = nil 62 | svc:status"paused" 63 | elseif res == "continue" then 64 | timeout = TIMEOUT 65 | svc:status"running" 66 | end 67 | end 68 | end 69 | else 70 | -- Event Queue 71 | local function on_event(evq, evid, svc, ev) 72 | if ev == 't' then 73 | win32.beep() 74 | else 75 | local res = svc:status() 76 | if res == "stop" then 77 | evq:del(evid) 78 | elseif res == "pause" then 79 | evq:timeout(evid) 80 | svc:status"paused" 81 | elseif res == "continue" then 82 | evq:timeout(evid, TIMEOUT) 83 | svc:status"running" 84 | end 85 | end 86 | end 87 | 88 | local evq = assert(sys.event_queue()) 89 | evq:add(svc, 'r', on_event, TIMEOUT) 90 | evq:loop() 91 | end 92 | 93 | svc:status"stopped" 94 | -------------------------------------------------------------------------------- /test/mem/test_mem.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | local sys = require("sys") 4 | 5 | local mem = sys.mem 6 | 7 | 8 | print"-- Plain array" 9 | do 10 | local arr = assert(mem.pointer(8)) -- default type is "char" 11 | 12 | for i = 0, 7 do arr[i] = 65 + i end 13 | assert("ABCDEFGH" == arr:tostring(8)) 14 | 15 | local s = "zstring" 16 | arr[0] = s 17 | assert(s == arr:tostring(#s)) 18 | print"OK" 19 | end 20 | 21 | 22 | print"-- String I/O" 23 | do 24 | local buf = assert(mem.pointer():alloc()) 25 | buf:write("1234567") 26 | assert("123" == buf:read(3)) 27 | assert("456" == buf:read(3)) 28 | assert("7" == buf:read(3)) 29 | print"OK" 30 | end 31 | 32 | 33 | print"-- Bits String" 34 | do 35 | local bits = assert(mem.pointer(8):type"bitstring") 36 | bits[7] = true 37 | 38 | local intp = assert(mem.pointer():type"int") 39 | bits(0, intp) -- set integer pointer to bitstring 40 | 41 | assert(intp[0] == 128) 42 | print"OK" 43 | end 44 | 45 | 46 | print"-- File Map" 47 | do 48 | local filename = "fmap" 49 | local f = assert(sys.handle():open(filename, "rw", 0x180, "creat")) 50 | 51 | local s1, s2 = "File ", "mapped." 52 | f:seek(#s1) 53 | f:write(s2) 54 | do 55 | local p = assert(mem.pointer()) 56 | assert(p:map(f, "w")) 57 | p[0] = s1 58 | p:free() 59 | end 60 | f:seek(0, "set") 61 | assert(f:read() == s1 .. s2) 62 | 63 | f:close() 64 | sys.remove(filename) 65 | print"OK" 66 | end 67 | 68 | 69 | print"-- Buffer" 70 | do 71 | local buf = assert(mem.pointer():alloc()) 72 | local s = "append string to buffer" 73 | buf:write(s) 74 | assert(string.len(s) == buf:seek() and s == buf:tostring()) 75 | buf:close() 76 | print"OK" 77 | end 78 | 79 | 80 | print"-- Buffer I/O Streams" 81 | do 82 | local stream = { 83 | data = "characters\nnewline\nend"; 84 | read = function(self) 85 | local data = self.data 86 | self.data = nil 87 | return data 88 | end; 89 | write = function(self, data) 90 | self.data = data 91 | return true 92 | end 93 | } 94 | local buf = assert(mem.pointer():alloc()) 95 | 96 | buf:input(stream) 97 | do 98 | local stream_data, data = stream.data 99 | while true do 100 | local line = buf:read"*l" 101 | if not line then break end 102 | if data then 103 | data = data .. '\n' .. line 104 | else 105 | data = line 106 | end 107 | end 108 | assert(data == stream_data) 109 | end 110 | 111 | buf:output(stream) 112 | local s = "auto-flush" 113 | buf:write(s) 114 | buf:close() -- flush & free 115 | assert(stream.data == s) 116 | print"OK" 117 | end 118 | 119 | 120 | -------------------------------------------------------------------------------- /src/sys_rand.c: -------------------------------------------------------------------------------- 1 | /* Lua System: Randomness */ 2 | 3 | #ifdef _WIN32 4 | #include 5 | #endif 6 | 7 | #define RAND_TYPENAME "sys.random" 8 | 9 | 10 | /* 11 | * Returns: [rand_udata] 12 | */ 13 | static int 14 | sys_random (lua_State *L) 15 | { 16 | #ifndef _WIN32 17 | fd_t *fdp = lua_newuserdata(L, sizeof(fd_t)); 18 | 19 | #if defined(__OpenBSD__) 20 | *fdp = open("/dev/arandom", O_RDONLY, 0); 21 | if (*fdp == (fd_t) -1) 22 | *fdp = open("/dev/urandom", O_RDONLY, 0); 23 | #else 24 | *fdp = open("/dev/urandom", O_RDONLY, 0); 25 | #endif 26 | if (*fdp != (fd_t) -1) { 27 | #else 28 | HCRYPTPROV *p = lua_newuserdata(L, sizeof(void *)); 29 | 30 | if (CryptAcquireContext(p, NULL, NULL, 31 | PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { 32 | #endif 33 | luaL_getmetatable(L, RAND_TYPENAME); 34 | lua_setmetatable(L, -2); 35 | return 1; 36 | } 37 | return sys_seterror(L, 0); 38 | } 39 | 40 | /* 41 | * Arguments: rand_udata 42 | */ 43 | static int 44 | rand_close (lua_State *L) 45 | { 46 | #ifndef _WIN32 47 | fd_t *fdp = (fd_t *) checkudata(L, 1, RAND_TYPENAME); 48 | 49 | if (*fdp != (fd_t) -1) { 50 | close(*fdp); 51 | *fdp = (fd_t) -1; 52 | } 53 | #else 54 | HCRYPTPROV *p = (HCRYPTPROV *) checkudata(L, 1, RAND_TYPENAME); 55 | 56 | if (*p != (HCRYPTPROV) -1) { 57 | CryptReleaseContext(*p, 0); 58 | *p = (HCRYPTPROV) -1; 59 | } 60 | #endif 61 | return 0; 62 | } 63 | 64 | /* 65 | * Arguments: rand_udata, [upper_bound (number) 66 | * | buffer (ludata), buffer_length (number)] 67 | * Returns: number 68 | */ 69 | static int 70 | rand_next (lua_State *L) 71 | { 72 | const int is_udata = lua_isuserdata(L, 2); 73 | const unsigned int ub = is_udata ? 0 74 | : (unsigned int) lua_tointeger(L, 2); 75 | unsigned int num; 76 | unsigned char *buf = is_udata ? lua_touserdata(L, 2) : # 77 | const int len = is_udata ? luaL_checkint(L, 3) : (int) sizeof(num); 78 | #ifndef _WIN32 79 | fd_t fd = (fd_t) lua_unboxinteger(L, 1, RAND_TYPENAME); 80 | int nr; 81 | 82 | sys_vm_leave(L); 83 | do nr = read(fd, (char *) buf, len); 84 | while (nr == -1 && sys_eintr()); 85 | sys_vm_enter(L); 86 | 87 | if (nr == len) { 88 | #else 89 | HCRYPTPROV prov = (HCRYPTPROV) lua_unboxpointer(L, 1, RAND_TYPENAME); 90 | int res; 91 | 92 | sys_vm_leave(L); 93 | res = CryptGenRandom(prov, len, buf); 94 | sys_vm_enter(L); 95 | 96 | if (res) { 97 | #endif 98 | lua_pushinteger(L, is_udata ? 1 99 | : (ub ? num % ub : num)); 100 | return 1; 101 | } 102 | return sys_seterror(L, 0); 103 | } 104 | 105 | 106 | #define RAND_METHODS \ 107 | {"random", sys_random} 108 | 109 | static luaL_Reg rand_meth[] = { 110 | {"__call", rand_next}, 111 | {"__gc", rand_close}, 112 | {NULL, NULL} 113 | }; 114 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | 2 | PLAT= none 3 | 4 | LUA= /opt/lua-5.2.3 5 | 6 | T= sys.so 7 | A= libluasys.a 8 | 9 | COPT= -O2 -fPIC -DNDEBUG 10 | CWARNS= -W -Wall -Wextra -pedantic \ 11 | -Waggregate-return \ 12 | -Wbad-function-cast \ 13 | -Wcast-align \ 14 | -Wcast-qual \ 15 | -Wdeclaration-after-statement \ 16 | -Wdisabled-optimization \ 17 | -Wmissing-prototypes \ 18 | -Wnested-externs \ 19 | -Wpointer-arith \ 20 | -Wshadow \ 21 | -Wsign-compare \ 22 | -Wstrict-prototypes \ 23 | -Wundef \ 24 | -Wwrite-strings \ 25 | -Wpadded \ 26 | # -Wunreachable-code 27 | 28 | CFLAGS= $(COPT) $(CWARNS) $(MYCFLAGS) -I$(LUA)/src 29 | LIBS= -lpthread $(MYLIBS) 30 | 31 | MYCFLAGS= 32 | MYLDFLAGS= -shared -Bdynamic 33 | MYLIBS= 34 | 35 | CC= gcc 36 | MAKE_DLL= $(CC) $(MYLDFLAGS) -o 37 | AR= ar rcu 38 | RANLIB= ranlib 39 | RM= rm -f 40 | STRIP= strip 41 | 42 | PLATS= generic linux bsd osx mingw 43 | 44 | OBJS= luasys.o sock/sys_sock.o 45 | LDOBJS= $(OBJS) 46 | 47 | 48 | ifneq (,$(findstring Windows,$(OS))) 49 | UNAME_S= $(shell uname -s) 50 | ifneq (,$(findstring MINGW,$(UNAME_S))) 51 | T= sys.dll 52 | A= sys.lib 53 | 54 | MYCFLAGS= -DLUA_BUILD_AS_DLL 55 | MYLDFLAGS= -s -shared 56 | LIBS= $(LUA)/src/lua51.dll -lkernel32 -luser32 -lwinmm -lshell32 -ladvapi32 -lws2_32 -lpsapi 57 | OBJS+= isa/isapi/isapi_dll.o 58 | endif 59 | endif 60 | 61 | 62 | default: $(PLAT) 63 | 64 | all: $(T) 65 | 66 | a: $(A) 67 | 68 | $(T): $(OBJS) 69 | $(MAKE_DLL) $@ $(LDOBJS) $(LIBS) 70 | $(STRIP) $@ 71 | 72 | $(A): $(OBJS) 73 | $(AR) $@ $? 74 | $(RANLIB) $@ 75 | 76 | 77 | none: 78 | @echo "Please choose a platform:" 79 | @echo " $(PLATS)" 80 | 81 | generic: 82 | $(MAKE) all MYCFLAGS= 83 | 84 | linux: 85 | $(MAKE) all MYCFLAGS="-DUSE_EPOLL -DUSE_EVENTFD" MYLIBS="-lrt" 86 | 87 | bsd: 88 | $(MAKE) all MYCFLAGS="-DUSE_KQUEUE" LDOBJS="*.o" 89 | 90 | osx: 91 | $(MAKE) all MYCFLAGS="-DUSE_KQUEUE" STRIP="echo strip" \ 92 | MYLDFLAGS="-bundle -undefined dynamic_lookup /usr/lib/bundle1.o" 93 | 94 | mingw: all 95 | 96 | clean: 97 | $(RM) $(T) $(A) $(LDOBJS) 98 | 99 | 100 | .PHONY: all $(PLATS) default a clean 101 | 102 | 103 | luasys.o: luasys.c sys_comm.c sys_date.c sys_env.c sys_evq.c sys_file.c \ 104 | sys_fs.c sys_log.c sys_proc.c sys_rand.c sys_unix.c common.h \ 105 | thread/sys_thread.c thread/thread_dpool.c \ 106 | thread/thread_pipe.c thread/thread_sync.c \ 107 | mem/sys_mem.c mem/membuf.c \ 108 | event/evq.c event/epoll.c event/kqueue.c event/poll.c \ 109 | event/select.c event/signal.c event/timeout.c \ 110 | event/evq.h event/epoll.h event/kqueue.h event/poll.h \ 111 | event/select.h event/timeout.h \ 112 | win32/sys_win32.c win32/win32_reg.c win32/win32_svc.c win32/win32_utf8.c 113 | sock/sys_sock.o: sock/sys_sock.c sock/sock_addr.c common.h 114 | isa/isapi/isapi_dll.o: isa/isapi/isapi_dll.c isa/isapi/isapi_ecb.c common.h 115 | -------------------------------------------------------------------------------- /src/win32/win32_utf8.c: -------------------------------------------------------------------------------- 1 | /* Lua System: Win32 specifics: UTF-8 */ 2 | 3 | /* 4 | * Convert UTF-8 to microsoft unicode. 5 | */ 6 | static void * 7 | utf8_to_utf16 (const char *s) 8 | { 9 | WCHAR *ws; 10 | int n; 11 | 12 | n = MultiByteToWideChar(CP_UTF8, 0, s, -1, NULL, 0); 13 | ws = malloc(n * sizeof(WCHAR)); 14 | if (!ws) return NULL; 15 | 16 | n = MultiByteToWideChar(CP_UTF8, 0, s, -1, ws, n); 17 | if (!n) { 18 | free(ws); 19 | ws = NULL; 20 | } 21 | return ws; 22 | } 23 | 24 | /* 25 | * Convert microsoft unicode to UTF-8. 26 | */ 27 | static void * 28 | utf16_to_utf8 (const WCHAR *ws) 29 | { 30 | char *s; 31 | int n; 32 | 33 | n = WideCharToMultiByte(CP_UTF8, 0, ws, -1, NULL, 0, NULL, 0); 34 | s = malloc(n); 35 | if (!s) return NULL; 36 | 37 | n = WideCharToMultiByte(CP_UTF8, 0, ws, -1, s, n, NULL, 0); 38 | if (!n) { 39 | free(s); 40 | s = NULL; 41 | } 42 | return s; 43 | } 44 | 45 | /* 46 | * Convert an ansi string to microsoft unicode, based on the 47 | * current codepage settings for file apis. 48 | */ 49 | static void * 50 | mbcs_to_utf16 (const char *mbcs) 51 | { 52 | WCHAR *ws; 53 | const int codepage = AreFileApisANSI() ? CP_ACP : CP_OEMCP; 54 | int n; 55 | 56 | n = MultiByteToWideChar(codepage, 0, mbcs, -1, NULL, 0); 57 | ws = malloc(n * sizeof(WCHAR)); 58 | if (!ws) return NULL; 59 | 60 | n = MultiByteToWideChar(codepage, 0, mbcs, -1, ws, n); 61 | if (!n) { 62 | free(ws); 63 | ws = NULL; 64 | } 65 | return ws; 66 | } 67 | 68 | /* 69 | * Convert microsoft unicode to multibyte character string, based on the 70 | * user's Ansi codepage. 71 | */ 72 | static void * 73 | utf16_to_mbcs (const WCHAR *ws) 74 | { 75 | char *mbcs; 76 | const int codepage = AreFileApisANSI() ? CP_ACP : CP_OEMCP; 77 | int n; 78 | 79 | n = WideCharToMultiByte(codepage, 0, ws, -1, NULL, 0, NULL, 0); 80 | mbcs = malloc(n); 81 | if (!mbcs) return NULL; 82 | 83 | n = WideCharToMultiByte(codepage, 0, ws, -1, mbcs, n, NULL, 0); 84 | if (!n) { 85 | free(mbcs); 86 | mbcs = NULL; 87 | } 88 | return mbcs; 89 | } 90 | 91 | /* 92 | * Convert multibyte character string to UTF-8. 93 | */ 94 | static void * 95 | mbcs_to_utf8 (const char *mbcs) 96 | { 97 | char *s; 98 | WCHAR *ws; 99 | 100 | ws = mbcs_to_utf16(mbcs); 101 | if (!ws) return NULL; 102 | 103 | s = utf16_to_utf8(ws); 104 | free(ws); 105 | return s; 106 | } 107 | 108 | /* 109 | * Convert UTF-8 to multibyte character string. 110 | */ 111 | static void * 112 | utf8_to_mbcs (const char *s) 113 | { 114 | char *mbcs; 115 | WCHAR *ws; 116 | 117 | ws = utf8_to_utf16(s); 118 | if (!ws) return NULL; 119 | 120 | mbcs = utf16_to_mbcs(ws); 121 | free(ws); 122 | return mbcs; 123 | } 124 | 125 | /* 126 | * Convert UTF-8 to OS filename. 127 | */ 128 | void * 129 | utf8_to_filename (const char *s) 130 | { 131 | return is_WinNT ? utf8_to_utf16(s) : utf8_to_mbcs(s); 132 | } 133 | 134 | /* 135 | * Convert OS filename to UTF-8. 136 | */ 137 | char * 138 | filename_to_utf8 (const void *s) 139 | { 140 | return is_WinNT ? utf16_to_utf8(s) : mbcs_to_utf8(s); 141 | } 142 | 143 | -------------------------------------------------------------------------------- /test/test_sys.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | local io = require("io") 4 | local sys = require("sys") 5 | local sock = require("sys.sock") 6 | 7 | 8 | print"-- file:isatty()" 9 | do 10 | assert(sys.stdin:isatty()) 11 | print("OK") 12 | end 13 | 14 | 15 | print"-- sys.handle <-> io.file" 16 | do 17 | local file = assert(io.open("test", "w")) 18 | local fd = sys.handle() 19 | fd:fileno(file) 20 | assert(fd:write"fd <") 21 | assert(fd:fdopen(file, "w")) 22 | assert(file:write"> file") 23 | file:flush() 24 | file:close() 25 | sys.remove"test" 26 | print("OK") 27 | end 28 | 29 | 30 | print"-- Logs" 31 | do 32 | local log = assert(sys.log()) 33 | assert(log:warn("Warning")) 34 | print("OK") 35 | end 36 | 37 | 38 | print"-- Random" 39 | do 40 | local rand = assert(sys.random()) 41 | for i = 1, 20 do 42 | sys.stdout:write(rand(10), "; ") 43 | end 44 | print("\nOK") 45 | end 46 | 47 | 48 | print"-- Emulate popen()" 49 | do 50 | local fdi, fdo = sys.handle(), sys.handle() 51 | assert(fdi:pipe(fdo)) 52 | local s = "test pipe" 53 | assert(sys.spawn("lua", 54 | {'-l', 'sys', '-e', 'sys.stdout:write[[' .. s .. ']]'}, 55 | nil, nil, fdo)) 56 | fdo:close() 57 | assert(fdi:read() == s) 58 | fdi:close() 59 | print("OK") 60 | end 61 | 62 | 63 | print"-- SocketPair" 64 | do 65 | local fdi, fdo = sock.handle(), sock.handle() 66 | assert(fdi:socket(fdo)) 67 | local s = "test socketpair" 68 | assert(fdo:write(s)) 69 | fdo:close() 70 | assert(fdi:read() == s) 71 | fdi:close() 72 | print("OK") 73 | end 74 | 75 | 76 | print"-- Interface List" 77 | do 78 | local ifaddrs = assert(sock.getifaddrs()) 79 | for i, iface in ipairs(ifaddrs) do 80 | local af = iface.family 81 | sys.stdout:write(i, "\taddress family: ", af, "\n") 82 | if af then 83 | if iface.addr then 84 | sys.stdout:write("\tinet addr: ", sock.inet_ntop(iface.addr), 85 | " <", sock.getnameinfo(iface.addr), ">", 86 | " Mask: ", sock.inet_ntop(iface.netmask), "\n") 87 | end 88 | local flags = iface.flags 89 | sys.stdout:write("\t", 90 | flags.up and "UP " or "", 91 | flags.loopback and "LOOPBACK " or "", 92 | flags.pointtopoint and "POINTOPOINT " or "", 93 | "\n") 94 | end 95 | end 96 | print("OK") 97 | end 98 | 99 | 100 | print"-- Directory List" 101 | do 102 | for file, type in sys.dir('.') do 103 | print(file, type and "DIR" or "FILE") 104 | end 105 | print("OK") 106 | end 107 | 108 | 109 | print"-- sys.date & sys.time" 110 | do 111 | local format = "%d.%m.%Y %H:%M:%S" 112 | local now = assert(sys.time()) 113 | local date = assert(sys.date(format)) 114 | local time = assert(sys.time(date, format)) 115 | assert(now == time) 116 | print("OK") 117 | end 118 | 119 | 120 | print"-- sys.loadavg" 121 | do 122 | local nprocs = sys.nprocs() 123 | for i = 1, 5 do 124 | local loadavg, is_per_cpu = assert(sys.loadavg()) 125 | if is_per_cpu then 126 | loadavg = loadavg / nprocs 127 | end 128 | print("loadavg:", loadavg) 129 | sys.thread.sleep(100) 130 | end 131 | print("OK") 132 | end 133 | 134 | 135 | -------------------------------------------------------------------------------- /test/sock/echosrvr.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | local sys = require"sys" 4 | local sock = require"sys.sock" 5 | 6 | 7 | local ONE_SHOT_CLIENT = false 8 | local DEBUG = false 9 | local EXTRA_LOOPS = 0 10 | 11 | local bind = { 12 | [8080] = "127.0.0.1", 13 | } 14 | 15 | local stderr = sys.stderr 16 | 17 | 18 | -- Multi-threaded event queue? 19 | local thread = sys.thread 20 | local is_multithread = (EXTRA_LOOPS and EXTRA_LOOPS > 0) 21 | if is_multithread then 22 | thread.init() 23 | end 24 | 25 | 26 | -- Pool of sockets 27 | local socket_get, socket_put 28 | do 29 | local pool = setmetatable({n = 0}, {__mode = "v"}) 30 | 31 | socket_get = function() 32 | local n = pool.n 33 | local fd = pool[n] 34 | if not fd then 35 | fd = sock.handle() 36 | n = 1 37 | end 38 | pool.n = n - 1 39 | return fd 40 | end 41 | 42 | socket_put = function(fd) 43 | local n = pool.n + 1 44 | pool.n, pool[n] = n, fd 45 | end 46 | end 47 | 48 | 49 | -- Channels 50 | local chan_insert, chan_remove 51 | do 52 | local nskt = 0 53 | 54 | chan_insert = function(evq, fd, cb, timeout) 55 | fd:nonblocking(true) 56 | if not evq:add_socket(fd, 'r', cb, timeout) then 57 | error(SYS_ERR) 58 | end 59 | 60 | if DEBUG then 61 | nskt = nskt + 1 62 | stderr:write("+ Insert in set (", nskt, ")\n") 63 | end 64 | end 65 | 66 | chan_remove = function(evq, evid, fd) 67 | evq:del(evid) 68 | fd:close() 69 | socket_put(fd) 70 | 71 | if DEBUG then 72 | nskt = nskt - 1 73 | stderr:write("- Remove from set (", nskt, ")\n") 74 | end 75 | end 76 | end 77 | 78 | local function process(evq, evid, fd, _, eof) 79 | local line 80 | if not eof then 81 | line = fd:read() 82 | end 83 | if line then 84 | line = fd:write(line) 85 | if ONE_SHOT_CLIENT then 86 | fd:shutdown() 87 | end 88 | end 89 | if not line then 90 | chan_remove(evq, evid, fd) 91 | end 92 | end 93 | 94 | local function accept(evq, evid, fd) 95 | local peer 96 | if DEBUG then 97 | peer = sock.addr() 98 | end 99 | local newfd = socket_get() 100 | 101 | if fd:accept(newfd, peer) then 102 | chan_insert(evq, newfd, process) 103 | 104 | if DEBUG then 105 | local port, addr = peer:inet() 106 | stderr:write("Peer: ", sock.inet_ntop(addr), ":", port, "\n") 107 | end 108 | else 109 | socket_put(newfd) 110 | stderr:write("accept: ", SYS_ERR, "\n") 111 | end 112 | end 113 | 114 | 115 | local evq = assert(sys.event_queue()) 116 | 117 | print("Binding servers...") 118 | local saddr = sock.addr() 119 | for port, host in pairs(bind) do 120 | local fd = sock.handle() 121 | assert(fd:socket()) 122 | assert(fd:sockopt("reuseaddr", 1)) 123 | assert(saddr:inet(port, sock.inet_pton(host))) 124 | assert(fd:bind(saddr)) 125 | assert(fd:listen()) 126 | assert(evq:add_socket(fd, 'accept', accept)) 127 | end 128 | 129 | -- Quit by Ctrl-C 130 | assert(evq:add_signal("INT", evq.stop)) 131 | 132 | print("Loop (+" .. (EXTRA_LOOPS or 0) .. ")...") 133 | if is_multithread then 134 | for i = 1, EXTRA_LOOPS do 135 | assert(thread.run(evq.loop, evq)) 136 | end 137 | end 138 | evq:loop() 139 | -------------------------------------------------------------------------------- /src/event/win32.h: -------------------------------------------------------------------------------- 1 | #ifndef EVQ_WIN32_H 2 | #define EVQ_WIN32_H 3 | 4 | #define EVQ_SOURCE "win32.c" 5 | 6 | #define NEVENT (MAXIMUM_WAIT_OBJECTS-1) 7 | 8 | /* Win32 Thread */ 9 | struct win32thr { 10 | struct event_queue *evq; 11 | struct timeout_queue *tq; 12 | struct win32thr *next, *next_ready; 13 | 14 | CRITICAL_SECTION sig_cs; 15 | HANDLE signal; 16 | unsigned int volatile n; /* count of events */ 17 | 18 | #define WTHR_SLEEP 1 19 | #define WTHR_POLL 2 20 | #define WTHR_READY 3 21 | #define WTHR_ACK 4 22 | unsigned int volatile state; 23 | 24 | unsigned int idx; /* result of Wait* */ 25 | HANDLE handles[NEVENT]; /* last handle is reserved for signal event */ 26 | struct event *events[NEVENT-1]; 27 | }; 28 | 29 | struct win32overlapped { 30 | union { 31 | DWORD Internal; 32 | DWORD err; 33 | } il; 34 | union { 35 | DWORD InternalHigh; 36 | DWORD rw_flags; 37 | } ih; 38 | union { 39 | struct { 40 | DWORD Offset; 41 | DWORD OffsetHigh; 42 | } w; 43 | struct win32overlapped *ov_next; 44 | } o; 45 | union { 46 | HANDLE hEvent; 47 | struct event *ev; 48 | } e; 49 | }; 50 | 51 | /* List of overlaps */ 52 | struct win32overlapped_list { 53 | struct win32overlapped *ov_head, *ov_tail; 54 | }; 55 | 56 | /* IOCR thread */ 57 | struct win32iocr_thread { 58 | int stop; /* terminate the thread? */ 59 | struct win32overlapped_list ov_list; /* overlaps to be ready */ 60 | struct win32overlapped * volatile ov_set; /* set overlaps to queue */ 61 | }; 62 | 63 | /* Win32 NT I/O Completion Routines */ 64 | struct win32iocr { 65 | HANDLE h; /* thread handle */ 66 | struct win32overlapped_list ov_list; /* overlaps to be queued */ 67 | struct win32iocr_thread *iocr_thr; /* IOCR thread */ 68 | struct win32overlapped * volatile ov_ready; /* ready overlaps */ 69 | }; 70 | 71 | #define EVENT_EXTRA \ 72 | struct win32thr *wth; \ 73 | union { \ 74 | unsigned int index; \ 75 | struct win32overlapped *ov; /* IOCR overlap */ \ 76 | } w; 77 | 78 | #define WIN32OV_BUF_IDX 6 /* initial buffer size on power of 2 */ 79 | #define WIN32OV_BUF_MAX 24 /* maximum buffer size on power of 2 */ 80 | #define WIN32OV_BUF_SIZE (WIN32OV_BUF_MAX - WIN32OV_BUF_IDX + 1) 81 | 82 | #define EVQ_EXTRA \ 83 | HANDLE ack_event; \ 84 | struct event *win_msg; /* window messages handler */ \ 85 | struct win32thr * volatile wth_ready; \ 86 | int volatile nwakeup; /* number of the re-polling threads */ \ 87 | int volatile sig_ready; /* triggered signals */ \ 88 | struct win32thr head; \ 89 | struct win32iocr iocr; \ 90 | int ov_buf_nevents; /* number of used overlaps of cur. buffer */ \ 91 | int ov_buf_index; /* index of current buffer */ \ 92 | struct win32overlapped *ov_free; /* head of free overlaps */ \ 93 | struct win32overlapped *ov_buffers[WIN32OV_BUF_SIZE]; 94 | 95 | EVQ_API int evq_add_regwatch (struct event_queue *evq, struct event *ev, 96 | HKEY hk); 97 | 98 | #define event_get_evq(ev) (ev)->wth->evq 99 | #define event_get_tq_head(ev) (ev)->wth->tq 100 | #define event_deleted(ev) ((ev)->wth == NULL) 101 | #define evq_is_empty(evq) (!((evq)->nevents || (evq)->head.next)) 102 | 103 | /* Have to initialize the event source */ 104 | #define EVQ_POST_INIT 105 | 106 | EVQ_API int evq_post_init (struct event *ev); 107 | 108 | #endif 109 | -------------------------------------------------------------------------------- /src/win32/getloadavg.c: -------------------------------------------------------------------------------- 1 | /* Lua System: Win32: loadavg() */ 2 | 3 | #if defined(SRWLOCK_INIT) && defined(WIN32_VISTA) 4 | 5 | #define filetime_int64(ft) ((int64_t) ((LARGE_INTEGER *) &(ft))->QuadPart) 6 | 7 | static struct { 8 | int64_t idle; 9 | int64_t kernel; 10 | int64_t user; 11 | } g_OldTimes; 12 | 13 | 14 | static int 15 | getloadavg (double *loadavg) 16 | { 17 | int res = -1; 18 | 19 | EnterCriticalSection(&g_CritSect); 20 | { 21 | FILETIME idleTime, kernelTime, userTime; 22 | 23 | if (GetSystemTimes(&idleTime, &kernelTime, &userTime)) { 24 | const int64_t idle = filetime_int64(idleTime); 25 | const int64_t kernel = filetime_int64(kernelTime); 26 | const int64_t user = filetime_int64(userTime); 27 | 28 | const int64_t idle_delta = idle - g_OldTimes.idle; 29 | const int64_t kernel_delta = kernel - g_OldTimes.kernel; 30 | const int64_t user_delta = user - g_OldTimes.user; 31 | 32 | *loadavg = 1.0 - (double) idle_delta / (kernel_delta + user_delta); 33 | 34 | g_OldTimes.idle = idle; 35 | g_OldTimes.kernel = kernel; 36 | g_OldTimes.user = user; 37 | 38 | res = 0; 39 | } 40 | } 41 | LeaveCriticalSection(&g_CritSect); 42 | 43 | return res; 44 | } 45 | 46 | #else 47 | 48 | typedef struct { 49 | DWORD status; 50 | union { 51 | LONG vLong; 52 | double vDouble; 53 | LONGLONG vLongLong; 54 | void *vPtr; 55 | } u; 56 | } PDH_VALUE; 57 | 58 | typedef long (WINAPI *PPdhOpenQuery) (LPCSTR, DWORD_PTR, HANDLE *); 59 | typedef long (WINAPI *PPdhAddEnglishCounter) (HANDLE, LPCSTR, DWORD_PTR, HANDLE *); 60 | typedef long (WINAPI *PPdhCollectQueryData) (HANDLE); 61 | typedef long (WINAPI *PPdhGetFormattedCounterValue) (HANDLE, DWORD, LPDWORD, PDH_VALUE *); 62 | 63 | #define PDH_CPU_QUERY "\\Processor(_Total)\\% Processor Time" 64 | 65 | 66 | static HINSTANCE hdll; 67 | static HANDLE hquery; 68 | static HANDLE hcounter; 69 | 70 | static PPdhOpenQuery pPdhOpenQuery; 71 | static PPdhAddEnglishCounter pPdhAddEnglishCounter; 72 | static PPdhCollectQueryData pPdhCollectQueryData; 73 | static PPdhGetFormattedCounterValue pPdhGetFormattedCounterValue; 74 | 75 | 76 | static int 77 | getloadavg (double *loadavg) 78 | { 79 | PDH_VALUE value; 80 | int res; 81 | 82 | if (!hdll) { 83 | hdll = LoadLibrary("pdh.dll"); 84 | if (!hdll) return -1; 85 | 86 | pPdhOpenQuery = (PPdhOpenQuery) 87 | GetProcAddress(hdll, "PdhOpenQueryA"); 88 | pPdhAddEnglishCounter = (PPdhAddEnglishCounter) 89 | GetProcAddress(hdll, "PdhAddEnglishCounterA"); 90 | pPdhCollectQueryData = (PPdhCollectQueryData) 91 | GetProcAddress(hdll, "PdhCollectQueryData"); 92 | pPdhGetFormattedCounterValue = (PPdhGetFormattedCounterValue) 93 | GetProcAddress(hdll, "PdhGetFormattedCounterValue"); 94 | 95 | res = pPdhOpenQuery(NULL, 0, &hquery); 96 | if (res) return res; 97 | 98 | res = pPdhAddEnglishCounter(hquery, PDH_CPU_QUERY, 0, &hcounter); 99 | if (res) return res; 100 | 101 | pPdhCollectQueryData(hquery); /* to avoid PDH_INVALID_DATA result */ 102 | } 103 | 104 | if (!hcounter) return -1; 105 | 106 | res = pPdhCollectQueryData(hquery); 107 | if (res) return res; 108 | 109 | res = pPdhGetFormattedCounterValue(hcounter, 0x8200, NULL, &value); 110 | if (res) return res; 111 | 112 | *loadavg = value.u.vDouble / 100.0; 113 | return 0; 114 | } 115 | 116 | #endif /* !WIN32_VISTA */ 117 | -------------------------------------------------------------------------------- /src/thread/thread_affin.c: -------------------------------------------------------------------------------- 1 | /* Lua System: Threading: CPU Affinity */ 2 | 3 | #if defined(__linux__) 4 | 5 | typedef cpu_set_t affin_mask_t; 6 | 7 | #define USE_PTHREAD_AFFIN 8 | 9 | #ifndef CPU_ALLOC 10 | #define CPU_ALLOC(n) malloc(n / CHAR_BIT) 11 | #define CPU_FREE(p) free(p) 12 | #endif 13 | #define CPU_NEW() CPU_ALLOC(CPU_SETSIZE) 14 | #define CPU_DEL(p) CPU_FREE(p) 15 | #define CPU_SIZEOF(p) sizeof(affin_mask_t) 16 | #define CPU_COUNTMAX(p) CPU_SETSIZE 17 | 18 | #elif defined(__FreeBSD__) 19 | 20 | #include 21 | 22 | typedef cpuset_t affin_mask_t; 23 | 24 | #define USE_PTHREAD_AFFIN 25 | 26 | #define CPU_NEW() malloc(sizeof(affin_mask_t)) 27 | #define CPU_DEL(p) free(p) 28 | #define CPU_SIZEOF(p) CPU_SETSIZE 29 | 30 | #elif defined(BSD) 31 | 32 | typedef cpuset_t affin_mask_t; 33 | 34 | #define USE_PTHREAD_AFFIN 35 | 36 | #define CPU_NEW() cpuset_create() 37 | #define CPU_DEL(p) cpuset_destroy(p) 38 | #define CPU_SIZEOF(p) cpuset_size(p) 39 | #define CPU_ZERO(p) cpuset_zero(p) 40 | #define CPU_SET(i,p) cpuset_set(i, p) 41 | #define CPU_ISSET(i,p) (cpuset_isset(i, p) > 0) 42 | 43 | #else /* _WIN32 */ 44 | 45 | typedef size_t affin_mask_t; 46 | 47 | #define CPU_NEW() malloc(sizeof(affin_mask_t)) 48 | #define CPU_DEL(p) free(p) 49 | #define CPU_SIZEOF(p) sizeof(affin_mask_t) 50 | #define CPU_ZERO(p) (*(p) = 0) 51 | #define CPU_SET(i,p) (*(p) |= ((affin_mask_t) 1 << (i))) 52 | #define CPU_ISSET(i,p) ((*(p) & ((affin_mask_t) 1 << (i))) != 0) 53 | 54 | #endif 55 | 56 | 57 | #ifndef CPU_COUNTMAX 58 | #define CPU_COUNTMAX(p) (CPU_SIZEOF(p) * CHAR_BIT) 59 | #endif 60 | 61 | #ifndef CPU_COUNT 62 | static int 63 | affin_cpu_count (affin_mask_t *mp) 64 | { 65 | const unsigned int nmax = CPU_COUNTMAX(mp); 66 | unsigned int i; 67 | int n = 0; 68 | 69 | for (i = 0; i < nmax; ++i) { 70 | if (CPU_ISSET(i, mp)) ++n; 71 | } 72 | return n; 73 | } 74 | 75 | #define CPU_COUNT(p) affin_cpu_count(p) 76 | #endif 77 | 78 | static int 79 | affin_cpu_offset (int cpu, affin_mask_t *mp) 80 | { 81 | const unsigned int nmax = CPU_COUNTMAX(mp); 82 | unsigned int i; 83 | 84 | for (i = 0; i < nmax; ++i) { 85 | if (CPU_ISSET(i, mp) && !--cpu) 86 | return (int) i; 87 | } 88 | return -1; 89 | } 90 | 91 | static int 92 | affin_cpu_fill (affin_mask_t *mp) 93 | { 94 | #if defined(USE_PTHREAD_AFFIN) 95 | const int res = pthread_getaffinity_np(pthread_self(), CPU_SIZEOF(mp), mp); 96 | if (res) errno = res; 97 | return res; 98 | #elif defined(_WIN32) 99 | { 100 | const HANDLE hProc = GetCurrentProcess(); 101 | DWORD_PTR proc_mask, sys_mask; 102 | 103 | if (GetProcessAffinityMask(hProc, &proc_mask, &sys_mask)) { 104 | *mp = proc_mask; 105 | return 0; 106 | } 107 | } 108 | return -1; 109 | #else 110 | (void) mp; 111 | 112 | return -1; 113 | #endif 114 | } 115 | 116 | static int 117 | affin_cpu_set (thread_id_t tid, int cpu) 118 | { 119 | affin_mask_t *mp = CPU_NEW(); 120 | int res = -1; 121 | 122 | if (!mp) goto err; 123 | 124 | if (!affin_cpu_fill(mp)) { 125 | if (cpu) { 126 | const int cpu_off = affin_cpu_offset(cpu, mp); 127 | 128 | if (cpu_off == -1) goto err_clean; 129 | 130 | CPU_ZERO(mp); 131 | CPU_SET(cpu_off, mp); 132 | } 133 | 134 | #if defined(USE_PTHREAD_AFFIN) 135 | res = pthread_setaffinity_np(tid, CPU_SIZEOF(mp), mp); 136 | if (res) errno = res; 137 | #elif defined(_WIN32) 138 | res = (SetThreadAffinityMask(tid, *mp) > 0) ? 0 : -1; 139 | #endif 140 | } 141 | err_clean: 142 | CPU_DEL(mp); 143 | err: 144 | return res; 145 | } 146 | 147 | /* 148 | * Returns: [number_of_processors (number)] 149 | */ 150 | static int 151 | affin_nprocs (lua_State *L) 152 | { 153 | affin_mask_t *mp = CPU_NEW(); 154 | int n = 0; 155 | 156 | if (mp) { 157 | if (!affin_cpu_fill(mp)) { 158 | n = CPU_COUNT(mp); 159 | } 160 | CPU_DEL(mp); 161 | } 162 | if (!n) return 0; 163 | lua_pushinteger(L, n); 164 | return 1; 165 | } 166 | 167 | 168 | #define AFFIN_METHODS \ 169 | {"nprocs", affin_nprocs} 170 | -------------------------------------------------------------------------------- /test/event/test_evq.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | local sys = require("sys") 4 | local sock = require"sys.sock" 5 | 6 | 7 | print"-- Directory Watch" 8 | do 9 | local evq = assert(sys.event_queue()) 10 | 11 | local filename = "test.tmp" 12 | local fd = sys.handle() 13 | 14 | local function on_change(evq, evid, path, ev) 15 | fd:close() 16 | sys.remove(filename) 17 | assert(ev == 'r', "file change notification expected") 18 | end 19 | 20 | sys.remove(filename) 21 | assert(evq:add_dirwatch(".", on_change, 100, true, false)) 22 | assert(fd:create(filename)) 23 | 24 | assert(evq:loop()) 25 | print"OK" 26 | end 27 | 28 | 29 | print"-- Sockets Chain" 30 | do 31 | local evq = assert(sys.event_queue()) 32 | 33 | local pipe1, pipe2, pipe3 34 | 35 | local function read_cb(evq, evid, fd) 36 | fd:read(1) 37 | evq:del(evid) 38 | if pipe3 then 39 | pipe2[2]:write("e") 40 | evq:del(pipe3[3]) 41 | pipe3[1]:close() 42 | pipe3[2]:close() 43 | pipe3 = nil 44 | end 45 | end 46 | 47 | local function create_socketpair() 48 | local pipe = {sock.handle(), sock.handle()} 49 | assert(pipe[1]:socket(pipe[2])) 50 | pipe[3] = assert(evq:add_socket(pipe[1], "r", read_cb)) 51 | return pipe 52 | end 53 | 54 | pipe1 = create_socketpair() 55 | pipe2 = create_socketpair() 56 | pipe3 = create_socketpair() 57 | 58 | pipe1[2]:write("e") 59 | 60 | assert(evq:loop()) 61 | print"OK" 62 | end 63 | 64 | 65 | print"-- Socket Pair" 66 | do 67 | local evq = assert(sys.event_queue()) 68 | 69 | local msg = "test" 70 | 71 | local function ev_cb(evq, evid, fd, ev) 72 | if ev == 'r' then 73 | local line = fd:recv() 74 | assert(line == msg, "Got: " .. tostring(line)) 75 | elseif ev == 'w' then 76 | fd:send(msg) 77 | else 78 | error("Bad event: " .. ev) 79 | end 80 | evq:del(evid) 81 | fd:close() 82 | end 83 | 84 | local sd0, sd1 = sock.handle(), sock.handle() 85 | assert(sd0:socket(sd1)) 86 | 87 | evq:add_socket(sd0, 'r', ev_cb) 88 | evq:add_socket(sd1, 'w', ev_cb) 89 | 90 | assert(evq:loop()) 91 | print"OK" 92 | end 93 | 94 | 95 | print"-- Coroutines" 96 | do 97 | local evq = assert(sys.event_queue()) 98 | 99 | local function sleep(msg) 100 | print(msg, coroutine.yield()) 101 | end 102 | 103 | local function start(co, num) 104 | local evid = assert(evq:add_timer(co, 10)) 105 | sleep"init" 106 | assert(evq:timeout(evid, 20)) 107 | sleep"work" 108 | assert(evq:timeout(evid, 30)) 109 | sleep"done" 110 | if num % 2 == 0 then 111 | assert(evq:del(evid)) 112 | end 113 | end 114 | 115 | for i = 1, 3 do 116 | local co = assert(coroutine.create(start)) 117 | assert(coroutine.resume(co, co, i)) 118 | end 119 | 120 | assert(evq:loop()) 121 | print"OK" 122 | end 123 | 124 | 125 | print"-- Signal: wait SIGINT" 126 | do 127 | local function on_signal(evq, evid, _, ev) 128 | if ev == 't' then 129 | assert(evq:timeout(evid)) 130 | assert(evq:ignore_signal("INT", false)) 131 | print"SIGINT enabled. Please, press Ctrl-C..." 132 | else 133 | assert(evq:del(evid)) 134 | print"Thanks!" 135 | end 136 | end 137 | 138 | local evq = assert(sys.event_queue()) 139 | 140 | assert(evq:add_signal("INT", on_signal, 3000)) 141 | assert(evq:ignore_signal("INT", true)) 142 | 143 | evq:loop(30000) 144 | print"OK" 145 | end 146 | 147 | 148 | -- WIN32 149 | local win32 = sys.win32 150 | 151 | if not win32 then return end 152 | 153 | 154 | print"-- Registry Watch" 155 | do 156 | local evq = assert(sys.event_queue()) 157 | 158 | local keyname = "test.tmp" 159 | local hk = win32.registry() 160 | 161 | assert(hk:open("HKEY_CURRENT_USER", 162 | [[Software\Microsoft\Windows\CurrentVersion]], 'rw')) 163 | 164 | local function on_change(evq, evid, hk, ev) 165 | hk:del_value(keyname) 166 | hk:close() 167 | assert(ev == 'r', "registry key change notification expected") 168 | end 169 | 170 | hk:del_value(keyname) 171 | assert(evq:add_regwatch(hk, on_change, 100, true, false)) 172 | assert(hk:set(keyname, 1234)) 173 | 174 | assert(evq:loop()) 175 | print"OK" 176 | end 177 | 178 | 179 | -------------------------------------------------------------------------------- /src/event/evq.h: -------------------------------------------------------------------------------- 1 | #ifndef EVQ_H 2 | #define EVQ_H 3 | 4 | #ifndef EVQ_API 5 | #define EVQ_API static 6 | #endif 7 | 8 | struct event; 9 | struct event_queue; 10 | 11 | #if defined(_WIN32) 12 | #include "win32.h" 13 | #elif defined(USE_KQUEUE) 14 | #include "kqueue.h" 15 | #elif defined(USE_EPOLL) 16 | #include "epoll.h" 17 | #elif defined(USE_POLL) 18 | #include "poll.h" 19 | #else 20 | #include "select.h" 21 | #endif 22 | 23 | #include "signal.h" 24 | #include "timeout.h" 25 | 26 | struct event { 27 | struct event *next_ready, *next_object; 28 | 29 | /* timeout */ 30 | struct event *prev, *next; 31 | struct timeout_queue *tq; 32 | msec_t timeout_at; 33 | 34 | #define EVENT_READ 0x00000001 35 | #define EVENT_WRITE 0x00000002 36 | #define EVENT_ONESHOT 0x00000004 37 | #define EVENT_DELETE 0x00000008 38 | #define EVENT_SOCKET 0x00000010 39 | #define EVENT_TIMER 0x00000020 40 | #define EVENT_PID 0x00000040 41 | #define EVENT_SIGNAL 0x00000080 42 | #define EVENT_WINMSG 0x00000100 43 | #define EVENT_DIRWATCH 0x00000200 /* directory watcher */ 44 | #define EVENT_REGWATCH 0x00000400 /* Windows registry watcher */ 45 | #define EVENT_TIMEOUT_MANUAL 0x00000800 /* don't auto-reset timeout on event */ 46 | #define EVENT_AIO 0x00001000 47 | #define EVENT_SOCKET_ACC_CONN 0x00002000 /* socket is listening or connecting */ 48 | #define EVENT_CALLBACK 0x00010000 /* callback exists */ 49 | #define EVENT_CALLBACK_CORO 0x00020000 /* callback is coroutine */ 50 | #define EVENT_CALLBACK_SCHED 0x00040000 /* callback is scheduler */ 51 | /* triggered events (result of waiting) */ 52 | #define EVENT_ACTIVE 0x00080000 53 | #define EVENT_READ_RES 0x00100000 54 | #define EVENT_WRITE_RES 0x00200000 55 | #define EVENT_TIMEOUT_RES 0x00400000 56 | #define EVENT_EOF_RES 0x00800000 57 | #define EVENT_MASK_RES 0x00F80000 58 | /* options: directory/registry watcher */ 59 | #define EVENT_WATCH_MODIFY 0x01000000 /* watch only content changes */ 60 | #define EVENT_WATCH_RECURSIVE 0x02000000 /* watch directories recursively */ 61 | /* options: AIO requests */ 62 | #define EVENT_AIO_PENDING 0x01000000 /* AIO read/write request not completed */ 63 | /* options: process status (result of oneshot waiting) */ 64 | #define EVENT_STATUS_MASK 0xFF000000 65 | #define EVENT_STATUS_SHIFT 24 /* last byte is process status */ 66 | unsigned int flags; 67 | 68 | int ev_id; 69 | fd_t fd; 70 | 71 | EVENT_EXTRA 72 | }; 73 | 74 | struct event_queue { 75 | #define EVQ_FLAG_STOP 0x01 /* break the loop? */ 76 | #define EVQ_FLAG_WAITING 0x02 /* waiting events? */ 77 | unsigned int volatile flags; 78 | 79 | unsigned int nevents; /* number of alive events */ 80 | 81 | int buf_nevents; /* number of used events of current buffer */ 82 | int buf_index; /* environ. index of current buffer */ 83 | 84 | msec_t now; /* current cached time */ 85 | 86 | struct event * volatile ev_ready; /* head of ready events */ 87 | struct event *ev_free; /* head of free events */ 88 | 89 | timeout_map_fn tq_map_fn; 90 | 91 | EVQ_APP_EXTRA 92 | EVQ_EXTRA 93 | }; 94 | 95 | EVQ_API int evq_init (struct event_queue *evq); 96 | EVQ_API void evq_done (struct event_queue *evq); 97 | 98 | EVQ_API int evq_add (struct event_queue *evq, struct event *ev); 99 | EVQ_API int evq_add_dirwatch (struct event_queue *evq, struct event *ev, 100 | const char *path); 101 | EVQ_API int evq_del (struct event *ev, const int reuse_fd); 102 | 103 | EVQ_API int evq_modify (struct event *ev, unsigned int flags); 104 | 105 | EVQ_API int evq_wait (struct event_queue *evq, struct sys_thread *td, 106 | msec_t timeout); 107 | 108 | EVQ_API int evq_set_timeout (struct event *ev, const msec_t msec); 109 | EVQ_API int evq_add_timer (struct event_queue *evq, struct event *ev, 110 | const msec_t msec); 111 | 112 | EVQ_API struct event *evq_process_active (struct event *ev, 113 | struct event *ev_ready, 114 | const msec_t now); 115 | 116 | #ifndef _WIN32 117 | 118 | #define event_get_evq(ev) (ev)->evq 119 | #define event_get_tq_head(ev) (ev)->evq->tq 120 | #define event_deleted(ev) ((ev)->evq == NULL) 121 | #define evq_is_empty(evq) (!(evq)->nevents) 122 | 123 | #endif 124 | 125 | #endif 126 | -------------------------------------------------------------------------------- /test/sock/echosrvr_co.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | local sys = require"sys" 4 | local sock = require"sys.sock" 5 | 6 | local thread = sys.thread 7 | assert(thread.init()) 8 | 9 | 10 | local ONE_SHOT_CLIENT = false 11 | local DEBUG = false 12 | 13 | local bind = { 14 | [8080] = "127.0.0.1", 15 | } 16 | 17 | local stderr = sys.stderr 18 | 19 | 20 | local evq = assert(sys.event_queue()) 21 | 22 | -- Scheduler 23 | local sched 24 | do 25 | local pool = setmetatable({n = 0}, {__mode = "v"}) 26 | 27 | local function controller(co, err) 28 | if err then return end 29 | if co then 30 | local n = pool.n + 1 31 | pool.n, pool[n] = n, co 32 | else 33 | local n = pool.n 34 | local co = pool[n] 35 | pool.n = co and (n - 1) or 0 36 | return co 37 | end 38 | end 39 | 40 | sched = assert(thread.scheduler(controller)) 41 | assert(sched:event_queue(evq)) 42 | end 43 | 44 | 45 | -- Pool of sockets 46 | local socket_get, socket_put 47 | do 48 | local pool = setmetatable({n = 0}, {__mode = "v"}) 49 | 50 | socket_get = function() 51 | local n = pool.n 52 | local fd = pool[n] 53 | if not fd then 54 | fd = sock.handle() 55 | n = 1 56 | end 57 | pool.n = n - 1 58 | return fd 59 | end 60 | 61 | socket_put = function(fd) 62 | local n = pool.n + 1 63 | pool.n, pool[n] = n, fd 64 | end 65 | end 66 | 67 | 68 | -- Socket non-blocking read/write operations 69 | local socket_read, socket_write 70 | do 71 | socket_read = function(fd) 72 | while true do 73 | local res = fd:read() 74 | if res then 75 | return res 76 | end 77 | if res == false then 78 | local _, eof = sched:wait_socket(evq, fd, 'r') 79 | if eof then break end 80 | else break end 81 | end 82 | end 83 | 84 | socket_write = function(fd, line) 85 | while true do 86 | local res = fd:write(line) 87 | if res then 88 | return res 89 | end 90 | if res == false then 91 | local _, eof = sched:wait_socket(evq, fd, 'w') 92 | if eof then break end 93 | else break end 94 | end 95 | end 96 | end 97 | 98 | 99 | -- Channels 100 | local chan_insert, chan_remove 101 | do 102 | local nskt = 0 103 | 104 | chan_insert = function(fd, task_fn) 105 | fd:nonblocking(true) 106 | if not sched:put(task_fn, fd) then 107 | error(SYS_ERR) 108 | end 109 | 110 | if DEBUG then 111 | nskt = nskt + 1 112 | stderr:write("+ Insert in set (", nskt, ")\n") 113 | end 114 | end 115 | 116 | chan_remove = function(fd) 117 | fd:close() 118 | socket_put(fd) 119 | 120 | if DEBUG then 121 | nskt = nskt - 1 122 | stderr:write("- Remove from set (", nskt, ")\n") 123 | end 124 | end 125 | end 126 | 127 | local function process(fd) 128 | local line = socket_read(fd) 129 | if line then 130 | line = socket_write(fd, line) 131 | if ONE_SHOT_CLIENT then 132 | fd:shutdown() 133 | end 134 | if line then 135 | return true 136 | end 137 | end 138 | chan_remove(fd) 139 | end 140 | 141 | local function task_process(fd) 142 | while process(fd) do end 143 | end 144 | 145 | local function accept(_, _, fd) 146 | local peer 147 | if DEBUG then 148 | peer = sock.addr() 149 | end 150 | local newfd = socket_get() 151 | 152 | if fd:accept(newfd, peer) then 153 | chan_insert(newfd, task_process) 154 | 155 | if DEBUG then 156 | local port, addr = peer:inet() 157 | stderr:write("Peer: ", sock.inet_ntop(addr), ":", port, "\n") 158 | end 159 | else 160 | socket_put(newfd) 161 | stderr:write("accept: ", SYS_ERR, "\n") 162 | end 163 | end 164 | 165 | local function task_accept(fd) 166 | while true do 167 | if not sched:wait_socket(evq, fd, 'accept') then 168 | error(SYS_ERR) 169 | end 170 | 171 | accept(nil, nil, fd) 172 | end 173 | end 174 | 175 | 176 | print("Binding servers...") 177 | do 178 | local saddr = sock.addr() 179 | for port, host in pairs(bind) do 180 | local fd = sock.handle() 181 | assert(fd:socket()) 182 | assert(fd:sockopt("reuseaddr", 1)) 183 | assert(saddr:inet(port, sock.inet_pton(host))) 184 | assert(fd:bind(saddr)) 185 | assert(fd:listen()) 186 | assert(fd:nonblocking(true)) 187 | --assert(sched:put(task_accept, fd)) 188 | assert(evq:add_socket(fd, 'accept', accept)) 189 | end 190 | end 191 | 192 | -- Quit by Ctrl-C 193 | do 194 | local function on_interrupt() 195 | evq:stop() 196 | sched:stop() 197 | end 198 | 199 | assert(evq:add_signal("INT", on_interrupt)) 200 | end 201 | 202 | print("Loop...") 203 | sched:loop() 204 | -------------------------------------------------------------------------------- /src/sys_log.c: -------------------------------------------------------------------------------- 1 | /* Lua System: Logging */ 2 | 3 | #ifndef _WIN32 4 | #include 5 | #endif 6 | 7 | #define LOG_TYPENAME "sys.log" 8 | 9 | /* Log event types */ 10 | #ifdef _WIN32 11 | #define LOG_TDEBUG EVENTLOG_SUCCESS 12 | #define LOG_TERROR EVENTLOG_ERROR_TYPE 13 | #define LOG_TWARN EVENTLOG_WARNING_TYPE 14 | #define LOG_TINFO EVENTLOG_INFORMATION_TYPE 15 | #else 16 | #define LOG_TDEBUG LOG_DEBUG 17 | #define LOG_TERROR LOG_ERR 18 | #define LOG_TWARN LOG_WARNING 19 | #define LOG_TINFO LOG_INFO 20 | #endif 21 | 22 | /* Log environ. table reserved indexes */ 23 | #define SYSLOG_ENV_IDENT 1 24 | 25 | struct sys_log { 26 | #ifdef _WIN32 27 | HANDLE h; 28 | #endif 29 | int type; 30 | }; 31 | 32 | 33 | /* 34 | * Arguments: [ident (string)] 35 | * Returns: [log_udata] 36 | */ 37 | static int 38 | sys_log (lua_State *L) 39 | { 40 | const int is_ident = (lua_type(L, 1) == LUA_TSTRING); 41 | const char *ident = is_ident ? lua_tostring(L, 1) : "lua"; 42 | struct sys_log *logp = lua_newuserdata(L, sizeof(struct sys_log)); 43 | 44 | #ifndef _WIN32 45 | openlog(ident, LOG_CONS, LOG_USER); 46 | { 47 | #else 48 | /* register the event source */ 49 | { 50 | HKEY app_hk = NULL, ident_hk = NULL; 51 | DWORD opt; 52 | 53 | RegOpenKeyExA(HKEY_LOCAL_MACHINE, 54 | "System\\CurrentControlSet\\Services\\EventLog\\Application", 55 | 0, KEY_WRITE, &app_hk); 56 | 57 | RegCreateKeyExA(app_hk, ident, 58 | 0, NULL, 0, KEY_WRITE, NULL, &ident_hk, &opt); 59 | 60 | if (ident_hk && opt == REG_CREATED_NEW_KEY) { 61 | RegSetValueExA(ident_hk, "EventMessageFile", 62 | 0, REG_EXPAND_SZ, 63 | (const unsigned char *) "%SystemRoot%\\System32\\netmsg.dll", 64 | sizeof("%SystemRoot%\\System32\\netmsg.dll")); 65 | 66 | opt = 1; 67 | RegSetValueExA(ident_hk, "TypesSupported", 68 | 0, REG_DWORD, (unsigned char *) &opt, sizeof(DWORD)); 69 | } 70 | RegCloseKey(ident_hk); 71 | RegCloseKey(app_hk); 72 | } 73 | 74 | logp->h = OpenEventLogA(NULL, ident); 75 | if (logp->h) { 76 | #endif 77 | logp->type = LOG_TERROR; 78 | luaL_getmetatable(L, LOG_TYPENAME); 79 | lua_setmetatable(L, -2); 80 | 81 | if (is_ident) { 82 | lua_newtable(L); /* environ. */ 83 | lua_pushvalue(L, 1); 84 | lua_rawseti(L, -2, SYSLOG_ENV_IDENT); 85 | lua_setfenv(L, -2); 86 | } 87 | return 1; 88 | } 89 | #ifdef _WIN32 90 | return sys_seterror(L, 0); 91 | #endif 92 | } 93 | 94 | /* 95 | * Arguments: log_udata 96 | */ 97 | static int 98 | log_close (lua_State *L) 99 | { 100 | #ifndef _WIN32 101 | (void) L; 102 | closelog(); 103 | #else 104 | struct sys_log *logp = checkudata(L, 1, LOG_TYPENAME); 105 | CloseEventLog(logp->h); 106 | #endif 107 | return 0; 108 | } 109 | 110 | /* 111 | * Arguments: log_udata, type (string: "debug", "error", "warn", "info") 112 | * Returns: log_report (function) 113 | */ 114 | static int 115 | log_type (lua_State *L) 116 | { 117 | struct sys_log *logp = checkudata(L, 1, LOG_TYPENAME); 118 | const char *type = lua_tostring(L, 2); 119 | 120 | if (type) { 121 | int t = LOG_TERROR; 122 | switch (type[0]) { 123 | case 'd': t = LOG_TDEBUG; break; 124 | case 'e': t = LOG_TERROR; break; 125 | case 'w': t = LOG_TWARN; break; 126 | case 'i': t = LOG_TINFO; break; 127 | default: luaL_argerror(L, 2, "invalid option"); 128 | } 129 | logp->type = t; 130 | } 131 | return luaL_getmetafield(L, 1, "__call"); 132 | } 133 | 134 | /* 135 | * Arguments: log_udata, message (string) 136 | * Returns: [log_udata] 137 | */ 138 | static int 139 | log_report (lua_State *L) 140 | { 141 | struct sys_log *logp = checkudata(L, 1, LOG_TYPENAME); 142 | const char *msg = luaL_checkstring(L, 2); 143 | 144 | #ifndef _WIN32 145 | sys_vm_leave(L); 146 | syslog(logp->type, "%s", msg); 147 | sys_vm_enter(L); 148 | #else 149 | const WCHAR *buf[9] = {NULL}; 150 | void *os_msg = utf8_to_filename(msg); 151 | 152 | if (!os_msg) 153 | return sys_seterror(L, ERROR_NOT_ENOUGH_MEMORY); 154 | 155 | sys_vm_leave(L); 156 | buf[0] = os_msg; 157 | 158 | if (is_WinNT) { 159 | ReportEventW(logp->h, (short) logp->type, 160 | 0, 3299, NULL, sizeof(buf) / sizeof(buf[0]), 0, 161 | (const WCHAR **) buf, NULL); 162 | } else { 163 | ReportEventA(logp->h, (short) logp->type, 164 | 0, 3299, NULL, sizeof(buf) / sizeof(buf[0]), 0, 165 | (const CHAR **) buf, NULL); 166 | } 167 | 168 | free(os_msg); 169 | sys_vm_enter(L); 170 | #endif 171 | lua_settop(L, 1); 172 | return 1; 173 | } 174 | 175 | 176 | #define LOG_METHODS \ 177 | {"log", sys_log} 178 | 179 | static luaL_Reg log_meth[] = { 180 | {"__index", log_type}, 181 | {"__call", log_report}, 182 | {"__gc", log_close}, 183 | {NULL, NULL} 184 | }; 185 | -------------------------------------------------------------------------------- /src/event/win32sig.c: -------------------------------------------------------------------------------- 1 | /* Win32 Console Signals */ 2 | 3 | /* Global signal events */ 4 | static struct { 5 | CRITICAL_SECTION cs; 6 | unsigned int volatile ignore; /* ignored signals */ 7 | struct event *events[EVQ_NSIG]; 8 | } g_Signal; 9 | static int volatile g_SignalInit = 0; 10 | 11 | /* References count do not trace */ 12 | #define signal_set(add) \ 13 | (!SetConsoleCtrlHandler((PHANDLER_ROUTINE) signal_handler, (add))) 14 | 15 | 16 | static struct event ** 17 | signal_gethead (int signo) 18 | { 19 | switch (signo) { 20 | case EVQ_SIGINT: signo = 0; break; 21 | case EVQ_SIGQUIT: signo = 1; break; 22 | case EVQ_SIGHUP: signo = 2; break; 23 | case EVQ_SIGTERM: signo = 3; break; 24 | default: return NULL; 25 | } 26 | return &g_Signal.events[signo]; 27 | } 28 | 29 | static int 30 | signal_handler (const int signo) 31 | { 32 | struct event **sig_evp = signal_gethead(signo); 33 | int res = 0; 34 | 35 | if (!sig_evp) return 0; 36 | 37 | EnterCriticalSection(&g_Signal.cs); 38 | if (g_Signal.ignore & (1 << signo)) 39 | res = 1; 40 | else { 41 | struct event *ev = *sig_evp; 42 | for (; ev; ev = ev->next_object) { 43 | if (!event_deleted(ev)) 44 | res |= evq_signal(ev->wth->evq, signo) ? 0 : 1; 45 | } 46 | } 47 | LeaveCriticalSection(&g_Signal.cs); 48 | return res; 49 | } 50 | 51 | EVQ_API void 52 | signal_init (void) 53 | { 54 | if (g_SignalInit) return; 55 | g_SignalInit = 1; 56 | 57 | InitCriticalSection(&g_Signal.cs); 58 | } 59 | 60 | EVQ_API int 61 | evq_signal (struct event_queue *evq, const int signo) 62 | { 63 | struct win32thr *wth = &evq->head; 64 | int res = 0; 65 | 66 | EnterCriticalSection(&wth->sig_cs); 67 | if (!evq->sig_ready) 68 | res = !SetEvent(wth->signal); 69 | evq->sig_ready |= 1 << signo; 70 | LeaveCriticalSection(&wth->sig_cs); 71 | return res; 72 | } 73 | 74 | EVQ_API int 75 | evq_ignore_signal (struct event_queue *evq, const int signo, const int ignore) 76 | { 77 | const int bit = 1 << signo; 78 | int res; 79 | 80 | (void) evq; 81 | 82 | EnterCriticalSection(&g_Signal.cs); 83 | if (!g_Signal.ignore && signal_set(1)) 84 | res = -1; 85 | else { 86 | if (ignore) 87 | g_Signal.ignore |= bit; 88 | else 89 | g_Signal.ignore &= ~bit; 90 | res = 0; 91 | } 92 | LeaveCriticalSection(&g_Signal.cs); 93 | return res; 94 | } 95 | 96 | static int 97 | signal_add (struct event_queue *evq, struct event *ev) 98 | { 99 | const int signo = (int) ((lua_Integer) ev->fd); 100 | struct event **sig_evp = signal_gethead(signo); 101 | 102 | if (!sig_evp) return -1; 103 | 104 | EnterCriticalSection(&g_Signal.cs); 105 | if (*sig_evp) 106 | ev->next_object = *sig_evp; 107 | else { 108 | if (signal_set(1)) 109 | goto err; 110 | ev->next_object = NULL; 111 | } 112 | *sig_evp = ev; 113 | g_Signal.ignore &= ~(1 << signo); /* don't ignore signal */ 114 | LeaveCriticalSection(&g_Signal.cs); 115 | 116 | evq->nevents++; 117 | return 0; 118 | err: 119 | LeaveCriticalSection(&g_Signal.cs); 120 | return -1; 121 | } 122 | 123 | static int 124 | signal_del (struct event *ev) 125 | { 126 | const int signo = (int) ((lua_Integer) ev->fd); 127 | struct event **sig_evp = signal_gethead(signo); 128 | int res = 0; 129 | 130 | EnterCriticalSection(&g_Signal.cs); 131 | if (*sig_evp == ev) { 132 | if (!(*sig_evp = ev->next_object) 133 | && !g_Signal.ignore && signal_set(0)) 134 | res = -1; 135 | } else { 136 | struct event *sig_ev = *sig_evp; 137 | 138 | while (sig_ev->next_object != ev) 139 | sig_ev = sig_ev->next_object; 140 | sig_ev->next_object = ev->next_object; 141 | } 142 | LeaveCriticalSection(&g_Signal.cs); 143 | return res; 144 | } 145 | 146 | static struct event * 147 | signal_process_actives (struct event_queue *evq, const int signo, 148 | struct event *ev_ready, const msec_t now) 149 | { 150 | struct event **sig_evp = signal_gethead(signo); 151 | struct event *ev, *ev_next = NULL; 152 | 153 | EnterCriticalSection(&g_Signal.cs); 154 | ev = *sig_evp; 155 | for (; ev; ev = ev->next_object) { 156 | if (ev->wth->evq == evq && !(ev->flags & EVENT_ACTIVE)) { 157 | ev->next_ready = ev_next; 158 | ev_next = ev; 159 | } 160 | } 161 | LeaveCriticalSection(&g_Signal.cs); 162 | 163 | while (ev_next) { 164 | ev = ev_next; 165 | ev_next = ev->next_ready; 166 | ev_ready = evq_process_active(ev, ev_ready, now); 167 | } 168 | return ev_ready; 169 | } 170 | 171 | static struct event * 172 | signal_process_interrupt (struct event_queue *evq, unsigned int sig_ready, 173 | struct event *ev_ready, const msec_t now) 174 | { 175 | int signo; 176 | 177 | sig_ready &= ~(1 << EVQ_SIGEVQ); 178 | 179 | for (signo = 0; sig_ready; ++signo, sig_ready >>= 1) { 180 | if (sig_ready & 1) 181 | ev_ready = signal_process_actives(evq, signo, ev_ready, now); 182 | } 183 | return ev_ready; 184 | } 185 | 186 | -------------------------------------------------------------------------------- /src/win32/sys_win32.c: -------------------------------------------------------------------------------- 1 | /* Lua System: Win32 specifics */ 2 | 3 | PCancelSynchronousIo pCancelSynchronousIo; 4 | PCancelIoEx pCancelIoEx; 5 | 6 | CRITICAL_SECTION g_CritSect; 7 | static int volatile g_CritSectInit = 0; 8 | 9 | #if !(defined(_WIN32_WCE) || defined(WIN32_VISTA)) 10 | int is_WinNT; 11 | #endif 12 | 13 | 14 | /* 15 | * Arguments: fd_udata, path (string), 16 | * [maximum_message_size (number), timeout (milliseconds)] 17 | * Returns: [fd_udata] 18 | */ 19 | static int 20 | win32_mailslot (lua_State *L) 21 | { 22 | fd_t fd, *fdp = checkudata(L, 1, FD_TYPENAME); 23 | const char *path = luaL_checkstring(L, 2); 24 | const DWORD max_size = (DWORD) lua_tointeger(L, 3); 25 | const DWORD timeout = (DWORD) luaL_optinteger(L, 4, MAILSLOT_WAIT_FOREVER); 26 | 27 | fd = CreateMailslotA(path, max_size, timeout, NULL); 28 | 29 | if (fd != (fd_t) -1) { 30 | *fdp = fd; 31 | lua_settop(L, 1); 32 | return 1; 33 | } 34 | return sys_seterror(L, 0); 35 | } 36 | 37 | /* 38 | * Arguments: fd_udata, [timeout (milliseconds)] 39 | * Returns: [fd_udata] 40 | */ 41 | static int 42 | win32_set_mailslot_info (lua_State *L) 43 | { 44 | fd_t fd = (fd_t) lua_unboxinteger(L, 1, FD_TYPENAME); 45 | const DWORD timeout = (DWORD) luaL_optinteger(L, 2, MAILSLOT_WAIT_FOREVER); 46 | 47 | if (SetMailslotInfo(fd, timeout)) { 48 | lua_settop(L, 1); 49 | return 1; 50 | } 51 | return sys_seterror(L, 0); 52 | } 53 | 54 | /* 55 | * Arguments: fd_udata 56 | * Returns: [next_message_size (number), message_count (number), 57 | * timeout (milliseconds)] 58 | */ 59 | static int 60 | win32_get_mailslot_info (lua_State *L) 61 | { 62 | fd_t fd = (fd_t) lua_unboxinteger(L, 1, FD_TYPENAME); 63 | DWORD next_size, count, timeout; 64 | 65 | if (GetMailslotInfo(fd, NULL, &next_size, &count, &timeout)) { 66 | if (next_size == MAILSLOT_NO_MESSAGE) 67 | next_size = count = 0; 68 | lua_pushinteger(L, next_size); 69 | lua_pushinteger(L, count); 70 | lua_pushinteger(L, timeout); 71 | return 3; 72 | } 73 | return sys_seterror(L, 0); 74 | } 75 | 76 | /* 77 | * Arguments: [frequency (hertz), duration (milliseconds)] 78 | */ 79 | static int 80 | win32_beep (lua_State *L) 81 | { 82 | const int freq = luaL_optint(L, 1, 1000); 83 | const int dur = luaL_optint(L, 2, 100); 84 | 85 | Beep(freq, dur); 86 | return 0; 87 | } 88 | 89 | /* 90 | * Arguments: drive_letter (string: A: .. Z:) 91 | * Returns: [dos_device_name (string)] 92 | */ 93 | static int 94 | win32_drive_dosname (lua_State *L) 95 | { 96 | const char *drive = luaL_checkstring(L, 1); 97 | char buf[MAX_PATHNAME]; 98 | const int res = QueryDosDeviceA(drive, buf, MAX_PATHNAME); 99 | 100 | if (res > 2) { 101 | lua_pushlstring(L, buf, res - 2); 102 | return 1; 103 | } 104 | return sys_seterror(L, 0); 105 | } 106 | 107 | 108 | #include "win32_reg.c" 109 | #include "win32_svc.c" 110 | #include "win32_utf8.c" 111 | 112 | 113 | #define WIN32_METHODS \ 114 | {"mailslot", win32_mailslot}, \ 115 | {"set_mailslot_info", win32_set_mailslot_info}, \ 116 | {"get_mailslot_info", win32_get_mailslot_info} 117 | 118 | static luaL_Reg win32_lib[] = { 119 | {"beep", win32_beep}, 120 | {"drive_dosname", win32_drive_dosname}, 121 | {"registry", reg_new}, 122 | {NULL, NULL} 123 | }; 124 | 125 | 126 | static void 127 | win32_init (lua_State *L) 128 | { 129 | (void) L; 130 | 131 | #ifndef _WIN32_WCE 132 | /* Is Win32 NT platform? */ 133 | { 134 | OSVERSIONINFO osvi; 135 | 136 | memset(&osvi, 0, sizeof(OSVERSIONINFO)); 137 | osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); 138 | GetVersionEx(&osvi); 139 | 140 | #ifndef WIN32_VISTA 141 | is_WinNT = (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT); 142 | #else 143 | if (osvi.dwPlatformId != VER_PLATFORM_WIN32_NT 144 | || osvi.dwMajorVersion < 6) 145 | luaL_argerror(L, 0, "Windows Vista+ expected"); 146 | #endif 147 | } 148 | #endif 149 | 150 | if (is_WinNT) { 151 | HANDLE mh = GetModuleHandleA("kernel32.dll"); 152 | 153 | pCancelSynchronousIo = (PCancelSynchronousIo) 154 | GetProcAddress(mh, "CancelSynchronousIo"); 155 | pCancelIoEx = (PCancelIoEx) 156 | GetProcAddress(mh, "CancelIoEx"); 157 | } 158 | 159 | /* Initialize global critical section */ 160 | if (!g_CritSectInit) { 161 | g_CritSectInit = 1; 162 | InitCriticalSection(&g_CritSect); 163 | } 164 | } 165 | 166 | /* 167 | * Arguments: ..., sys_lib (table) 168 | */ 169 | static void 170 | luaopen_sys_win32 (lua_State *L) 171 | { 172 | win32_init(L); 173 | 174 | luaL_newlib(L, win32_lib); 175 | lua_pushvalue(L, -1); /* push win32_lib */ 176 | lua_setfield(L, -3, "win32"); 177 | 178 | luaopen_sys_win32_service(L); 179 | lua_pop(L, 1); /* pop win32_lib */ 180 | 181 | luaL_newmetatable(L, WREG_TYPENAME); 182 | lua_pushvalue(L, -1); /* push metatable */ 183 | lua_setfield(L, -2, "__index"); /* metatable.__index = metatable */ 184 | luaL_setfuncs(L, reg_meth, 0); 185 | lua_pop(L, 1); 186 | } 187 | -------------------------------------------------------------------------------- /src/event/timeout.c: -------------------------------------------------------------------------------- 1 | /* Timeouts */ 2 | 3 | static void 4 | timeout_reset (struct event *ev, const msec_t now) 5 | { 6 | struct timeout_queue *tq = ev->tq; 7 | const msec_t msec = tq->msec; 8 | 9 | ev->timeout_at = now + msec; 10 | if (!ev->next) 11 | return; 12 | 13 | if (ev->prev) 14 | ev->prev->next = ev->next; 15 | else 16 | tq->ev_head = ev->next; 17 | 18 | ev->next->prev = ev->prev; 19 | ev->next = NULL; 20 | ev->prev = tq->ev_tail; 21 | tq->ev_tail->next = ev; 22 | tq->ev_tail = ev; 23 | } 24 | 25 | static void 26 | timeout_del (struct event *ev) 27 | { 28 | struct timeout_queue *tq = ev->tq; 29 | struct event *ev_prev, *ev_next; 30 | 31 | if (!tq) return; 32 | ev->tq = NULL; 33 | 34 | ev_prev = ev->prev; 35 | ev_next = ev->next; 36 | 37 | if (!ev_prev && !ev_next) { 38 | struct event_queue *evq = event_get_evq(ev); 39 | struct timeout_queue **tq_headp = &event_get_tq_head(ev); 40 | struct event **ev_freep = &event_get_evq(ev)->ev_free; 41 | struct timeout_queue *tq_prev = tq->tq_prev; 42 | struct timeout_queue *tq_next = tq->tq_next; 43 | 44 | if (tq_prev) 45 | tq_prev->tq_next = tq_next; 46 | else 47 | *tq_headp = tq_next; 48 | 49 | if (tq_next) 50 | tq_next->tq_prev = tq_prev; 51 | 52 | ((struct event *) tq)->next_ready = *ev_freep; 53 | *ev_freep = ((struct event *) tq); 54 | 55 | if (evq->tq_map_fn) { 56 | /* remove from map */ 57 | (void) evq->tq_map_fn(evq, tq, tq->msec, 1); 58 | } 59 | return; 60 | } 61 | 62 | if (ev_prev) 63 | ev_prev->next = ev_next; 64 | else 65 | tq->ev_head = ev_next; 66 | 67 | if (ev_next) 68 | ev_next->prev = ev_prev; 69 | else 70 | tq->ev_tail = ev_prev; 71 | } 72 | 73 | static int 74 | timeout_add (struct event *ev, msec_t msec, const msec_t now) 75 | { 76 | struct event_queue *evq = event_get_evq(ev); 77 | struct timeout_queue **tq_headp = &event_get_tq_head(ev); 78 | struct timeout_queue *tq, *tq_prev; 79 | 80 | tq_prev = NULL; 81 | tq = *tq_headp; 82 | 83 | if (evq->tq_map_fn) { 84 | /* search from map */ 85 | tq_prev = evq->tq_map_fn(evq, NULL, msec, 0); 86 | if (tq_prev) tq = tq_prev; 87 | } else { 88 | /* search from sorted list */ 89 | for (; tq && tq->msec < msec; tq = tq->tq_next) 90 | tq_prev = tq; 91 | } 92 | 93 | if (!tq || tq->msec != msec) { 94 | struct event **ev_freep = &event_get_evq(ev)->ev_free; 95 | struct timeout_queue *tq_new = (struct timeout_queue *) *ev_freep; 96 | 97 | if (!tq_new) return -1; 98 | *ev_freep = (*ev_freep)->next_ready; 99 | 100 | tq_new->tq_next = tq; 101 | if (tq) tq->tq_prev = tq_new; 102 | tq = tq_new; 103 | tq->tq_prev = tq_prev; 104 | 105 | if (tq_prev) 106 | tq_prev->tq_next = tq; 107 | else 108 | *tq_headp = tq; 109 | 110 | tq->msec = msec; 111 | tq->ev_head = ev; 112 | ev->prev = NULL; 113 | 114 | if (evq->tq_map_fn) { 115 | /* add to map */ 116 | (void) evq->tq_map_fn(evq, tq, msec, 0); 117 | } 118 | } else { 119 | ev->prev = tq->ev_tail; 120 | if (tq->ev_tail) 121 | tq->ev_tail->next = ev; 122 | else 123 | tq->ev_head = ev; 124 | } 125 | tq->ev_tail = ev; 126 | ev->next = NULL; 127 | ev->tq = tq; 128 | ev->timeout_at = now + msec; 129 | return 0; 130 | } 131 | 132 | static msec_t 133 | timeout_get (const struct timeout_queue *tq, msec_t min, const msec_t now) 134 | { 135 | if (!tq) return min; 136 | 137 | if (min == TIMEOUT_INFINITE) 138 | min = tq->ev_head->timeout_at; 139 | else min += now; 140 | 141 | do { 142 | const msec_t t = tq->ev_head->timeout_at; 143 | if ((long) t < (long) min) 144 | min = t; 145 | tq = tq->tq_next; 146 | } while (tq); 147 | 148 | { 149 | const long d = (long) min - (long) now; 150 | return (d < 0L) ? 0L 151 | : (d > (long) MAX_TIMEOUT ? (msec_t) MAX_TIMEOUT : (msec_t) d); 152 | } 153 | } 154 | 155 | static struct event * 156 | timeout_process (struct timeout_queue *tq, struct event *ev_ready, 157 | const msec_t now) 158 | { 159 | const long timeout = (long) now + MIN_TIMEOUT; 160 | 161 | while (tq) { 162 | struct event *ev_head = tq->ev_head; 163 | 164 | if (ev_head && (long) ev_head->timeout_at < timeout) { 165 | struct event *ev = ev_head; 166 | const msec_t timeout_at = now + tq->msec; 167 | 168 | do { 169 | ev->flags |= EVENT_ACTIVE | EVENT_TIMEOUT_RES; 170 | ev->timeout_at = timeout_at; 171 | 172 | ev->next_ready = ev_ready; 173 | ev_ready = ev; 174 | ev = ev->next; 175 | } while (ev && (long) ev->timeout_at < timeout); 176 | 177 | if (ev) { 178 | /* recycle timeout queue */ 179 | tq->ev_head = ev; /* head */ 180 | ev->prev = NULL; 181 | tq->ev_tail->next = ev_head; /* middle */ 182 | ev_head->prev = tq->ev_tail; 183 | tq->ev_tail = ev_ready; /* tail */ 184 | ev_ready->next = NULL; 185 | } 186 | } 187 | tq = tq->tq_next; 188 | } 189 | return ev_ready; 190 | } 191 | 192 | -------------------------------------------------------------------------------- /src/event/select.c: -------------------------------------------------------------------------------- 1 | /* Generic Select */ 2 | 3 | EVQ_API int 4 | evq_init (struct event_queue *evq) 5 | { 6 | pthread_mutex_init(&evq->sig_cs, NULL); 7 | 8 | { 9 | fd_t *sig_fd = evq->sig_fd; 10 | unsigned int fd; 11 | 12 | sig_fd[0] = sig_fd[1] = (fd_t) -1; 13 | if (pipe(sig_fd) || fcntl(sig_fd[0], F_SETFL, O_NONBLOCK)) 14 | goto err; 15 | 16 | fd = (unsigned int) sig_fd[0]; 17 | FD_SET(fd, &evq->readset); 18 | evq->max_fd = fd; 19 | evq->npolls++; 20 | } 21 | 22 | evq->now = sys_milliseconds(); 23 | return 0; 24 | err: 25 | evq_done(evq); 26 | return -1; 27 | } 28 | 29 | EVQ_API void 30 | evq_done (struct event_queue *evq) 31 | { 32 | pthread_mutex_destroy(&evq->sig_cs); 33 | 34 | close(evq->sig_fd[0]); 35 | close(evq->sig_fd[1]); 36 | } 37 | 38 | EVQ_API int 39 | evq_add (struct event_queue *evq, struct event *ev) 40 | { 41 | unsigned int fd; 42 | 43 | ev->evq = evq; 44 | 45 | if (ev->flags & EVENT_SIGNAL) 46 | return signal_add(evq, ev); 47 | 48 | if (evq->npolls >= FD_SETSIZE) 49 | return -1; 50 | 51 | fd = (unsigned int) ev->fd; 52 | if (ev->flags & EVENT_READ) 53 | FD_SET(fd, &evq->readset); 54 | else 55 | FD_SET(fd, &evq->writeset); 56 | 57 | if ((int) evq->max_fd != -1 && evq->max_fd < fd) 58 | evq->max_fd = fd; 59 | 60 | evq->events[evq->npolls] = ev; 61 | ev->index = evq->npolls++; 62 | 63 | evq->nevents++; 64 | return 0; 65 | } 66 | 67 | EVQ_API int 68 | evq_add_dirwatch (struct event_queue *evq, struct event *ev, const char *path) 69 | { 70 | (void) evq; 71 | (void) ev; 72 | (void) path; 73 | 74 | return -1; 75 | } 76 | 77 | EVQ_API int 78 | evq_del (struct event *ev, int reuse_fd) 79 | { 80 | struct event_queue *evq = ev->evq; 81 | const unsigned int ev_flags = ev->flags; 82 | 83 | (void) reuse_fd; 84 | 85 | if (ev->tq) timeout_del(ev); 86 | 87 | ev->evq = NULL; 88 | evq->nevents--; 89 | 90 | if (ev_flags & EVENT_TIMER) return 0; 91 | 92 | if (ev_flags & EVENT_SIGNAL) 93 | return signal_del(evq, ev); 94 | 95 | { 96 | const unsigned int fd = (unsigned int) ev->fd; 97 | 98 | if (ev_flags & EVENT_READ) 99 | FD_CLR(fd, &evq->readset); 100 | else 101 | FD_CLR(fd, &evq->writeset); 102 | 103 | if (evq->max_fd == fd) 104 | evq->max_fd = -1; 105 | } 106 | 107 | if (ev->index < --evq->npolls) { 108 | struct event **events = evq->events; 109 | const int i = ev->index; 110 | 111 | events[i] = events[evq->npolls]; 112 | events[i]->index = i; 113 | } 114 | return 0; 115 | } 116 | 117 | EVQ_API int 118 | evq_modify (struct event *ev, unsigned int flags) 119 | { 120 | struct event_queue *evq = ev->evq; 121 | const unsigned int fd = (unsigned int) ev->fd; 122 | 123 | if (ev->flags & EVENT_READ) 124 | FD_CLR(fd, &evq->readset); 125 | else 126 | FD_CLR(fd, &evq->writeset); 127 | 128 | if (flags & EVENT_READ) 129 | FD_SET(fd, &evq->readset); 130 | else 131 | FD_SET(fd, &evq->writeset); 132 | 133 | return 0; 134 | } 135 | 136 | EVQ_API int 137 | evq_wait (struct event_queue *evq, struct sys_thread *td, msec_t timeout) 138 | { 139 | struct event *ev_ready; 140 | fd_set work_readset = evq->readset; 141 | fd_set work_writeset = evq->writeset; 142 | struct timeval tv, *tvp; 143 | struct event **events = evq->events; 144 | const int npolls = evq->npolls; 145 | int i, nready, max_fd; 146 | 147 | if (timeout != 0L) { 148 | timeout = timeout_get(evq->tq, timeout, evq->now); 149 | if (timeout == 0L) { 150 | ev_ready = timeout_process(evq->tq, NULL, evq->now); 151 | goto end; 152 | } 153 | } 154 | if (timeout == TIMEOUT_INFINITE) 155 | tvp = NULL; 156 | else { 157 | tv.tv_sec = timeout / 1000; 158 | tv.tv_usec = (timeout % 1000) * 1000; 159 | tvp = &tv; 160 | } 161 | 162 | max_fd = evq->max_fd; 163 | if (max_fd == -1) { 164 | for (i = 1; i < npolls; ++i) { 165 | struct event *ev = events[i]; 166 | if (max_fd < ev->fd) 167 | max_fd = ev->fd; 168 | } 169 | evq->max_fd = max_fd; 170 | } 171 | 172 | if (td) sys_vm2_leave(td); 173 | nready = select(max_fd + 1, &work_readset, &work_writeset, NULL, tvp); 174 | if (td) sys_vm2_enter(td); 175 | 176 | evq->now = sys_milliseconds(); 177 | 178 | if (nready == -1) 179 | return (errno == EINTR) ? 0 : -1; 180 | 181 | ev_ready = evq->ev_ready; 182 | if (tvp) { 183 | if (!nready) { 184 | if (evq->tq) { 185 | struct event *ev = timeout_process(evq->tq, ev_ready, evq->now); 186 | if (ev != ev_ready) { 187 | ev_ready = ev; 188 | goto end; 189 | } 190 | } 191 | return SYS_ERR_TIMEOUT; 192 | } 193 | 194 | timeout = evq->now; 195 | } 196 | 197 | if (FD_ISSET(evq->sig_fd[0], &work_readset)) { 198 | ev_ready = signal_process_interrupt(evq, ev_ready, timeout); 199 | --nready; 200 | } 201 | 202 | for (i = 1; i < npolls && nready; i++) { 203 | struct event *ev = events[i]; 204 | unsigned int res = 0; 205 | 206 | if ((ev->flags & EVENT_READ) && FD_ISSET(ev->fd, &work_readset)) 207 | res |= EVENT_READ_RES; 208 | else if ((ev->flags & EVENT_WRITE) && FD_ISSET(ev->fd, &work_writeset)) 209 | res |= EVENT_WRITE_RES; 210 | 211 | if (!res) continue; 212 | 213 | ev->flags |= res; 214 | if (!(ev->flags & EVENT_ACTIVE)) { 215 | ev->flags |= EVENT_ACTIVE; 216 | if (ev->flags & EVENT_ONESHOT) 217 | evq_del(ev, 1); 218 | else if (ev->tq && !(ev->flags & EVENT_TIMEOUT_MANUAL)) 219 | timeout_reset(ev, timeout); 220 | 221 | ev->next_ready = ev_ready; 222 | ev_ready = ev; 223 | } 224 | --nready; 225 | } 226 | if (!ev_ready) return 0; 227 | end: 228 | evq->ev_ready = ev_ready; 229 | return 0; 230 | } 231 | 232 | -------------------------------------------------------------------------------- /src/event/poll.c: -------------------------------------------------------------------------------- 1 | /* Poll */ 2 | 3 | #define POLLFD_READ (POLLIN | POLLERR | POLLHUP | POLLNVAL) 4 | #define POLLFD_WRITE (POLLOUT | POLLERR | POLLHUP | POLLNVAL) 5 | 6 | 7 | EVQ_API int 8 | evq_init (struct event_queue *evq) 9 | { 10 | evq->events = malloc(NEVENT * sizeof(void *)); 11 | if (!evq->events) 12 | return -1; 13 | 14 | evq->fdset = malloc(NEVENT * sizeof(struct pollfd)); 15 | if (!evq->fdset) { 16 | free(evq->events); 17 | return -1; 18 | } 19 | 20 | pthread_mutex_init(&evq->sig_cs, NULL); 21 | 22 | { 23 | fd_t *sig_fd = evq->sig_fd; 24 | struct pollfd *fdp; 25 | 26 | sig_fd[0] = sig_fd[1] = (fd_t) -1; 27 | if (pipe(sig_fd) || fcntl(sig_fd[0], F_SETFL, O_NONBLOCK)) 28 | goto err; 29 | 30 | fdp = &evq->fdset[0]; 31 | fdp->fd = sig_fd[0]; 32 | fdp->events = POLLIN; 33 | fdp->revents = 0; 34 | } 35 | 36 | evq->npolls++; 37 | evq->max_polls = NEVENT; 38 | 39 | evq->now = sys_milliseconds(); 40 | return 0; 41 | err: 42 | evq_done(evq); 43 | return -1; 44 | } 45 | 46 | EVQ_API void 47 | evq_done (struct event_queue *evq) 48 | { 49 | pthread_mutex_destroy(&evq->sig_cs); 50 | 51 | close(evq->sig_fd[0]); 52 | close(evq->sig_fd[1]); 53 | 54 | free(evq->fdset); 55 | free(evq->events); 56 | } 57 | 58 | EVQ_API int 59 | evq_add (struct event_queue *evq, struct event *ev) 60 | { 61 | unsigned int npolls; 62 | 63 | ev->evq = evq; 64 | 65 | if (ev->flags & EVENT_SIGNAL) 66 | return signal_add(evq, ev); 67 | 68 | npolls = evq->npolls; 69 | if (npolls >= evq->max_polls) { 70 | const unsigned int n = 2 * evq->max_polls; 71 | void *p; 72 | 73 | if (!(p = realloc(evq->events, n * sizeof(void *)))) 74 | return -1; 75 | evq->events = p; 76 | 77 | if (!(p = realloc(evq->fdset, n * sizeof(struct pollfd)))) 78 | return -1; 79 | evq->fdset = p; 80 | evq->max_polls = n; 81 | } 82 | 83 | { 84 | struct pollfd *fdp = &evq->fdset[npolls]; 85 | 86 | fdp->fd = ev->fd; 87 | fdp->events = (ev->flags & EVENT_READ) ? POLLIN : POLLOUT; 88 | fdp->revents = 0; 89 | } 90 | 91 | evq->npolls++; 92 | evq->events[npolls] = ev; 93 | ev->index = npolls; 94 | 95 | evq->nevents++; 96 | return 0; 97 | } 98 | 99 | EVQ_API int 100 | evq_add_dirwatch (struct event_queue *evq, struct event *ev, const char *path) 101 | { 102 | (void) evq; 103 | (void) ev; 104 | (void) path; 105 | 106 | return -1; 107 | } 108 | 109 | EVQ_API int 110 | evq_del (struct event *ev, int reuse_fd) 111 | { 112 | struct event_queue *evq = ev->evq; 113 | const unsigned int ev_flags = ev->flags; 114 | unsigned int npolls; 115 | int i; 116 | 117 | (void) reuse_fd; 118 | 119 | if (ev->tq) timeout_del(ev); 120 | 121 | ev->evq = NULL; 122 | evq->nevents--; 123 | 124 | if (ev_flags & EVENT_TIMER) return 0; 125 | 126 | if (ev_flags & EVENT_SIGNAL) 127 | return signal_del(evq, ev); 128 | 129 | npolls = --evq->npolls; 130 | if (ev->index < npolls) { 131 | struct event **events = evq->events; 132 | 133 | i = ev->index; 134 | events[i] = events[npolls]; 135 | events[i]->index = i; 136 | evq->fdset[i] = evq->fdset[npolls]; 137 | } 138 | 139 | if (npolls > NEVENT / 2 && npolls <= evq->max_polls / 4) { 140 | void *p; 141 | 142 | i = (evq->max_polls /= 2); 143 | if ((p = realloc(evq->events, i * sizeof(void *)))) 144 | evq->events = p; 145 | if ((p = realloc(evq->fdset, i * sizeof(struct pollfd)))) 146 | evq->fdset = p; 147 | } 148 | return 0; 149 | } 150 | 151 | EVQ_API int 152 | evq_modify (struct event *ev, unsigned int flags) 153 | { 154 | short *eventp = &ev->evq->fdset[ev->index].events; 155 | 156 | *eventp = (flags & EVENT_READ) ? POLLIN : POLLOUT; 157 | return 0; 158 | } 159 | 160 | EVQ_API int 161 | evq_wait (struct event_queue *evq, struct sys_thread *td, msec_t timeout) 162 | { 163 | struct event *ev_ready; 164 | struct event **events = evq->events; 165 | struct pollfd *fdset = evq->fdset; 166 | const int npolls = evq->npolls; 167 | int i, nready; 168 | 169 | if (timeout != 0L) { 170 | timeout = timeout_get(evq->tq, timeout, evq->now); 171 | if (timeout == 0L) { 172 | ev_ready = timeout_process(evq->tq, NULL, evq->now); 173 | goto end; 174 | } 175 | } 176 | 177 | if (td) sys_vm2_leave(td); 178 | nready = poll(fdset, npolls, (int) timeout); 179 | if (td) sys_vm2_enter(td); 180 | 181 | evq->now = sys_milliseconds(); 182 | 183 | if (nready == -1) 184 | return (errno == EINTR) ? 0 : -1; 185 | 186 | ev_ready = evq->ev_ready; 187 | if (timeout != TIMEOUT_INFINITE) { 188 | if (!nready) { 189 | if (evq->tq) { 190 | struct event *ev = timeout_process(evq->tq, ev_ready, evq->now); 191 | if (ev != ev_ready) { 192 | ev_ready = ev; 193 | goto end; 194 | } 195 | } 196 | return SYS_ERR_TIMEOUT; 197 | } 198 | 199 | timeout = evq->now; 200 | } 201 | 202 | if (fdset[0].revents & POLLIN) { 203 | fdset[0].revents = 0; 204 | ev_ready = signal_process_interrupt(evq, ev_ready, timeout); 205 | --nready; 206 | } 207 | 208 | for (i = 1; i < npolls && nready; i++) { 209 | const int revents = fdset[i].revents; 210 | struct event *ev; 211 | unsigned int res; 212 | 213 | if (!revents) continue; 214 | 215 | fdset[i].revents = 0; 216 | ev = events[i]; 217 | 218 | res = (revents & POLLHUP) ? EVENT_EOF_RES : 0; 219 | if ((revents & POLLFD_READ) && (ev->flags & EVENT_READ)) 220 | res |= EVENT_READ_RES; 221 | else if ((revents & POLLFD_WRITE) && (ev->flags & EVENT_WRITE)) 222 | res |= EVENT_WRITE_RES; 223 | 224 | ev->flags |= res; 225 | if (!(ev->flags & EVENT_ACTIVE)) { 226 | ev->flags |= EVENT_ACTIVE; 227 | if (ev->flags & EVENT_ONESHOT) 228 | evq_del(ev, 1); 229 | else if (ev->tq && !(ev->flags & EVENT_TIMEOUT_MANUAL)) 230 | timeout_reset(ev, timeout); 231 | 232 | ev->next_ready = ev_ready; 233 | ev_ready = ev; 234 | } 235 | --nready; 236 | } 237 | if (!ev_ready) return 0; 238 | end: 239 | evq->ev_ready = ev_ready; 240 | return 0; 241 | } 242 | 243 | -------------------------------------------------------------------------------- /src/event/epoll.c: -------------------------------------------------------------------------------- 1 | /* EPoll */ 2 | 3 | #ifndef EPOLLRDHUP 4 | #define EPOLLRDHUP 0 5 | #endif 6 | 7 | #define EPOLLFD_HUP (EPOLLRDHUP | EPOLLHUP) 8 | #define EPOLLFD_READ (EPOLLIN | EPOLLERR | EPOLLFD_HUP) 9 | #define EPOLLFD_WRITE (EPOLLOUT | EPOLLERR | EPOLLHUP) 10 | 11 | EVQ_API int 12 | evq_init (struct event_queue *evq) 13 | { 14 | evq->epoll_fd = epoll_create(NEVENT); 15 | if (evq->epoll_fd == -1) 16 | return -1; 17 | 18 | pthread_mutex_init(&evq->sig_cs, NULL); 19 | 20 | { 21 | fd_t *sig_fd = evq->sig_fd; 22 | struct epoll_event epev; 23 | 24 | memset(&epev, 0, sizeof(struct epoll_event)); 25 | epev.events = EPOLLIN; 26 | 27 | #ifdef USE_EVENTFD 28 | sig_fd[0] = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); 29 | if (sig_fd[0] == -1) 30 | goto err; 31 | #else 32 | sig_fd[0] = sig_fd[1] = (fd_t) -1; 33 | if (pipe(sig_fd) || fcntl(sig_fd[0], F_SETFL, O_NONBLOCK)) 34 | goto err; 35 | #endif 36 | 37 | if (epoll_ctl(evq->epoll_fd, EPOLL_CTL_ADD, sig_fd[0], &epev)) 38 | goto err; 39 | } 40 | 41 | evq->now = sys_milliseconds(); 42 | return 0; 43 | err: 44 | evq_done(evq); 45 | return -1; 46 | } 47 | 48 | EVQ_API void 49 | evq_done (struct event_queue *evq) 50 | { 51 | pthread_mutex_destroy(&evq->sig_cs); 52 | 53 | #ifdef USE_EVENTFD 54 | close(evq->sig_fd[0]); 55 | #else 56 | close(evq->sig_fd[0]); 57 | close(evq->sig_fd[1]); 58 | #endif 59 | 60 | close(evq->epoll_fd); 61 | } 62 | 63 | EVQ_API int 64 | evq_add (struct event_queue *evq, struct event *ev) 65 | { 66 | const unsigned int ev_flags = ev->flags; 67 | 68 | ev->evq = evq; 69 | 70 | if (ev_flags & EVENT_SIGNAL) 71 | return signal_add(evq, ev); 72 | 73 | { 74 | struct epoll_event epev; 75 | 76 | memset(&epev, 0, sizeof(struct epoll_event)); 77 | epev.events = ((ev_flags & EVENT_READ) ? EPOLLIN : EPOLLOUT) 78 | | ((ev_flags & EVENT_ONESHOT) ? EPOLLONESHOT : 0); 79 | epev.data.ptr = ev; 80 | if (epoll_ctl(evq->epoll_fd, EPOLL_CTL_ADD, ev->fd, &epev) == -1) 81 | return -1; 82 | } 83 | 84 | evq->nevents++; 85 | return 0; 86 | } 87 | 88 | EVQ_API int 89 | evq_add_dirwatch (struct event_queue *evq, struct event *ev, const char *path) 90 | { 91 | const unsigned int filter = (ev->flags & EVENT_WATCH_MODIFY) 92 | ? IN_MODIFY : IN_ALL_EVENTS ^ IN_ACCESS; 93 | 94 | #ifdef IN_NONBLOCK 95 | ev->fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); 96 | #else 97 | ev->fd = inotify_init(); 98 | fcntl(ev->fd, F_SETFL, O_NONBLOCK); 99 | #endif 100 | if (ev->fd == -1) return -1; 101 | 102 | if (inotify_add_watch(ev->fd, path, filter) == -1) { 103 | close(ev->fd); 104 | return -1; 105 | } 106 | 107 | return evq_add(evq, ev); 108 | } 109 | 110 | EVQ_API int 111 | evq_del (struct event *ev, const int reuse_fd) 112 | { 113 | struct event_queue *evq = ev->evq; 114 | const unsigned int ev_flags = ev->flags; 115 | 116 | if (ev->tq) timeout_del(ev); 117 | 118 | ev->evq = NULL; 119 | evq->nevents--; 120 | 121 | if (ev_flags & EVENT_TIMER) return 0; 122 | 123 | if (ev_flags & EVENT_SIGNAL) 124 | return signal_del(evq, ev); 125 | 126 | if (ev_flags & EVENT_DIRWATCH) 127 | return close(ev->fd); 128 | 129 | if (reuse_fd) 130 | epoll_ctl(evq->epoll_fd, EPOLL_CTL_DEL, ev->fd, NULL); 131 | return 0; 132 | } 133 | 134 | EVQ_API int 135 | evq_modify (struct event *ev, unsigned int flags) 136 | { 137 | struct epoll_event epev; 138 | 139 | memset(&epev, 0, sizeof(struct epoll_event)); 140 | epev.events = (flags & EVENT_READ) ? EPOLLIN : EPOLLOUT; 141 | epev.data.ptr = ev; 142 | return epoll_ctl(ev->evq->epoll_fd, EPOLL_CTL_MOD, ev->fd, &epev); 143 | } 144 | 145 | EVQ_API int 146 | evq_wait (struct event_queue *evq, struct sys_thread *td, msec_t timeout) 147 | { 148 | struct epoll_event ep_events[NEVENT]; 149 | struct epoll_event *epev; 150 | struct event *ev_ready; 151 | int nready; 152 | 153 | if (timeout != 0L) { 154 | timeout = timeout_get(evq->tq, timeout, evq->now); 155 | if (timeout == 0L) { 156 | ev_ready = timeout_process(evq->tq, NULL, evq->now); 157 | goto end; 158 | } 159 | } 160 | 161 | if (td) sys_vm2_leave(td); 162 | nready = epoll_wait(evq->epoll_fd, ep_events, NEVENT, (int) timeout); 163 | if (td) sys_vm2_enter(td); 164 | 165 | evq->now = sys_milliseconds(); 166 | 167 | if (nready == -1) 168 | return (errno == EINTR) ? 0 : -1; 169 | 170 | ev_ready = evq->ev_ready; 171 | if (timeout != TIMEOUT_INFINITE) { 172 | if (!nready) { 173 | if (evq->tq) { 174 | struct event *ev = timeout_process(evq->tq, ev_ready, evq->now); 175 | if (ev != ev_ready) { 176 | ev_ready = ev; 177 | goto end; 178 | } 179 | } 180 | return SYS_ERR_TIMEOUT; 181 | } 182 | 183 | timeout = evq->now; 184 | } 185 | 186 | for (epev = ep_events; nready--; ++epev) { 187 | const int revents = epev->events; 188 | struct event *ev; 189 | unsigned int res; 190 | 191 | if (!revents) continue; 192 | 193 | ev = epev->data.ptr; 194 | if (!ev) { 195 | ev_ready = signal_process_interrupt(evq, ev_ready, timeout); 196 | continue; 197 | } 198 | 199 | res = (revents & EPOLLFD_HUP) ? EVENT_EOF_RES : 0; 200 | if ((revents & EPOLLFD_READ) && (ev->flags & EVENT_READ)) { 201 | res |= EVENT_READ_RES; 202 | 203 | if (ev->flags & EVENT_DIRWATCH) { /* skip inotify data */ 204 | char buf[BUFSIZ]; 205 | int n; 206 | do n = read(ev->fd, buf, sizeof(buf)); 207 | while (n == -1 && errno == EINTR); 208 | } 209 | } else if ((revents & EPOLLFD_WRITE) && (ev->flags & EVENT_WRITE)) { 210 | res |= EVENT_WRITE_RES; 211 | } 212 | 213 | ev->flags |= res; 214 | if (!(ev->flags & EVENT_ACTIVE)) { 215 | ev->flags |= EVENT_ACTIVE; 216 | if (ev->flags & EVENT_ONESHOT) 217 | evq_del(ev, 1); 218 | else if (ev->tq && !(ev->flags & EVENT_TIMEOUT_MANUAL)) 219 | timeout_reset(ev, timeout); 220 | 221 | ev->next_ready = ev_ready; 222 | ev_ready = ev; 223 | } 224 | } 225 | if (!ev_ready) return 0; 226 | end: 227 | evq->ev_ready = ev_ready; 228 | return 0; 229 | } 230 | 231 | -------------------------------------------------------------------------------- /src/event/win32thr.c: -------------------------------------------------------------------------------- 1 | /* Win32 Threads with WaitForMultipleObjects */ 2 | 3 | struct win32thr_arg { 4 | struct event_queue *evq; 5 | HANDLE signal; 6 | }; 7 | 8 | static int 9 | win32thr_sleep (struct win32thr *wth) 10 | { 11 | const struct event_queue *evq = wth->evq; 12 | CRITICAL_SECTION *wth_cs; 13 | int polling, is_ready; 14 | 15 | if (wth == &evq->head) 16 | return 0; 17 | wth_cs = &wth->sig_cs; 18 | polling = 0; 19 | 20 | EnterCriticalSection(wth_cs); 21 | is_ready = (wth->state == WTHR_READY); 22 | if (wth->state == WTHR_POLL) { 23 | wth->state = WTHR_ACK; 24 | SetEvent(wth->signal); 25 | polling = 1; 26 | } 27 | LeaveCriticalSection(wth_cs); 28 | 29 | if (polling) 30 | WaitForSingleObject(evq->ack_event, INFINITE); 31 | 32 | return is_ready; 33 | } 34 | 35 | static int 36 | win32thr_poll (struct event_queue *evq) 37 | { 38 | struct win32thr *wth = evq->head.next; 39 | int changed = 0, polled = 0; 40 | int oldn = 0; /* previous available wth->n */ 41 | 42 | evq->nwakeup = 0; 43 | 44 | EnterCriticalSection(&evq->head.sig_cs); 45 | while (wth) { 46 | CRITICAL_SECTION *wth_cs = &wth->sig_cs; 47 | int sleeping; 48 | 49 | EnterCriticalSection(wth_cs); 50 | sleeping = (wth->state == WTHR_SLEEP); 51 | LeaveCriticalSection(wth_cs); 52 | 53 | if (sleeping) { 54 | HANDLE signal = wth->signal; 55 | 56 | /* terminate empty thread */ 57 | if (!wth->n) { 58 | struct win32thr *p; 59 | 60 | /* delayed deletion of last empty thread */ 61 | if (oldn > NEVENT/2 && !wth->next) 62 | break; 63 | 64 | for (p = &evq->head; p->next != wth; p = p->next) 65 | continue; 66 | 67 | p->next = wth->next; 68 | wth = p; 69 | changed = 1; 70 | } else { 71 | oldn = wth->n; 72 | wth->state = WTHR_POLL; 73 | evq->nwakeup++; 74 | polled = 1; 75 | } 76 | SetEvent(signal); 77 | } 78 | wth = wth->next; 79 | } 80 | LeaveCriticalSection(&evq->head.sig_cs); 81 | 82 | /* wait of starting of all polling threads */ 83 | if (polled) 84 | WaitForSingleObject(evq->ack_event, INFINITE); 85 | 86 | return changed; 87 | } 88 | 89 | static DWORD WINAPI 90 | win32thr_wait (struct event_queue *evq) 91 | { 92 | CRITICAL_SECTION *head_cs; 93 | HANDLE head_signal; 94 | struct win32thr wth; 95 | 96 | InitCriticalSection(&wth.sig_cs); 97 | wth.state = WTHR_SLEEP; 98 | wth.n = 0; 99 | wth.tq = NULL; 100 | wth.handles[0] = wth.signal = ((struct win32thr_arg *) evq)->signal; 101 | wth.evq = evq = ((struct win32thr_arg *) evq)->evq; 102 | wth.next = evq->head.next; 103 | evq->head.next = &wth; 104 | head_cs = &evq->head.sig_cs; 105 | head_signal = evq->head.signal; 106 | 107 | SetEvent(evq->ack_event); 108 | 109 | for (; ; ) { 110 | msec_t now; 111 | unsigned int res, n; 112 | 113 | WaitForSingleObject(wth.signal, INFINITE); 114 | if (!(n = wth.n)) break; 115 | 116 | EnterCriticalSection(head_cs); 117 | now = evq->now; 118 | if (--evq->nwakeup == 0) 119 | SetEvent(evq->ack_event); /* wake up poller */ 120 | LeaveCriticalSection(head_cs); 121 | 122 | res = WaitForMultipleObjects(n + 1, wth.handles, FALSE, 123 | timeout_get(wth.tq, TIMEOUT_INFINITE, now)); 124 | wth.idx = res; 125 | res = (res == WAIT_TIMEOUT) || (res < (WAIT_OBJECT_0 + n)); 126 | 127 | EnterCriticalSection(&wth.sig_cs); 128 | if (wth.state == WTHR_ACK) { 129 | SetEvent(evq->ack_event); 130 | ResetEvent(wth.signal); 131 | } 132 | wth.state = res ? WTHR_READY : WTHR_SLEEP; 133 | LeaveCriticalSection(&wth.sig_cs); 134 | 135 | if (res) { 136 | EnterCriticalSection(head_cs); 137 | wth.next_ready = evq->wth_ready; 138 | evq->wth_ready = &wth; 139 | evq->sig_ready |= (1 << EVQ_SIGEVQ); 140 | SetEvent(head_signal); 141 | LeaveCriticalSection(head_cs); 142 | } 143 | } 144 | CloseHandle(wth.signal); 145 | DeleteCriticalSection(&wth.sig_cs); 146 | return 0; 147 | } 148 | 149 | static struct win32thr * 150 | win32thr_init (struct event_queue *evq) 151 | { 152 | struct win32thr_arg arg; 153 | HANDLE hThr; 154 | DWORD id; 155 | 156 | arg.evq = evq; 157 | arg.signal = CreateEvent(NULL, FALSE, FALSE, NULL); 158 | if (arg.signal == NULL) 159 | return NULL; 160 | 161 | hThr = CreateThread(NULL, 4096, 162 | (LPTHREAD_START_ROUTINE) win32thr_wait, &arg, 0, &id); 163 | if (hThr == NULL) { 164 | CloseHandle(arg.signal); 165 | return NULL; 166 | } 167 | SetThreadPriority(hThr, THREAD_PRIORITY_ABOVE_NORMAL); 168 | CloseHandle(hThr); 169 | 170 | WaitForSingleObject(evq->ack_event, INFINITE); 171 | return evq->head.next; 172 | } 173 | 174 | static int 175 | win32thr_add (struct win32thr *wth, struct event *ev) 176 | { 177 | struct event_queue *evq = wth->evq; 178 | HANDLE hEvent; 179 | unsigned int i = wth->n; 180 | 181 | ev->wth = wth; 182 | 183 | if (!(ev->flags & EVENT_SOCKET)) 184 | hEvent = ev->fd; 185 | else { 186 | const unsigned int ev_flags = ev->flags; 187 | unsigned int event; 188 | 189 | hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); /* manual-reset */ 190 | if (hEvent == NULL) 191 | return -1; 192 | 193 | event = (ev_flags & EVENT_READ) 194 | ? ((ev_flags & EVENT_SOCKET_ACC_CONN) ? FD_ACCEPT : WFD_READ) 195 | : ((ev_flags & EVENT_SOCKET_ACC_CONN) ? FD_CONNECT : WFD_WRITE); 196 | 197 | if (WSAEventSelect((SOCKET) ev->fd, hEvent, event) == SOCKET_ERROR) { 198 | CloseHandle(hEvent); 199 | return -1; 200 | } 201 | } 202 | 203 | wth->handles[i + 1] = wth->signal; /* raise signal event */ 204 | wth->handles[i] = hEvent; 205 | wth->events[i] = ev; 206 | ev->w.index = i; 207 | 208 | wth->n++; 209 | evq->nevents++; 210 | return 0; 211 | } 212 | 213 | static int 214 | win32thr_del (struct win32thr *wth, struct event *ev) 215 | { 216 | int i, n = --wth->n; 217 | int res = 0; 218 | 219 | if (ev->tq) timeout_del(ev); 220 | 221 | ev->wth = NULL; 222 | wth->evq->nevents--; 223 | 224 | i = ev->w.index; 225 | if (ev->flags & EVENT_SOCKET) { 226 | HANDLE hEvent = wth->handles[i]; 227 | WSAEventSelect((SOCKET) ev->fd, hEvent, 0); 228 | CloseHandle(hEvent); 229 | } else if (ev->flags & EVENT_DIRWATCH) { 230 | res = !FindCloseChangeNotification(ev->fd); 231 | } else if (ev->flags & EVENT_REGWATCH) { 232 | CloseHandle(ev->fd); 233 | } 234 | 235 | if (i < n) { 236 | ev = wth->events[n]; 237 | ev->w.index = i; 238 | wth->events[i] = ev; 239 | wth->handles[i] = wth->handles[n]; 240 | } 241 | wth->handles[n] = wth->signal; /* lower signal event */ 242 | 243 | return res; 244 | } 245 | 246 | -------------------------------------------------------------------------------- /src/event/kqueue.c: -------------------------------------------------------------------------------- 1 | /* KQueue */ 2 | 3 | #include 4 | 5 | #ifndef O_EVTONLY 6 | #define O_EVTONLY O_RDONLY 7 | #endif 8 | 9 | EVQ_API int 10 | evq_init (struct event_queue *evq) 11 | { 12 | evq->kqueue_fd = kqueue(); 13 | if (evq->kqueue_fd == -1) 14 | return -1; 15 | 16 | pthread_mutex_init(&evq->sig_cs, NULL); 17 | 18 | { 19 | fd_t *sig_fd = evq->sig_fd; 20 | struct kevent kev; 21 | 22 | memset(&kev, 0, sizeof(struct kevent)); 23 | kev.filter = EVFILT_READ; 24 | kev.flags = EV_ADD; 25 | 26 | sig_fd[0] = sig_fd[1] = (fd_t) -1; 27 | if (pipe(sig_fd) || fcntl(sig_fd[0], F_SETFL, O_NONBLOCK)) 28 | goto err; 29 | 30 | kev.ident = sig_fd[0]; 31 | if (kevent(evq->kqueue_fd, &kev, 1, NULL, 0, NULL)) 32 | goto err; 33 | } 34 | 35 | evq->now = sys_milliseconds(); 36 | return 0; 37 | err: 38 | evq_done(evq); 39 | return -1; 40 | } 41 | 42 | EVQ_API void 43 | evq_done (struct event_queue *evq) 44 | { 45 | pthread_mutex_destroy(&evq->sig_cs); 46 | 47 | close(evq->sig_fd[0]); 48 | close(evq->sig_fd[1]); 49 | 50 | close(evq->kqueue_fd); 51 | } 52 | 53 | static int 54 | kqueue_set (struct event_queue *evq, struct event *ev, int filter, int action) 55 | { 56 | struct kevent *kev = evq->kev_list; 57 | 58 | if (evq->nchanges >= NEVENT) { 59 | int res; 60 | 61 | do res = kevent(evq->kqueue_fd, kev, evq->nchanges, NULL, 0, NULL); 62 | while (res == -1 && errno == EINTR); 63 | 64 | if (res == -1) return -1; 65 | 66 | evq->nchanges = 1; 67 | } else 68 | kev += evq->nchanges++; 69 | 70 | memset(kev, 0, sizeof(struct kevent)); 71 | kev->ident = ev->fd; 72 | kev->filter = filter; 73 | kev->flags = action | ((ev->flags & EVENT_ONESHOT) ? EV_ONESHOT : 0); 74 | kev->udata = (kev_udata_t) ev; 75 | return 0; 76 | } 77 | 78 | EVQ_API int 79 | evq_add (struct event_queue *evq, struct event *ev) 80 | { 81 | const unsigned int ev_flags = ev->flags; 82 | 83 | ev->evq = evq; 84 | 85 | if (ev_flags & EVENT_SIGNAL) 86 | return signal_add(evq, ev); 87 | 88 | if (kqueue_set(evq, ev, 89 | ((ev_flags & EVENT_READ) ? EVFILT_READ : EVFILT_WRITE), EV_ADD)) 90 | return -1; 91 | 92 | evq->nevents++; 93 | return 0; 94 | } 95 | 96 | EVQ_API int 97 | evq_add_dirwatch (struct event_queue *evq, struct event *ev, const char *path) 98 | { 99 | const int flags = NOTE_DELETE | NOTE_WRITE | NOTE_EXTEND | NOTE_ATTRIB 100 | | NOTE_LINK | NOTE_RENAME | NOTE_REVOKE; 101 | 102 | const unsigned int filter = (ev->flags & EVENT_WATCH_MODIFY) 103 | ? NOTE_WRITE : flags; 104 | 105 | ev->evq = evq; 106 | 107 | ev->fd = open(path, O_EVTONLY); 108 | if (ev->fd == -1) return -1; 109 | 110 | { 111 | struct kevent kev; 112 | int res; 113 | 114 | memset(&kev, 0, sizeof(struct kevent)); 115 | kev.ident = ev->fd; 116 | kev.filter = EVFILT_VNODE; 117 | kev.flags = EV_ADD; 118 | kev.fflags = filter; 119 | kev.udata = (kev_udata_t) ev; 120 | 121 | do res = kevent(evq->kqueue_fd, &kev, 1, NULL, 0, NULL); 122 | while (res == -1 && errno == EINTR); 123 | 124 | if (res == -1) { 125 | close(ev->fd); 126 | return -1; 127 | } 128 | } 129 | 130 | evq->nevents++; 131 | return 0; 132 | } 133 | 134 | EVQ_API int 135 | evq_del (struct event *ev, const int reuse_fd) 136 | { 137 | struct event_queue *evq = ev->evq; 138 | const unsigned int ev_flags = ev->flags; 139 | 140 | if (ev->tq) timeout_del(ev); 141 | 142 | ev->evq = NULL; 143 | evq->nevents--; 144 | 145 | if (ev_flags & EVENT_TIMER) return 0; 146 | 147 | if (ev_flags & EVENT_SIGNAL) 148 | return signal_del(evq, ev); 149 | 150 | if (ev_flags & EVENT_DIRWATCH) 151 | return close(ev->fd); 152 | 153 | if (!reuse_fd) return 0; 154 | 155 | return kqueue_set(evq, ev, 156 | ((ev_flags & EVENT_READ) ? EVFILT_READ : EVFILT_WRITE), EV_DELETE); 157 | } 158 | 159 | EVQ_API int 160 | evq_modify (struct event *ev, unsigned int flags) 161 | { 162 | struct event_queue *evq = ev->evq; 163 | 164 | if (kqueue_set(evq, ev, 165 | ((ev->flags & EVENT_READ) ? EVFILT_READ : EVFILT_WRITE), EV_DELETE)) 166 | return -1; 167 | 168 | return kqueue_set(evq, ev, 169 | ((flags & EVENT_READ) ? EVFILT_READ : EVFILT_WRITE), EV_ADD); 170 | } 171 | 172 | EVQ_API int 173 | evq_wait (struct event_queue *evq, struct sys_thread *td, msec_t timeout) 174 | { 175 | struct event *ev_ready; 176 | struct kevent *kev = evq->kev_list; 177 | struct timespec ts, *tsp; 178 | int nready; 179 | 180 | if (timeout != 0L) { 181 | timeout = timeout_get(evq->tq, timeout, evq->now); 182 | if (timeout == 0L) { 183 | ev_ready = timeout_process(evq->tq, NULL, evq->now); 184 | goto end; 185 | } 186 | } 187 | if (timeout == TIMEOUT_INFINITE) 188 | tsp = NULL; 189 | else { 190 | ts.tv_sec = timeout / 1000; 191 | ts.tv_nsec = (timeout % 1000) * 1000000; 192 | tsp = &ts; 193 | } 194 | 195 | if (td) sys_vm2_leave(td); 196 | nready = kevent(evq->kqueue_fd, kev, evq->nchanges, kev, NEVENT, tsp); 197 | if (td) sys_vm2_enter(td); 198 | 199 | evq->nchanges = 0; 200 | evq->now = sys_milliseconds(); 201 | 202 | if (nready == -1) 203 | return (errno == EINTR) ? 0 : -1; 204 | 205 | ev_ready = evq->ev_ready; 206 | if (tsp) { 207 | if (!nready) { 208 | if (evq->tq) { 209 | struct event *ev = timeout_process(evq->tq, ev_ready, evq->now); 210 | if (ev != ev_ready) { 211 | ev_ready = ev; 212 | goto end; 213 | } 214 | } 215 | return SYS_ERR_TIMEOUT; 216 | } 217 | 218 | timeout = evq->now; 219 | } 220 | 221 | for (; nready--; ++kev) { 222 | struct event *ev; 223 | const int kev_flags = kev->flags; 224 | const int filter = kev->filter; 225 | unsigned int res; 226 | 227 | if (kev_flags & EV_ERROR) 228 | continue; 229 | 230 | if (filter == EVFILT_SIGNAL) { 231 | ev_ready = signal_process_actives(evq, kev->ident, 232 | ev_ready, timeout); 233 | continue; 234 | } 235 | 236 | ev = (struct event *) kev->udata; 237 | if (!ev) { 238 | ev_ready = signal_process_interrupt(evq, ev_ready, timeout); 239 | continue; 240 | } 241 | 242 | res = (kev_flags & EV_EOF) ? EVENT_EOF_RES : 0; 243 | if ((filter == EVFILT_READ || filter == EVFILT_VNODE) 244 | && (ev->flags & EVENT_READ)) 245 | res |= EVENT_READ_RES; 246 | else if ((filter == EVFILT_WRITE) && (ev->flags & EVENT_WRITE)) 247 | res |= EVENT_WRITE_RES; 248 | 249 | ev->flags |= res; 250 | if (!(ev->flags & EVENT_ACTIVE)) { 251 | ev->flags |= EVENT_ACTIVE; 252 | if (ev->flags & EVENT_ONESHOT) 253 | evq_del(ev, 1); 254 | else if (ev->tq && !(ev->flags & EVENT_TIMEOUT_MANUAL)) 255 | timeout_reset(ev, timeout); 256 | 257 | ev->next_ready = ev_ready; 258 | ev_ready = ev; 259 | } 260 | } 261 | if (!ev_ready) return 0; 262 | end: 263 | evq->ev_ready = ev_ready; 264 | return 0; 265 | } 266 | 267 | -------------------------------------------------------------------------------- /src/thread/thread_dpool.c: -------------------------------------------------------------------------------- 1 | /* Lua System: Threading: Data Pool */ 2 | 3 | #define DPOOL_TYPENAME "sys.thread.data_pool" 4 | 5 | struct data_pool { 6 | unsigned int volatile n; /* count of data in storage */ 7 | 8 | int volatile nwaits; /* number of blocked readers */ 9 | int volatile nput; /* number of items to put as new data */ 10 | lua_State * volatile L; /* data writer */ 11 | 12 | unsigned int idx, top; /* storage indexes */ 13 | unsigned int max; /* maximum watermark of data */ 14 | 15 | #define DPOOL_PUTONFULL 1 16 | #define DPOOL_GETONEMPTY 2 17 | #define DPOOL_OPEN 4 18 | unsigned int flags; 19 | 20 | thread_event_t tev; /* synchronization */ 21 | }; 22 | 23 | 24 | /* 25 | * Returns: [dpool_udata] 26 | */ 27 | static int 28 | dpool_new (lua_State *L) 29 | { 30 | struct data_pool *dp = lua_newuserdata(L, sizeof(struct data_pool)); 31 | memset(dp, 0, sizeof(struct data_pool)); 32 | dp->max = (unsigned int) -1; 33 | 34 | if (!thread_event_new(&dp->tev)) { 35 | dp->flags |= DPOOL_OPEN; 36 | luaL_getmetatable(L, DPOOL_TYPENAME); 37 | lua_setmetatable(L, -2); 38 | 39 | lua_newtable(L); /* data and callbacks storage */ 40 | lua_setfenv(L, -2); 41 | return 1; 42 | } 43 | return sys_seterror(L, 0); 44 | } 45 | 46 | /* 47 | * Arguments: dpool_udata 48 | */ 49 | static int 50 | dpool_close (lua_State *L) 51 | { 52 | struct data_pool *dp = checkudata(L, 1, DPOOL_TYPENAME); 53 | 54 | if (dp->flags & DPOOL_OPEN) { 55 | dp->flags ^= DPOOL_OPEN; 56 | thread_event_del(&dp->tev); 57 | } 58 | return 0; 59 | } 60 | 61 | /* 62 | * Arguments: dpool_udata, data_items (any) ... 63 | */ 64 | static int 65 | dpool_put (lua_State *L) 66 | { 67 | struct sys_thread *td = sys_thread_get(); 68 | struct data_pool *dp = checkudata(L, 1, DPOOL_TYPENAME); 69 | int nput = lua_gettop(L) - 1; 70 | 71 | if (!td) luaL_argerror(L, 0, "Threading not initialized"); 72 | if (!nput) luaL_argerror(L, 2, "data expected"); 73 | 74 | lua_getfenv(L, 1); /* storage */ 75 | lua_insert(L, 1); 76 | 77 | if (dp->n >= dp->max) { 78 | if (dp->flags & DPOOL_PUTONFULL) { 79 | lua_rawgetp(L, 1, (void *) DPOOL_PUTONFULL); 80 | lua_insert(L, 2); 81 | lua_call(L, 1 + nput, LUA_MULTRET); 82 | nput = lua_gettop(L) - 1; 83 | if (!nput) return 0; 84 | } else { 85 | do { 86 | const int res = thread_event_wait(&dp->tev, td, 87 | TIMEOUT_INFINITE); 88 | 89 | sys_thread_check(td, L); 90 | if (res) return sys_seterror(L, 0); 91 | } while (dp->n >= dp->max); 92 | } 93 | } 94 | 95 | /* Try directly move data between threads */ 96 | if (dp->nwaits && !dp->L) { 97 | dp->L = L; 98 | dp->nput = nput; 99 | thread_event_signal(&dp->tev); 100 | sys_thread_switch(td); 101 | dp->L = NULL; 102 | if (!dp->nput) return 0; /* moved to thread */ 103 | dp->nput = 0; 104 | } 105 | 106 | /* Keep data in the storage */ 107 | { 108 | int top = dp->top; 109 | 110 | lua_pushinteger(L, nput); 111 | do { 112 | lua_rawseti(L, 1, ++top); 113 | } while (nput--); 114 | dp->top = top; 115 | 116 | if (!dp->n++) { 117 | thread_event_signal(&dp->tev); 118 | } 119 | } 120 | return 0; 121 | } 122 | 123 | /* 124 | * Arguments: dpool_udata, [timeout (milliseconds)] 125 | * Returns: data_items (any) ... 126 | */ 127 | static int 128 | dpool_get (lua_State *L) 129 | { 130 | struct sys_thread *td = sys_thread_get(); 131 | struct data_pool *dp = checkudata(L, 1, DPOOL_TYPENAME); 132 | const msec_t timeout = lua_isnoneornil(L, 2) 133 | ? TIMEOUT_INFINITE : (msec_t) lua_tointeger(L, 2); 134 | int nput; 135 | 136 | if (!td) luaL_argerror(L, 0, "Threading not initialized"); 137 | 138 | lua_settop(L, 1); 139 | lua_getfenv(L, 1); /* storage */ 140 | lua_insert(L, 1); 141 | 142 | if ((dp->flags & DPOOL_GETONEMPTY) && !dp->n) { 143 | lua_rawgetp(L, 1, (void *) DPOOL_GETONEMPTY); 144 | lua_insert(L, 2); 145 | lua_call(L, 1, LUA_MULTRET); 146 | nput = lua_gettop(L) - 1; 147 | if (nput) return nput; 148 | } 149 | 150 | for (; ; ) { 151 | /* get from storage */ 152 | if (dp->n) { 153 | const int idx = dp->idx + 1; 154 | int i; 155 | 156 | lua_rawgeti(L, 1, idx); 157 | nput = (int) lua_tointeger(L, -1); 158 | lua_pushnil(L); 159 | lua_rawseti(L, 1, idx); 160 | dp->idx = idx + nput; 161 | for (i = dp->idx; i > idx; --i) { 162 | lua_rawgeti(L, 1, i); 163 | lua_pushnil(L); 164 | lua_rawseti(L, 1, i); 165 | } 166 | if (dp->idx == dp->top) 167 | dp->idx = dp->top = 0; 168 | if (dp->n-- == dp->max) { 169 | thread_event_signal(&dp->tev); 170 | } 171 | return nput; 172 | } 173 | 174 | /* wait signal */ 175 | { 176 | int res; 177 | 178 | dp->nwaits++; 179 | res = thread_event_wait(&dp->tev, td, timeout); 180 | dp->nwaits--; 181 | 182 | sys_thread_check(td, L); 183 | if (res) { 184 | if (res == 1) { 185 | lua_pushboolean(L, 0); 186 | return 1; /* timed out */ 187 | } 188 | return sys_seterror(L, 0); 189 | } 190 | } 191 | 192 | /* get directly from another thread */ 193 | nput = dp->nput; 194 | if (nput) { 195 | luaL_checkstack(L, nput, NULL); 196 | lua_xmove(dp->L, L, nput); 197 | dp->nput = 0; 198 | return nput; 199 | } 200 | } 201 | } 202 | 203 | /* 204 | * Arguments: dpool_udata, [maximum (number)] 205 | * Returns: dpool_udata | maximum (number) 206 | */ 207 | static int 208 | dpool_max (lua_State *L) 209 | { 210 | struct data_pool *dp = checkudata(L, 1, DPOOL_TYPENAME); 211 | 212 | if (lua_isnoneornil(L, 2)) 213 | lua_pushinteger(L, dp->max); 214 | else { 215 | dp->max = (unsigned int) luaL_checkint(L, 2); 216 | lua_settop(L, 1); 217 | } 218 | return 1; 219 | } 220 | 221 | /* 222 | * Arguments: dpool_udata, [put_on_full (function), get_on_empty (function)] 223 | */ 224 | static int 225 | dpool_callbacks (lua_State *L) 226 | { 227 | struct data_pool *dp = checkudata(L, 1, DPOOL_TYPENAME); 228 | 229 | lua_settop(L, 3); 230 | lua_getfenv(L, 1); /* storage of callbacks */ 231 | 232 | dp->flags &= ~(DPOOL_PUTONFULL | DPOOL_GETONEMPTY); 233 | dp->flags |= (lua_isfunction(L, 2) ? DPOOL_PUTONFULL : 0) 234 | | (lua_isfunction(L, 3) ? DPOOL_GETONEMPTY : 0); 235 | 236 | lua_pushvalue(L, 2); 237 | lua_rawsetp(L, -2, (void *) DPOOL_PUTONFULL); 238 | 239 | lua_pushvalue(L, 3); 240 | lua_rawsetp(L, -2, (void *) DPOOL_GETONEMPTY); 241 | 242 | return 0; 243 | } 244 | 245 | /* 246 | * Arguments: dpool_udata 247 | * Returns: number 248 | */ 249 | static int 250 | dpool_count (lua_State *L) 251 | { 252 | struct data_pool *dp = checkudata(L, 1, DPOOL_TYPENAME); 253 | 254 | lua_pushinteger(L, dp->n); 255 | return 1; 256 | } 257 | 258 | /* 259 | * Arguments: dpool_udata 260 | * Returns: string 261 | */ 262 | static int 263 | dpool_tostring (lua_State *L) 264 | { 265 | struct data_pool *dp = checkudata(L, 1, DPOOL_TYPENAME); 266 | 267 | lua_pushfstring(L, DPOOL_TYPENAME " (%p)", dp); 268 | return 1; 269 | } 270 | 271 | 272 | #define DPOOL_METHODS \ 273 | {"data_pool", dpool_new} 274 | 275 | static luaL_Reg dpool_meth[] = { 276 | {"put", dpool_put}, 277 | {"get", dpool_get}, 278 | {"max", dpool_max}, 279 | {"callbacks", dpool_callbacks}, 280 | {"__len", dpool_count}, 281 | {"__tostring", dpool_tostring}, 282 | {"__gc", dpool_close}, 283 | {NULL, NULL} 284 | }; 285 | -------------------------------------------------------------------------------- /src/event/signal.c: -------------------------------------------------------------------------------- 1 | /* Signals */ 2 | 3 | #include 4 | 5 | 6 | /* Global signal events */ 7 | static struct { 8 | pthread_mutex_t cs; 9 | struct event *events[EVQ_NSIG]; 10 | } g_Signal; 11 | static int volatile g_SignalInit = 0; 12 | 13 | 14 | static struct event ** 15 | signal_gethead (int signo) 16 | { 17 | switch (signo) { 18 | case EVQ_SIGINT: signo = 0; break; 19 | case EVQ_SIGQUIT: signo = 1; break; 20 | case EVQ_SIGHUP: signo = 2; break; 21 | case EVQ_SIGTERM: signo = 3; break; 22 | case EVQ_SIGCHLD: signo = 4; break; 23 | default: return NULL; 24 | } 25 | return &g_Signal.events[signo]; 26 | } 27 | 28 | static void 29 | signal_handler (const int signo) 30 | { 31 | #ifdef USE_KQUEUE 32 | (void) signo; 33 | #else 34 | struct event **sig_evp; 35 | 36 | if (signo == SYS_SIGINTR) return; 37 | 38 | sig_evp = signal_gethead(signo); 39 | if (!sig_evp) return; 40 | 41 | pthread_mutex_lock(&g_Signal.cs); 42 | { 43 | struct event *ev = *sig_evp; 44 | for (; ev; ev = ev->next_object) { 45 | if (!event_deleted(ev)) 46 | evq_signal(ev->evq, signo); 47 | } 48 | } 49 | pthread_mutex_unlock(&g_Signal.cs); 50 | #endif 51 | } 52 | 53 | EVQ_API void 54 | signal_init (void) 55 | { 56 | if (g_SignalInit) return; 57 | g_SignalInit = 1; 58 | 59 | /* Initialize critical section */ 60 | pthread_mutex_init(&g_Signal.cs, NULL); 61 | /* Ignore sigpipe or it will crash us */ 62 | signal_set(SIGPIPE, SIG_IGN); 63 | /* To interrupt blocking syscalls */ 64 | signal_set(SYS_SIGINTR, signal_handler); 65 | } 66 | 67 | static int 68 | evq_interrupt (struct event_queue *evq) 69 | { 70 | #if defined(USE_EPOLL) && defined(USE_EVENTFD) 71 | const fd_t fd = evq->sig_fd[0]; 72 | const int64_t data = 1; 73 | #else 74 | const fd_t fd = evq->sig_fd[1]; 75 | const char data = 0; 76 | #endif 77 | int nw; 78 | 79 | do nw = write(fd, &data, sizeof(data)); 80 | while (nw == -1 && errno == EINTR); 81 | 82 | return (nw == -1) ? -1 : 0; 83 | } 84 | 85 | EVQ_API int 86 | evq_signal (struct event_queue *evq, const int signo) 87 | { 88 | int res = 0; 89 | 90 | pthread_mutex_lock(&evq->sig_cs); 91 | if (!evq->sig_ready) 92 | res = evq_interrupt(evq); 93 | evq->sig_ready |= 1 << signo; 94 | pthread_mutex_unlock(&evq->sig_cs); 95 | return res; 96 | } 97 | 98 | EVQ_API int 99 | signal_set (const int signo, sig_handler_t func) 100 | { 101 | struct sigaction act; 102 | int res; 103 | 104 | act.sa_handler = func; 105 | sigemptyset(&act.sa_mask); 106 | act.sa_flags = (signo == SYS_SIGINTR) ? 0 : SA_RESTART; 107 | 108 | do res = sigaction(signo, &act, NULL); 109 | while (res == -1 && errno == EINTR); 110 | 111 | return res; 112 | } 113 | 114 | #ifdef USE_KQUEUE 115 | static int 116 | signal_kqueue (struct event_queue *evq, const int signo, const int action) 117 | { 118 | struct kevent kev; 119 | int res; 120 | 121 | memset(&kev, 0, sizeof(struct kevent)); 122 | kev.ident = signo; 123 | kev.filter = EVFILT_SIGNAL; 124 | kev.flags = action; 125 | 126 | do res = kevent(evq->kqueue_fd, &kev, 1, NULL, 0, NULL); 127 | while (res == -1 && errno == EINTR); 128 | 129 | return res; 130 | } 131 | #endif 132 | 133 | EVQ_API int 134 | evq_ignore_signal (struct event_queue *evq, const int signo, const int ignore) 135 | { 136 | #ifndef USE_KQUEUE 137 | (void) evq; 138 | #else 139 | if (signal_kqueue(evq, signo, ignore ? EV_DELETE : EV_ADD)) 140 | return -1; 141 | #endif 142 | if (ignore) 143 | return signal_set(signo, SIG_IGN); 144 | else { 145 | struct event **sig_evp = signal_gethead(signo); 146 | int res; 147 | 148 | pthread_mutex_lock(&g_Signal.cs); 149 | res = signal_set(signo, 150 | (sig_evp && *sig_evp ? signal_handler : SIG_DFL)); 151 | pthread_mutex_unlock(&g_Signal.cs); 152 | return res; 153 | } 154 | } 155 | 156 | static int 157 | signal_add (struct event_queue *evq, struct event *ev) 158 | { 159 | const int signo = (ev->flags & EVENT_PID) ? EVQ_SIGCHLD : (int) ev->fd; 160 | struct event **sig_evp = signal_gethead(signo); 161 | 162 | if (!sig_evp) return -1; 163 | 164 | pthread_mutex_lock(&g_Signal.cs); 165 | if (*sig_evp) 166 | ev->next_object = *sig_evp; 167 | else { 168 | #ifdef USE_KQUEUE 169 | if (signal_kqueue(evq, signo, EV_ADD)) 170 | goto err; 171 | #endif 172 | if (signal_set(signo, signal_handler)) 173 | goto err; 174 | ev->next_object = NULL; 175 | } 176 | *sig_evp = ev; 177 | pthread_mutex_unlock(&g_Signal.cs); 178 | 179 | evq->nevents++; 180 | return 0; 181 | err: 182 | pthread_mutex_unlock(&g_Signal.cs); 183 | return -1; 184 | } 185 | 186 | static int 187 | signal_del (struct event_queue *evq, struct event *ev) 188 | { 189 | const int signo = (ev->flags & EVENT_PID) ? EVQ_SIGCHLD 190 | : (int) ev->fd; 191 | struct event **sig_evp = signal_gethead(signo); 192 | int res = 0; 193 | 194 | pthread_mutex_lock(&g_Signal.cs); 195 | if (*sig_evp == ev) { 196 | if (!(*sig_evp = ev->next_object)) { 197 | #ifndef USE_KQUEUE 198 | (void) evq; 199 | #else 200 | res |= signal_kqueue(evq, signo, EV_DELETE); 201 | #endif 202 | res |= signal_set(signo, SIG_DFL); 203 | } 204 | } else { 205 | struct event *sig_ev = *sig_evp; 206 | 207 | while (sig_ev->next_object != ev) 208 | sig_ev = sig_ev->next_object; 209 | sig_ev->next_object = ev->next_object; 210 | } 211 | pthread_mutex_unlock(&g_Signal.cs); 212 | return res; 213 | } 214 | 215 | static int 216 | signal_process_child (struct event *ev) 217 | { 218 | const int fd = (int) ev->fd; 219 | int pid, status; 220 | 221 | do pid = waitpid(fd, &status, WNOHANG); 222 | while (pid == -1 && errno == EINTR); 223 | 224 | if (pid == fd) { 225 | ev->flags |= !WIFEXITED(status) ? EVENT_STATUS_MASK 226 | : ((unsigned int) WEXITSTATUS(status) << EVENT_STATUS_SHIFT); 227 | return 0; 228 | } 229 | return -1; 230 | } 231 | 232 | static struct event * 233 | signal_process_actives (struct event_queue *evq, const int signo, 234 | struct event *ev_ready, const msec_t now) 235 | { 236 | struct event **sig_evp = signal_gethead(signo); 237 | struct event *ev, *ev_next = NULL; 238 | 239 | pthread_mutex_lock(&g_Signal.cs); 240 | ev = *sig_evp; 241 | for (; ev; ev = ev->next_object) { 242 | if (ev->evq == evq && !(ev->flags & EVENT_ACTIVE)) { 243 | ev->next_ready = ev_next; 244 | ev_next = ev; 245 | } 246 | } 247 | pthread_mutex_unlock(&g_Signal.cs); 248 | 249 | while (ev_next) { 250 | ev = ev_next; 251 | ev_next = ev->next_ready; 252 | if (signo == EVQ_SIGCHLD && signal_process_child(ev)) 253 | continue; 254 | ev_ready = evq_process_active(ev, ev_ready, now); 255 | } 256 | return ev_ready; 257 | } 258 | 259 | static struct event * 260 | signal_process_interrupt (struct event_queue *evq, struct event *ev_ready, 261 | const msec_t now) 262 | { 263 | unsigned int sig_ready; 264 | int signo; 265 | 266 | pthread_mutex_lock(&evq->sig_cs); 267 | /* reset interruption event */ 268 | { 269 | const fd_t fd = evq->sig_fd[0]; 270 | char buf[8]; /* USE_EVENTFD: 8 bytes required */ 271 | int nr; 272 | 273 | do nr = read(fd, buf, sizeof(buf)); 274 | while (nr == -1 && errno == EINTR); 275 | } 276 | sig_ready = evq->sig_ready; 277 | evq->sig_ready = 0; 278 | pthread_mutex_unlock(&evq->sig_cs); 279 | 280 | sig_ready &= ~(1 << EVQ_SIGEVQ); 281 | 282 | for (signo = 0; sig_ready; ++signo, sig_ready >>= 1) { 283 | if (sig_ready & 1) 284 | ev_ready = signal_process_actives(evq, signo, ev_ready, now); 285 | } 286 | return ev_ready; 287 | } 288 | 289 | -------------------------------------------------------------------------------- /test/isa/fcgi.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | -- Lua Internet Server Application: FastCGI Launcher 4 | 5 | local sys = require("sys") 6 | local sock = require("sys.sock") 7 | 8 | local fcgi_decode, fcgi_encode = sys.fcgi_decode, sys.fcgi_encode 9 | 10 | sys.thread.init() 11 | 12 | 13 | local bind = { 14 | [9000] = "127.0.0.1", 15 | } 16 | 17 | local BUFER_OUT_SIZE = 32768 18 | local BUFER_IN_SIZE = 8192 19 | 20 | 21 | -- Event Queue 22 | local evq = assert(sys.event_queue()) 23 | 24 | 25 | -- Log message 26 | local log 27 | do 28 | local stderr = sys.stderr 29 | 30 | log = function(title, msg) 31 | stderr:write(title, ": ", msg or SYS_ERR, "\n") 32 | end 33 | end 34 | 35 | 36 | -- Request's metatable 37 | local request_meta = {} 38 | do 39 | -- Request keys: CGI variables (in upper case), 40 | -- "keep_conn", "stdin" (data), 41 | -- "id" (request_id), "channel" (reference), 42 | -- "error" (message), "next_ready" (request_id), 43 | -- "state" (state of request processing: "headers", "out"), 44 | -- "prev" (last written record) 45 | 46 | request_meta.__index = request_meta 47 | 48 | -- Get header variable 49 | request_meta.getvar = function(req, name) 50 | return req[name] 51 | end 52 | 53 | -- Data from stdin 54 | request_meta.data = function(req) 55 | return req.stdin 56 | end 57 | 58 | -- Read rest data 59 | request_meta.read = function(req) 60 | end 61 | 62 | -- Output 63 | local function out(req, s) 64 | local chan = req.channel 65 | local buffer_out = chan.buffer_out 66 | 67 | chan.prev = fcgi_encode(buffer_out, req.id, chan.prev, s) 68 | 69 | if buffer_out:seek() > BUFER_OUT_SIZE 70 | and chan.fd:write(buffer_out) ~= nil 71 | then 72 | chan.prev = nil 73 | end 74 | end 75 | 76 | -- Write 77 | request_meta.write = function(req, s, ...) 78 | if req.state ~= "out" then 79 | out(req, req.state == "headers" and "\r\n" 80 | or "Content-type: text/html\r\n\r\n") 81 | req.state = "out" 82 | end 83 | 84 | if s and s ~= "" then out(req, s) end 85 | 86 | if select('#', ...) ~= 0 then 87 | return req:write(...) 88 | end 89 | end 90 | 91 | -- Set header variable 92 | request_meta.header = function(req, name, value) 93 | if req.state == "out" then 94 | error"Headers already sent" 95 | end 96 | 97 | out(req, name .. ": " .. value .. "\r\n") 98 | end 99 | 100 | -- Close the request 101 | request_meta.close = function(req) 102 | local chan = req.channel 103 | local fd, buffer_out = chan.fd, chan.buffer_out 104 | 105 | chan.prev = nil 106 | fcgi_encode(buffer_out, req.id) 107 | 108 | if not fd:write(buffer_out) then 109 | log("socket.write") 110 | buffer_out:seek(0) 111 | end 112 | end 113 | end 114 | 115 | 116 | -- Channels (Connections with front-end) 117 | local channels = {} 118 | do 119 | -- Pool of free channels 120 | local pool = setmetatable({n = 0}, {__mode = "v"}) 121 | 122 | local function pool_get() 123 | local n = pool.n 124 | local chan = pool[n] 125 | if not chan then 126 | n = 1 127 | chan = { 128 | request_meta = request_meta, 129 | fd = sock.handle() 130 | } 131 | 132 | local buf = sys.mem.pointer():alloc(BUFER_OUT_SIZE) 133 | if not buf then return end 134 | chan.buffer_out = buf 135 | 136 | buf = sys.mem.pointer():alloc(BUFER_IN_SIZE) 137 | if not buf then return end 138 | chan.buffer_in = buf 139 | end 140 | pool.n = n - 1 141 | return chan 142 | end 143 | 144 | local function pool_put(chan) 145 | local n = pool.n + 1 146 | pool.n, pool[n] = n, chan 147 | end 148 | 149 | 150 | -- Get the free channel 151 | channels.get = pool_get 152 | 153 | -- Put the free channel 154 | channels.put = pool_put 155 | 156 | -- Close the channel 157 | channels.close = function(evq, evid, fd) 158 | local chan = channels[fd] 159 | channels[fd] = nil 160 | pool_put(chan) 161 | 162 | evq:del(evid) 163 | fd:close() 164 | end 165 | end 166 | 167 | 168 | -- Directory monitoring and files caching 169 | local dir_loadfile 170 | do 171 | local Match = string.match 172 | local dirs = {} 173 | 174 | -- Directory changed 175 | local function on_change(evq, evid, dir) 176 | evq:del(evid) 177 | dirs[dir] = nil 178 | end 179 | 180 | -- Get file from directory or from cache 181 | dir_loadfile = function(path) 182 | local dir, filename = Match(path, [[(.-)([^/\]+)$]]) 183 | local dir_files = dirs[dir] 184 | local is_watch = true 185 | local chunk, err 186 | 187 | if dir_files then 188 | chunk = dir_files[filename] 189 | elseif evq:add_dirwatch(dir, on_change, true) then 190 | dir_files = {} 191 | dirs[dir] = dir_files 192 | else 193 | is_watch = false 194 | end 195 | if not chunk then 196 | chunk, err = loadfile(path) 197 | if chunk and is_watch then 198 | dir_files[filename] = chunk 199 | end 200 | end 201 | return chunk, err 202 | end 203 | end 204 | 205 | 206 | -- Process requests 207 | local process 208 | do 209 | local xpcall, traceback = sys.xpcall, debug.traceback 210 | 211 | -- Process request 212 | local function process_request(req) 213 | local path = req:getvar"PATH_TRANSLATED" 214 | if not path then 215 | -- nginx: fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info$fastcgi_script_name; 216 | error("PATH_TRANSLATED expected") 217 | end 218 | 219 | local chunk, err = dir_loadfile(path) 220 | local status 221 | 222 | req.state = "headers" 223 | if not err then 224 | status, err = xpcall(traceback, chunk, req) 225 | else 226 | req:header("Status", 500) 227 | end 228 | if not status then 229 | req:write("\n\n
", err)
230 |     end
231 | 
232 |     req:close()
233 |     return status
234 |   end
235 | 
236 |   -- Process requests
237 |   process = function(evq, evid, fd, _, eof)
238 |     local chan = channels[fd]
239 |     local buffer_in = chan.buffer_in
240 |     local status
241 | 
242 |     if not eof and fd:read(buffer_in) then
243 |       local request_id = fcgi_decode(buffer_in, chan)
244 | 
245 |       while request_id do
246 |         local req = chan[request_id]
247 | 
248 |         if req.error then
249 |           chan[request_id] = nil
250 |           log("req.error", req.error)
251 |         else
252 |           status = process_request(req)
253 |         end
254 | 
255 |         status = req.keep_conn
256 |         request_id = req.next_ready
257 |       end
258 |     end
259 |     if not status then
260 |       channels.close(evq, evid, fd)
261 |     end
262 |   end
263 | end
264 | 
265 | 
266 | -- Accept new client
267 | local function accept(evq, evid, fd)
268 |   local channel = channels.get()
269 |   if not channel then
270 |     log("channels.get")
271 |     return
272 |   end
273 | 
274 |   fd = fd:accept(channel.fd)
275 |   if not fd then
276 |     channels.put(channel)
277 |     log("accept")
278 |     return
279 |   end
280 | 
281 |   --fd:nonblocking(true)
282 |   --fd:sockopt("tcp_nodelay", 1)
283 | 
284 |   evid = evq:add_socket(fd, 'r', process)
285 |   if not evid then
286 |     fd:close()
287 |     channels.put(channel)
288 |     log("evq.add")
289 |     return
290 |   end
291 | 
292 |   channels[fd] = channel
293 | end
294 | 
295 | 
296 | local saddr = sock.addr()
297 | for port, host in pairs(bind) do
298 |   local fd = sock.handle()
299 |   assert(fd:socket())
300 |   assert(fd:sockopt("reuseaddr", 1))
301 |   assert(saddr:inet(port, sock.inet_pton(host)))
302 |   assert(fd:bind(saddr))
303 |   assert(fd:listen())
304 |   assert(evq:add_socket(fd, 'accept', accept))
305 | end
306 | 
307 | -- Quit by Ctrl-C
308 | assert(evq:add_signal("INT", evq.stop))
309 | 
310 | evq:loop()
311 | 


--------------------------------------------------------------------------------
/src/luasys.c:
--------------------------------------------------------------------------------
  1 | /* System library for Lua */
  2 | 
  3 | #include "common.h"
  4 | 
  5 | #include 
  6 | 
  7 | 
  8 | #ifndef _WIN32
  9 | 
 10 | #include 
 11 | #include 
 12 | #include 
 13 | #if defined(BSD)
 14 | #include 
 15 | #endif
 16 | 
 17 | #define SYS_FILE_PERMISSIONS	0644  /* default file permissions */
 18 | 
 19 | #else
 20 | 
 21 | #include "win32/getloadavg.c"
 22 | 
 23 | #endif
 24 | 
 25 | 
 26 | /*
 27 |  * Arguments: ..., [number]
 28 |  * Returns: string
 29 |  */
 30 | static int
 31 | sys_strerror (lua_State *L)
 32 | {
 33 |   const int err = luaL_optint(L, -1, SYS_ERRNO);
 34 | 
 35 | #ifndef _WIN32
 36 | #if defined(BSD) || (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600)
 37 |   char buf[512];
 38 | 
 39 |   if (!err) goto success;
 40 | 
 41 |   if (!strerror_r(err, buf, sizeof(buf))) {
 42 |     lua_pushstring(L, buf);
 43 |     return 1;
 44 |   }
 45 | #endif
 46 |   lua_pushstring(L, strerror(err));
 47 | #else
 48 |   const int flags = FORMAT_MESSAGE_IGNORE_INSERTS
 49 |    | FORMAT_MESSAGE_FROM_SYSTEM;
 50 |   WCHAR buf[512];
 51 | 
 52 |   if (!err) goto success;
 53 | 
 54 |   if (is_WinNT
 55 |    ? FormatMessageW(flags, NULL, err, 0, buf, sizeof(buf) / sizeof(buf[0]), NULL)
 56 |    : FormatMessageA(flags, NULL, err, 0, (char *) buf, sizeof(buf), NULL)) {
 57 |     char *s = filename_to_utf8(buf);
 58 |     if (s) {
 59 |       char *cp;
 60 |       for (cp = s; *cp != '\0'; ++cp) {
 61 |         if (*cp == '\r' || *cp == '\n')
 62 |           *cp = ' ';
 63 |       }
 64 | 
 65 |       lua_pushstring(L, s);
 66 |       free(s);
 67 |       return 1;
 68 |     }
 69 |   }
 70 |   lua_pushfstring(L, "System error %d", err);
 71 | #endif
 72 |   return 1;
 73 |  success:
 74 |   lua_pushliteral(L, "OK");
 75 |   return 1;
 76 | }
 77 | 
 78 | /*
 79 |  * Returns: nil, string
 80 |  */
 81 | int
 82 | sys_seterror (lua_State *L, int err)
 83 | {
 84 |   if (err) {
 85 | #ifndef _WIN32
 86 |     errno = err;
 87 | #else
 88 |     SetLastError(err);
 89 | #endif
 90 |   }
 91 |   lua_pushnil(L);
 92 |   sys_strerror(L);
 93 |   lua_pushvalue(L, -1);
 94 |   lua_setglobal(L, SYS_ERROR_MESSAGE);
 95 |   return 2;
 96 | }
 97 | 
 98 | 
 99 | /*
100 |  * Returns: number_of_processors (number)
101 |  */
102 | static int
103 | sys_nprocs (lua_State *L)
104 | {
105 | #ifndef _WIN32
106 |   int n = 1;
107 | #if defined(_SC_NPROCESSORS_ONLN)
108 |   n = sysconf(_SC_NPROCESSORS_ONLN);
109 |   if (n == -1) n = 1;
110 | #elif defined(BSD)
111 |   int mib[2];
112 |   size_t len = sizeof(int);
113 | 
114 |   mib[0] = CTL_HW;
115 |   mib[1] = HW_NCPU;
116 |   sysctl(mib, 2, &n, &len, NULL, 0);
117 | #endif
118 |   lua_pushinteger(L, n);
119 | #else
120 |   SYSTEM_INFO si;
121 |   GetSystemInfo(&si);
122 |   lua_pushinteger(L, si.dwNumberOfProcessors);
123 | #endif
124 |   return 1;
125 | }
126 | 
127 | /*
128 |  * Returns: processors_load_average (number), is_per_cpu (boolean)
129 |  */
130 | static int
131 | sys_loadavg (lua_State *L)
132 | {
133 |   double loadavg;
134 | #ifndef _WIN32
135 |   const int res = 0;
136 | 
137 |   if (getloadavg(&loadavg, 1) == 1) {
138 |     const int is_per_cpu = 1;
139 | #else
140 |   const int res = getloadavg(&loadavg);
141 | 
142 |   if (!res) {
143 |     const int is_per_cpu = 0;
144 | #endif
145 |     lua_pushnumber(L, (lua_Number) loadavg);
146 |     lua_pushboolean(L, is_per_cpu);
147 |     return 2;
148 |   }
149 |   return sys_seterror(L, res);
150 | }
151 | 
152 | /*
153 |  * Arguments: [number_of_files (number)]
154 |  * Returns: number_of_files (number)
155 |  */
156 | static int
157 | sys_limit_nfiles (lua_State *L)
158 | {
159 | #ifndef _WIN32
160 |   const int narg = lua_gettop(L);
161 |   struct rlimit rlim;
162 | 
163 |   rlim.rlim_max = 0;
164 |   getrlimit(RLIMIT_NOFILE, &rlim);
165 |   lua_pushinteger(L, rlim.rlim_max);
166 | 
167 |   if (narg != 0) {
168 |     const int n = lua_tointeger(L, 1);
169 | 
170 |     rlim.rlim_cur = rlim.rlim_max = n;
171 |     if (setrlimit(RLIMIT_NOFILE, &rlim))
172 |       return sys_seterror(L, 0);
173 |   }
174 |   return 1;
175 | #else
176 |   (void) L;
177 | 
178 |   lua_pushinteger(L, -1);
179 |   return 1;
180 | #endif
181 | }
182 | 
183 | /*
184 |  * Arguments: string
185 |  * Returns: number
186 |  */
187 | static int
188 | sys_toint (lua_State *L)
189 | {
190 |   const char *s = lua_tostring(L, 1);
191 |   int num = 0, sign = 1;
192 | 
193 |   if (s) {
194 |     switch (*s) {
195 |     case '-': sign = -1;
196 |     case '+': ++s;
197 |     }
198 |     while (*s >= '0' && *s <= '9')
199 |       num = (num << 3) + (num << 1) + (*s++ & ~'0');
200 |   }
201 |   lua_pushinteger(L, sign * num);
202 |   return 1;
203 | }
204 | 
205 | /*
206 |  * Arguments: error_handler (function), function, any ...
207 |  * Returns: status (boolean), any ...
208 |  */
209 | static int
210 | sys_xpcall (lua_State *L)
211 | {
212 |   const int status = lua_pcall(L, lua_gettop(L) - 2, LUA_MULTRET, 1);
213 | 
214 |   lua_pushboolean(L, !status);
215 |   lua_insert(L, 2);
216 |   return lua_gettop(L) - 1;
217 | }
218 | 
219 | 
220 | #include "isa/fcgi/sys_fcgi.c"
221 | #include "mem/sys_mem.c"
222 | #include "thread/sys_thread.c"
223 | 
224 | #ifndef _WIN32
225 | #include "sys_unix.c"
226 | #else
227 | #include "win32/sys_win32.c"
228 | #endif
229 | 
230 | #include "sys_file.c"
231 | #include "sys_date.c"
232 | #include "sys_env.c"
233 | #include "sys_evq.c"
234 | #include "sys_fs.c"
235 | #include "sys_log.c"
236 | #include "sys_proc.c"
237 | #include "sys_rand.c"
238 | 
239 | 
240 | static luaL_Reg sys_lib[] = {
241 |   {"strerror",		sys_strerror},
242 |   {"nprocs",		sys_nprocs},
243 |   {"loadavg",		sys_loadavg},
244 |   {"limit_nfiles",	sys_limit_nfiles},
245 |   {"toint",		sys_toint},
246 |   {"xpcall",		sys_xpcall},
247 |   DATE_METHODS,
248 |   ENV_METHODS,
249 |   EVQ_METHODS,
250 |   FCGI_METHODS,
251 |   FD_METHODS,
252 |   FS_METHODS,
253 |   LOG_METHODS,
254 |   PROC_METHODS,
255 |   RAND_METHODS,
256 | #ifndef _WIN32
257 |   UNIX_METHODS,
258 | #endif
259 |   {NULL, NULL}
260 | };
261 | 
262 | 
263 | /*
264 |  * Arguments: ..., sys_lib (table)
265 |  */
266 | static void
267 | createmeta (lua_State *L)
268 | {
269 |   const int top = lua_gettop(L);
270 |   const struct meta_s {
271 |     const char *tname;
272 |     luaL_Reg *meth;
273 |     int is_index;
274 |   } meta[] = {
275 |     {DIR_TYPENAME,		dir_meth,	0},
276 |     {EVQ_TYPENAME,		evq_meth,	1},
277 |     {FD_TYPENAME,		fd_meth,	1},
278 |     {PERIOD_TYPENAME,		period_meth,	1},
279 |     {PID_TYPENAME,		pid_meth,	1},
280 |     {LOG_TYPENAME,		log_meth,	0},
281 |     {RAND_TYPENAME,		rand_meth,	0},
282 |   };
283 |   int i;
284 | 
285 |   for (i = 0; i < (int) (sizeof(meta) / sizeof(struct meta_s)); ++i) {
286 |     luaL_newmetatable(L, meta[i].tname);
287 |     if (meta[i].is_index) {
288 |       lua_pushvalue(L, -1);  /* push metatable */
289 |       lua_setfield(L, -2, "__index");  /* metatable.__index = metatable */
290 |     }
291 |     luaL_setfuncs(L, meta[i].meth, 0);
292 |     lua_pop(L, 1);
293 |   }
294 | 
295 |   /* Predefined file handles */
296 |   luaL_getmetatable(L, FD_TYPENAME);
297 |   {
298 |     const char *std[] = {"stdin", "stdout", "stderr"};
299 | 
300 |     for (i = 3; i--; ) {
301 | #ifndef _WIN32
302 |       const fd_t fd = i;
303 | #else
304 |       const fd_t fd = GetStdHandle(i == 0 ? STD_INPUT_HANDLE
305 |        : (i == 1 ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE));
306 | #endif
307 |       lua_pushstring(L, std[i]);
308 |       lua_boxinteger(L, fd);
309 |       lua_pushvalue(L, -3);  /* metatable */
310 |       lua_pushboolean(L, 1);
311 |       lua_rawseti(L, -2, (int) ((lua_Integer) fd));  /* don't close std. handles */
312 |       lua_setmetatable(L, -2);
313 |       lua_rawset(L, top);
314 |     }
315 |   }
316 |   lua_settop(L, top);
317 | }
318 | 
319 | 
320 | LUALIB_API int
321 | luaopen_sys (lua_State *L)
322 | {
323 |   luaL_register(L, LUA_SYSLIBNAME, sys_lib);
324 |   createmeta(L);
325 | 
326 |   signal_init();
327 | 
328 | #ifdef _WIN32
329 |   luaopen_sys_win32(L);
330 | #endif
331 |   luaopen_sys_mem(L);
332 |   luaopen_sys_thread(L);
333 | 
334 |   return 1;
335 | }
336 | 


--------------------------------------------------------------------------------
/src/isa/isapi/isapi_dll.c:
--------------------------------------------------------------------------------
  1 | /* Lua System: Internet Server Application: ISAPI Extension */
  2 | 
  3 | #include "../../common.h"
  4 | 
  5 | #include 
  6 | 
  7 | #ifndef NDEBUG
  8 | #include 
  9 | 
 10 | static FILE *flog;
 11 | #endif
 12 | 
 13 | 
 14 | #define LISAPI_DESCR	"Lua ISAPI Extension"
 15 | 
 16 | #define LISAPI_POOL_THREADS	8
 17 | 
 18 | /* Global Lua State */
 19 | static struct {
 20 |   lua_State *L;
 21 |   struct sys_thread *vmtd;
 22 | 
 23 |   int nthreads;
 24 |   struct sys_thread *threads[LISAPI_POOL_THREADS];
 25 | 
 26 |   char root[MAX_PATHNAME];
 27 | } g_ISAPI;
 28 | 
 29 | static void lisapi_createmeta (lua_State *L);
 30 | 
 31 | 
 32 | #include "isapi_ecb.c"
 33 | 
 34 | 
 35 | static int
 36 | traceback (lua_State *L) {
 37 | #if LUA_VERSION_NUM < 502
 38 |   lua_getfield(L, LUA_GLOBALSINDEX, "debug");
 39 |   if (!lua_istable(L, -1)) {
 40 |     lua_pop(L, 1);
 41 |     return 1;
 42 |   }
 43 |   lua_getfield(L, -1, "traceback");
 44 |   if (!lua_isfunction(L, -1)) {
 45 |     lua_pop(L, 2);
 46 |     return 1;
 47 |   }
 48 |   lua_pushvalue(L, 1);  /* pass error message */
 49 |   lua_pushinteger(L, 2);  /* skip this function and traceback */
 50 |   lua_call(L, 2, 1);  /* call debug.traceback */
 51 |   return 1;
 52 | #else
 53 |   const char *msg = lua_tostring(L, 1);
 54 |   if (msg)
 55 |     luaL_traceback(L, L, msg, 1);
 56 |   else if (!lua_isnoneornil(L, 1)) {  /* is there an error object? */
 57 |     if (!luaL_callmeta(L, 1, "__tostring"))  /* try its 'tostring' metamethod */
 58 |       lua_pushliteral(L, "(no error message)");
 59 |   }
 60 |   return 1;
 61 | #endif
 62 | }
 63 | 
 64 | static int
 65 | lisapi_init (void)
 66 | {
 67 |   lua_State *L;
 68 |   char path[MAX_PATHNAME*2];
 69 | 
 70 |   if (g_ISAPI.vmtd) return 0;
 71 | 
 72 |   L = luaL_newstate();
 73 |   if (!L) return -1;
 74 | 
 75 | #ifndef NDEBUG
 76 |   {
 77 |     sprintf(path, "%s\\luaisapi.log", g_ISAPI.root);
 78 |     flog = fopen(path, "a");
 79 |     if (!flog) goto err_log;
 80 |   }
 81 | #endif
 82 | 
 83 |   luaL_openlibs(L);  /* open standard libraries */
 84 | 
 85 |   lua_pushcfunction(L, traceback);  /* 1: traceback function */
 86 | 
 87 |   /* load initialization script */
 88 |   {
 89 |     sprintf(path, "%s\\isapi.lua", g_ISAPI.root);
 90 |     if (luaL_loadfile(L, path))
 91 |       goto err;
 92 |   }
 93 | 
 94 |   lua_pushstring(L, g_ISAPI.root);
 95 |   if (lua_pcall(L, 1, 2, 1)
 96 |    || !lua_isfunction(L, 2)  /* 2: request handler */
 97 |    || !lua_isfunction(L, 3))  /* 3: closing handler */
 98 |     goto err;
 99 | 
100 |   g_ISAPI.vmtd = sys_thread_get();
101 |   if (g_ISAPI.vmtd) {
102 |     lisapi_createmeta(L);
103 | 
104 |     g_ISAPI.nthreads = 0;
105 |     g_ISAPI.L = L;
106 |     sys_vm_leave(L);
107 |     sys_thread_set(NULL);
108 |     return 0;
109 |   }
110 | #ifndef NDEBUG
111 |   lua_pushliteral(L, "Threading not initialized");
112 | #endif
113 | 
114 |  err:
115 | #ifndef NDEBUG
116 |   fprintf(flog, "init: %s\n", lua_tostring(L, -1));
117 |   fclose(flog);
118 |  err_log:
119 | #endif
120 |   lua_close(L);
121 |   return -1;
122 | }
123 | 
124 | static struct sys_thread *
125 | lisapi_open (LPEXTENSION_CONTROL_BLOCK ecb)
126 | {
127 |   lua_State *L;
128 |   struct sys_thread *td;
129 | 
130 |   if (g_ISAPI.nthreads) {
131 |     LPEXTENSION_CONTROL_BLOCK *ecbp;
132 | 
133 |     td = g_ISAPI.threads[--g_ISAPI.nthreads];
134 |     L = sys_thread_tolua(td);
135 | 
136 |     ecbp = checkudata(L, -1, ECB_TYPENAME);
137 |     *ecbp = ecb;
138 |   } else {
139 |     td = sys_thread_new(g_ISAPI.L, g_ISAPI.vmtd, NULL, 0);
140 |     if (!td) return NULL;
141 | 
142 |     L = sys_thread_tolua(td);
143 | 
144 |     lua_pushvalue(g_ISAPI.L, 1);  /* traceback function */
145 |     lua_pushvalue(g_ISAPI.L, 2);  /* process function */
146 |     lua_xmove(g_ISAPI.L, L, 2);  /* move functions to L */
147 | 
148 |     lua_boxpointer(L, ecb);
149 |     luaL_getmetatable(L, ECB_TYPENAME);
150 |     lua_setmetatable(L, -2);
151 |   }
152 | 
153 |   sys_thread_set(td);
154 |   return td;
155 | }
156 | 
157 | static void
158 | lisapi_close (struct sys_thread *td, int status)
159 | {
160 |   if (status || g_ISAPI.nthreads >= LISAPI_POOL_THREADS)
161 |     sys_thread_del(td);
162 |   else
163 |     g_ISAPI.threads[g_ISAPI.nthreads++] = td;
164 | 
165 |   sys_thread_set(NULL);
166 | }
167 | 
168 | 
169 | BOOL WINAPI DllMain (HANDLE hmodule, DWORD reason, LPVOID reserved);
170 | 
171 | BOOL WINAPI
172 | DllMain (HANDLE hmodule, DWORD reason, LPVOID reserved)
173 | {
174 |   (void) reserved;
175 | 
176 |   if (reason == DLL_PROCESS_ATTACH) {
177 |     int n = GetModuleFileNameA(hmodule, g_ISAPI.root, MAX_PATHNAME);
178 |     char *sep;
179 | 
180 |     if (!n) return FALSE;
181 | 
182 |     sep = strrchr(g_ISAPI.root, '\\');
183 |     if (sep) *sep = '\0';
184 |   } else if (reason == DLL_PROCESS_DETACH)
185 |     TerminateExtension(0);
186 | 
187 |   return TRUE;
188 | }
189 | 
190 | BOOL WINAPI
191 | GetExtensionVersion (HSE_VERSION_INFO *ver)
192 | {
193 |   ver->dwExtensionVersion = MAKELONG(HSE_VERSION_MINOR, HSE_VERSION_MAJOR);
194 |   memcpy(ver->lpszExtensionDesc, LISAPI_DESCR, sizeof(LISAPI_DESCR));
195 | 
196 |   return !lisapi_init();
197 | }
198 | 
199 | BOOL WINAPI
200 | TerminateExtension (DWORD flags)
201 | {
202 |   (void) flags;
203 | 
204 |   if (g_ISAPI.vmtd) {
205 |     sys_vm2_enter(g_ISAPI.vmtd);
206 |     sys_thread_set(g_ISAPI.vmtd);
207 | 
208 |     if (lua_pcall(g_ISAPI.L, 0, 0, 1)) {
209 | #ifndef NDEBUG
210 |       fprintf(flog, "close: %s\n", lua_tostring(g_ISAPI.L, -1));
211 | #endif
212 |     }
213 |     lua_close(g_ISAPI.L);
214 | 
215 | #ifndef NDEBUG
216 |     fclose(flog);
217 | #endif
218 |     g_ISAPI.vmtd = NULL;
219 |   }
220 |   return TRUE;
221 | }
222 | 
223 | DWORD WINAPI
224 | HttpExtensionProc (LPEXTENSION_CONTROL_BLOCK ecb)
225 | {
226 |   lua_State *L;
227 |   struct sys_thread *td;
228 |   DWORD res = HSE_STATUS_SUCCESS;
229 |   int status;
230 | 
231 |   sys_vm2_enter(g_ISAPI.vmtd);
232 | 
233 |   td = lisapi_open(ecb);
234 |   if (!td) goto err;
235 | 
236 |   L = sys_thread_tolua(td);
237 | 
238 |   lua_pushvalue(L, -2);  /* process function */
239 |   lua_pushvalue(L, -2);  /* ecb_udata */
240 | 
241 |   ecb->dwHttpStatusCode = 200;
242 |   status = lua_pcall(L, 1, 0, 1);
243 | 
244 |   if (ecb->dwHttpStatusCode & ECB_STATUS_PENDING) {
245 |     ecb->dwHttpStatusCode &= ~ECB_STATUS_PENDING;
246 |     res = HSE_STATUS_PENDING;
247 |   } else {
248 |     if (ecb->dwHttpStatusCode & ~ECB_STATUS_MASK) {
249 |       ecb->dwHttpStatusCode &= ECB_STATUS_MASK;
250 | 
251 |       lua_pushnil(L);
252 |       lua_rawsetp(L, LUA_REGISTRYINDEX, ecb);
253 |     }
254 |     if (status) {
255 |       const char *s;
256 |       size_t len;
257 |       union sys_rwptr s_ptr;  /* to avoid "const cast" warning */
258 | 
259 |       lua_pushliteral(L, "\n\n
");
260 |       lua_insert(L, -2);
261 |       lua_concat(L, 2);
262 |       s = lua_tolstring(L, -1, &len);
263 | 
264 | #ifndef NDEBUG
265 |       fprintf(flog, "process: %s\n", s);
266 | #endif
267 | 
268 |       ecb->dwHttpStatusCode = 500;
269 |       s_ptr.r = s;
270 |       ecb->WriteClient(ecb->ConnID, s_ptr.w, (DWORD *) &len, 0);
271 |       lua_pop(L, 1);
272 |     }
273 |   }
274 | 
275 |   lisapi_close(td, status);
276 |  err:
277 |   sys_vm2_leave(g_ISAPI.vmtd);
278 |   return res;
279 | }
280 | 
281 | 
282 | static luaL_Reg isapi_lib[] = {
283 |   {"ecb",	ecb_new},
284 |   {NULL, NULL}
285 | };
286 | 
287 | 
288 | static void
289 | lisapi_createmeta (lua_State *L)
290 | {
291 |   /* already created? */
292 |   luaL_getmetatable(L, ECB_TYPENAME);
293 |   {
294 |     const int created = !lua_isnil(L, -1);
295 |     lua_pop(L, 1);
296 |     if (created) return;
297 |   }
298 | 
299 |   luaL_newmetatable(L, ECB_TYPENAME);
300 |   lua_pushvalue(L, -1);  /* push metatable */
301 |   lua_setfield(L, -2, "__index");  /* metatable.__index = metatable */
302 |   luaL_setfuncs(L, ecb_meth, 0);
303 |   lua_pop(L, 1);
304 | }
305 | 
306 | LUALIB_API int
307 | luaopen_sys_isapi (lua_State *L)
308 | {
309 |   luaL_register(L, LUA_ISAPILIBNAME, isapi_lib);
310 |   lisapi_createmeta(L);
311 |   return 1;
312 | }
313 | 


--------------------------------------------------------------------------------
/src/isa/isapi/isapi_ecb.c:
--------------------------------------------------------------------------------
  1 | /* Lua System: Internet Server Application: ISAPI Extension Control Block */
  2 | 
  3 | #define ECB_TYPENAME	"sys.isa.isapi"
  4 | 
  5 | #define ECB_STATUS_MASK		0x0FFF
  6 | #define ECB_STATUS_HEADERS	0x1000
  7 | #define ECB_STATUS_HEADERS_SEND	0x2000
  8 | #define ECB_STATUS_PENDING	0x4000
  9 | 
 10 | 
 11 | /*
 12 |  * Returns: ecb_udata
 13 |  */
 14 | static int
 15 | ecb_new (lua_State *L)
 16 | {
 17 |   lua_boxpointer(L, NULL);
 18 |   luaL_getmetatable(L, ECB_TYPENAME);
 19 |   lua_setmetatable(L, -2);
 20 |   return 1;
 21 | }
 22 | 
 23 | /*
 24 |  * Arguments: ecb_udata, [handle (ludata)]
 25 |  * Returns: [ecb_udata | handle (ludata)]
 26 |  */
 27 | static int
 28 | ecb_handle (lua_State *L)
 29 | {
 30 |   LPEXTENSION_CONTROL_BLOCK *ecbp = checkudata(L, 1, ECB_TYPENAME);
 31 | 
 32 |   if (lua_gettop(L) > 1) {
 33 |     *ecbp = lua_touserdata(L, 2);
 34 |     lua_settop(L, 1);
 35 |   } else {
 36 |     if (!*ecbp) lua_pushnil(L);
 37 |     else lua_pushlightuserdata(L, *ecbp);
 38 |   }
 39 |   return 1;
 40 | }
 41 | 
 42 | /*
 43 |  * Arguments: ecb_udata, variable_name (string)
 44 |  * Returns: value (string | number)
 45 |  */
 46 | static int
 47 | ecb_getvar (lua_State *L)
 48 | {
 49 |   LPEXTENSION_CONTROL_BLOCK ecb = lua_unboxpointer(L, 1, ECB_TYPENAME);
 50 |   const char *name = luaL_checkstring(L, 2);
 51 |   const char *val = NULL;
 52 | 
 53 |   if (!strcmp(name, "REQUEST_METHOD"))
 54 |     val = ecb->lpszMethod;
 55 |   else if (!strcmp(name, "QUERY_STRING"))
 56 |     val = ecb->lpszQueryString;
 57 |   else if (!strcmp(name, "PATH_INFO"))
 58 |     val = ecb->lpszPathInfo;
 59 |   else if (!strcmp(name, "PATH_TRANSLATED"))
 60 |     val = ecb->lpszPathTranslated;
 61 |   else if (!strcmp(name, "CONTENT_TYPE"))
 62 |     val = ecb->lpszContentType;
 63 |   else if (!strcmp(name, "CONTENT_LENGTH")) {
 64 |     lua_pushinteger(L, ecb->cbTotalBytes);
 65 |     return 1;
 66 |   }
 67 |   if (val) {
 68 |     lua_pushstring(L, val);
 69 |     return 1;
 70 |   } else {
 71 |     char buf[SYS_BUFSIZE];
 72 |     DWORD len = sizeof(buf);
 73 |     union sys_rwptr name_ptr;  /* to avoid "const cast" warning */
 74 | 
 75 |     name_ptr.r = name;
 76 |     if (ecb->GetServerVariable(ecb->ConnID, name_ptr.w, buf, &len)) {
 77 |       lua_pushlstring(L, buf, len - 1);
 78 |       return 1;
 79 |     }
 80 |   }
 81 |   return sys_seterror(L, 0);
 82 | }
 83 | 
 84 | /*
 85 |  * Arguments: ecb_udata
 86 |  * Returns: data (string)
 87 |  */
 88 | static int
 89 | ecb_data (lua_State *L)
 90 | {
 91 |   LPEXTENSION_CONTROL_BLOCK ecb = lua_unboxpointer(L, 1, ECB_TYPENAME);
 92 | 
 93 |   lua_pushlstring(L, (const char *) ecb->lpbData, ecb->cbAvailable);
 94 |   return 1;
 95 | }
 96 | 
 97 | /*
 98 |  * Arguments: ecb_udata, [membuf_udata, count (number)]
 99 |  * Returns: [string | count (number)]
100 |  */
101 | static int
102 | ecb_read (lua_State *L)
103 | {
104 |   LPEXTENSION_CONTROL_BLOCK ecb = lua_unboxpointer(L, 1, ECB_TYPENAME);
105 |   size_t n = !lua_isnumber(L, -1) ? ~((size_t) 0)
106 |    : (size_t) lua_tointeger(L, -1);
107 |   const size_t len = n;  /* how much total to read */
108 |   size_t rlen;  /* how much to read */
109 |   int nr;  /* number of bytes actually read */
110 |   struct sys_thread *td = sys_thread_get();
111 |   struct sys_buffer sb;
112 |   char buf[SYS_BUFSIZE];
113 |   int res = 0;
114 | 
115 |   sys_buffer_write_init(L, 2, &sb, buf, sizeof(buf));
116 |   do {
117 |     rlen = (n <= sb.size) ? n : sb.size;
118 |     if (td) sys_vm2_leave(td);
119 |     {
120 |       DWORD l;
121 |       nr = ecb->ReadClient(ecb->ConnID, sb.ptr.w, &l) ? (int) l : -1;
122 |     }
123 |     if (td) sys_vm2_enter(td);
124 |     if (nr == -1) break;
125 |     n -= nr;  /* still have to read 'n' bytes */
126 |   } while ((n != 0L && nr == (int) rlen)  /* until end of count or eof */
127 |    && sys_buffer_write_next(L, &sb, buf, 0));
128 |   if (nr <= 0 && len == n) {
129 |     res = -1;
130 |   } else {
131 |     if (!sys_buffer_write_done(L, &sb, buf, nr))
132 |       lua_pushinteger(L, len - n);
133 |   }
134 |   if (td) sys_thread_check(td, L);
135 |   if (!res) return 1;
136 |   return sys_seterror(L, 0);
137 | }
138 | 
139 | /*
140 |  * Arguments: ecb_udata, {string | membuf_udata} ...
141 |  * Returns: [success/partial (boolean), count (number)]
142 |  */
143 | static int
144 | ecb_write (lua_State *L)
145 | {
146 |   LPEXTENSION_CONTROL_BLOCK ecb = lua_unboxpointer(L, 1, ECB_TYPENAME);
147 |   size_t n = 0;  /* number of chars actually write */
148 |   int i, narg = lua_gettop(L);
149 | 
150 |   if (ecb->dwHttpStatusCode & ECB_STATUS_HEADERS_SEND) {
151 |     ecb->dwHttpStatusCode ^= ECB_STATUS_HEADERS_SEND;
152 | 
153 |     lua_pushfstring(L, "HTTP/1.1 %d\r\n",
154 |      (ecb->dwHttpStatusCode & ECB_STATUS_MASK));
155 | 
156 |     lua_rawgetp(L, LUA_REGISTRYINDEX, ecb);
157 | 
158 |     lua_pushliteral(L, "\r\n");
159 |     lua_concat(L, 3);
160 | 
161 |     lua_insert(L, 2);
162 |     ++narg;
163 |   }
164 | 
165 |   for (i = 2; i <= narg; ++i) {
166 |     struct sys_buffer sb;
167 |     int nw;
168 | 
169 |     if (!sys_buffer_read_init(L, i, &sb)
170 |      || sb.size == 0)  /* don't close the connection */
171 |       continue;
172 |     sys_vm_leave(L);
173 |     {
174 |       DWORD l = (DWORD) sb.size;
175 |       nw = ecb->WriteClient(ecb->ConnID, sb.ptr.w, &l, 0)
176 |        ? (int) l : -1;
177 |     }
178 |     sys_vm_enter(L);
179 |     if (nw == -1) {
180 |       if (n > 0) break;
181 |       return sys_seterror(L, 0);
182 |     }
183 |     n += nw;
184 |     sys_buffer_read_next(&sb, nw);
185 |     if ((size_t) nw < sb.size) break;
186 |   }
187 |   lua_pushboolean(L, (i > narg));
188 |   lua_pushinteger(L, n);
189 |   return 2;
190 | }
191 | 
192 | /*
193 |  * Arguments: ecb_udata, name (string), value (string | number)
194 |  */
195 | static int
196 | ecb_header (lua_State *L)
197 | {
198 |   LPEXTENSION_CONTROL_BLOCK ecb = lua_unboxpointer(L, 1, ECB_TYPENAME);
199 |   const int headers = (ecb->dwHttpStatusCode & ~ECB_STATUS_MASK);
200 |   const char *name = luaL_checkstring(L, 2);
201 | 
202 |   if (headers == ECB_STATUS_HEADERS)
203 |     luaL_error(L, "Headers already sent");
204 | 
205 |   if (!strcmp(name, "Status")) {
206 |     ecb->dwHttpStatusCode &= ~ECB_STATUS_MASK;
207 |     ecb->dwHttpStatusCode |= lua_tointeger(L, 3) & ECB_STATUS_MASK;
208 |     return 0;
209 |   }
210 |   luaL_checktype(L, 3, LUA_TSTRING);
211 | 
212 |   lua_pushliteral(L, ": ");
213 |   lua_insert(L, 3);
214 |   lua_pushliteral(L, "\r\n");
215 | 
216 |   if (headers) {
217 |     lua_rawgetp(L, LUA_REGISTRYINDEX, ecb);
218 |   } else {
219 |     lua_pushliteral(L, "");
220 |   }
221 | 
222 |   lua_concat(L, 5);
223 |   lua_rawsetp(L, LUA_REGISTRYINDEX, ecb);
224 | 
225 |   ecb->dwHttpStatusCode |= ECB_STATUS_HEADERS | ECB_STATUS_HEADERS_SEND;
226 |   return 0;
227 | }
228 | 
229 | /*
230 |  * Arguments: ecb_udata
231 |  */
232 | static int
233 | ecb_req_pending (lua_State *L)
234 | {
235 |   LPEXTENSION_CONTROL_BLOCK ecb = lua_unboxpointer(L, 1, ECB_TYPENAME);
236 | 
237 |   ecb->dwHttpStatusCode |= ECB_STATUS_PENDING;
238 |   return 0;
239 | }
240 | 
241 | /*
242 |  * Arguments: ecb_udata
243 |  * Returns: [ecb_udata]
244 |  */
245 | static int
246 | ecb_req_done (lua_State *L)
247 | {
248 |   LPEXTENSION_CONTROL_BLOCK ecb = lua_unboxpointer(L, 1, ECB_TYPENAME);
249 |   DWORD res = HSE_STATUS_SUCCESS;
250 | 
251 |   if (ecb->dwHttpStatusCode & ~ECB_STATUS_MASK) {
252 |     ecb->dwHttpStatusCode &= ECB_STATUS_MASK;
253 | 
254 |     lua_pushnil(L);
255 |     lua_rawsetp(L, LUA_REGISTRYINDEX, ecb);
256 |   }
257 | 
258 |   if (ecb->ServerSupportFunction(ecb->ConnID, HSE_REQ_DONE_WITH_SESSION,
259 |    &res, NULL,NULL)) {
260 |     lua_settop(L, 1);
261 |     return 1;
262 |   }
263 |   return sys_seterror(L, 0);
264 | }
265 | 
266 | 
267 | static luaL_Reg ecb_meth[] = {
268 |   {"handle",		ecb_handle},
269 |   {"getvar",		ecb_getvar},
270 |   {"data",		ecb_data},
271 |   {"read",		ecb_read},
272 |   {"write",		ecb_write},
273 |   {"header",		ecb_header},
274 |   {"req_pending",	ecb_req_pending},
275 |   {"req_done",		ecb_req_done},
276 |   {SYS_BUFIO_TAG,	NULL},  /* can operate with buffers */
277 |   {NULL, NULL}
278 | };
279 | 


--------------------------------------------------------------------------------
/src/mem/membuf.c:
--------------------------------------------------------------------------------
  1 | /* Lua System: Memory Buffers: Streams */
  2 | 
  3 | #define MEM_BUFLINE	256
  4 | 
  5 | 
  6 | /*
  7 |  * Arguments: membuf_udata, ...
  8 |  */
  9 | static int
 10 | stream_write (lua_State *L, struct membuf *mb)
 11 | {
 12 |   const int bufio = (mb->flags & MEM_OSTREAM_BUFIO);
 13 |   int res;
 14 | 
 15 |   lua_getfenv(L, 1);
 16 |   lua_rawgeti(L, -1, MEM_OUTPUT);  /* stream object */
 17 |   lua_getfield(L, -1, "write");
 18 |   lua_insert(L, -2);
 19 | 
 20 |   if (bufio)
 21 |     lua_pushvalue(L, 1);
 22 |   else
 23 |     lua_pushlstring(L, mb->data, mb->offset);
 24 |   lua_call(L, 2, 1);
 25 | 
 26 |   res = lua_toboolean(L, -1);
 27 |   lua_pop(L, 2);  /* pop environ. and result */
 28 | 
 29 |   if (res && !bufio) mb->offset = 0;
 30 |   return res;
 31 | }
 32 | 
 33 | static int
 34 | membuf_addlstring (lua_State *L, struct membuf *mb, const char *s,
 35 |                    const size_t size)
 36 | {
 37 |   int offset = mb->offset;
 38 |   size_t len = mb->len;
 39 |   const size_t newlen = offset + size;
 40 | 
 41 |   if (newlen >= len) {
 42 |     const unsigned int flags = mb->flags;
 43 |     void *p;
 44 | 
 45 |     if ((flags & MEM_OSTREAM) && stream_write(L, mb)) {
 46 |       offset = mb->offset;
 47 |       len = mb->len;
 48 |       if (size < len - offset)
 49 |         goto end;
 50 |     }
 51 |     while ((len *= 2) <= newlen)
 52 |       continue;
 53 |     if (!(flags & MEM_ALLOC) || !(p = realloc(mb->data, len)))
 54 |       return 0;
 55 |     mb->len = (int) len;
 56 |     mb->data = p;
 57 |   }
 58 |  end:
 59 |   if (s != NULL) {
 60 |     memcpy(mb->data + offset, s, size);
 61 |     mb->offset = offset + (int) size;
 62 |   }
 63 |   return 1;
 64 | }
 65 | 
 66 | /*
 67 |  * Arguments: membuf_udata, string ...
 68 |  * Returns: [boolean]
 69 |  */
 70 | static int
 71 | membuf_write (lua_State *L)
 72 | {
 73 |   struct membuf *mb = checkudata(L, 1, MEM_TYPENAME);
 74 |   int narg, i;
 75 | 
 76 |   narg = lua_gettop(L);
 77 |   for (i = 2; i <= narg; ++i) {
 78 |     size_t len = lua_rawlen(L, i);
 79 |     if (len && !membuf_addlstring(L, mb, lua_tostring(L, i), len))
 80 |       return 0;
 81 |   }
 82 |   lua_pushboolean(L, 1);
 83 |   return 1;
 84 | }
 85 | 
 86 | /*
 87 |  * Arguments: membuf_udata, string ...
 88 |  * Returns: [boolean]
 89 |  */
 90 | static int
 91 | membuf_writeln (lua_State *L)
 92 | {
 93 |   lua_pushliteral(L, "\n");
 94 |   return membuf_write(L);
 95 | }
 96 | 
 97 | /*
 98 |  * Arguments: membuf_udata, [num_bytes (number), offset (number)]
 99 |  * Returns: string
100 |  */
101 | static int
102 | membuf_tostring (lua_State *L)
103 | {
104 |   struct membuf *mb = checkudata(L, 1, MEM_TYPENAME);
105 |   const int len = luaL_optint(L, 2, mb->offset);
106 |   const int off = (int) lua_tointeger(L, 3);
107 | 
108 |   lua_pushlstring(L, mb->data + off, len);
109 |   return 1;
110 | }
111 | 
112 | /*
113 |  * Arguments: membuf_udata, [offset (number)]
114 |  * Returns: membuf_udata | offset (number)
115 |  */
116 | static int
117 | membuf_seek (lua_State *L)
118 | {
119 |   struct membuf *mb = checkudata(L, 1, MEM_TYPENAME);
120 | 
121 |   if (lua_gettop(L) > 1) {
122 |     mb->offset = (int) lua_tointeger(L, 2);
123 |     lua_settop(L, 1);
124 |   } else
125 |     lua_pushinteger(L, mb->offset);
126 |   return 1;
127 | }
128 | 
129 | 
130 | /*
131 |  * Arguments: membuf_udata, stream
132 |  */
133 | static int
134 | membuf_assosiate (lua_State *L, int type)
135 | {
136 |   struct membuf *mb = checkudata(L, 1, MEM_TYPENAME);
137 |   const int idx = (type == MEM_ISTREAM) ? MEM_INPUT : MEM_OUTPUT;
138 | 
139 |   lua_settop(L, 2);
140 |   if (lua_isnoneornil(L, 2))
141 |     mb->flags &= ~type;
142 |   else {
143 |     mb->flags |= type;
144 | 
145 |     lua_getfield(L, -1, SYS_BUFIO_TAG);
146 |     if (!lua_isnil(L, -1)) {
147 |       mb->flags |= (type == MEM_ISTREAM)
148 |        ? MEM_ISTREAM_BUFIO : MEM_OSTREAM_BUFIO;
149 |     }
150 |     lua_pop(L, 1);
151 |   }
152 | 
153 |   lua_getfenv(L, 1);
154 |   if (!lua_istable(L, -1)) {
155 |     lua_pop(L, 1);
156 |     lua_newtable(L);
157 |     lua_pushvalue(L, -1);
158 |     lua_setfenv(L, 1);
159 |   }
160 |   lua_pushvalue(L, 2);
161 |   lua_rawseti(L, -2, idx);
162 |   return 0;
163 | }
164 | 
165 | /*
166 |  * Arguments: membuf_udata, consumer_stream
167 |  */
168 | static int
169 | membuf_output (lua_State *L)
170 | {
171 |   return membuf_assosiate(L, MEM_OSTREAM);
172 | }
173 | 
174 | /*
175 |  * Arguments: membuf_udata, producer_stream
176 |  */
177 | static int
178 | membuf_input (lua_State *L)
179 | {
180 |   return membuf_assosiate(L, MEM_ISTREAM);
181 | }
182 | 
183 | 
184 | /*
185 |  * Arguments: membuf_udata, ..., stream, function
186 |  * Returns: [boolean]
187 |  */
188 | static void
189 | stream_read (lua_State *L, size_t l, const int bufio)
190 | {
191 |   int narg = 1;
192 | 
193 |   lua_pushvalue(L, -2);
194 |   lua_pushvalue(L, -2);
195 | 
196 |   if (bufio) {
197 |     lua_pushvalue(L, 1);
198 |     ++narg;
199 |   }
200 |   if (l != (size_t) -1) {
201 |     lua_pushinteger(L, l);
202 |     ++narg;
203 |   }
204 |   lua_call(L, narg, 1);
205 | }
206 | 
207 | static int
208 | read_bytes (lua_State *L, struct membuf *mb, size_t l)
209 | {
210 |   int n = mb->offset;
211 | 
212 |   if (!n && (mb->flags & MEM_ISTREAM)) {
213 |     stream_read(L, l, (mb->flags & MEM_ISTREAM_BUFIO));
214 |     return 1;
215 |   }
216 | 
217 |   if (l > (size_t) n) l = n;
218 |   if (l) {
219 |     char *p = mb->data;  /* avoid warning */
220 |     lua_pushlstring(L, p, l);
221 |     n -= (int) l;
222 |     mb->offset = n;
223 |     if (n) memmove(p, p + l, n);
224 |   } else
225 |     lua_pushnil(L);
226 |   return 1;
227 | }
228 | 
229 | static int
230 | read_line (lua_State *L, struct membuf *mb)
231 | {
232 |   const char *nl, *s = mb->data;
233 |   size_t l, n = mb->offset;
234 | 
235 |   if (n && (nl = memchr(s, '\n', n))) {
236 |     char *p = mb->data;  /* avoid warning */
237 |     l = nl - p;
238 |     lua_pushlstring(L, p, l);
239 |     n -= l + 1;
240 |     mb->offset = (int) n;
241 |     if (n) memmove(p, nl + 1, n);
242 |     return 1;
243 |   }
244 |   if (!(mb->flags & MEM_ISTREAM)) {
245 |     n = 1;
246 |     goto end;
247 |   }
248 |   for (; ; ) {
249 |     stream_read(L, MEM_BUFLINE, 0);
250 |     s = lua_tolstring(L, -1, &n);
251 |     if (!n) {
252 |       n = 1;
253 |       break;
254 |     }
255 |     if (*s == '\n')
256 |       break;
257 |     nl = memchr(s + 1, '\n', n - 1);
258 |     l = !nl ? n : (size_t) (nl - s);
259 |     if (!membuf_addlstring(L, mb, s, l))
260 |       return 0;
261 |     /* tail */
262 |     if (nl) {
263 |       n -= l;
264 |       s = nl;
265 |       break;
266 |     }
267 |     lua_pop(L, 1);
268 |   }
269 |  end:
270 |   l = mb->offset;
271 |   if (l != 0)
272 |     lua_pushlstring(L, mb->data, l);
273 |   else
274 |     lua_pushnil(L);
275 |   mb->offset = 0;
276 |   return (!--n) ? 1 : membuf_addlstring(L, mb, s + 1, n);
277 | }
278 | 
279 | /*
280 |  * Arguments: membuf_udata, [count (number) | mode (string: "*l", "*a")]
281 |  * Returns: [string | number]
282 |  */
283 | static int
284 | membuf_read (lua_State *L)
285 | {
286 |   struct membuf *mb = checkudata(L, 1, MEM_TYPENAME);
287 | 
288 |   lua_settop(L, 2);
289 |   if (mb->flags & MEM_ISTREAM) {
290 |     lua_getfenv(L, 1);
291 |     lua_rawgeti(L, -1, MEM_INPUT);  /* stream object */
292 |     lua_getfield(L, -1, "read");
293 |     lua_insert(L, -2);
294 |   }
295 | 
296 |   if (lua_type(L, 2) == LUA_TNUMBER)
297 |     read_bytes(L, mb, lua_tointeger(L, 2));
298 |   else {
299 |     const char *s = luaL_optstring(L, 2, "*a");
300 | 
301 |     switch (s[1]) {
302 |     case 'l':
303 |       return read_line(L, mb);
304 |     case 'a':
305 |       read_bytes(L, mb, ~((size_t) 0));
306 |       break;
307 |     default:
308 |       luaL_argerror(L, 2, "invalid option");
309 |     }
310 |   }
311 |   return 1;
312 | }
313 | 
314 | /*
315 |  * Arguments: membuf_udata, [close (boolean)]
316 |  * Returns: [membuf_udata]
317 |  */
318 | static int
319 | membuf_flush (lua_State *L)
320 | {
321 |   struct membuf *mb = checkudata(L, 1, MEM_TYPENAME);
322 |   const int is_close = lua_toboolean(L, 2);
323 |   int res = 1;
324 | 
325 |   if (mb->flags & MEM_OSTREAM) {
326 |     res = stream_write(L, mb);
327 |     if (is_close) mem_free(L);
328 |   }
329 |   lua_settop(L, 1);
330 |   return res;
331 | }
332 | 
333 | /*
334 |  * Arguments: membuf_udata
335 |  * Returns: [membuf_udata]
336 |  */
337 | static int
338 | membuf_close (lua_State *L)
339 | {
340 |   lua_pushboolean(L, 1);
341 |   return membuf_flush(L);
342 | }
343 | 
344 | 


--------------------------------------------------------------------------------
/src/event/win32iocr.c:
--------------------------------------------------------------------------------
  1 | /* Win32 NT I/O Completion Routines */
  2 | 
  3 | #define IOCR_NENTRY	48  /* number of events in head to trigger IOCR */
  4 | 
  5 | #define win32iocr_apc_put(apc_fn,evq,param) \
  6 |     (QueueUserAPC((apc_fn), (evq)->iocr.h, (param)) ? 0 : -1)
  7 | 
  8 | #define win32iocr_signal(evq) \
  9 |     win32iocr_apc_put(win32iocr_apc_signal, (evq), 0)
 10 | 
 11 | #define win32iocr_list_put(ov_list,ov) \
 12 |   do { \
 13 |     (ov)->o.ov_next = (ov_list).ov_head; \
 14 |     if (!(ov_list).ov_head) \
 15 |       (ov_list).ov_tail = (ov); \
 16 |     (ov_list).ov_head = (ov); \
 17 |   } while (0)
 18 | 
 19 | #define win32iocr_list_move(ov_list,ov_dest) \
 20 |   do { \
 21 |     (ov_list).ov_tail->o.ov_next = (ov_dest); \
 22 |     (ov_dest) = (ov_list).ov_head; \
 23 |     (ov_list).ov_head = (ov_list).ov_tail = NULL; \
 24 |   } while (0)
 25 | 
 26 | 
 27 | /* Global Thread Local Storage Index */
 28 | static DWORD g_IOCR_TLSIndex = TLS_OUT_OF_INDEXES;
 29 | 
 30 | 
 31 | static struct win32overlapped *
 32 | win32iocr_overlapped_alloc (struct event_queue *evq)
 33 | {
 34 |   struct win32overlapped *ov;
 35 |   const int n = evq->ov_buf_nevents;
 36 |   const int buf_idx = evq->ov_buf_index;
 37 |   const int nmax = (1 << (buf_idx + WIN32OV_BUF_IDX));
 38 | 
 39 |   ov = evq->ov_buffers[buf_idx];
 40 |   if (ov) {
 41 |     ov += n;
 42 |     if (++evq->ov_buf_nevents >= nmax) {
 43 |       evq->ov_buf_nevents = 0;
 44 |       evq->ov_buf_index++;
 45 |     }
 46 |   } else {
 47 |     if (buf_idx >= WIN32OV_BUF_SIZE
 48 |      || !(ov = malloc(nmax * sizeof(struct win32overlapped))))
 49 |       return NULL;
 50 | 
 51 |     evq->ov_buffers[buf_idx] = ov;
 52 |     evq->ov_buf_nevents = 1;
 53 |   }
 54 |   return ov;
 55 | }
 56 | 
 57 | static struct win32overlapped *
 58 | win32iocr_overlapped_new (struct event_queue *evq)
 59 | {
 60 |   struct win32overlapped *ov = evq->ov_free;
 61 | 
 62 |   if (ov)
 63 |     evq->ov_free = ov->o.ov_next;
 64 |   else
 65 |     ov = win32iocr_overlapped_alloc(evq);
 66 | 
 67 |   if (ov) memset(ov, 0, sizeof(struct win32overlapped));
 68 |   return ov;
 69 | }
 70 | 
 71 | static void
 72 | win32iocr_overlapped_del (struct event_queue *evq, struct win32overlapped *ov)
 73 | {
 74 |   ov->o.ov_next = evq->ov_free;
 75 |   evq->ov_free = ov;
 76 | }
 77 | 
 78 | 
 79 | static void WINAPI
 80 | win32iocr_apc_signal (ULONG_PTR param)
 81 | {
 82 |   (void) param;
 83 | }
 84 | 
 85 | static void WINAPI
 86 | win32iocr_apc_cancel (ULONG_PTR fd)
 87 | {
 88 |   CancelIo((HANDLE) fd);
 89 | }
 90 | 
 91 | static void WINAPI
 92 | win32iocr_completion (DWORD err, DWORD n, struct win32overlapped *ov,
 93 |                       DWORD flags)
 94 | {
 95 |   struct win32iocr_thread *iocr_thr = TlsGetValue(g_IOCR_TLSIndex);
 96 | 
 97 |   (void) err;  /* OVERLAPPED.Internal */
 98 |   (void) n;
 99 |   (void) flags;
100 | 
101 |   win32iocr_list_put(iocr_thr->ov_list, ov);
102 | }
103 | 
104 | static void
105 | win32iocr_set_handle (struct win32iocr_thread *iocr_thr,
106 |                       struct win32overlapped *ov)
107 | {
108 |   static WSABUF buf = {0, 0};
109 | 
110 |   struct event *ev = ov->e.ev;
111 |   unsigned int rw_flags;
112 |   sd_t sd;
113 | 
114 |   if (!ev) goto ready;
115 | 
116 |   sd = (sd_t) ev->fd;
117 |   rw_flags = ov->ih.rw_flags;
118 |   ov->ih.rw_flags = 0;
119 | 
120 |   if (rw_flags == EVENT_READ) {
121 |     DWORD flags = 0;
122 | 
123 |     if (!WSARecv(sd, &buf, 1, NULL, &flags, (OVERLAPPED *) ov,
124 |      (LPWSAOVERLAPPED_COMPLETION_ROUTINE) win32iocr_completion)
125 |      || WSAGetLastError() == WSA_IO_PENDING)
126 |       return;
127 |   } else {
128 |     if (!WSASend(sd, &buf, 1, NULL, 0, (OVERLAPPED *) ov,
129 |      (LPWSAOVERLAPPED_COMPLETION_ROUTINE) win32iocr_completion)
130 |      || WSAGetLastError() == WSA_IO_PENDING)
131 |       return;
132 |   }
133 |   ov->il.err = WSAGetLastError();
134 |  ready:
135 |   win32iocr_list_put(iocr_thr->ov_list, ov);
136 | }
137 | 
138 | static void
139 | win32iocr_submit (struct event_queue *evq)
140 | {
141 |   CRITICAL_SECTION *head_cs = &evq->head.sig_cs;
142 | 
143 |   EnterCriticalSection(head_cs);
144 |   win32iocr_list_move(evq->iocr.ov_list, evq->iocr.iocr_thr->ov_set);
145 |   LeaveCriticalSection(head_cs);
146 | 
147 |   (void) win32iocr_signal(evq);
148 | }
149 | 
150 | static DWORD WINAPI
151 | win32iocr_wait (struct event_queue *evq)
152 | {
153 |   CRITICAL_SECTION *head_cs = &evq->head.sig_cs;
154 |   HANDLE head_signal = evq->head.signal;
155 |   struct win32iocr_thread iocr_thr;
156 | 
157 |   memset(&iocr_thr, 0, sizeof(struct win32iocr_thread));
158 |   TlsSetValue(g_IOCR_TLSIndex, &iocr_thr);
159 | 
160 |   evq->iocr.iocr_thr = &iocr_thr;
161 |   SetEvent(evq->ack_event);
162 | 
163 |   while (!iocr_thr.stop) {
164 |     struct win32overlapped *ov_set = NULL;
165 | 
166 |     EnterCriticalSection(head_cs);
167 |     if (iocr_thr.ov_list.ov_head) {
168 |       win32iocr_list_move(iocr_thr.ov_list, evq->iocr.ov_ready);
169 |       SetEvent(head_signal);
170 |     }
171 |     ov_set = iocr_thr.ov_set;
172 |     iocr_thr.ov_set = NULL;
173 |     LeaveCriticalSection(head_cs);
174 | 
175 |     /* handle read/write requests */
176 |     while (ov_set) {
177 |       win32iocr_set_handle(&iocr_thr, ov_set);
178 |       ov_set = ov_set->o.ov_next;
179 |     }
180 | 
181 |     if (!iocr_thr.ov_list.ov_head)
182 |       SleepEx(INFINITE, TRUE);
183 |   }
184 |   return 0;
185 | }
186 | 
187 | static void
188 | win32iocr_init (struct event_queue *evq)
189 | {
190 |   HANDLE hThr;
191 |   DWORD id;
192 | 
193 |   if (g_IOCR_TLSIndex == TLS_OUT_OF_INDEXES) {
194 |     g_IOCR_TLSIndex = TlsAlloc();
195 |     if (g_IOCR_TLSIndex == TLS_OUT_OF_INDEXES)
196 |       return;
197 |   }
198 | 
199 |   hThr = CreateThread(NULL, 4096,
200 |    (LPTHREAD_START_ROUTINE) win32iocr_wait, evq, 0, &id);
201 |   if (hThr) {
202 |     SetThreadPriority(hThr, THREAD_PRIORITY_ABOVE_NORMAL);
203 |     WaitForSingleObject(evq->ack_event, INFINITE);
204 |     evq->iocr.h = hThr;
205 |   }
206 | }
207 | 
208 | static void
209 | win32iocr_done (struct event_queue *evq)
210 | {
211 |   struct win32overlapped **ovp = evq->ov_buffers;
212 |   unsigned int i;
213 | 
214 |   if (evq->iocr.h) {
215 |     evq->iocr.iocr_thr->stop = 1;
216 |     if (!win32iocr_signal(evq))
217 |       WaitForSingleObject(evq->iocr.h, INFINITE);
218 |     CloseHandle(evq->iocr.h);
219 |   }
220 | 
221 |   for (i = 0; *ovp && i < WIN32OV_BUF_SIZE; ++i) {
222 |     free(*ovp++);
223 |   }
224 | }
225 | 
226 | static struct event *
227 | win32iocr_process (struct event_queue *evq, struct win32overlapped *ov,
228 |                    struct event *ev_ready, const msec_t now)
229 | {
230 |   struct win32overlapped *ov_next;
231 | 
232 |   for (; ov; ov = ov_next) {
233 |     struct event *ev;
234 |     const DWORD err = ov->il.err;
235 |     const int cancelled = (err == STATUS_CANCELLED) || !ov->e.ev;
236 | 
237 |     ov_next = ov->o.ov_next;
238 |     win32iocr_overlapped_del(evq, ov);
239 |     if (cancelled)
240 |       continue;
241 | 
242 |     ev = ov->e.ev;
243 |     ev->w.ov = NULL;
244 |     ev->flags &= ~EVENT_AIO_PENDING;  /* have to set AIO request */
245 |     ev->flags |= err ? EVENT_EOF_RES
246 |      : ((ev->flags & EVENT_READ) ? EVENT_READ_RES : EVENT_WRITE_RES);
247 | 
248 |     if (!(ev->flags & EVENT_ACTIVE)) {
249 |       ev->flags |= EVENT_ACTIVE;
250 |       if (ev->flags & EVENT_ONESHOT)
251 |         evq_del(ev, 1);
252 |       else if (ev->tq && !(ev->flags & EVENT_TIMEOUT_MANUAL))
253 |         timeout_reset(ev, now);
254 | 
255 |       ev->next_ready = ev_ready;
256 |       ev_ready = ev;
257 |     }
258 |   }
259 |   return ev_ready;
260 | }
261 | 
262 | static void
263 | win32iocr_cancel (struct event_queue *evq, struct event *ev)
264 | {
265 |   struct win32overlapped *ov = ev->w.ov;
266 | 
267 |   ov->e.ev = NULL;
268 |   ev->w.ov = NULL;
269 |   ev->flags &= ~EVENT_AIO_PENDING;
270 | 
271 |   if (pCancelIoEx)
272 |     pCancelIoEx(ev->fd, (OVERLAPPED *) ov);
273 |   else
274 |     win32iocr_apc_put(win32iocr_apc_cancel, evq, (ULONG_PTR) ev->fd);
275 | }
276 | 
277 | static int
278 | win32iocr_set (struct event *ev, const unsigned int rw_flags)
279 | {
280 |   struct event_queue *evq = ev->wth->evq;
281 |   struct win32overlapped *ov = win32iocr_overlapped_new(evq);
282 | 
283 |   if (!ov) return -1;
284 | 
285 |   ov->ih.rw_flags = rw_flags & (EVENT_READ | EVENT_WRITE);
286 |   ov->e.ev = ev;
287 |   ev->w.ov = ov;
288 |   ev->flags |= EVENT_AIO_PENDING;  /* AIO request is installed */
289 |   win32iocr_list_put(evq->iocr.ov_list, ov);
290 |   return 0;
291 | }
292 | 
293 | 


--------------------------------------------------------------------------------
/src/thread/thread_sync.c:
--------------------------------------------------------------------------------
  1 | /* Lua System: Threading: Synchronization */
  2 | 
  3 | #if !defined(_WIN32) || (defined(SRWLOCK_INIT) && defined(WIN32_VISTA))
  4 | #define USE_PTHREAD_SYNC
  5 | #endif
  6 | 
  7 | 
  8 | /* Critical Section */
  9 | #if !defined(_WIN32)
 10 | typedef pthread_mutex_t		thread_critsect_t;
 11 | #elif defined(USE_PTHREAD_SYNC)
 12 | typedef SRWLOCK			thread_critsect_t;
 13 | #else
 14 | typedef CRITICAL_SECTION	thread_critsect_t;
 15 | #endif
 16 | 
 17 | 
 18 | static int
 19 | thread_critsect_new (thread_critsect_t *tcs)
 20 | {
 21 | #if !defined(_WIN32)
 22 |   const int res = pthread_mutex_init(tcs, NULL);
 23 |   if (res) errno = res;
 24 |   return res;
 25 | #elif defined(USE_PTHREAD_SYNC)
 26 |   InitializeSRWLock(tcs);
 27 |   return 0;
 28 | #else
 29 |   return !InitCriticalSection(tcs);
 30 | #endif
 31 | }
 32 | 
 33 | #if !defined(_WIN32)
 34 | #define thread_critsect_del(tcs)	pthread_mutex_destroy(tcs)
 35 | #elif defined(USE_PTHREAD_SYNC)
 36 | #define thread_critsect_del(tcs)	((void) (tcs))
 37 | #else
 38 | #define thread_critsect_del(tcs)	DeleteCriticalSection(tcs)
 39 | #endif
 40 | 
 41 | #if !defined(_WIN32)
 42 | #define thread_critsect_enter(tcs)	pthread_mutex_lock(tcs)
 43 | #elif defined(USE_PTHREAD_SYNC)
 44 | #define thread_critsect_enter(tcs)	(AcquireSRWLockExclusive(tcs), 0)
 45 | #else
 46 | #define thread_critsect_enter(tcs)	EnterCriticalSection(tcs)
 47 | #endif
 48 | 
 49 | #if !defined(_WIN32)
 50 | #define thread_critsect_leave(tcs)	pthread_mutex_unlock(tcs)
 51 | #elif defined(USE_PTHREAD_SYNC)
 52 | #define thread_critsect_leave(tcs)	(ReleaseSRWLockExclusive(tcs), 0)
 53 | #else
 54 | #define thread_critsect_leave(tcs)	LeaveCriticalSection(tcs)
 55 | #endif
 56 | 
 57 | 
 58 | /* Condition */
 59 | #if !defined(_WIN32)
 60 | typedef pthread_cond_t		thread_cond_t;
 61 | #elif defined(USE_PTHREAD_SYNC)
 62 | typedef CONDITION_VARIABLE	thread_cond_t;
 63 | #else
 64 | typedef HANDLE			thread_cond_t;
 65 | #endif
 66 | 
 67 | 
 68 | static int
 69 | thread_cond_new (thread_cond_t *tcond)
 70 | {
 71 | #if !defined(_WIN32)
 72 |   const int res = pthread_cond_init(tcond, NULL);
 73 |   if (res) errno = res;
 74 |   return res;
 75 | #elif defined(USE_PTHREAD_SYNC)
 76 |   InitializeConditionVariable(tcond);
 77 |   return 0;
 78 | #else
 79 |   *tcond = CreateEvent(NULL, FALSE, FALSE, NULL);  /* auto-reset */
 80 |   return (*tcond != NULL) ? 0 : -1;
 81 | #endif
 82 | }
 83 | 
 84 | #if !defined(_WIN32)
 85 | #define thread_cond_del(tcond)		pthread_cond_destroy(tcond)
 86 | #elif defined(USE_PTHREAD_SYNC)
 87 | #define thread_cond_del(tcond)		((void) (tcond), 0)
 88 | #else
 89 | #define thread_cond_del(tcond)		CloseHandle(*tcond)
 90 | #endif
 91 | 
 92 | #if !defined(_WIN32)
 93 | #define thread_cond_signal(tcond)	pthread_cond_signal(tcond)
 94 | #elif defined(USE_PTHREAD_SYNC)
 95 | #define thread_cond_signal(tcond)	(WakeConditionVariable(tcond), 0)
 96 | #else
 97 | #define thread_cond_signal(tcond)	!SetEvent(*tcond)
 98 | #endif
 99 | 
100 | #if defined(USE_PTHREAD_SYNC)
101 | 
102 | #if !defined(_WIN32)
103 | 
104 | static void
105 | thread_timespec (struct timespec *ts, const msec_t timeout)
106 | {
107 |   struct timeval tv;
108 | 
109 |   gettimeofday(&tv, NULL);
110 |   tv.tv_sec += timeout / 1000;
111 |   tv.tv_usec += (timeout % 1000) * 1000;
112 |   if (tv.tv_usec >= 1000000) {
113 |     tv.tv_sec++;
114 |     tv.tv_usec -= 1000000;
115 |   }
116 | 
117 |   ts->tv_sec = tv.tv_sec;
118 |   ts->tv_nsec = tv.tv_usec * 1000;
119 | }
120 | 
121 | #endif
122 | 
123 | static int
124 | thread_cond_wait_nolock (thread_cond_t *condp, thread_critsect_t *csp,
125 |                          const msec_t timeout)
126 | {
127 | #if !defined(_WIN32)
128 |   int res;
129 | 
130 |   if (timeout == TIMEOUT_INFINITE) {
131 |     res = pthread_cond_wait(condp, csp);
132 |   } else {
133 |     struct timespec ts;
134 |     thread_timespec(&ts, timeout);
135 | 
136 |     res = pthread_cond_timedwait(condp, csp, &ts);
137 |   }
138 | 
139 |   if (res) {
140 |     if (res == ETIMEDOUT)
141 |       return 1;
142 |     errno = res;
143 |     return -1;
144 |   }
145 | #else
146 |   if (!SleepConditionVariableSRW(condp, csp, timeout, 0)) {
147 |     return (GetLastError() == ERROR_TIMEOUT) ? 1 : -1;
148 |   }
149 | #endif
150 |   return 0;
151 | }
152 | 
153 | static int
154 | thread_cond_wait_value (thread_cond_t *condp, thread_critsect_t *csp,
155 |                         volatile unsigned int *signalled,
156 |                         const unsigned int test_value,
157 |                         const int reset, const msec_t timeout)
158 | {
159 |   int res = 0;
160 | 
161 | #if !defined(_WIN32)
162 |   if (timeout == TIMEOUT_INFINITE) {
163 |     thread_critsect_enter(csp);
164 |     while (*signalled != test_value && !res)
165 |       res = pthread_cond_wait(condp, csp);
166 |   } else {
167 |     struct timespec ts;
168 |     thread_timespec(&ts, timeout);
169 | 
170 |     thread_critsect_enter(csp);
171 |     while (*signalled != test_value && !res)
172 |       res = pthread_cond_timedwait(condp, csp, &ts);
173 |   }
174 |   if (!res && reset) *signalled = 0;
175 |   thread_critsect_leave(csp);
176 | 
177 |   if (res) {
178 |     if (res == ETIMEDOUT)
179 |       return 1;
180 |     errno = res;
181 |     return -1;
182 |   }
183 | #else
184 |   thread_critsect_enter(csp);
185 |   while (*signalled != test_value && !res)
186 |     res = !SleepConditionVariableSRW(condp, csp, timeout, 0);
187 |   if (!res && reset) *signalled = 0;
188 |   thread_critsect_leave(csp);
189 | 
190 |   if (res) return (GetLastError() == ERROR_TIMEOUT) ? 1 : -1;
191 | #endif
192 |   return 0;
193 | }
194 | 
195 | #else  /* Win32 */
196 | 
197 | static int
198 | thread_cond_wait_nolock (thread_cond_t *condp, thread_critsect_t *csp,
199 |                          const msec_t timeout)
200 | {
201 |   int res;
202 | 
203 |   thread_critsect_leave(csp);
204 |   res = WaitForSingleObject(*condp, timeout);
205 |   thread_critsect_enter(csp);
206 | 
207 |   return (res == WAIT_OBJECT_0) ? 0
208 |    : (res == WAIT_TIMEOUT) ? 1 : -1;
209 | }
210 | 
211 | #if 0
212 | static int
213 | thread_cond_wait_value (thread_cond_t *condp, thread_critsect_t *csp,
214 |                         volatile unsigned int *signalled,
215 |                         const unsigned int test_value,
216 |                         const int reset, const msec_t timeout)
217 | {
218 |   int res = WAIT_OBJECT_0;
219 | 
220 |   (void) csp;
221 | 
222 |   while (*signalled != test_value && res == WAIT_OBJECT_0) {
223 |     res = WaitForSingleObject(*condp, timeout);
224 |   }
225 |   if (res == WAIT_OBJECT_0) {
226 |     if (reset) *signalled = 0;
227 |     return 0;
228 |   }
229 |   return (res == WAIT_TIMEOUT) ? 1 : -1;
230 | }
231 | #endif
232 | 
233 | #endif  /* !defined(USE_PTHREAD_SYNC) */
234 | 
235 | #ifdef _WIN32
236 | 
237 | static int
238 | thread_handle_wait (HANDLE h, const msec_t timeout)
239 | {
240 |   const int res = WaitForSingleObject(h, timeout);
241 | 
242 |   return (res == WAIT_OBJECT_0) ? 0
243 |    : (res == WAIT_TIMEOUT) ? 1 : -1;
244 | }
245 | 
246 | #endif
247 | 
248 | 
249 | /* Event */
250 | typedef struct {
251 |   thread_cond_t cond;
252 | #if defined(USE_PTHREAD_SYNC)
253 |   thread_critsect_t cs;
254 | #define THREAD_EVENT_SIGNALLED	1
255 |   unsigned int volatile signalled;
256 | #endif
257 | } thread_event_t;
258 | 
259 | 
260 | static int
261 | thread_event_new (thread_event_t *tev)
262 | {
263 |   int res = thread_cond_new(&tev->cond);
264 | #if defined(USE_PTHREAD_SYNC)
265 |   if (!res) {
266 |     res = thread_critsect_new(&tev->cs);
267 |     if (res) {
268 |       (void) thread_cond_del(&tev->cond);
269 |     }
270 |   }
271 | #endif
272 |   return res;
273 | }
274 | 
275 | static int
276 | thread_event_del (thread_event_t *tev)
277 | {
278 | #if defined(USE_PTHREAD_SYNC)
279 |   thread_critsect_del(&tev->cs);
280 | #endif
281 |   return thread_cond_del(&tev->cond);
282 | }
283 | 
284 | static int
285 | thread_event_wait (thread_event_t *tev, struct sys_thread *td,
286 |                    const msec_t timeout)
287 | {
288 |   int res;
289 | 
290 |   sys_vm2_leave(td);
291 | #if defined(USE_PTHREAD_SYNC)
292 |   res = thread_cond_wait_value(&tev->cond, &tev->cs,
293 |    &tev->signalled, THREAD_EVENT_SIGNALLED, 1, timeout);
294 | #else
295 |   res = thread_handle_wait(tev->cond, timeout);
296 | #endif
297 |   sys_vm2_enter(td);
298 |   return res;
299 | }
300 | 
301 | #if defined(USE_PTHREAD_SYNC)
302 | #define thread_event_signal_nolock(tev) \
303 |     ((tev)->signalled = THREAD_EVENT_SIGNALLED, \
304 |      thread_cond_signal(&(tev)->cond))
305 | #else
306 | #define thread_event_signal_nolock(tev)		!SetEvent((tev)->cond)
307 | #endif
308 | 
309 | static int
310 | thread_event_signal (thread_event_t *tev)
311 | {
312 | #if defined(USE_PTHREAD_SYNC)
313 |   thread_critsect_t *csp = &tev->cs;
314 |   int res;
315 | 
316 |   thread_critsect_enter(csp);
317 |   tev->signalled = THREAD_EVENT_SIGNALLED;
318 |   res = thread_cond_signal(&tev->cond);
319 |   thread_critsect_leave(csp);
320 | 
321 |   if (res) errno = res;
322 |   return res;
323 | #else
324 |   return !SetEvent(tev->cond);
325 | #endif
326 | }
327 | 
328 | 


--------------------------------------------------------------------------------
/src/win32/win32_svc.c:
--------------------------------------------------------------------------------
  1 | /* Lua System: Win32 specifics: Service */
  2 | 
  3 | #define WSVC_TYPENAME	"sys.win32.service"
  4 | 
  5 | typedef DWORD (WINAPI *svc_func_t) (void *);
  6 | 
  7 | 
  8 | /*
  9 |  * Arguments: name (string), filename (string), [manual_start (boolean)]
 10 |  * Returns: [boolean]
 11 |  */
 12 | static int
 13 | svc_install (lua_State *L)
 14 | {
 15 |   const char *name = luaL_checkstring(L, 1);
 16 |   int pathlen = (int) lua_rawlen(L, 2);
 17 |   const int manual = lua_toboolean(L, 3);
 18 |   SC_HANDLE mngr = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
 19 |   SC_HANDLE sc = NULL;
 20 | 
 21 |   /* include space & term. '\0' */
 22 |   if (!pathlen || ++pathlen >= MAX_PATHNAME)
 23 |     return 0;
 24 | 
 25 |   if (mngr) {
 26 |     char path[MAX_PATHNAME*2];
 27 |     const int n = GetModuleFileNameA(NULL, path, MAX_PATHNAME);
 28 | 
 29 |     if (n) {
 30 |       path[n] = ' ';
 31 |       memcpy(path + n + 1, lua_tostring(L, 2), pathlen);
 32 |       sc = CreateServiceA(mngr, name, name,
 33 |        SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
 34 |        manual ? SERVICE_DEMAND_START : SERVICE_AUTO_START,
 35 |        SERVICE_ERROR_NORMAL,
 36 |        path, 0, 0, 0, 0, 0);
 37 |       if (sc) CloseServiceHandle(sc);
 38 |     }
 39 |     CloseServiceHandle(mngr);
 40 |   }
 41 |   if (sc) {
 42 |     lua_pushboolean(L, 1);
 43 |     return 1;
 44 |   }
 45 |   return sys_seterror(L, 0);
 46 | }
 47 | 
 48 | /*
 49 |  * Arguments: name (string)
 50 |  * Returns: [boolean]
 51 |  */
 52 | static int
 53 | svc_uninstall (lua_State *L)
 54 | {
 55 |   const char *name = lua_tostring(L, 1);
 56 |   SC_HANDLE mngr = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
 57 |   int res = 0;
 58 | 
 59 |   if (mngr) {
 60 |     SC_HANDLE sc = OpenServiceA(mngr, name, SERVICE_ALL_ACCESS | DELETE);
 61 | 
 62 |     if (sc) {
 63 |       SERVICE_STATUS status;
 64 |       int n = 2;  /* count of attempts to stop the service */
 65 | 
 66 |       do {
 67 |         if (QueryServiceStatus(sc, &status)
 68 |          && status.dwCurrentState == SERVICE_STOPPED) {
 69 |           res = DeleteService(sc);
 70 |           break;
 71 |         }
 72 |         ControlService(sc, SERVICE_CONTROL_STOP, &status);
 73 |         Sleep(1000);
 74 |       } while (--n);
 75 |       CloseServiceHandle(sc);
 76 |     }
 77 |     CloseServiceHandle(mngr);
 78 |   }
 79 |   if (res) {
 80 |     lua_pushboolean(L, 1);
 81 |     return 1;
 82 |   }
 83 |   return sys_seterror(L, 0);
 84 | }
 85 | 
 86 | 
 87 | /* Service global variables */
 88 | static struct {
 89 |   SERVICE_STATUS status;
 90 |   SERVICE_STATUS_HANDLE hstatus;
 91 |   HANDLE event;
 92 |   int accept_pause_cont;
 93 | } g_Service;
 94 | 
 95 | static void WINAPI
 96 | svc_controller (DWORD code)
 97 | {
 98 |   int is_accept = 0;
 99 | 
100 |   switch (code) {
101 |   case SERVICE_CONTROL_SHUTDOWN:
102 |   case SERVICE_CONTROL_STOP:
103 |     g_Service.status.dwCurrentState = SERVICE_STOP_PENDING;
104 |     is_accept = 1;
105 |     break;
106 |   case SERVICE_CONTROL_PAUSE:
107 |   case SERVICE_CONTROL_CONTINUE:
108 |     if (g_Service.accept_pause_cont) {
109 |       g_Service.status.dwCurrentState = (code == SERVICE_CONTROL_PAUSE)
110 |        ? SERVICE_PAUSE_PENDING : SERVICE_CONTINUE_PENDING;
111 |       is_accept = 1;
112 |     }
113 |     break;
114 |   }
115 | 
116 |   SetServiceStatus(g_Service.hstatus, &g_Service.status);
117 |   if (is_accept)
118 |     SetEvent(g_Service.event);
119 | }
120 | 
121 | static void WINAPI
122 | svc_main (DWORD argc, char *argv[])
123 | {
124 |   (void) argc;
125 | 
126 |   g_Service.hstatus = RegisterServiceCtrlHandlerA(argv[0], svc_controller);
127 |   if (g_Service.hstatus) {
128 |     /* initialise service status */
129 |     g_Service.status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
130 |     g_Service.status.dwControlsAccepted = SERVICE_ACCEPT_STOP
131 |      | SERVICE_ACCEPT_SHUTDOWN
132 |      | (g_Service.accept_pause_cont ? SERVICE_ACCEPT_PAUSE_CONTINUE : 0);
133 |     g_Service.status.dwWin32ExitCode = NO_ERROR;
134 |     g_Service.status.dwServiceSpecificExitCode = 0;
135 |     g_Service.status.dwCheckPoint = 0;
136 |     g_Service.status.dwWaitHint = 10000;
137 | 
138 |     g_Service.status.dwCurrentState = SERVICE_RUNNING;
139 |     SetServiceStatus(g_Service.hstatus, &g_Service.status);
140 |   }
141 |   SetEvent(g_Service.event);
142 | }
143 | 
144 | static DWORD WINAPI
145 | svc_start (char *svc_name)
146 | {
147 |   SERVICE_TABLE_ENTRY table[] = {{NULL, NULL}, {NULL, NULL}};
148 | 
149 |   table[0].lpServiceName = svc_name;
150 |   table[0].lpServiceProc = svc_main;
151 | 
152 |   return !StartServiceCtrlDispatcher(table);
153 | }
154 | 
155 | /*
156 |  * Arguments: name (string), [accept pause & continue (boolean)]
157 |  * Returns: [svc_udata]
158 |  */
159 | static int
160 | svc_handle (lua_State *L)
161 | {
162 |   const char *svc_name = luaL_checkstring(L, 1);
163 |   const int accept_pause_cont = lua_toboolean(L, 2);
164 |   char name_buf[128];
165 |   HANDLE hThr;
166 |   DWORD id;
167 | 
168 |   if (g_Service.event)
169 |     luaL_argerror(L, 0, "already running");
170 |   else {
171 |     HANDLE *hep = lua_newuserdata(L, sizeof(HANDLE));
172 |     luaL_getmetatable(L, WSVC_TYPENAME);
173 |     lua_setmetatable(L, -2);
174 | 
175 |     *hep = CreateEvent(NULL, TRUE, FALSE, NULL);  /* manual-reset */
176 |     if (!*hep) goto err;
177 | 
178 |     g_Service.event = *hep;
179 |     g_Service.accept_pause_cont = accept_pause_cont;
180 |   }
181 | 
182 |   strncpy(name_buf, svc_name, sizeof(name_buf));
183 |   name_buf[sizeof(name_buf) - 1] = '\0';
184 | 
185 |   hThr = CreateThread(NULL, 4096, (svc_func_t) svc_start, name_buf, 0, &id);
186 |   if (hThr != NULL) {
187 |     CloseHandle(hThr);
188 | 
189 |     WaitForSingleObject(g_Service.event, INFINITE);
190 |     ResetEvent(g_Service.event);
191 | 
192 |     if (g_Service.hstatus)
193 |       return 1;
194 |   }
195 |  err:
196 |   return sys_seterror(L, 0);
197 | }
198 | 
199 | /*
200 |  * Arguments: svc_udata
201 |  */
202 | static int
203 | svc_close (lua_State *L)
204 | {
205 |   HANDLE *hep = checkudata(L, 1, WSVC_TYPENAME);
206 |   if (*hep) {
207 |     CloseHandle(*hep);
208 |     *hep = NULL;
209 |   }
210 |   return 0;
211 | }
212 | 
213 | /*
214 |  * Arguments: svc_udata, [status (string: "stopped", "paused", "running")]
215 |  * Returns: [svc_udata | control_code (string: "stop", "pause", "continue")]
216 |  */
217 | static int
218 | svc_status (lua_State *L)
219 | {
220 |   const char *s = lua_tostring(L, 2);
221 | 
222 |   if (s) {
223 |     int st;
224 |     switch (s[0]) {
225 |     case 's': st = SERVICE_STOPPED; break;
226 |     case 'p': st = SERVICE_PAUSED; break;
227 |     case 'r': st = SERVICE_RUNNING; break;
228 |     default: return 0;
229 |     }
230 |     g_Service.status.dwCurrentState = st;
231 | 
232 |     SetServiceStatus(g_Service.hstatus, &g_Service.status);
233 |     Sleep(0);  /* yield to service controller thread */
234 | 
235 |     lua_settop(L, 1);
236 |   } else {
237 |     switch (g_Service.status.dwCurrentState) {
238 |     case SERVICE_STOPPED:
239 |     case SERVICE_STOP_PENDING:
240 |       s = "stop"; break;
241 |     case SERVICE_RUNNING:
242 |       s = "running"; break;
243 |     case SERVICE_CONTINUE_PENDING:
244 |       s = "continue"; break;
245 |     case SERVICE_PAUSE_PENDING:
246 |     case SERVICE_PAUSED:
247 |       s = "pause"; break;
248 |     }
249 |     lua_pushstring(L, s);
250 |   }
251 |   return 1;
252 | }
253 | 
254 | /*
255 |  * Arguments: svc_udata, [timeout (milliseconds)]
256 |  * Returns: [signalled/timedout (boolean)]
257 |  */
258 | static int
259 | svc_wait (lua_State *L)
260 | {
261 |   HANDLE he = (HANDLE) lua_unboxpointer(L, 1, WSVC_TYPENAME);
262 |   DWORD res = (DWORD) luaL_optinteger(L, 2, INFINITE);
263 | 
264 |   sys_vm_leave(L);
265 |   res = WaitForSingleObject(he, res);
266 |   ResetEvent(he);
267 |   sys_vm_enter(L);
268 | 
269 |   if (res != WAIT_FAILED) {
270 |     lua_pushboolean(L, res == WAIT_OBJECT_0);
271 |     return 1;
272 |   }
273 |   return 0;
274 | }
275 | 
276 | /*
277 |  * Arguments: svc_udata
278 |  * Returns: string
279 |  */
280 | static int
281 | svc_tostring (lua_State *L)
282 | {
283 |   HANDLE he = (HANDLE) lua_unboxpointer(L, 1, WSVC_TYPENAME);
284 |   lua_pushfstring(L, WSVC_TYPENAME " (%p)", he);
285 |   return 1;
286 | }
287 | 
288 | 
289 | static luaL_Reg svc_meth[] = {
290 |   {"status",		svc_status},
291 |   {"wait",		svc_wait},
292 |   {"__tostring",	svc_tostring},
293 |   {"__gc",		svc_close},
294 |   {NULL, NULL}
295 | };
296 | 
297 | static luaL_Reg svc_lib[] = {
298 |   {"install",		svc_install},
299 |   {"uninstall",		svc_uninstall},
300 |   {"handle",		svc_handle},
301 |   {NULL, NULL}
302 | };
303 | 
304 | 
305 | /*
306 |  * Arguments: ..., win32_lib (table)
307 |  */
308 | static void
309 | luaopen_sys_win32_service (lua_State *L)
310 | {
311 |   luaL_newlib(L, svc_lib);
312 |   lua_setfield(L, -2, "service");
313 | 
314 |   luaL_newmetatable(L, WSVC_TYPENAME);
315 |   lua_pushvalue(L, -1);  /* push metatable */
316 |   lua_setfield(L, -2, "__index");  /* metatable.__index = metatable */
317 |   luaL_setfuncs(L, svc_meth, 0);
318 |   lua_pop(L, 1);
319 | }
320 | 


--------------------------------------------------------------------------------