├── COPYRIGHT ├── README ├── VERSION ├── src ├── .gitignore ├── Makefile ├── common.h ├── event │ ├── epoll.c │ ├── epoll.h │ ├── evq.c │ ├── evq.h │ ├── kqueue.c │ ├── kqueue.h │ ├── poll.c │ ├── poll.h │ ├── select.c │ ├── select.h │ ├── signal.c │ ├── signal.h │ ├── timeout.c │ ├── timeout.h │ ├── win32.c │ ├── win32.h │ ├── win32iocr.c │ ├── win32sig.c │ └── win32thr.c ├── isa │ ├── fcgi │ │ └── sys_fcgi.c │ └── isapi │ │ ├── isapi_dll.c │ │ ├── isapi_dll.def │ │ └── isapi_ecb.c ├── luasys.c ├── luasys.h ├── mem │ ├── membuf.c │ └── sys_mem.c ├── msvcbuild.bat ├── sock │ ├── sock_addr.c │ └── sys_sock.c ├── sys_comm.c ├── sys_date.c ├── sys_env.c ├── sys_evq.c ├── sys_file.c ├── sys_fs.c ├── sys_log.c ├── sys_proc.c ├── sys_rand.c ├── sys_unix.c ├── thread │ ├── sys_thread.c │ ├── thread_affin.c │ ├── thread_affin_mach.c │ ├── thread_dpool.c │ ├── thread_pipe.c │ ├── thread_sched.c │ └── thread_sync.c └── win32 │ ├── getloadavg.c │ ├── strptime.c │ ├── sys_win32.c │ ├── win32_reg.c │ ├── win32_svc.c │ └── win32_utf8.c └── test ├── event ├── bench.lua ├── bench_cascade.lua ├── test_evq.lua └── test_time.lua ├── isa ├── fcgi.lua └── isapi.lua ├── libuv ├── benchmark-million-timers.lua └── benchmark-thread.lua ├── mem ├── bench.lua └── test_mem.lua ├── sock ├── connect.lua ├── daytimeclnt.lua ├── echoclnt.lua ├── echosrvr.lua ├── echosrvr_co.lua ├── manyclnt.lua ├── mcastrecv.lua ├── mcastsend.lua ├── trace.sh ├── udprecv.lua ├── udpsend.lua ├── udpsendraw.lua ├── unixrecv.lua └── unixsend.lua ├── test_pid.lua ├── test_sys.lua ├── thread ├── affin.lua ├── dinner.lua ├── dpool.lua ├── sched │ ├── bench.lua │ ├── test_evq.lua │ └── test_sched.lua ├── stdin.lua ├── test_thread.lua └── vms.lua └── win32 ├── beep_svc.lua └── test_win32.lua /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 1.8 -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | *.a 2 | *.o 3 | *.dll 4 | *.so 5 | -------------------------------------------------------------------------------- /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/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/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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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/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/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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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/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/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/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/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/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/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_dll.def:
--------------------------------------------------------------------------------
1 | EXPORTS
2 | GetExtensionVersion
3 | TerminateExtension
4 | HttpExtensionProc
5 | 


--------------------------------------------------------------------------------
/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/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/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 | 


--------------------------------------------------------------------------------
/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/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 | 


--------------------------------------------------------------------------------
/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 | 


--------------------------------------------------------------------------------
/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/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/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 | 


--------------------------------------------------------------------------------
/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 | 


--------------------------------------------------------------------------------
/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 | 


--------------------------------------------------------------------------------
/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/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/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/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/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 | 


--------------------------------------------------------------------------------
/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/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/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 | 


--------------------------------------------------------------------------------
/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/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 | 


--------------------------------------------------------------------------------
/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 | 


--------------------------------------------------------------------------------
/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/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 | 


--------------------------------------------------------------------------------
/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/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 | 


--------------------------------------------------------------------------------
/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/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 | 


--------------------------------------------------------------------------------
/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/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 | 


--------------------------------------------------------------------------------
/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 | 


--------------------------------------------------------------------------------
/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/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/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 | 


--------------------------------------------------------------------------------
/test/sock/trace.sh:
--------------------------------------------------------------------------------
1 | 
2 | strace -r -T -x -e raw=poll,select -o trace.log ./echosrvr.lua
3 | 


--------------------------------------------------------------------------------
/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/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/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/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/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/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 | 


--------------------------------------------------------------------------------
/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/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/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/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/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 | 


--------------------------------------------------------------------------------
/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 | 


--------------------------------------------------------------------------------
/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/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/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/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/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/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 | 


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