├── .gitignore ├── .gitmodules ├── .travis.yml ├── Makefile ├── README.md ├── flock.c ├── flock_posix.h ├── flock_win.h ├── ipc.c ├── ipc.h ├── luaipc-scm-0.rockspec ├── memfile.c ├── memfile.h ├── mmap.c ├── mmap_posix.h ├── mmap_win.h ├── osx ├── sem_timedwait.c └── sem_timedwait.h ├── proc.c ├── proc_posix.h ├── proc_win.h ├── sem.c ├── sem_osx.h ├── sem_posix.h ├── sem_win.h ├── shm.c ├── shm_posix.h ├── shm_win.h ├── strfile.c └── tests ├── mmapcat.lua ├── mmaprev.lua ├── proctest.lua ├── procwork.lua ├── semtest.lua ├── semwork.lua ├── shmread.lua ├── shmtest.lua ├── shmwrite.lua └── str.lua /.gitignore: -------------------------------------------------------------------------------- 1 | # docco output directory 2 | #docs/ 3 | 4 | # local files for remembering stuff 5 | HISTO 6 | TODO 7 | 8 | # temporary files 9 | .*.swp 10 | 11 | # object files 12 | *.o 13 | *.obj 14 | 15 | # libraries 16 | *.so 17 | *.dll 18 | *.a 19 | *.lib 20 | *.exp 21 | 22 | # executables 23 | *.exe 24 | 25 | # precompiled Lua bytecode files 26 | *.luac 27 | 28 | # LuaRocks packages 29 | *.rock 30 | 31 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule ".travis"] 2 | path = .travis 3 | url = https://github.com/siffiejoe/lua-travis 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | 3 | # Select container based VM (for os == linux). 4 | sudo: false 5 | 6 | # Use gcc as the C compiler (clang is also available). 7 | compiler: gcc 8 | 9 | # Specify operating systems to test on (linux and/or osx). 10 | os: 11 | - linux 12 | - osx 13 | 14 | # Environment variables for the build. 15 | env: 16 | global: 17 | - LUAROCKS=2.3.0 18 | matrix: 19 | - LUA=LuaJIT-2.0.4 20 | - LUA=Lua-5.2.4 21 | - LUA=5.3.2 22 | 23 | # Only test changes to the master branch. 24 | branches: 25 | only: 26 | - master 27 | 28 | # Disable email notifications. 29 | notifications: 30 | email: false 31 | 32 | # Install dependencies (Lua, Luarocks, ...). 33 | install: 34 | - . .travis/install.sh 35 | - luarocks install luasocket 36 | 37 | # Build (and test?) your project. 38 | script: 39 | - luarocks make CFLAGS="-Wall -O2 -fPIC" 40 | - (cd tests && lua str.lua) 41 | - (cd tests && lua mmapcat.lua) 42 | - (cd tests && lua mmaprev.lua) 43 | - (cd tests && lua proctest.lua) 44 | - (cd tests && lua semtest.lua) 45 | - (cd tests && lua shmtest.lua) 46 | 47 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all clean install 2 | 3 | # works for Lua 5.2 on Debian based Linuxes: 4 | LUA_INCDIR = /usr/include/lua5.2 5 | DLL_INSTALL_DIR = /usr/local/lib/lua/5.2 6 | EXTRAFLAGS = -D_POSIX_C_SOURCE=200809L -pthread 7 | EXTRALIBS = -lrt 8 | 9 | CFLAGS = -Wall -fpic -O2 -g 10 | INCLUDES = -I${LUA_INCDIR} 11 | LIBFLAG = -shared 12 | LIB_EXTENSION = so 13 | 14 | DLLNAME = ipc.${LIB_EXTENSION} 15 | SOURCES = ipc.c memfile.c strfile.c shm.c mmap.c sem.c flock.c proc.c 16 | OBJECTS = ${SOURCES:%.c=%.o} 17 | 18 | all: ${DLLNAME} 19 | 20 | ${DLLNAME}: ${OBJECTS} 21 | ${CC} ${EXTRAFLAGS} ${LIBFLAG} -o $@ ${OBJECTS} ${EXTRALIBS} 22 | 23 | .c.o: 24 | ${CC} ${CFLAGS} ${EXTRAFLAGS} ${INCLUDES} -c -o $@ $< 25 | 26 | install: ${DLLNAME} 27 | mkdir -p ${DLL_INSTALL_DIR} 28 | cp ${DLLNAME} ${DLL_INSTALL_DIR} 29 | 30 | clean: 31 | rm -f ${DLLNAME} ${OBJECTS} 32 | 33 | # dependencies 34 | ipc.o: ipc.c ipc.h 35 | memfile.o: memfile.c memfile.h ipc.h Makefile 36 | strfile.o: strfile.c memfile.h ipc.h Makefile 37 | shm.o: shm.c memfile.h ipc.h shm_posix.h shm_win.h Makefile 38 | mmap.o: mmap.c memfile.h ipc.h mmap_posix.h mmap_win.h Makefile 39 | sem.o: sem.c ipc.h sem_posix.h sem_win.h Makefile 40 | flock.o: flock.c ipc.h flock_posix.h flock_win.h Makefile 41 | proc.o: proc.c ipc.h proc_posix.h proc_win.h Makefile 42 | 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/siffiejoe/lua-luaipc.svg?branch=master)](https://travis-ci.org/siffiejoe/lua-luaipc) 2 | 3 | # LuaIPC -- Inter-Process Communication for Lua # 4 | 5 | ## Introduction ## 6 | 7 | Since Lua is mostly written in portable ISO C it comes with the bare 8 | minimum of batteries. There are some extension libraries that provide 9 | a portable (usually lowest common denominator) interface to some parts 10 | of the operating system not covered by ISO C, like sockets 11 | ([LuaSocket][1]), or filesystem operations ([LuaFileSystem][2]). 12 | **LuaIPC** is such a library consisting of multiple modules for 13 | inter-process communication in Lua. It allows you to: 14 | 15 | * create/read/write shared memory 16 | * create/manipulate semaphores 17 | * memory-map files 18 | * lock/unlock (parts of) files 19 | * spawn subprocesses and communicate with them using unnamed pipes 20 | 21 | The implementation should be portable to Lua 5.1-5.3 on recent Windows 22 | and POSIX machines (tested on Win 7, Ubuntu Linux, FreeBSD, and OSX). 23 | 24 | [1]: http://w3.impa.br/~diego/software/luasocket/ 25 | [2]: http://keplerproject.github.io/luafilesystem/ 26 | 27 | 28 | ## Reference ## 29 | 30 | In general all functions return a true-ish value on success (`true` if 31 | there is nothing better to return). This applies only to functions 32 | that do not aim for compatibility with existing Lua functions (like 33 | e.g. the file methods). On error, usually `nil`, an error message, 34 | and a numeric error code are returned. All handles created by this 35 | library are finalized during garbage-collection, so manually cleaning 36 | up is optional under most circumstances. 37 | 38 | The **LuaIPC** library consists of the following modules: 39 | 40 | 41 | ### ipc.shm ### 42 | 43 | ```lua 44 | local shm = require( "ipc.shm" ) 45 | ``` 46 | 47 | A shared memory segment is a piece of RAM that can be accessed by 48 | multiple processes at the same time. Since Lua does not have any form 49 | of pointer arithmetic, a file-like interface is provided instead. 50 | The shared memory segment is deleted automatically when all its 51 | handles are closed. It is unspecified whether you can still open a 52 | new handle to an existing shared memory segment once its original 53 | creator has closed the handle. On some OSes this module might allocate 54 | more than the requested number of bytes (usually a multiple of the 55 | page size). To be portable you have to agree on the real size (e.g. 56 | by storing it at the beginning of the shared memory segment) and 57 | adjust the file size that this module uses by calling `h:truncate()` 58 | for all parties that attached to the shared memory (see below). 59 | 60 | The module provides the following functions: 61 | 62 | * `shm.create( name, size ) ==> handle` 63 | * `shm.attach( name ) ==> handle` 64 | 65 | `shm.create()` creates a new shared memory segment with the given 66 | `name` and `size` (in bytes), and returns an attached handle to it. If 67 | a memory segment with that `name` already exists, this function fails. 68 | `shm.attach()` on the other hand tries to open an existing shared 69 | memory segment with the given `name`. In both cases `name` is not a 70 | file path: you cannot find it on the filesystem (well, on POSIX you 71 | can), and it must not contain any directory separators! 72 | 73 | A shared memory handle has all the methods that a normal Lua file 74 | handle has, and additionally: 75 | 76 | * `h:size() ==> bytes` 77 | * `h:addr() ==> pointer` 78 | * `h:truncate( bytes ) ==> true` 79 | 80 | `h:size()` returns the size of the shared memory segment in bytes, and 81 | `h:addr()` returns the starting address as a lightuserdata. Unless you 82 | use some form of FFI, this is probably not very useful for you. The 83 | `h:truncate()` method can tell this module to use less than the 84 | current shared memory size for its file-like interface. 85 | 86 | The file-like methods behave exactly like their Lua file equivalents 87 | with the following exception: Currently the `"*n"` format specifier of 88 | `read`/`lines` is not supported. Since handles are not actually Lua 89 | file objects, `io.type()` will return `nil` when called with such a 90 | handle. 91 | 92 | 93 | ### ipc.sem ### 94 | 95 | ```lua 96 | local sem = require( "ipc.sem" ) 97 | ``` 98 | 99 | A semaphore is a shared integer counter that you can atomically 100 | increment or decrement, but its value will never go below zero. If a 101 | decrement operation would cause the value to become negative, the call 102 | blocks (and/or returns `false` for the non-blocking/waiting calls). 103 | The semaphore is deleted automatically when all its handles are 104 | closed. It is unspecified whether you can still open a new handle to 105 | an existing semaphore once its original creator has closed the handle. 106 | 107 | The module provides the following function: 108 | 109 | * `sem.open( name [, n] ) ==> handle` 110 | 111 | If the initial counter value `n` is given (and not `0`), `sem.open` 112 | tries to create a new semaphore with the given `name`. If such a 113 | semaphore already exists, this function fails. If `n` is zero or 114 | absent, `sem.open` tries to open an existing semaphore with the given 115 | `name`. Anyways, `name` is not a file path: you cannot find it on the 116 | filesystem (well, on POSIX you can), and it must not contain any 117 | directory separators! 118 | 119 | A semaphore handle has the following methods: 120 | 121 | * `h:inc() ==> true` 122 | * `h:dec( [timeout] ) ==> boolean` 123 | * `h:close() ==> true` 124 | 125 | `h:inc()` increments the semaphore value (if successful). `h:dec()` 126 | tries to decrement the semaphore value. If the optional `timeout` (in 127 | seconds, can have a fractional part) is absent or negative, the 128 | function will block until some other process increments the semaphore. 129 | Otherwise `h:dec()` will wait at most `timeout` seconds and return 130 | `true` or `false` depending on the success (errors still result in 131 | `nil`, error message, and error code). 132 | 133 | 134 | ### ipc.mmap ### 135 | 136 | ```lua 137 | local mmap = require( "ipc.mmap" ) 138 | ``` 139 | 140 | Memory-mapped I/O is usually faster than normal file I/O, because by 141 | making the kernel buffers available at a certain memory address, you 142 | can save the copy operations to user-supplied buffers. Memory-mapping 143 | also has some draw-backs: Most OSes don't allow mapping a zero-length 144 | file or very large files (e.g. larger than the address space). Also, 145 | this module does not allow resizing the file while it is mapped, so 146 | you can't write beyond the initial bounds of the memory-mapped file. 147 | 148 | The module provides the following function: 149 | 150 | * `mmap.pagesize` 151 | * `mmap.open( filepath [, mode [, offset [, size]]] ) ==> handle` 152 | 153 | `mmap.open()` opens the given `filepath` and maps the contents into 154 | memory. `mode` can be `"r"` (the default), `"w"`, or `"rw"`. On 155 | success a mmap handle is returned. Offsets usually must be given in 156 | multiples of the current pages size (or equivalent). `mmap.pagesize` 157 | contains this number. Default value for `size` is `0` which uses the 158 | current size of the file. 159 | 160 | An mmap handle has all the methods that a normal Lua file handle has, 161 | and additionally: 162 | 163 | * `h:size() ==> bytes` 164 | * `h:addr() ==> pointer` 165 | * `h:truncate( bytes ) ==> true` 166 | 167 | `h:size()` returns the size of the memory map in bytes, and `h:addr()` 168 | returns the starting address as a lightuserdata. Unless you use some 169 | form of FFI, this is probably not very useful for you. `h:truncate()` 170 | can shrink the size of the memory mapping, but it's mostly useful for 171 | the `ipc.shm` module (see there). 172 | 173 | The file-like methods behave exactly like their Lua file equivalents 174 | with the following exception: Currently the `"*n"` format specifier of 175 | `read`/`lines` is not supported. Since handles are not actually Lua 176 | file objects, `io.type()` will return `nil` when called with such a 177 | handle. 178 | 179 | 180 | ### ipc.filelock ### 181 | 182 | ```lua 183 | local filelock = require( "ipc.filelock" ) 184 | ``` 185 | 186 | This module contains functions for (un-)locking byte ranges of an 187 | opened Lua file. The locks might be advisory (you have to check for 188 | locks yourself) or mandatory (file functions will honor the locks 189 | automatically) depending on the OS. Also, the file locking functions 190 | can't be used to synchonize different threads in the same process. 191 | There is similar functionality in [LuaFileSystem][2], but in LFS all 192 | locking is non-blocking. It is explicitly *not* supported to mix the 193 | locking functions from LFS and from this module. 194 | 195 | This module provides the following functions: 196 | 197 | * `filelock.lock( file, mode [, offset [, nbytes]] ) ==> true` 198 | * `filelock.trylock( file, mode [, offset [, nbytes]] ) ==> boolean` 199 | * `filelock.unlock( file [, offset [, nbytes]] ) ==> true` 200 | 201 | `filelock.lock()` acquires a lock for the given range of `nbytes` 202 | bytes starting at `offset` (default `0`) in the Lua file object. If 203 | `nbytes` is absent (or `0`), the whole file is locked. If the given 204 | range already is locked by another process, `filelock.lock()` will 205 | block until the other process releases that lock. `filelock.trylock()` 206 | is similar, but it will not block. Instead it returns false if another 207 | process already holds a lock on the given byte range. 208 | `filelock.unlock` unlocks the given byte range that has been locked by 209 | the same process before. 210 | 211 | 212 | ### ipc.proc ### 213 | 214 | ```lua 215 | local proc = require( "ipc.proc" ) 216 | ``` 217 | 218 | On the most common OSes Lua provides the `io.popen()` function to 219 | spawn a subprocess and capture its output *or* provide its input via 220 | unnamed pipes. This module can capture `stdout` *and* `stderr` 221 | simultaneously, while still providing input for the spawned command. 222 | To avoid deadlocks as much as possible and to work around platform 223 | differences the interface is callback-based. 224 | 225 | This module provides the following functions/fields: 226 | 227 | * `proc.spawn( cmd, options ) ==> handle` 228 | * `proc.EOF ==> lightuserdata` 229 | 230 | Similar to `os.execute()` and `io.popen()`, `proc.spawn()` takes a 231 | command (`cmd`) as string and runs it using the shell. The `options` 232 | table specifies the callback function (field `callback`), and which 233 | streams to redirect via pipes (fields `stdin`, `stdout`, and `stderr`; 234 | a true value creates a pipe, a false value uses the default streams of 235 | the parent process -- you can also specify Lua file objects). 236 | 237 | A process handle has the following methods: 238 | 239 | * `h:write( ... ) ==> true` 240 | * `h:kill( "term"/"kill" ) ==> true` 241 | * `h:wait() ==> true/nil, string, number` 242 | 243 | `h:write()` accepts strings and enqueues them to be sent to the child 244 | process' `stdin` stream when it is ready. You may also pass `proc.EOF` 245 | to close the `stdin` stream when all enqueued output has been sent. 246 | The `h:kill()` function sends either a `SIGTERM` (requesting graceful 247 | shutdown) or a `SIGKILL` (for immediate shutdown) to the child 248 | process. `h:wait()` starts a small blocking event loop that will send 249 | enqueued data to the child process' `stdin`, read data from the 250 | child's `stdout`/`stderr` streams, and/or wait for the child process 251 | to exit. The callback given to the `proc.spawn()` function is called 252 | with the stream name (`"stdout"` or `"stdin"`) and the received data 253 | (or the usual error values) when output from the child is available. 254 | On Lua 5.2+ you may yield from the callback function. Return values of 255 | `h:wait()` are the same as for `os.execute()` on recent Lua versions. 256 | 257 | 258 | ### ipc.strfile ### 259 | 260 | ```lua 261 | local strfile = require( "ipc.strfile" ) 262 | ``` 263 | 264 | This module is not about IPC at all. It allows you to create a 265 | file-like object from a Lua string. It shares most of the code for 266 | file handling with the `ipc.shm` and `ipc.mmap` modules, and it is 267 | often useful -- that's why it is here. It has exactly the same methods 268 | as the shared memory or mmap handles (except you are not allowed to 269 | `write`). 270 | 271 | The following function is provided: 272 | 273 | * `strfile.open( str ) ==> handle` 274 | 275 | 276 | ## Contact ## 277 | 278 | Philipp Janda, siffiejoe(a)gmx.net 279 | 280 | Comments and feedback are always welcome. 281 | 282 | 283 | ## License ## 284 | 285 | **LuaIPC** is *copyrighted free software* distributed under the MIT 286 | license (the same license as Lua 5.1). The full license text follows: 287 | 288 | LuaIPC (c) 2015, 2016 Philipp Janda 289 | 290 | Permission is hereby granted, free of charge, to any person obtaining 291 | a copy of this software and associated documentation files (the 292 | "Software"), to deal in the Software without restriction, including 293 | without limitation the rights to use, copy, modify, merge, publish, 294 | distribute, sublicense, and/or sell copies of the Software, and to 295 | permit persons to whom the Software is furnished to do so, subject to 296 | the following conditions: 297 | 298 | The above copyright notice and this permission notice shall be 299 | included in all copies or substantial portions of the Software. 300 | 301 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 302 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 303 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 304 | IN NO EVENT SHALL THE AUTHOR OR COPYRIGHT HOLDER BE LIABLE FOR ANY 305 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 306 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 307 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 308 | 309 | The `sem_timedwait()` implementation used on OSX was written by Keith 310 | Shortridge at the Australian Astronomical Observatory. See the 311 | comments in `osx/sem_timedwait.c` for details. 312 | 313 | -------------------------------------------------------------------------------- /flock.c: -------------------------------------------------------------------------------- 1 | #ifndef _POSIX_C_SOURCE 2 | # define _POSIX_C_SOURCE 200112L 3 | #endif 4 | #ifndef _FILE_OFFSET_BITS 5 | # define _LARGEFILE_SOURCE 1 6 | # define _FILE_OFFSET_BITS 64 7 | #endif 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "ipc.h" 13 | 14 | 15 | /* check for POSIX */ 16 | #if defined( unix ) || defined( __unix ) || defined( __unix__ ) || \ 17 | (defined( __APPLE__ ) && defined( __MACH__ )) || \ 18 | HAVE_UNISTD_H 19 | # include 20 | # if defined( _POSIX_VERSION ) && _POSIX_VERSION >= 200112L 21 | # define HAVE_FLOCK 22 | # include "flock_posix.h" 23 | # endif 24 | #endif 25 | 26 | 27 | /* check for Windows */ 28 | #if !defined( HAVE_FLOCK ) && \ 29 | defined( _WIN32 ) && !defined( __CYGWIN__ ) 30 | # define HAVE_FLOCK 31 | # include "flock_win.h" 32 | #endif 33 | 34 | 35 | #ifdef HAVE_FLOCK 36 | 37 | static int pusherror( lua_State* L, int code ) { 38 | char buf[ IPC_MAXERRMSG ]; 39 | ipc_flock_error( buf, sizeof( buf ), code ); 40 | lua_pushnil( L ); 41 | lua_pushstring( L, buf ); 42 | lua_pushinteger( L, code ); 43 | return 3; 44 | } 45 | 46 | 47 | static void invalidate_input_buffer( FILE* f ) { 48 | /* Linux (and apparently many other implementations) discard 49 | * unread characters from the input buffer if fflush is called on 50 | * an input file, but this is not guaranteed by ISO C. */ 51 | fflush( f ); 52 | /* This should also invalidate the input buffer unless the 53 | * implementation checks for that specific case. */ 54 | fseek( f, 0, SEEK_CUR ); 55 | /* If both methods don't work, we are out of luck. But using 56 | * low-level file locking with buffered IO is a bad idea 57 | * anyway! */ 58 | } 59 | 60 | 61 | static int l_flock_lock( lua_State* L ) { 62 | FILE* f = ipc_checkfile( L, 1 ); 63 | static char const* const mnames[] = { "r", "w", "rw", NULL }; 64 | static int const modes[] = { 0, 1, 1 }; 65 | int is_wlock = modes[ luaL_checkoption( L, 2, "rw", mnames ) ]; 66 | ipc_flock_off_t start = IPC_OPTBIGINT( ipc_flock_off_t, L, 3, 0 ); 67 | ipc_flock_off_t len = IPC_OPTBIGINT( ipc_flock_off_t, L, 4, 0 ); 68 | int rv = ipc_flock_lock( f, is_wlock, NULL, start, len ); 69 | if( rv != 0 ) 70 | return pusherror( L, rv ); 71 | /* try to flush input buffer */ 72 | invalidate_input_buffer( f ); 73 | lua_pushboolean( L, 1 ); 74 | return 1; 75 | } 76 | 77 | 78 | static int l_flock_trylock( lua_State* L ) { 79 | FILE* f = ipc_checkfile( L, 1 ); 80 | static char const* const mnames[] = { "r", "w", "rw", NULL }; 81 | static int const modes[] = { 0, 1, 1 }; 82 | int is_wlock = modes[ luaL_checkoption( L, 2, "rw", mnames ) ]; 83 | ipc_flock_off_t start = IPC_OPTBIGINT( ipc_flock_off_t, L, 3, 0 ); 84 | ipc_flock_off_t len = IPC_OPTBIGINT( ipc_flock_off_t, L, 4, 0 ); 85 | int could_lock = 0; 86 | int rv = ipc_flock_lock( f, is_wlock, &could_lock, start, len ); 87 | if( rv != 0 ) 88 | return pusherror( L, rv ); 89 | if( could_lock ) /* try to flush input buffer */ 90 | invalidate_input_buffer( f ); 91 | lua_pushboolean( L, could_lock ); 92 | return 1; 93 | } 94 | 95 | 96 | static int l_flock_unlock( lua_State* L ) { 97 | FILE* f = ipc_checkfile( L, 1 ); 98 | ipc_flock_off_t start = (ipc_flock_off_t)luaL_optinteger( L, 2, 0 ); 99 | ipc_flock_off_t len = (ipc_flock_off_t)luaL_optinteger( L, 3, 0 ); 100 | int rv = 0; 101 | fflush( f ); /* flush output buffer */ 102 | rv = ipc_flock_unlock( f, start, len ); 103 | if( rv != 0 ) 104 | return pusherror( L, rv ); 105 | lua_pushboolean( L, 1 ); 106 | return 1; 107 | } 108 | 109 | 110 | IPC_API int luaopen_ipc_filelock( lua_State* L ) { 111 | luaL_Reg const functions[] = { 112 | { "lock", l_flock_lock }, 113 | { "trylock", l_flock_trylock }, 114 | { "unlock", l_flock_unlock }, 115 | { NULL, NULL } 116 | }; 117 | luaL_newlib( L, functions ); 118 | return 1; 119 | } 120 | 121 | #else /* no implementation for this platform available: */ 122 | IPC_API int luaopen_ipc_filelock( lua_State* L ) { 123 | IPC_NOTIMPLEMENTED( L ); 124 | return 0; 125 | } 126 | #endif 127 | 128 | -------------------------------------------------------------------------------- /flock_posix.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | typedef off_t ipc_flock_off_t; 10 | 11 | 12 | static void ipc_flock_error( char* buf, size_t len, int code ) { 13 | if( len > 0 && strerror_r( code, buf, len ) != (int)0 ) { 14 | strncpy( buf, "unknown error", len-1 ); 15 | buf[ len-1 ] = '\0'; 16 | } 17 | } 18 | 19 | 20 | static int ipc_flock_lock( FILE* f, int is_wlock, int* could_lock, 21 | ipc_flock_off_t start, 22 | ipc_flock_off_t len ) { 23 | int rv = 0; 24 | int fd = fileno( f ); 25 | int op = could_lock != NULL ? F_SETLK : F_SETLKW; 26 | struct flock fl; 27 | fl.l_type = is_wlock ? F_WRLCK : F_RDLCK; 28 | fl.l_whence = SEEK_SET; 29 | fl.l_start = start; 30 | fl.l_len = len; 31 | IPC_EINTR( rv, fcntl( fd, op, &fl ) ); 32 | if( rv < 0 ) { 33 | if( could_lock != NULL && 34 | (errno == EACCES || errno == EAGAIN) ) { 35 | *could_lock = 0; 36 | return 0; 37 | } 38 | return IPC_ERR( errno ); 39 | } 40 | if( could_lock != NULL ) 41 | *could_lock = 1; 42 | return 0; 43 | } 44 | 45 | 46 | static int ipc_flock_unlock( FILE* f, ipc_flock_off_t start, 47 | ipc_flock_off_t len ) { 48 | struct flock fl; 49 | fl.l_type = F_UNLCK; 50 | fl.l_whence = SEEK_SET; 51 | fl.l_start = start; 52 | fl.l_len = len; 53 | if( fcntl( fileno( f ), F_SETLK, &fl ) < 0 ) 54 | return IPC_ERR( errno ); 55 | return 0; 56 | } 57 | 58 | -------------------------------------------------------------------------------- /flock_win.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #define WIN32_LEAN_AND_MEAN 6 | #include 7 | #include 8 | 9 | 10 | typedef ULONGLONG ipc_flock_off_t; 11 | 12 | 13 | static void ipc_flock_error( char* buf, size_t len, int code ) { 14 | if( len > 0 ) { 15 | if( 0 == FormatMessageA( FORMAT_MESSAGE_FROM_SYSTEM | 16 | FORMAT_MESSAGE_IGNORE_INSERTS, 17 | NULL, 18 | code, 19 | 0, 20 | buf, 21 | len, 22 | NULL ) ) { 23 | strncpy( buf, "unknown error", len-1 ); 24 | buf[ len-1 ] = '\0'; 25 | } else { /* Windows puts an extra newline in there! */ 26 | size_t n = strlen( buf ); 27 | while( n > 0 && isspace( (unsigned char)buf[ --n ] ) ) 28 | buf[ n ] = '\0'; 29 | } 30 | } 31 | } 32 | 33 | 34 | static int ipc_flock_lock( FILE* f, int is_wlock, int* could_lock, 35 | ipc_flock_off_t start, 36 | ipc_flock_off_t len ) { 37 | HANDLE fh = (HANDLE)_get_osfhandle( _fileno( f ) ); 38 | DWORD flags = is_wlock ? LOCKFILE_EXCLUSIVE_LOCK : 0; 39 | DWORD lenlo = (DWORD)len, lenhi = (DWORD)(len >> 32); 40 | OVERLAPPED ov; 41 | if( fh == (HANDLE)INVALID_HANDLE_VALUE ) 42 | return IPC_ERR( ERROR_INVALID_HANDLE ); 43 | if( could_lock != NULL ) 44 | flags |= LOCKFILE_FAIL_IMMEDIATELY; 45 | ov.Offset = (DWORD)start; 46 | ov.OffsetHigh = (DWORD)(start >> 32); 47 | ov.hEvent = NULL; 48 | if( len == 0 ) 49 | lenhi = lenlo = (DWORD)-1; 50 | if( !LockFileEx( fh, flags, 0, lenlo, lenhi, &ov ) ) { 51 | int code = GetLastError(); 52 | if( could_lock != NULL && 53 | (code == ERROR_LOCK_VIOLATION || code == ERROR_IO_PENDING) ) { 54 | *could_lock = 0; 55 | return 0; 56 | } 57 | return IPC_ERR( code ); 58 | } 59 | if( could_lock != NULL ) 60 | *could_lock = 1; 61 | return 0; 62 | } 63 | 64 | 65 | static int ipc_flock_unlock( FILE* f, ipc_flock_off_t start, 66 | ipc_flock_off_t len ) { 67 | HANDLE fh = (HANDLE)_get_osfhandle( _fileno( f ) ); 68 | DWORD lenlo = (DWORD)len, lenhi = (DWORD)(len >> 32); 69 | DWORD offlo = (DWORD)start, offhi = (DWORD)(start >> 32); 70 | if( fh == (HANDLE)INVALID_HANDLE_VALUE ) 71 | return IPC_ERR( ERROR_INVALID_HANDLE ); 72 | if( len == 0 ) 73 | lenhi = lenlo = (DWORD)-1; 74 | if( !UnlockFile( fh, offlo, offhi, lenlo, lenhi ) ) 75 | return IPC_ERR( GetLastError() ); 76 | return 0; 77 | } 78 | 79 | -------------------------------------------------------------------------------- /ipc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "ipc.h" 8 | 9 | 10 | IPC_LOCAL FILE* ipc_checkfile( lua_State* L, int idx ) { 11 | #if LUA_VERSION_NUM == 501 /* Lua 5.1 / LuaJIT */ 12 | FILE** fp = luaL_checkudata( L, idx, LUA_FILEHANDLE ); 13 | if( *fp == NULL ) 14 | luaL_error( L, "attempt to use closed file" ); 15 | return *fp; 16 | #elif LUA_VERSION_NUM >= 502 && LUA_VERSION_NUM <= 503 17 | luaL_Stream* s = luaL_checkudata( L, idx, LUA_FILEHANDLE ); 18 | if( s->closef == 0 || s->f == NULL ) 19 | luaL_error( L, "attempt to use closed file" ); 20 | return s->f; 21 | #else 22 | #error unsupported Lua version 23 | #endif 24 | } 25 | 26 | 27 | IPC_LOCAL FILE* ipc_testfile( lua_State* L, int idx ) { 28 | #if LUA_VERSION_NUM == 501 /* Lua 5.1 / LuaJIT */ 29 | FILE** fp = luaL_testudata( L, idx, LUA_FILEHANDLE ); 30 | if( fp == NULL ) 31 | return NULL; 32 | else if( *fp == NULL ) 33 | luaL_error( L, "attempt to use closed filed" ); 34 | return *fp; 35 | #elif LUA_VERSION_NUM >= 502 && LUA_VERSION_NUM <= 503 36 | luaL_Stream* s = luaL_testudata( L, idx, LUA_FILEHANDLE ); 37 | if( s == NULL ) 38 | return NULL; 39 | else if( s->closef == 0 || s->f == NULL ) 40 | luaL_error( L, "attempt to use closed filed" ); 41 | return s->f; 42 | #else 43 | #error unsupported Lua version 44 | #endif 45 | } 46 | 47 | 48 | IPC_LOCAL int ipc_getuservaluefield( lua_State* L, int idx, 49 | char const* name ) { 50 | luaL_checkstack( L, 2, "not enough stack space" ); 51 | lua_getuservalue( L, idx ); 52 | if( lua_type( L, -1 ) == LUA_TTABLE ) 53 | lua_getfield( L, -1, name ); 54 | else 55 | lua_pushnil( L ); 56 | lua_replace( L, -2 ); 57 | return lua_type( L, -1 ); 58 | } 59 | 60 | 61 | IPC_LOCAL void ipc_setuservaluefield( lua_State* L, int idx, 62 | char const* name ) { 63 | luaL_checkstack( L, 2, "not enough stack space" ); 64 | lua_getuservalue( L, idx ); 65 | if( lua_type( L, -1 ) != LUA_TTABLE ) 66 | luaL_error( L, "attempt to set field of non-table uservalue" ); 67 | lua_pushvalue( L, -2 ); 68 | lua_setfield( L, -2, name ); 69 | lua_pop( L, 2 ); 70 | } 71 | 72 | 73 | IPC_LOCAL int ipc_err( char const* file, int line, char const* func, 74 | int code ) { 75 | if( code != 0 ) { 76 | if( func != NULL ) 77 | fprintf( stderr, "[%s:%d] error return (%d) in function '%s'\n", 78 | file, line, code, func ); 79 | else 80 | fprintf( stderr, "[%s:%d]: error return (%d)\n", 81 | file, line, code ); 82 | fflush( stderr ); 83 | } 84 | return code; 85 | } 86 | 87 | 88 | /* implementation of compatibility functions */ 89 | #if LUA_VERSION_NUM == 501 90 | IPC_LOCAL int ipc_absindex( lua_State* L, int idx ) { 91 | if( idx < 0 && idx > LUA_REGISTRYINDEX ) 92 | idx += lua_gettop( L )+1; 93 | return idx; 94 | } 95 | 96 | 97 | IPC_LOCAL void* ipc_testudata( lua_State* L, int idx, 98 | char const* name ) { 99 | void* p = lua_touserdata( L, idx ); 100 | if( p == NULL || !lua_getmetatable( L, idx ) ) 101 | return NULL; 102 | else { 103 | int res = 0; 104 | luaL_getmetatable( L, name ); 105 | res = lua_rawequal( L, -1, -2 ); 106 | lua_pop( L, 2 ); 107 | if( !res ) 108 | p = NULL; 109 | } 110 | return p; 111 | } 112 | #endif /* LUA_VERSION_NUM == 501 */ 113 | 114 | 115 | #ifdef _WIN32 116 | /* LuaRocks with MSVC can't really handle multiple modules in a single 117 | * DLL, so we have to export the luaopen_ functions ourself, and let 118 | * LuaRocks think that the ipc.dll contains the ipc module: */ 119 | IPC_API int luaopen_ipc( lua_State* L ) { 120 | (void)L; 121 | return 0; 122 | } 123 | #endif 124 | 125 | -------------------------------------------------------------------------------- /ipc.h: -------------------------------------------------------------------------------- 1 | #ifndef IPC_H_ 2 | #define IPC_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | 10 | #ifndef IPC_LOCAL 11 | /* emulate CLANG feature checking on other compilers */ 12 | # ifndef __has_attribute 13 | # define __has_attribute( _x ) 0 14 | # endif 15 | # if !defined( _WIN32 ) && !defined( __CYGWIN__ ) && \ 16 | ((defined( __GNUC__ ) && __GNUC__ >= 4 ) || \ 17 | __has_attribute( __visibility__ )) 18 | # define IPC_LOCAL __attribute__((__visibility__("hidden"))) 19 | # else 20 | # define IPC_LOCAL 21 | # endif 22 | #endif 23 | 24 | #ifndef IPC_API 25 | # ifdef _WIN32 26 | # define IPC_API __declspec(dllexport) 27 | # else 28 | # define IPC_API extern 29 | # endif 30 | #endif 31 | 32 | 33 | /* maximum expected length of error messages */ 34 | #define IPC_MAXERRMSG 200 35 | 36 | 37 | #ifndef NDEBUG 38 | # if (defined( __STDC_VERSION__ ) && __STDC_VERSION__ >= 199901L) || \ 39 | defined( __GNUC__ ) || defined( __clang__ ) 40 | # define IPC_ERR( code ) (ipc_err( __FILE__, __LINE__, __func__, (int)(code) )) 41 | # elif defined( _MSC_VER ) && _MSC_VER >= 1100L 42 | # define IPC_ERR( code ) (ipc_err( __FILE__, __LINE__, __FUNCTION__, (int)(code) )) 43 | # else 44 | # define IPC_ERR( code ) (ipc_err( __FILE__, __LINE__, NULL, (int)(code) )) 45 | # endif 46 | #else 47 | # define IPC_ERR( code ) ((int)(code)) 48 | #endif 49 | 50 | 51 | #define IPC_NOTIMPLEMENTED( L ) \ 52 | luaL_error( L, "module '%s' not implemented on this platform", \ 53 | lua_tostring( L, 1 ) ) 54 | 55 | 56 | #define IPC_EINTR( _rv, _call ) \ 57 | do { \ 58 | _rv = _call; \ 59 | } while( _rv < 0 && errno == EINTR ) 60 | 61 | 62 | #define IPC_OPTBIGINT( _t, _l, _i, _d ) \ 63 | ((sizeof( _t ) > sizeof( lua_Integer ) && \ 64 | sizeof( lua_Number ) > sizeof( lua_Integer )) \ 65 | ? (_t)luaL_optnumber( _l, _i, _d ) \ 66 | : (_t)luaL_optinteger( _l, _i, _d )) 67 | 68 | 69 | IPC_LOCAL FILE* ipc_checkfile( lua_State* L, int idx ); 70 | IPC_LOCAL FILE* ipc_testfile( lua_State* L, int idx ); 71 | IPC_LOCAL int ipc_getuservaluefield( lua_State* L, int idx, 72 | char const* name ); 73 | IPC_LOCAL void ipc_setuservaluefield( lua_State* L, int idx, 74 | char const* name ); 75 | IPC_LOCAL int ipc_err( char const* file, int line, char const* func, 76 | int code ); 77 | 78 | 79 | /* compatibility functions for older Lua versions */ 80 | #if LUA_VERSION_NUM == 501 81 | 82 | typedef int lua_KContext; 83 | 84 | IPC_LOCAL int ipc_absindex( lua_State* L, int idx ); 85 | #define lua_absindex( L, i ) ipc_absindex( L, i ) 86 | 87 | IPC_LOCAL void* ipc_testudata( lua_State* L, int idx, 88 | char const* name ); 89 | #define luaL_testudata( L, i, n ) ipc_testudata( L, i, n ) 90 | 91 | #define lua_rawlen( L, i ) lua_objlen( L, i ) 92 | 93 | #define lua_setuservalue( L, i ) lua_setfenv( L, i ) 94 | #define lua_getuservalue( L, i ) lua_getfenv( L, i ) 95 | 96 | #define luaL_newlib( L, r ) \ 97 | (lua_newtable( L ), luaL_register( L, NULL, r )) 98 | 99 | #define lua_callk( L, na, nr, ctx, cont ) \ 100 | ((void)ctx, (void)cont, lua_call( L, na, nr )) 101 | 102 | #define lua_pcallk( L, na, nr, err, ctx, cont ) \ 103 | ((void)ctx, (void)cont, lua_pcall( L, na, nr, err )) 104 | 105 | #elif LUA_VERSION_NUM == 502 106 | 107 | typedef int lua_KContext; 108 | 109 | #define LUA_KFUNCTION( _name ) \ 110 | static int (_name)( lua_State* L, int status, lua_KContext ctx ); \ 111 | static int (_name ## _52)( lua_State* L ) { \ 112 | lua_KContext ctx; \ 113 | int status = lua_getctx( L, &ctx ); \ 114 | return (_name)( L, status, ctx ); \ 115 | } \ 116 | static int (_name)( lua_State* L, int status, lua_KContext ctx ) 117 | 118 | #define lua_callk( L, na, nr, ctx, cont ) \ 119 | lua_callk( L, na, nr, ctx, cont ## _52 ) 120 | 121 | #define lua_pcallk( L, na, nr, err, ctx, cont ) \ 122 | lua_pcallk( L, na, nr, err, ctx, cont ## _52 ) 123 | 124 | #ifdef lua_call 125 | # undef lua_call 126 | # define lua_call( L, na, nr ) \ 127 | (lua_callk)( L, na, nr, 0, NULL ) 128 | #endif 129 | 130 | #ifdef lua_pcall 131 | # undef lua_pcall 132 | # define lua_pcall( L, na, nr, err ) \ 133 | (lua_pcallk( L, na, nr, err, 0, NULL ) 134 | #endif 135 | 136 | #endif /* LUA_VERSION_NUM */ 137 | 138 | 139 | #ifndef LUA_KFUNCTION 140 | 141 | /* definition for everything except Lua 5.2 */ 142 | #define LUA_KFUNCTION( _name ) \ 143 | static int (_name)( lua_State* L, int status, lua_KContext ctx ) 144 | 145 | #endif 146 | 147 | 148 | #endif /* IPC_H_ */ 149 | 150 | -------------------------------------------------------------------------------- /luaipc-scm-0.rockspec: -------------------------------------------------------------------------------- 1 | package = "luaipc" 2 | version = "scm-0" 3 | source = { 4 | url = "git://github.com/siffiejoe/lua-luaipc" 5 | } 6 | description = { 7 | summary = "Portable binding for various IPC mechanisms.", 8 | homepage = "https://github.com/siffiejoe/lua-luaipc/", 9 | license = "MIT" 10 | } 11 | -- we probably support more, but it lacks testing (and 12 | -- probably the build details are different): 13 | supported_platforms = { "linux", "windows", "freebsd", "macosx" } 14 | dependencies = { 15 | "lua >= 5.1, < 5.4", 16 | } 17 | 18 | build = { 19 | type = "make", 20 | variables = { 21 | DLL_INSTALL_DIR = "$(LIBDIR)", 22 | LUA_INCDIR = "$(LUA_INCDIR)", 23 | CC = "$(CC)", 24 | CFLAGS = "$(CFLAGS) -DNDEBUG", 25 | LIBFLAG = "$(LIBFLAG)", 26 | LIB_EXTENSION = "$(LIB_EXTENSION)", 27 | EXTRAFLAGS = "", 28 | EXTRALIBS = "", 29 | }, 30 | platforms = { 31 | linux = { 32 | variables = { 33 | EXTRAFLAGS = "-D_POSIX_C_SOURCE=200809L -pthread", 34 | EXTRALIBS = "-lrt" 35 | } 36 | }, 37 | freebsd = { 38 | variables = { 39 | EXTRALIBS = "-lc" 40 | } 41 | }, 42 | windows = { 43 | type = "builtin", 44 | modules = { 45 | ipc = { 46 | sources = { 47 | "ipc.c", "memfile.c", "strfile.c", "shm.c", 48 | "mmap.c", "sem.c", "flock.c", "proc.c", 49 | }, 50 | defines = { 51 | "IPC_API=__declspec(dllexport)", 52 | --"NDEBUG", 53 | "_CRT_SECURE_NO_WARNINGS", 54 | }, 55 | } 56 | } 57 | } 58 | } 59 | } 60 | 61 | -------------------------------------------------------------------------------- /memfile.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "ipc.h" 6 | #include "memfile.h" 7 | 8 | 9 | /* used to identify metatable */ 10 | #define NAME "ipc.memfile" 11 | #define OBJECT_INDEX 1 12 | #define CLOSE_INDEX 2 13 | #define FLUSH_INDEX 3 14 | 15 | typedef struct { 16 | char* addr; 17 | size_t n; 18 | size_t p; 19 | int flags; 20 | } memfile; 21 | 22 | 23 | static memfile* tomemfile( lua_State* L, int idx ) { 24 | memfile* mf = luaL_checkudata( L, idx, NAME ); 25 | if( mf->addr == NULL ) 26 | luaL_error( L, "attempt to use closed in-memory file" ); 27 | return mf; 28 | } 29 | 30 | 31 | static int read_all( lua_State* L, memfile* mf ) { 32 | lua_pushlstring( L, mf->addr + mf->p, mf->n - mf->p ); 33 | mf->p = mf->n; 34 | return LUA_TSTRING; 35 | } 36 | 37 | 38 | static int read_n( lua_State* L, memfile* mf, size_t n ) { 39 | if( mf->p == mf->n ) 40 | lua_pushnil( L ); 41 | else { 42 | if( mf->n - mf->p < n ) 43 | n = mf->n - mf->p; 44 | lua_pushlstring( L, mf->addr + mf->p, n ); 45 | mf->p += n; 46 | } 47 | return lua_type( L, -1 ); 48 | } 49 | 50 | 51 | static int read_line( lua_State* L, memfile* mf, 52 | int store_nl ) { 53 | if( mf->p == mf->n ) 54 | lua_pushnil( L ); 55 | else { 56 | luaL_Buffer b; 57 | int oldc = '\0'; 58 | int c = '\0'; 59 | luaL_buffinit( L, &b ); 60 | while( mf->p != mf->n && 61 | (c = mf->addr[ mf->p++ ]) != '\n' ) { 62 | if( oldc == '\r' ) { 63 | luaL_addchar( &b, oldc ); 64 | oldc = '\0'; 65 | } 66 | if( c == '\r' ) { 67 | oldc = '\r'; 68 | } else 69 | luaL_addchar( &b, c ); 70 | } 71 | if( oldc == '\r' && (c != '\n' || store_nl) ) 72 | luaL_addchar( &b, oldc ); 73 | if( c == '\n' && store_nl ) 74 | luaL_addchar( &b, c ); 75 | luaL_pushresult( &b ); 76 | } 77 | return lua_type( L, -1 ); 78 | } 79 | 80 | 81 | static int lines_iter( lua_State* L ) { 82 | memfile* mf = tomemfile( L, lua_upvalueindex( 1 ) ); 83 | int nupvals = lua_tointeger( L, lua_upvalueindex( 2 ) ); 84 | int i = 3; 85 | lua_settop( L, 0 ); 86 | luaL_checkstack( L, nupvals-2, "not enough stack space" ); 87 | for( i = 3; i <= nupvals; ++i ) { 88 | if( lua_type( L, lua_upvalueindex( i ) ) == LUA_TNUMBER ) { 89 | lua_Number n = lua_tonumber( L, lua_upvalueindex( i ) ); 90 | lua_Integer x = lua_tointeger( L, lua_upvalueindex( i ) ); 91 | if( x < 0 || (lua_Number)x != n ) 92 | luaL_error( L, "invalid format" ); 93 | if( LUA_TNIL == read_n( L, mf, x ) ) 94 | return i-2; 95 | } else { 96 | char const* fmt = lua_tostring( L, lua_upvalueindex( i ) ); 97 | if( !fmt ) 98 | luaL_error( L, "invalid format" ); 99 | if( *fmt == '*' ) /* '*' is optional in later Lua versions */ 100 | fmt++; 101 | switch( *fmt ) { 102 | case 'a': 103 | if( LUA_TNIL == read_all( L, mf ) ) 104 | return i-2; 105 | break; 106 | case 'l': 107 | if( LUA_TNIL == read_line( L, mf, 0 ) ) 108 | return i-2; 109 | break; 110 | case 'L': 111 | if( LUA_TNIL == read_line( L, mf, 1 ) ) 112 | return i-2; 113 | break; 114 | default: 115 | luaL_error( L, "invalid format" ); 116 | break; 117 | } 118 | } 119 | } 120 | return nupvals-2; 121 | } 122 | 123 | 124 | static int memfile_flush( lua_State* L ) { 125 | memfile* mf = tomemfile( L, 1 ); 126 | int top; 127 | if( (mf->flags & MEMFILE_W) == 0 ) { 128 | lua_pushnil( L ); 129 | lua_pushliteral( L, "permission denied" ); 130 | return 2; 131 | } 132 | lua_getuservalue( L, 1 ); 133 | top = lua_gettop( L ); 134 | lua_rawgeti( L, -1, FLUSH_INDEX ); 135 | lua_rawgeti( L, -2, OBJECT_INDEX ); 136 | lua_pushinteger( L, (lua_Integer)mf->p ); 137 | if( lua_type( L, -3 ) == LUA_TFUNCTION ) 138 | lua_call( L, 2, LUA_MULTRET ); 139 | else { 140 | lua_pop( L, 3 ); 141 | lua_pushboolean( L, 1 ); 142 | } 143 | return lua_gettop( L )-top; 144 | } 145 | 146 | 147 | static int memfile_lines( lua_State* L ) { 148 | memfile* mf = tomemfile( L, 1 ); 149 | int top = lua_gettop( L ); 150 | if( (mf->flags & MEMFILE_R) == 0 ) 151 | luaL_error( L, "permission denied" ); 152 | if( top == 1 ) { 153 | lua_pushliteral( L, "l" ); 154 | top = 2; 155 | }; 156 | lua_pushinteger( L, top+1 ); 157 | lua_insert( L, 2 ); /* memfile, nupvals, arg1, ... */ 158 | lua_pushcclosure( L, lines_iter, top+1 ); 159 | return 1; 160 | } 161 | 162 | 163 | static int memfile_read( lua_State* L ) { 164 | memfile* mf = tomemfile( L, 1 ); 165 | int top = lua_gettop( L ); 166 | int i = 2; 167 | if( (mf->flags & MEMFILE_R) == 0 ) { 168 | lua_pushnil( L ); 169 | lua_pushliteral( L, "permission denied" ); 170 | return 2; 171 | } 172 | if( top == 1 ) { 173 | lua_pushliteral( L, "l" ); 174 | top = 2; 175 | } 176 | luaL_checkstack( L, top-1, "not enough stack space" ); 177 | for( i = 2; i <= top; ++i ) { 178 | if( lua_type( L, i ) == LUA_TNUMBER ) { 179 | lua_Number n = lua_tonumber( L, i ); 180 | lua_Integer x = lua_tointeger( L, i ); 181 | if( x < 0 || (lua_Number)x != n ) 182 | luaL_argerror( L, i, "invalid format" ); 183 | if( LUA_TNIL == read_n( L, mf, x ) ) 184 | return i-1; 185 | } else { 186 | char const* fmt = lua_tostring( L, i ); 187 | if( !fmt ) 188 | luaL_argerror( L, i, "invalid format" ); 189 | if( *fmt == '*' ) /* '*' is optional in later Lua versions */ 190 | fmt++; 191 | switch( *fmt ) { 192 | case 'a': 193 | if( LUA_TNIL == read_all( L, mf ) ) 194 | return i-1; 195 | break; 196 | case 'l': 197 | if( LUA_TNIL == read_line( L, mf, 0 ) ) 198 | return i-1; 199 | break; 200 | case 'L': 201 | if( LUA_TNIL == read_line( L, mf, 1 ) ) 202 | return i-1; 203 | break; 204 | default: 205 | luaL_argerror( L, i, "invalid format" ); 206 | break; 207 | } 208 | } 209 | } 210 | return top-1; 211 | } 212 | 213 | 214 | static int memfile_seek( lua_State* L ) { 215 | static char const* const modenames[] = { "set", "cur", "end", NULL }; 216 | memfile* mf = tomemfile( L, 1 ); 217 | int op = luaL_checkoption( L, 2, "cur", modenames ); 218 | lua_Integer offset = luaL_optinteger( L, 3, 0 ); 219 | int ok = 1; 220 | switch( op ) { 221 | case 0: /* SEEK_SET */ 222 | if( offset < 0 || offset > mf->n ) 223 | ok = 0; 224 | else 225 | mf->p = offset; 226 | break; 227 | case 1: /* SEEK_CUR */ 228 | if( (offset < 0 && -(offset+1) >= mf->p ) || 229 | (offset >= 0 && offset > mf->n-mf->p) ) 230 | ok = 0; 231 | else 232 | mf->p += offset; 233 | break; 234 | default: /* SEEK_END */ 235 | if( offset > 0 || 236 | (offset < 0 && -(offset+1) >= mf->n) ) 237 | ok = 0; 238 | else 239 | mf->p = mf->n + offset; 240 | break; 241 | }; 242 | if( ok ) { 243 | lua_pushinteger( L, (lua_Integer)mf->p ); 244 | return 1; 245 | } else { 246 | lua_pushnil( L ); 247 | lua_pushliteral( L, "invalid offset" ); 248 | return 2; 249 | } 250 | } 251 | 252 | 253 | static int memfile_setvbuf( lua_State* L ) { 254 | static char const* const modenames[] = { "no", "full", "line", NULL }; 255 | (void)tomemfile( L, 1 ); 256 | (void)luaL_checkoption(L, 2, NULL, modenames ); 257 | (void)luaL_optinteger( L, 3, 0 ); 258 | /* there are no caches for memory buffers */ 259 | lua_pushboolean( L, 1 ); 260 | return 1; 261 | } 262 | 263 | 264 | static int memfile_write( lua_State* L ) { 265 | memfile* mf = luaL_checkudata( L, 1, NAME ); 266 | int ok = 1; 267 | int arg = 2; 268 | int top = lua_gettop( L ); 269 | if( (mf->flags & MEMFILE_W) == 0 ) { 270 | lua_pushnil( L ); 271 | lua_pushliteral( L, "permission denied" ); 272 | return 2; 273 | } 274 | for( arg = 2; arg <= top && ok; ++arg ) { 275 | size_t len = 0; 276 | char const* s = NULL; 277 | if( NULL == (s = lua_tolstring( L, arg, &len ) ) ) 278 | s = luaL_checklstring( L, arg, &len ); 279 | if( mf->p+len > mf->n ) { 280 | len = mf->n - mf->p; 281 | ok = 0; 282 | } 283 | memcpy( mf->addr + mf->p, s, len ); 284 | mf->p += len; 285 | } 286 | lua_settop( L, 1 ); 287 | if( ok ) 288 | return 1; 289 | else { 290 | lua_pushnil( L ); 291 | lua_pushliteral( L, "not enough space" ); 292 | return 2; 293 | } 294 | } 295 | 296 | 297 | static int memfile_addr( lua_State* L ) { 298 | memfile* mf = tomemfile( L, 1 ); 299 | lua_pushlightuserdata( L, mf->addr ); 300 | return 1; 301 | } 302 | 303 | 304 | static int memfile_size( lua_State* L ) { 305 | memfile* mf = tomemfile( L, 1 ); 306 | lua_pushinteger( L, mf->n ); 307 | return 1; 308 | } 309 | 310 | 311 | static int memfile_truncate( lua_State* L ) { 312 | memfile* mf = tomemfile( L, 1 ); 313 | size_t sz = luaL_checkinteger( L, 2 ); 314 | if( sz > mf->n ) { 315 | lua_pushnil( L ); 316 | lua_pushliteral( L, "new size too large" ); 317 | return 2; 318 | } 319 | mf->n = sz; 320 | if( mf->p > sz ) 321 | mf->p = sz; 322 | lua_pushboolean( L, 1 ); 323 | return 1; 324 | } 325 | 326 | 327 | static int memfile_close( lua_State* L ) { 328 | memfile* mf = tomemfile( L, 1 ); 329 | int top; 330 | lua_getuservalue( L, 1 ); 331 | top = lua_gettop( L ); 332 | lua_rawgeti( L, -1, CLOSE_INDEX ); 333 | lua_rawgeti( L, -2, OBJECT_INDEX ); 334 | if( lua_type( L, -2 ) == LUA_TFUNCTION ) 335 | lua_call( L, 1, LUA_MULTRET ); 336 | else { 337 | lua_pop( L, 2 ); 338 | lua_pushboolean( L, 1 ); 339 | } 340 | mf->addr = NULL; 341 | luaL_checkstack( L, 1, "not enough stack space" ); 342 | #if LUA_VERSION_NUM > 501 343 | lua_pushnil( L ); 344 | #else 345 | lua_pushvalue( L, LUA_GLOBALSINDEX ); 346 | #endif 347 | lua_setuservalue( L, 1 ); 348 | return lua_gettop( L )-top; 349 | } 350 | 351 | 352 | static int memfile_gc( lua_State* L ) { 353 | memfile* mf = lua_touserdata( L, 1 ); 354 | if( mf->addr != NULL ) { 355 | lua_getuservalue( L, 1 ); 356 | lua_rawgeti( L, -1, CLOSE_INDEX ); 357 | lua_rawgeti( L, -2, OBJECT_INDEX ); 358 | if( lua_type( L, -2 ) == LUA_TFUNCTION ) 359 | lua_call( L, 1, 0 ); 360 | } 361 | return 0; 362 | } 363 | 364 | 365 | IPC_LOCAL void* memfile_udata( lua_State* L, int idx, 366 | char const* name ) { 367 | int i = lua_absindex( L, idx ); 368 | (void)tomemfile( L, i ); 369 | lua_getuservalue( L, i ); 370 | lua_rawgeti( L, -1, OBJECT_INDEX ); 371 | lua_replace( L, i ); /* replace memfile object */ 372 | lua_pop( L, 1 ); /* remove uservalue table */ 373 | return luaL_checkudata( L, i, name ); 374 | } 375 | 376 | 377 | IPC_LOCAL void memfile_new( lua_State* L, void* addr, size_t n, 378 | int perms, int oidx, int closeidx, 379 | int flushidx ) { 380 | memfile* mf = NULL; 381 | oidx = lua_absindex( L, oidx ); 382 | closeidx = lua_absindex( L, closeidx ); 383 | flushidx = lua_absindex( L, flushidx ); 384 | mf = lua_newuserdata( L, sizeof( *mf ) ); 385 | mf->addr = addr; 386 | mf->n = n; 387 | mf->p = 0; 388 | mf->flags = perms; 389 | lua_newtable( L ); 390 | if( oidx ) { 391 | lua_pushvalue( L, oidx ); 392 | lua_rawseti( L, -2, OBJECT_INDEX ); 393 | } 394 | if( closeidx ) { 395 | lua_pushvalue( L, closeidx ); 396 | lua_rawseti( L, -2, CLOSE_INDEX ); 397 | } 398 | if( flushidx ) { 399 | lua_pushvalue(L, flushidx ); 400 | lua_rawseti( L, -2, FLUSH_INDEX ); 401 | } 402 | lua_setuservalue( L, -2 ); 403 | if( luaL_newmetatable( L, NAME ) ) { 404 | luaL_Reg const methods[] = { 405 | { "flush", memfile_flush }, 406 | { "lines", memfile_lines }, 407 | { "read", memfile_read }, 408 | { "seek", memfile_seek }, 409 | { "setvbuf", memfile_setvbuf }, 410 | { "write", memfile_write }, 411 | { "addr", memfile_addr }, 412 | { "size", memfile_size }, 413 | { "truncate", memfile_truncate }, 414 | { "close", memfile_close }, 415 | { NULL, NULL } 416 | }; 417 | luaL_newlib( L, methods ); 418 | lua_setfield( L, -2, "__index" ); 419 | lua_pushcfunction( L, memfile_gc ); 420 | lua_setfield( L, -2, "__gc" ); 421 | lua_pushboolean( L, 0 ); 422 | lua_setfield( L, -2, "__metatable" ); 423 | } 424 | lua_setmetatable( L, -2 ); 425 | } 426 | 427 | -------------------------------------------------------------------------------- /memfile.h: -------------------------------------------------------------------------------- 1 | #ifndef MEMFILE_H_ 2 | #define MEMFILE_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include "ipc.h" 8 | 9 | 10 | #define MEMFILE_R 1 11 | #define MEMFILE_W 2 12 | #define MEMFILE_RW (MEMFILE_R|MEMFILE_W) 13 | 14 | 15 | IPC_LOCAL void* memfile_udata( lua_State* L, int idx, 16 | char const* name ); 17 | 18 | IPC_LOCAL void memfile_new( lua_State* L, void* addr, size_t n, 19 | int perms, int oidx, int closeidx, 20 | int flushidx ); 21 | 22 | 23 | #endif /* MEMFILE_H_ */ 24 | 25 | -------------------------------------------------------------------------------- /mmap.c: -------------------------------------------------------------------------------- 1 | #ifndef _POSIX_C_SOURCE 2 | # define _POSIX_C_SOURCE 200112L 3 | #endif 4 | #ifndef _FILE_OFFSET_BITS 5 | # define _LARGEFILE_SOURCE 1 6 | # define _FILE_OFFSET_BITS 64 7 | #endif 8 | #include 9 | #include 10 | #include 11 | #include "ipc.h" 12 | #include "memfile.h" 13 | 14 | 15 | /* check for POSIX */ 16 | #if defined( unix ) || defined( __unix ) || defined( __unix__ ) || \ 17 | (defined( __APPLE__ ) && defined( __MACH__ )) || \ 18 | HAVE_UNISTD_H 19 | # include 20 | # if defined( _POSIX_VERSION ) && _POSIX_VERSION >= 200112L && \ 21 | defined( _POSIX_MAPPED_FILES ) && _POSIX_MAPPED_FILES > 0 22 | # define HAVE_MMAP 23 | # include "mmap_posix.h" 24 | # endif 25 | #endif 26 | 27 | 28 | /* check for Windows */ 29 | #if !defined( HAVE_MMAP ) && \ 30 | defined( _WIN32 ) && !defined( __CYGWIN__ ) 31 | # define HAVE_MMAP 32 | # include "mmap_win.h" 33 | #endif 34 | 35 | 36 | #ifdef HAVE_MMAP 37 | 38 | #define NAME "ipc.mmap" 39 | 40 | typedef struct { 41 | ipc_mmap_handle h; /* platform specific data */ 42 | /* extra management info: */ 43 | char is_valid; 44 | } l_mmap_handle; 45 | 46 | 47 | static int pusherror( lua_State* L, int code ) { 48 | char buf[ IPC_MAXERRMSG ]; 49 | ipc_mmap_error( buf, sizeof( buf ), code ); 50 | lua_pushnil( L ); 51 | lua_pushstring( L, buf ); 52 | lua_pushinteger( L, code ); 53 | return 3; 54 | } 55 | 56 | 57 | static int l_mmap_close( lua_State* L ) { 58 | l_mmap_handle* h = luaL_checkudata( L, 1, NAME ); 59 | int rv = 0; 60 | if( !h->is_valid ) 61 | luaL_error( L, "attempt to use invalid mmap object" ); 62 | rv = ipc_mmap_close( &h->h ); 63 | if( rv != 0 ) 64 | return pusherror( L, rv ); 65 | h->is_valid = 0; 66 | lua_pushboolean( L, 1 ); 67 | return 1; 68 | } 69 | 70 | 71 | static int l_mmap_gc( lua_State* L ) { 72 | l_mmap_handle* h = lua_touserdata( L, 1 ); 73 | if( h->is_valid ) 74 | ipc_mmap_close( &h->h ); 75 | return 0; 76 | } 77 | 78 | 79 | #ifdef IPC_MMAP_HAVE_FLUSH 80 | static int l_mmap_flush( lua_State* L ) { 81 | l_mmap_handle* h = luaL_checkudata( L, 1, NAME ); 82 | size_t pos = luaL_checkinteger( L, 2 ); 83 | int rv = 0; 84 | if( !h->is_valid ) 85 | luaL_error( L, "attempt to use invalid mmap object" ); 86 | rv = ipc_mmap_flush( &h->h, pos ); 87 | if( rv != 0 ) 88 | return pusherror( L, rv ); 89 | lua_pushboolean( L, 1 ); 90 | return 1; 91 | } 92 | #endif 93 | 94 | 95 | static int l_mmap_open( lua_State* L ) { 96 | static char const* const modenames[] = { 97 | "r", "w", "rw", NULL 98 | }; 99 | static int const modes[] = { 100 | MEMFILE_R, MEMFILE_W, MEMFILE_RW 101 | }; 102 | char const* name = luaL_checkstring( L, 1 ); 103 | int mode = modes[ luaL_checkoption( L, 2, "r", modenames ) ]; 104 | ipc_mmap_off_t offset = IPC_OPTBIGINT( ipc_mmap_off_t, L, 3, 0 ); 105 | size_t size = luaL_optinteger( L, 4, 0 ); 106 | l_mmap_handle* h = lua_newuserdata( L, sizeof( *h ) ); 107 | int rv = 0; 108 | h->is_valid = 0; 109 | luaL_getmetatable( L, NAME ); 110 | lua_setmetatable( L, -2 ); 111 | rv = ipc_mmap_open( &h->h, name, mode, offset, size ); 112 | if( rv != 0 ) 113 | return pusherror( L, rv ); 114 | h->is_valid = 1; 115 | lua_pushcfunction( L, l_mmap_close ); 116 | #ifdef IPC_MMAP_HAVE_FLUSH 117 | lua_pushcfunction( L, l_mmap_flush ); 118 | memfile_new( L, ipc_mmap_addr( &h->h ), ipc_mmap_size( &h->h ), 119 | mode, -3, -2, -1 ); 120 | #else 121 | memfile_new( L, ipc_mmap_addr( &h->h ), ipc_mmap_size( &h->h ), 122 | mode, -2, -1, 0 ); 123 | #endif 124 | return 1; 125 | } 126 | 127 | 128 | IPC_API int luaopen_ipc_mmap( lua_State* L ) { 129 | luaL_Reg const functions[] = { 130 | { "open", l_mmap_open }, 131 | { NULL, NULL }, /* reserve space */ 132 | { NULL, NULL } 133 | }; 134 | if( !luaL_newmetatable( L, NAME ) ) 135 | luaL_error( L, "redefinition of metatable '%s'", NAME ); 136 | lua_pushcfunction( L, l_mmap_gc ); 137 | lua_setfield( L, -2, "__gc" ); 138 | lua_pop( L, 1 ); 139 | luaL_newlib( L, functions ); 140 | lua_pushinteger( L, (lua_Integer)ipc_mmap_pagesize() ); 141 | lua_setfield( L, -2, "pagesize" ); 142 | return 1; 143 | } 144 | 145 | #else /* no implementation for this platform available: */ 146 | IPC_API int luaopen_ipc_mmap( lua_State* L ) { 147 | IPC_NOTIMPLEMENTED( L ); 148 | return 0; 149 | } 150 | #endif 151 | 152 | -------------------------------------------------------------------------------- /mmap_posix.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | 12 | typedef off_t ipc_mmap_off_t; 13 | 14 | 15 | typedef struct { 16 | void* addr; 17 | size_t len; 18 | } ipc_mmap_handle; 19 | 20 | static void* ipc_mmap_addr( ipc_mmap_handle* h ) { 21 | return h->addr; 22 | } 23 | 24 | static size_t ipc_mmap_size( ipc_mmap_handle* h ) { 25 | return h->len; 26 | } 27 | 28 | 29 | static void ipc_mmap_error( char* buf, size_t len, int code ) { 30 | if( len > 0 && strerror_r( code, buf, len ) != (int)0 ) { 31 | strncpy( buf, "unknown error", len-1 ); 32 | buf[ len-1 ] = '\0'; 33 | } 34 | } 35 | 36 | 37 | static size_t ipc_mmap_pagesize( void ) { 38 | long result = sysconf( _SC_PAGESIZE ); 39 | if( result < 1 ) 40 | result = 4096; 41 | return (size_t)result; 42 | } 43 | 44 | 45 | static int ipc_mmap_open( ipc_mmap_handle* h, char const* name, 46 | int mode, off_t offset, size_t size ) { 47 | int fd, oflags = 0, mmflags = 0; 48 | if( (mode & MEMFILE_RW) == MEMFILE_RW ) { 49 | oflags = O_RDWR; 50 | mmflags = PROT_READ | PROT_WRITE; 51 | } else if( mode & MEMFILE_R ) { 52 | oflags = O_RDONLY; 53 | mmflags = PROT_READ; 54 | } else if( mode & MEMFILE_W ) { 55 | oflags = O_RDWR; 56 | mmflags = PROT_WRITE; 57 | } 58 | #ifdef O_CLOEXEC 59 | oflags |= O_CLOEXEC; 60 | #endif 61 | fd = open( name, oflags ); 62 | if( fd < 0 ) 63 | return IPC_ERR( errno ); 64 | h->len = size; 65 | if( size == 0 ) { /* figure out its size */ 66 | struct stat buf; 67 | if( fstat( fd, &buf ) < 0 ) { 68 | int saved_errno = errno; 69 | close( fd ); 70 | return IPC_ERR( saved_errno ); 71 | } 72 | if( buf.st_size < offset ) { 73 | close( fd ); 74 | return IPC_ERR( EINVAL ); 75 | } 76 | if( buf.st_size - offset > (size_t)-1 ) 77 | h->len = (size_t)-1; 78 | else 79 | h->len = buf.st_size - offset; 80 | } 81 | /* create mmap */ 82 | h->addr = mmap( NULL, h->len, mmflags, MAP_SHARED, fd, offset ); 83 | if( h->addr == MAP_FAILED ) { 84 | int saved_errno = errno; 85 | close( fd ); 86 | return IPC_ERR( saved_errno ); 87 | } 88 | close( fd ); /* we don't need it anymore! */ 89 | return 0; 90 | } 91 | 92 | 93 | static int ipc_mmap_close( ipc_mmap_handle* h ) { 94 | int rv = munmap( h->addr, h->len ); 95 | if( rv < 0 ) 96 | return IPC_ERR( errno ); 97 | return 0; 98 | } 99 | 100 | 101 | #if defined( _POSIX_SYNCHRONIZED_IO ) && _POSIX_SYNCHRONIZED_IO > 0 102 | # define IPC_MMAP_HAVE_FLUSH 103 | static int ipc_mmap_flush( ipc_mmap_handle* h, size_t pos ) { 104 | int rv = msync( h->addr, pos, MS_ASYNC|MS_INVALIDATE ); 105 | if( rv < 0 ) 106 | return IPC_ERR( errno ); 107 | return 0; 108 | } 109 | #endif 110 | 111 | -------------------------------------------------------------------------------- /mmap_win.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #define WIN32_LEAN_AND_MEAN 6 | #include 7 | 8 | 9 | typedef ULONGLONG ipc_mmap_off_t; 10 | 11 | 12 | typedef struct { 13 | void* addr; 14 | size_t len; 15 | } ipc_mmap_handle; 16 | 17 | static void* ipc_mmap_addr( ipc_mmap_handle* h ) { 18 | return h->addr; 19 | } 20 | 21 | static size_t ipc_mmap_size( ipc_mmap_handle* h ) { 22 | return h->len; 23 | } 24 | 25 | 26 | static void ipc_mmap_error( char* buf, size_t len, int code ) { 27 | if( len > 0 ) { 28 | if( 0 == FormatMessageA( FORMAT_MESSAGE_FROM_SYSTEM | 29 | FORMAT_MESSAGE_IGNORE_INSERTS, 30 | NULL, 31 | code, 32 | 0, 33 | buf, 34 | len, 35 | NULL ) ) { 36 | strncpy( buf, "unknown error", len-1 ); 37 | buf[ len-1 ] = '\0'; 38 | } else { /* Windows puts an extra newline in there! */ 39 | size_t n = strlen( buf ); 40 | while( n > 0 && isspace( (unsigned char)buf[ --n ] ) ) 41 | buf[ n ] = '\0'; 42 | } 43 | } 44 | } 45 | 46 | 47 | static size_t ipc_mmap_pagesize( void ) { 48 | SYSTEM_INFO si; 49 | GetSystemInfo( &si ); 50 | return si.dwAllocationGranularity; 51 | } 52 | 53 | 54 | static int ipc_mmap_open( ipc_mmap_handle* h, char const* name, 55 | int mode, ULONGLONG offset, size_t size ) { 56 | HANDLE hfile; 57 | HANDLE hmap; 58 | ULONGLONG msize; 59 | int cfflags = 0; 60 | int fmflags = 0; 61 | int mvflags = 0; 62 | /* figure out the open flags */ 63 | if( mode & MEMFILE_W ) { 64 | cfflags = GENERIC_READ | GENERIC_WRITE; 65 | fmflags = PAGE_READWRITE; 66 | mvflags = FILE_MAP_ALL_ACCESS; 67 | } else if( mode & MEMFILE_R ) { 68 | cfflags = GENERIC_READ; 69 | fmflags = PAGE_READONLY; 70 | mvflags = FILE_MAP_READ; 71 | } 72 | /* open file */ 73 | hfile = CreateFileA( name, 74 | cfflags, 75 | FILE_SHARE_READ | 76 | FILE_SHARE_WRITE, 77 | NULL, 78 | OPEN_EXISTING, 79 | FILE_ATTRIBUTE_NORMAL, 80 | NULL ); 81 | if( hfile == INVALID_HANDLE_VALUE ) 82 | return IPC_ERR( GetLastError() ); 83 | h->len = size; 84 | if( size == 0 ) { /* figure out its size */ 85 | LARGE_INTEGER fsize; 86 | if( !GetFileSizeEx( hfile, &fsize ) ) { 87 | int saved_errno = GetLastError(); 88 | CloseHandle( hfile ); 89 | return IPC_ERR( saved_errno ); 90 | } 91 | if( fsize.QuadPart < offset ) { 92 | CloseHandle( hfile ); 93 | return IPC_ERR( ERROR_INVALID_PARAMETER ); 94 | } 95 | if( fsize.QuadPart - offset > (size_t)-1 ) 96 | h->len = (size_t)-1; 97 | else 98 | h->len = fsize.QuadPart - offset; 99 | } 100 | msize = (ULONGLONG)h->len + offset; 101 | /* create the anonymous file mapping */ 102 | hmap = CreateFileMappingA( hfile, 103 | NULL, 104 | fmflags, 105 | (DWORD)(msize >> 32), 106 | (DWORD)msize, 107 | NULL ); 108 | if( hmap == NULL ) { 109 | int saved_errno = GetLastError(); 110 | CloseHandle( hfile ); 111 | return IPC_ERR( saved_errno ); 112 | } 113 | /* get an address for the file mapping */ 114 | h->addr = MapViewOfFile( hmap, 115 | mvflags, 116 | (DWORD)(offset >> 32), 117 | (DWORD)offset, 118 | 0 ); 119 | if( h->addr == NULL ) { 120 | int saved_errno = GetLastError(); 121 | CloseHandle( hmap ); 122 | CloseHandle( hfile ); 123 | return IPC_ERR( saved_errno ); 124 | } 125 | CloseHandle( hmap ); 126 | CloseHandle( hfile ); 127 | return 0; 128 | } 129 | 130 | 131 | static int ipc_mmap_close( ipc_mmap_handle* h ) { 132 | if( !UnmapViewOfFile( h->addr ) ) 133 | return IPC_ERR( GetLastError() ); 134 | return 0; 135 | } 136 | 137 | 138 | #define IPC_MMAP_HAVE_FLUSH 139 | static int ipc_mmap_flush( ipc_mmap_handle* h, size_t pos ) { 140 | if( !FlushViewOfFile( h->addr, pos ) ) 141 | return IPC_ERR( GetLastError() ); 142 | return 0; 143 | } 144 | 145 | -------------------------------------------------------------------------------- /osx/sem_timedwait.c: -------------------------------------------------------------------------------- 1 | /* 2 | * s e m _ t i m e d w a i t 3 | * 4 | * Function: 5 | * Implements a version of sem_timedwait(). 6 | * 7 | * Description: 8 | * Not all systems implement sem_timedwait(), which is a version of 9 | * sem_wait() with a timeout. Mac OS X is one example, at least up to 10 | * and including version 10.6 (Leopard). If such a function is needed, 11 | * this code provides a reasonable implementation, which I think is 12 | * compatible with the standard version, although possibly less 13 | * efficient. It works by creating a thread that interrupts a normal 14 | * sem_wait() call after the specified timeout. 15 | * 16 | * Call: 17 | * 18 | * The Linux man pages say: 19 | * 20 | * #include 21 | * 22 | * int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout); 23 | * 24 | * sem_timedwait() is the same as sem_wait(), except that abs_timeout 25 | * specifies a limit on the amount of time that the call should block if 26 | * the decrement cannot be immediately performed. The abs_timeout argument 27 | * points to a structure that specifies an absolute timeout in seconds and 28 | * nanoseconds since the Epoch (00:00:00, 1 January 1970). This structure 29 | * is defined as follows: 30 | * 31 | * struct timespec { 32 | * time_t tv_sec; Seconds 33 | * long tv_nsec; Nanoseconds [0 .. 999999999] 34 | * }; 35 | * 36 | * If the timeout has already expired by the time of the call, and the 37 | * semaphore could not be locked immediately, then sem_timedwait() fails 38 | * with a timeout error (errno set to ETIMEDOUT). 39 | * If the operation can be performed immediately, then sem_timedwait() 40 | * never fails with a timeout error, regardless of the value of abs_timeout. 41 | * Furthermore, the validity of abs_timeout is not checked in this case. 42 | * 43 | * Limitations: 44 | * 45 | * The mechanism used involves sending a SIGUSR2 signal to the thread 46 | * calling sem_timedwait(). The handler for this signal is set to a null 47 | * routine which does nothing, and with any flags for the signal 48 | * (eg SA_RESTART) cleared. Note that this effective disabling of the 49 | * SIGUSR2 signal is a side-effect of using this routine, and means it 50 | * may not be a completely transparent plug-in replacement for a 51 | * 'normal' sig_timedwait() call. Since OS X does not declare the 52 | * sem_timedwait() call in its standard include files, the relevant 53 | * declaration (shown above in the man pages extract) will probably have 54 | * to be added to any code that uses this. 55 | * 56 | * Compiling: 57 | * This compiles and runs cleanly on OS X (10.6) with gcc with the 58 | * -Wall -ansi -pedantic flags. On Linux, using -ansi causes a sweep of 59 | * compiler complaints about the timespec structure, but it compiles 60 | * and works fine with just -Wall -pedantic. (Since Linux provides 61 | * sem_timedwait() anyway, this really isn't needed on Linux.) However, 62 | * since Linux provides sem_timedwait anyway, the sem_timedwait() 63 | * code in this file is only compiled on OS X, and is a null on other 64 | * systems. 65 | * 66 | * Testing: 67 | * This file contains a test program that exercises the sem_timedwait 68 | * code. It is compiled if the pre-processor variable TEST is defined. 69 | * For more details, see the comments for the test routine at the end 70 | * of the file. 71 | * 72 | * Author: Keith Shortridge, AAO. 73 | * 74 | * History: 75 | * 8th Sep 2009. Original version. KS. 76 | * 24th Sep 2009. Added test that the calling thread still exists before 77 | * trying to set the timed-out flag. KS. 78 | * 2nd Oct 2009. No longer restores the original SIGUSR2 signal handler. 79 | * See comments in the body of the code for more details. 80 | * Prototypes for now discontinued internal routines removed. 81 | * 12th Aug 2010. Added the cleanup handler, so that this code no longer 82 | * leaks resources if the calling thread is cancelled. KS. 83 | * 21st Sep 2011. Added copyright notice below. Modified header comments 84 | * to describe the use of SIGUSR2 more accurately in the 85 | * light of the 2/10/09 change above. Now undefs DEBUG 86 | * before defining it, to avoid any possible clash. KS. 87 | * 14th Feb 2012. Tidied out a number of TABs that had got into the 88 | * code. KS. 89 | * 6th May 2013. Copyright notice modified to one based on the MIT licence, 90 | * which is more permissive than the previous notice. KS. 91 | * 92 | * Copyright (c) Australian Astronomical Observatory (AAO), (2013). 93 | * Permission is hereby granted, free of charge, to any person obtaining a 94 | * copy of this software and associated documentation files (the "Software"), 95 | * to deal in the Software without restriction, including without limitation 96 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 97 | * and/or sell copies of the Software, and to permit persons to whom the 98 | * Software is furnished to do so, subject to the following conditions: 99 | * 100 | * The above copyright notice and this permission notice shall be included in 101 | * all copies or substantial portions of the Software. 102 | * 103 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 104 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 105 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 106 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 107 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 108 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 109 | * IN THE SOFTWARE. 110 | */ 111 | 112 | #ifdef __APPLE__ 113 | 114 | #include 115 | #include 116 | #include 117 | #include 118 | #include 119 | #include 120 | #include 121 | #include 122 | #include 123 | #include 124 | #include 125 | 126 | #include "sem_timedwait.h" 127 | 128 | /* Some useful definitions - TRUE, FALSE, and DEBUG */ 129 | 130 | #undef TRUE 131 | #define TRUE 1 132 | #undef FALSE 133 | #define FALSE 0 134 | #undef DEBUG 135 | #define DEBUG printf 136 | 137 | /* A structure of type timeoutDetails is passed to the thread used to 138 | * implement the timeout. 139 | */ 140 | 141 | typedef struct { 142 | struct timespec delay; /* Specifies the delay, relative to now */ 143 | pthread_t callingThread; /* The thread doing the sem_wait call */ 144 | volatile short *timedOutShort; /* Address of a flag set to indicate that 145 | * the timeout was triggered. */ 146 | } timeoutDetails; 147 | 148 | /* A structure of type cleanupDetails is passed to the thread cleanup 149 | * routine which is called at the end of the routine or if the thread calling 150 | * it is cancelled. 151 | */ 152 | 153 | typedef struct { 154 | pthread_t *threadIdAddr; /* Address of the variable that holds 155 | * the Id of the timeout thread. */ 156 | struct sigaction *sigHandlerAddr; /* Address of the old signal action 157 | * handler. */ 158 | volatile short *timedOutShort; /* Address of a flag set to indicate that 159 | * the timeout was triggered. */ 160 | } cleanupDetails; 161 | 162 | /* Forward declarations of internal routines */ 163 | 164 | static void* timeoutThreadMain (void* passedPtr); 165 | static int triggerSignal (int Signal, pthread_t Thread); 166 | static void ignoreSignal (int Signal); 167 | static void timeoutThreadCleanup (void* passedPtr); 168 | 169 | /* -------------------------------------------------------------------------- */ 170 | /* 171 | * s e m _ t i m e d w a i t 172 | * 173 | * This is the main code for the sem_timedwait() implementation. 174 | */ 175 | 176 | int sem_timedwait ( 177 | sem_t *sem, 178 | const struct timespec *abs_timeout) 179 | { 180 | int result = 0; /* Code returned by this routine 0 or -1 */ 181 | 182 | /* "Under no circumstances shall the function fail if the semaphore 183 | * can be locked immediately". So we try to get it quickly to see if we 184 | * can avoid all the timeout overheads. 185 | */ 186 | 187 | if (sem_trywait(sem) == 0) { 188 | 189 | /* Yes, got it immediately. */ 190 | 191 | result = 0; 192 | 193 | } else { 194 | 195 | /* No, we've got to do it with a sem_wait() call and a thread to run 196 | * the timeout. First, work out the time from now to the specified 197 | * timeout, which we will pass to the timeout thread in a way that can 198 | * be used to pass to nanosleep(). So we need this in seconds and 199 | * nanoseconds. Along the way, we check for an invalid passed time, 200 | * and for one that's already expired. 201 | */ 202 | 203 | if ((abs_timeout->tv_nsec < 0) || (abs_timeout->tv_nsec > 1000000000)) { 204 | 205 | /* Passed time is invalid */ 206 | 207 | result = -1; 208 | errno = EINVAL; 209 | 210 | } else { 211 | 212 | struct timeval currentTime; /* Time now */ 213 | long secsToWait,nsecsToWait; /* Seconds and nsec to delay */ 214 | gettimeofday (¤tTime,NULL); 215 | secsToWait = abs_timeout->tv_sec - currentTime.tv_sec; 216 | nsecsToWait = (abs_timeout->tv_nsec - (currentTime.tv_usec * 1000)); 217 | while (nsecsToWait < 0) { 218 | nsecsToWait += 1000000000; 219 | secsToWait--; 220 | } 221 | if ((secsToWait < 0) || ((secsToWait == 0) && (nsecsToWait < 0))) { 222 | 223 | /* Time has passed. Report an immediate timeout. */ 224 | 225 | result = -1; 226 | errno = ETIMEDOUT; 227 | 228 | } else { 229 | 230 | /* We're going to have to do a sem_wait() with a timeout thread. 231 | * The thread will wait the specified time, then will issue a 232 | * SIGUSR2 signal that will interrupt the sem_wait() call. 233 | * We pass the thread the id of the current thread, the delay, 234 | * and the address of a flag to set on a timeout, so we can 235 | * distinguish an interrupt caused by the timeout thread from 236 | * one caused by some other signal. 237 | */ 238 | 239 | volatile short timedOut; /* Flag to set on timeout */ 240 | timeoutDetails details; /* All the stuff the thread must know */ 241 | struct sigaction oldSignalAction; /* Current signal setting */ 242 | pthread_t timeoutThread; /* Id of timeout thread */ 243 | cleanupDetails cleaningDetails; /* What the cleanup routine needs */ 244 | int oldCancelState; /* Previous cancellation state */ 245 | int ignoreCancelState; /* Used in call, but ignored */ 246 | int createStatus; /* Status of pthread_create() call */ 247 | 248 | /* If the current thread is cancelled (and CML does do this) 249 | * we don't want to leave our timer thread running - if we've 250 | * started the thread we want to make sure we join it in order 251 | * to release its resources. So we set a cleanup handler to 252 | * do this. We pass it the address of the structure that will 253 | * hold all it needs to know. While we set all this up, 254 | * we prevent ourselves being cancelled, so all this data is 255 | * coherent. 256 | */ 257 | 258 | pthread_setcancelstate (PTHREAD_CANCEL_DISABLE,&oldCancelState); 259 | timeoutThread = (pthread_t) 0; 260 | cleaningDetails.timedOutShort = &timedOut; 261 | cleaningDetails.threadIdAddr = &timeoutThread; 262 | cleaningDetails.sigHandlerAddr = &oldSignalAction; 263 | pthread_cleanup_push (timeoutThreadCleanup,&cleaningDetails); 264 | 265 | /* Set up the details for the thread. Clear the timeout flag, 266 | * record the current SIGUSR2 action settings so we can restore 267 | * them later. 268 | */ 269 | 270 | details.delay.tv_sec = secsToWait; 271 | details.delay.tv_nsec = nsecsToWait; 272 | details.callingThread = pthread_self(); 273 | details.timedOutShort = &timedOut; 274 | timedOut = FALSE; 275 | sigaction (SIGUSR2,NULL,&oldSignalAction); 276 | 277 | /* Start up the timeout thread. Once we've done that, we can 278 | * restore the previous cancellation state. 279 | */ 280 | 281 | createStatus = pthread_create(&timeoutThread,NULL, 282 | timeoutThreadMain, (void*)&details); 283 | pthread_setcancelstate (oldCancelState,&ignoreCancelState); 284 | 285 | if (createStatus < 0) { 286 | 287 | /* Failed to create thread. errno will already be set properly */ 288 | 289 | result = -1; 290 | 291 | } else { 292 | 293 | /* Thread created OK. This is where we wait for the semaphore. 294 | */ 295 | 296 | if (sem_wait(sem) == 0) { 297 | 298 | /* Got the semaphore OK. We return zero, and all's well. */ 299 | 300 | result = 0; 301 | 302 | } else { 303 | 304 | /* If we got a -1 error from sem_wait(), it may be because 305 | * it was interrupted by a timeout, or failed for some 306 | * other reason. We check for the expected timeout 307 | * condition, which is an 'interrupted' status and the 308 | * timeout flag set by the timeout thread. We report that as 309 | * a timeout error. Anything else is some other error and 310 | * errno is already set properly. 311 | */ 312 | 313 | result = -1; 314 | if (errno == EINTR) { 315 | if (timedOut) errno = ETIMEDOUT; 316 | } 317 | } 318 | 319 | } 320 | 321 | /* The cleanup routine - timeoutThreadCleanup() - packages up 322 | * any tidying up that is needed, including joining with the 323 | * timer thread. This will be called if the current thread is 324 | * cancelled, but we need it to happen anyway, so we set the 325 | * execute flag true here as we remove it from the list of 326 | * cleanup routines to be called. So normally, this line amounts 327 | * to calling timeoutThreadCleanup(). 328 | */ 329 | 330 | pthread_cleanup_pop (TRUE); 331 | } 332 | } 333 | } 334 | return (result); 335 | } 336 | 337 | /* -------------------------------------------------------------------------- */ 338 | /* 339 | * t i m e o u t T h r e a d C l e a n u p 340 | * 341 | * This internal routine tidies up at the end of a sem_timedwait() call. 342 | * It is set as a cleanup routine for the current thread (not the timer 343 | * thread) so it is executed even if the thread is cancelled. This is 344 | * important, as we need to tidy up the timeout thread. If we took the 345 | * semaphore (in other words, if we didn't timeout) then the timer thread 346 | * will still be running, sitting in its nanosleep() call, and we need 347 | * to cancel it. If the timer thread did signal a timeout then it will 348 | * now be closing down. In either case, we need to join it (using a call 349 | * to pthread_join()) or its resources will never be released. 350 | * The single argument is a pointer to a cleanupDetails structure that has 351 | * all the routine needs to know. 352 | */ 353 | 354 | static void timeoutThreadCleanup (void* passedPtr) 355 | { 356 | /* Get what we need from the structure we've been passed. */ 357 | 358 | cleanupDetails *detailsPtr = (cleanupDetails*) passedPtr; 359 | short timedOut = *(detailsPtr->timedOutShort); 360 | pthread_t timeoutThread = *(detailsPtr->threadIdAddr); 361 | 362 | /* If we created the thread, stop it - doesn't matter if it's no longer 363 | * running, pthread_cancel can handle that. We make sure we wait for it 364 | * to complete, because it is this pthread_join() call that releases any 365 | * memory the thread may have allocated. Note that cancelling a thread is 366 | * generally not a good idea, because of the difficulty of cleaning up 367 | * after it, but this is a very simple thread that does nothing but call 368 | * nanosleep(), and that we can cancel quite happily. 369 | */ 370 | 371 | if (!timedOut) pthread_cancel(timeoutThread); 372 | pthread_join(timeoutThread,NULL); 373 | 374 | /* The code originally restored the old action handler, which generally 375 | * was the default handler that caused the task to exit. Just occasionally, 376 | * there seem to be cases where the signal is still queued and ready to 377 | * trigger even though the thread that presumably sent it off just before 378 | * it was cancelled has finished. I had thought that once we'd joined 379 | * that thread, we could be sure of not seeing the signal, but that seems 380 | * not to be the case, and so restoring a handler that will allow the task 381 | * to crash is not a good idea, and so the line below has been commented 382 | * out. 383 | * 384 | * sigaction (SIGUSR2,detailsPtr->sigHandlerAddr,NULL); 385 | */ 386 | } 387 | 388 | /* -------------------------------------------------------------------------- */ 389 | /* 390 | * t i m e o u t T h r e a d M a i n 391 | * 392 | * This internal routine is the main code for the timeout thread. 393 | * The single argument is a pointer to a timeoutDetails structure that has 394 | * all the thread needs to know - thread to signal, delay time, and the 395 | * address of a flag to set if it triggers a timeout. 396 | */ 397 | 398 | static void* timeoutThreadMain (void* passedPtr) 399 | { 400 | void* Return = (void*) 0; 401 | 402 | /* We grab all the data held in the calling thread right now. In some 403 | * cases, we find that the calling thread has vanished and released 404 | * its memory, including the details structure, by the time the timeout 405 | * expires, and then we get an access violation when we try to set the 406 | * 'timed out' flag. 407 | */ 408 | 409 | timeoutDetails details = *((timeoutDetails*) passedPtr); 410 | struct timespec requestedDelay = details.delay; 411 | 412 | /* We do a nanosleep() for the specified delay, and then trigger a 413 | * timeout. Note that we allow for the case where the nanosleep() is 414 | * interrupted, and restart it for the remaining time. If the 415 | * thread that is doing the sem_wait() call gets the semaphore, it 416 | * will cancel this thread, which is fine as we aren't doing anything 417 | * other than a sleep and a signal. 418 | */ 419 | 420 | for (;;) { 421 | struct timespec remainingDelay; 422 | if (nanosleep (&requestedDelay,&remainingDelay) == 0) { 423 | break; 424 | } else if (errno == EINTR) { 425 | requestedDelay = remainingDelay; 426 | } else { 427 | Return = (void*) errno; 428 | break; 429 | } 430 | } 431 | 432 | /* We've completed the delay without being cancelled, so we now trigger 433 | * the timeout by sending a signal to the calling thread. And that's it, 434 | * although we set the timeout flag first to indicate that it was us 435 | * that interrupted the sem_wait() call. One precaution: before we 436 | * try to set the timed-out flag, make sure the calling thread still 437 | * exists - this may not be the case if things are closing down a bit 438 | * messily. We check this quickly using a zero test signal. 439 | */ 440 | 441 | if (pthread_kill(details.callingThread,0) == 0) { 442 | *(details.timedOutShort) = TRUE; 443 | if (triggerSignal (SIGUSR2,details.callingThread) < 0) { 444 | Return = (void*) errno; 445 | } 446 | } 447 | 448 | return Return; 449 | } 450 | 451 | /* -------------------------------------------------------------------------- */ 452 | /* 453 | * t r i g g e r S i g n a l 454 | * 455 | * This is a general purpose routine that sends a specified signal to 456 | * a specified thread, setting up a signal handler that does nothing, 457 | * and then giving the signal. The only effect will be to interrupt any 458 | * operation that is currently blocking - in this case, we expect this to 459 | * be a sem_wait() call. 460 | */ 461 | 462 | static int triggerSignal (int Signal, pthread_t Thread) 463 | { 464 | int Result = 0; 465 | struct sigaction SignalDetails; 466 | SignalDetails.sa_handler = ignoreSignal; 467 | SignalDetails.sa_flags = 0; 468 | (void) sigemptyset(&SignalDetails.sa_mask); 469 | if ((Result = sigaction(Signal,&SignalDetails,NULL)) == 0) { 470 | Result = pthread_kill(Thread,Signal); 471 | } 472 | return Result; 473 | } 474 | 475 | /* -------------------------------------------------------------------------- */ 476 | /* 477 | * i g n o r e S i g n a l 478 | * 479 | * And this is the signal handler that does nothing. (It clears its argument, 480 | * but this has no effect and prevents a compiler warning about an unused 481 | * argument.) 482 | */ 483 | 484 | static void ignoreSignal (int Signal) { 485 | Signal = 0; 486 | } 487 | 488 | #endif 489 | 490 | /* -------------------------------------------------------------------------- */ 491 | /* 492 | * T e s t c o d e 493 | * 494 | * The rest of the code here is used to test sem_timedwait(), and is 495 | * compiled only if the pre-processor variable TEST is set. The test 496 | * program sets up a random timeout and a random delay after which a 497 | * test semaphore will become available. It starts a thread to release the 498 | * semaphore after the specified delay, and issues a sem_timedwait() call 499 | * to take the semaphore, with the specified timeout. It repeats this 500 | * several times, and finally reports the number of times the semaphore 501 | * was taken, the number of times it timed out, and the number of these 502 | * occurrences that were unexpected - ie a semaphore being taken although 503 | * the timeout was less than the delay before it was set, or vice versa. 504 | * The main() routine of the test returns the number of unexpected 505 | * occurrences, which will be zero if the code is working properly. 506 | * 507 | * To run: 508 | * 509 | * gcc -o timed -Wall -ansi -pedantic -DTEST sem_timedwait.c -lpthread 510 | * ./timed [count] [timescale] 511 | * 512 | * On some Linux systems, you may need to drop the -ansi - see comments 513 | * at start of file. On OS X systems, most tests up to a time frame of 514 | * 0.001 secs show nothing happening in an unexpected sequence. On a 515 | * Linux 2.4 system, with its lower time resolution, tests will show 516 | * occasional cases where things don't happen in the expected order, but 517 | * these are not counted as unexpected if the two random times are less 518 | * than 10 msec apart. 519 | */ 520 | 521 | #ifdef TEST 522 | 523 | #include 524 | #include 525 | #include 526 | 527 | /* A giverDetails structure is used to pass the necessary information 528 | * to the thread that is started to release the semaphore. 529 | */ 530 | 531 | typedef struct { 532 | sem_t *semAddr; /* Address of semaphore to release */ 533 | float delaySecs; /* Time to wait before release */ 534 | } giverDetails; 535 | 536 | /* -------------------------------------------------------------------------- */ 537 | /* 538 | * g i v e r T h r e a d M a i n 539 | * 540 | * This is the main code for the thread that releases the semaphore after 541 | * a specified delay. The single argument is the address of a giverDetails 542 | * structure, which specifies the delay time and the semaphore to release. 543 | * If the sem_timedwait() call in the main thread times-out, this thread 544 | * is cancelled and so will not release the semaphore. 545 | */ 546 | 547 | static void* giverThreadMain (void* passedPtr) 548 | { 549 | /* All we do is sleep the specified time and then release the semaphore */ 550 | 551 | giverDetails *details = (giverDetails*) passedPtr; 552 | long uSecs = (long)(details->delaySecs * 1000000.0); 553 | usleep (uSecs); 554 | if (sem_post(details->semAddr) < 0) { 555 | perror ("sem_post"); 556 | } 557 | return NULL; 558 | } 559 | 560 | /* -------------------------------------------------------------------------- */ 561 | /* 562 | * m a i n 563 | * 564 | * The main test routine. This creates a semaphore, then sets up a series 565 | * of sem_timedwait() calls on it. For each call it starts a thread that 566 | * will release the semaphore after a random time, and specifies another 567 | * similar random time as the timeout for the sem_timedwait() call. It 568 | * then checks that what happens is what it expects. 569 | * 570 | * The pogram takes two optional arguments. The first is an integer giving 571 | * the nuber of sem_timedwait() calls it is to attempt (default 10) and the 572 | * second is a floatig point value that gives the time scale - it is the 573 | * maximum time in seconds for the two random times that are generated for 574 | * each sem_timedwait() call. The times should be fairly evenly distributed 575 | * between this value and a hard-coded minimum of 0.001 seconds. The default 576 | * time scale is 1.0. 577 | * 578 | * Note that on all systems, if the difference between the two random times 579 | * (the timeout and the delay before the semaphore is given) is comparable 580 | * with the scheduling resolution of the system - which is only 10 581 | * milliseconds on a standard Linux 2.4 kernel - you can expect to get 582 | * some cases where the 'wrong' timer goes off first. So these cases are 583 | * logged, but anything with a difference of less than 10 msec isn't 584 | * included in the unexpected count. 585 | */ 586 | 587 | int main ( 588 | int argc, 589 | char* argv[]) 590 | { 591 | char semName[1024]; /* Semaphore name if we need to use sem_open */ 592 | sem_t theSem; /* The semaphore itself */ 593 | sem_t *semAddr; /* The address of the semaphore */ 594 | struct timespec absTime; /* Absolute time at which we timeout */ 595 | struct timeval currentTime; /* The time right now */ 596 | pthread_t giverThread; /* Id for the thread that releases the sem */ 597 | giverDetails details; /* Details passed to the giver thread */ 598 | int randomShort; /* Random number in range 0..65535 */ 599 | float randomDelaySecs; /* Random delay before semaphore given */ 600 | float randomTimeoutSecs; /* Random timeout value */ 601 | int intSecs; /* Integer number of seconds */ 602 | long intNsecs; /* Nanoseconds in delay time */ 603 | int msecs; /* Milliseconds between the two times */ 604 | short retry; /* Controls the EAGAIN loop */ 605 | int count; /* Number of tries at the semaphore so far */ 606 | int takenCount = 0; /* Number ot time the semaphore was taken */ 607 | int unexpectedCount = 0; /* Number of unexpected occurrences */ 608 | int timeoutCount = 0; /* Number of times we timed out */ 609 | float timeScaleSecs = 1.0; /* Time scale - from command line */ 610 | int maxCount = 10; /* Times through the test loop - from command line */ 611 | 612 | /* Get the command line arguments, the number of tries, and the time 613 | * scale to use. 614 | */ 615 | 616 | if (argc >= 3) { 617 | timeScaleSecs = atof(argv[2]); 618 | } 619 | if (argc >= 2) { 620 | maxCount = atoi(argv[1]); 621 | } 622 | 623 | /* Creating a semaphore is awkward - some systems support sem_init(), 624 | * which is nice, but OS X only supports sem_open() and returns ENOSYS 625 | * for sem_init(). This code handles both cases. The semaphore is 626 | * created taken. 627 | */ 628 | 629 | semName[0] = '\0'; 630 | semAddr = &theSem; 631 | if (sem_init(semAddr,0,0) < 0) { 632 | if (errno == ENOSYS) { 633 | sprintf (semName,"/tmp/test_%ld.sem",(long)getpid()); 634 | if ((semAddr = sem_open(semName, 635 | O_CREAT|O_EXCL,S_IWUSR | S_IRUSR,0)) 636 | == (sem_t*)SEM_FAILED) { 637 | perror ("creating semaphore"); 638 | } 639 | } 640 | } 641 | 642 | /* Loop through the specified number of tests. */ 643 | 644 | for (count = 0; count < maxCount; count++) { 645 | 646 | /* Generate random times for the timeout and for the delay before 647 | * the semaphore is given. I'm only using the last 16 bits from random() 648 | * becasuse that's good enough for this and saves me worrying about 649 | * handling really big integers near the 32 bit limit. First for the 650 | * delay used by the giver thread. 651 | */ 652 | 653 | randomShort = random() & 0xffff; 654 | randomDelaySecs = ((float)randomShort)/65536.0 * timeScaleSecs; 655 | if (randomDelaySecs < 0.001) randomDelaySecs = 0.001; 656 | details.semAddr = semAddr; 657 | details.delaySecs = randomDelaySecs; 658 | 659 | /* And now for the timeout, which has to be converted into an absolute 660 | * time from now in the form required by sem_timedwait. 661 | */ 662 | 663 | randomShort = random() & 0xffff; 664 | randomTimeoutSecs = ((float)randomShort)/65536.0 * timeScaleSecs; 665 | if (randomTimeoutSecs < 0.001) randomTimeoutSecs = 0.001; 666 | intSecs = (int)randomTimeoutSecs; 667 | intNsecs = (long)((randomTimeoutSecs - (float)intSecs) * 1000000000.0); 668 | gettimeofday (¤tTime,NULL); 669 | absTime.tv_sec = currentTime.tv_sec + intSecs; 670 | absTime.tv_nsec = (currentTime.tv_usec * 1000) + intNsecs; 671 | while (absTime.tv_nsec > 1000000000) { 672 | absTime.tv_sec++; 673 | absTime.tv_nsec -= 1000000000; 674 | } 675 | 676 | /* Create the 'giver' thread, which will release the semaphore after 677 | * the specified delay time. 678 | */ 679 | 680 | pthread_create(&giverThread,NULL,giverThreadMain,(void*)&details); 681 | 682 | /* Now try to take the semaphore and see what happens - timeout or 683 | * a taken semaphore? The retry loop handles any cases where an 684 | * EAGAIN problem is signalled. 685 | */ 686 | 687 | retry = TRUE; 688 | while (retry) { 689 | retry = FALSE; 690 | if (sem_timedwait (semAddr,&absTime) == 0) { 691 | 692 | /* We got the semaphore. See if we expected to. */ 693 | 694 | takenCount++; 695 | if (randomDelaySecs > randomTimeoutSecs) { 696 | msecs = (int)((randomDelaySecs - randomTimeoutSecs) * 1000.0); 697 | printf ( 698 | "Sem taken first, delay %f timeout %f, diff %d msec\n", 699 | randomDelaySecs,randomTimeoutSecs,msecs); 700 | if (msecs < 10) { 701 | printf ("Time difference too short to count as an error\n"); 702 | } else { 703 | unexpectedCount++; 704 | } 705 | } 706 | } else { 707 | 708 | /* We failed. See if this was a timeout, in which case see if 709 | * it was expected. If not, check for EAGAIN and retry, or log 710 | * an error in all other cases. 711 | */ 712 | 713 | if (errno != ETIMEDOUT) { 714 | if (errno == EAGAIN) { 715 | retry = TRUE; 716 | } else { 717 | perror ("Timed wait"); 718 | } 719 | } else { 720 | timeoutCount++; 721 | if (randomDelaySecs < randomTimeoutSecs) { 722 | msecs = (int)((randomTimeoutSecs - randomDelaySecs) * 1000.0); 723 | printf ( 724 | "Timedout first, delay %f timeout %f, diff %d msec\n", 725 | randomDelaySecs,randomTimeoutSecs,msecs); 726 | if (msecs < 10) { 727 | printf ( 728 | "Time difference too short to count as an error\n"); 729 | } else { 730 | unexpectedCount++; 731 | } 732 | } 733 | } 734 | } 735 | } 736 | 737 | /* Cancel the giver thread if it's still running (ie a timeout or other 738 | * error), and wait for it to complete - that's needed to release its 739 | * resources. 740 | */ 741 | 742 | pthread_cancel(giverThread); 743 | pthread_join(giverThread,NULL); 744 | 745 | /* Something to show we're still running. */ 746 | 747 | if (((count + 1) % 25) == 0) { 748 | printf ("Tries: %d, Taken %d, Timedout %d, unexpected %d\n", 749 | count + 1,takenCount, timeoutCount,unexpectedCount); 750 | } 751 | 752 | /* And then back to try again. Note that the semaphore shuld now be 753 | * taken. Either it was releaed by the giver and we took it in the 754 | * sem_timedwait() call, or we timed out, in which case it's still 755 | * taken. So the next sem_timedwait() call will wait, just like this 756 | * one. 757 | */ 758 | } 759 | printf ("Final results: Taken %d, Timedout %d, unexpected %d\n", 760 | takenCount,timeoutCount,unexpectedCount); 761 | return unexpectedCount; 762 | } 763 | 764 | #endif 765 | -------------------------------------------------------------------------------- /osx/sem_timedwait.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/siffiejoe/lua-luaipc/4dae67b99fd5f31ba8e2a5ce2ca1506c66176d9c/osx/sem_timedwait.h -------------------------------------------------------------------------------- /proc.c: -------------------------------------------------------------------------------- 1 | #ifndef _POSIX_C_SOURCE 2 | # define _POSIX_C_SOURCE 200112L 3 | #endif 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "ipc.h" 9 | 10 | 11 | /* check for POSIX */ 12 | #if defined( unix ) || defined( __unix ) || defined( __unix__ ) || \ 13 | (defined( __APPLE__ ) && defined( __MACH__ )) || \ 14 | HAVE_UNISTD_H 15 | # include 16 | # if defined( _POSIX_VERSION ) && _POSIX_VERSION >= 200112L 17 | # define HAVE_PROC 18 | # include "proc_posix.h" 19 | # endif 20 | #endif 21 | 22 | 23 | /* check for Windows */ 24 | #if !defined( HAVE_PROC ) && \ 25 | defined( _WIN32 ) && !defined( __CYGWIN__ ) 26 | # define HAVE_PROC 27 | # include "proc_win.h" 28 | #endif 29 | 30 | 31 | #ifdef HAVE_PROC 32 | 33 | #define NAME "ipc.proc" 34 | 35 | 36 | /* needed for its address: */ 37 | static char EOF_id = 0; 38 | 39 | 40 | typedef struct { 41 | ipc_proc_handle h; /* platform specific data */ 42 | /* extra management info: */ 43 | char is_valid; 44 | char may_write; 45 | } l_proc_handle; 46 | 47 | 48 | static int pusherror( lua_State* L, int code ) { 49 | char buf[ IPC_MAXERRMSG ]; 50 | ipc_proc_error( buf, sizeof( buf ), code ); 51 | lua_pushnil( L ); 52 | lua_pushstring( L, buf ); 53 | lua_pushinteger( L, code ); 54 | return 3; 55 | } 56 | 57 | 58 | static int l_proc_gc( lua_State* L ) { 59 | l_proc_handle* h = lua_touserdata( L, 1 ); 60 | if( h->is_valid ) { 61 | int status = 0; 62 | char const* what = NULL; 63 | ipc_proc_kill( &h->h, IPC_PROC_SIGKILL ); 64 | ipc_proc_wait( &h->h, &status, &what ); 65 | } 66 | return 0; 67 | } 68 | 69 | 70 | static int l_proc_kill( lua_State* L ) { 71 | l_proc_handle* h = luaL_checkudata( L, 1, NAME ); 72 | char const* const signames[] = { 73 | "term", "kill", NULL 74 | }; 75 | int const sigs[] = { 76 | IPC_PROC_SIGTERM, IPC_PROC_SIGKILL 77 | }; 78 | int sig = sigs[ luaL_checkoption( L, 2, NULL, signames ) ]; 79 | int rv = 0; 80 | if( !h->is_valid ) 81 | luaL_error( L, "attempt to use invalid process object" ); 82 | rv = ipc_proc_kill( &h->h, sig ); 83 | if( rv != 0 ) 84 | return pusherror( L, rv ); 85 | lua_pushboolean( L, 1 ); 86 | return 1; 87 | } 88 | 89 | 90 | static void compactenv( lua_State* L, int idx ) { 91 | int n = 0; 92 | lua_getuservalue( L, idx ); 93 | if( lua_type( L, -1 ) != LUA_TTABLE ) 94 | luaL_error( L, "does not have uservalue/environment table" ); 95 | n = (int)lua_rawlen( L, -1 ); 96 | if( n > 1 ) { 97 | int i = 1; 98 | luaL_checkstack( L, n, "not enough stack space" ); 99 | for( i = 1; i <= n; ++i ) 100 | lua_rawgeti( L, -i, i ); 101 | lua_concat( L, n ); 102 | lua_rawseti( L, -2, 1 ); 103 | for( i = 2; i <= n; ++i ) { 104 | lua_pushnil( L ); 105 | lua_rawseti( L, -2, i ); 106 | } 107 | } 108 | lua_pop( L, 1 ); 109 | } 110 | 111 | 112 | static int addtoenv( lua_State* L, int idx1, int idx2 ) { 113 | int n = 0; 114 | idx2 = lua_absindex( L, idx2 ); 115 | lua_getuservalue( L, idx1 ); 116 | if( lua_type( L, -1 ) != LUA_TTABLE ) 117 | luaL_error( L, "does not have uservalue/environment table" ); 118 | n = (int)lua_rawlen( L, -1 )+1; 119 | lua_pushvalue( L, idx2 ); 120 | lua_rawseti( L, -2, n ); 121 | lua_pop( L, 1 ); 122 | return n; 123 | } 124 | 125 | 126 | static int startoutput( lua_State* L, l_proc_handle* h, int idx ) { 127 | char const* str = NULL; 128 | size_t len = 0; 129 | int completed = 0; 130 | int rv = 0; 131 | /* concatenate all pending strings in the uservalue sequence */ 132 | compactenv( L, idx ); 133 | lua_getuservalue( L, idx ); /* env */ 134 | lua_rawgeti( L, -1, 1 ); /* env, str */ 135 | if( lua_type( L, -1 ) == LUA_TSTRING ) { 136 | /* don't let the string be gc'd */ 137 | lua_pushvalue( L, -1 ); /* env, str, str */ 138 | lua_setfield( L, -3, "stdinbuf" ); /* env, str */ 139 | str = lua_tolstring( L, -1, &len ); 140 | /* enqueue the output request to the child's stdin */ 141 | rv = ipc_proc_write( &h->h, str, len, &completed ); 142 | if( rv != 0 ) 143 | return rv; 144 | /* clear uservalue sequence */ 145 | lua_pushnil( L ); /* env, str, nil */ 146 | lua_rawseti( L, -3, 1 ); /* env, str */ 147 | if( completed ) { /* output completed already?! */ 148 | /* output string can be gc'd now! */ 149 | lua_pushnil( L ); /* env, str, nil */ 150 | lua_setfield( L, -3, "stdinbuf" ); /* env, str */ 151 | } 152 | } 153 | /* check whether we can/should close the pipe now */ 154 | lua_getfield( L, -2, "stdinbuf" ); /* env, str, stdinbuf */ 155 | lua_getfield( L, -3, "iseof" ); /* env, str, stdinbuf, iseof */ 156 | if( lua_type( L, -2 ) == LUA_TNIL && lua_type( L, -1 ) != LUA_TNIL ) 157 | rv = ipc_proc_closestdin( &h->h ); 158 | lua_pop( L, 4 ); 159 | return rv; 160 | } 161 | 162 | 163 | LUA_KFUNCTION( l_proc_waitk ) { 164 | l_proc_handle* h = lua_touserdata( L, 1 ); 165 | int child_complete = 0; 166 | int stdin_complete = 0; 167 | int stdout_complete = 0; 168 | int stderr_complete = 0; 169 | int rv = 0; 170 | char const* data = NULL; 171 | size_t len = 0; 172 | (void)status; 173 | lua_settop( L, 5 ); /* handle, child_complete, ..., stderr_complete */ 174 | /* lua_settop */ 175 | switch( ctx ) { 176 | case 0: 177 | do { 178 | /* call blocking ipc function to check for readable input or 179 | * writable output (also checks for dead child) */ 180 | rv = ipc_proc_waitio( &h->h, &child_complete, &stdin_complete, 181 | &stdout_complete, &stderr_complete ); 182 | if( rv != 0 ) 183 | return pusherror( L, rv ); 184 | /* this function may yield, so we have to save all temporary 185 | * data in the Lua state. */ 186 | lua_pushboolean( L, child_complete ); 187 | lua_replace( L, 2 ); 188 | lua_pushboolean( L, stdin_complete ); 189 | lua_replace( L, 3 ); 190 | lua_pushboolean( L, stdout_complete ); 191 | lua_replace( L, 4 ); 192 | lua_pushboolean( L, stderr_complete ); 193 | lua_replace( L, 5 ); 194 | /* call handler function from uservalue */ 195 | if( lua_toboolean( L, 4 ) ) { /* stdout_complete */ 196 | ipc_getuservaluefield( L, 1, "callback" ); 197 | lua_pushliteral( L, "stdout" ); 198 | rv = ipc_proc_stdoutready( &h->h, &data, &len ); 199 | if( rv != 0 ) { 200 | lua_callk( L, pusherror( L, rv )+1, 0, 1, l_proc_waitk ); 201 | } else if( len > 0 ) { 202 | lua_pushlstring( L, data, len ); 203 | lua_callk( L, 2, 0, 1, l_proc_waitk ); 204 | } else 205 | lua_pop( L, 2 ); 206 | } 207 | case 1: 208 | if( lua_toboolean( L, 5 ) ) { /* stderr_complete */ 209 | ipc_getuservaluefield( L, 1, "callback" ); 210 | lua_pushliteral( L, "stderr" ); 211 | rv = ipc_proc_stderrready( &h->h, &data, &len ); 212 | if( rv != 0 ) { 213 | lua_callk( L, pusherror( L, rv )+1, 0, 2, l_proc_waitk ); 214 | } else if( len > 0 ) { 215 | lua_pushlstring( L, data, len ); 216 | lua_callk( L, 2, 0, 2, l_proc_waitk ); 217 | } else 218 | lua_pop( L, 2 ); 219 | } 220 | case 2: 221 | if( lua_toboolean( L, 2 ) ) { /* child_complete */ 222 | int stat = 0; 223 | char const* what = NULL; 224 | rv = ipc_proc_wait( &h->h, &stat, &what ); 225 | h->is_valid = 0; 226 | if( rv != 0 ) 227 | return pusherror( L, rv ); 228 | else { 229 | if( stat == 0 && *what == 'e' ) 230 | lua_pushboolean( L, 1 ); 231 | else 232 | lua_pushnil( L ); 233 | lua_pushstring( L, what ); 234 | lua_pushinteger( L, stat ); 235 | return 3; 236 | } 237 | } 238 | if( lua_toboolean( L, 3 ) ) { /* stdin_complete */ 239 | rv = startoutput( L, h, 1 ); 240 | if( rv != 0 ) 241 | return pusherror( L, rv ); 242 | } 243 | } while( 1 ); 244 | } 245 | /* should never happen: */ 246 | return luaL_error( L, "invalid ctx in process wait function" ); 247 | } 248 | 249 | static int l_proc_wait( lua_State* L ) { 250 | /* we do parameter checking here, so that we can avoid it in the 251 | * continuation function above. */ 252 | l_proc_handle* h = luaL_checkudata( L, 1, NAME ); 253 | if( !h->is_valid ) 254 | luaL_error( L, "attempt to use invalid process object" ); 255 | return l_proc_waitk( L, 0, 0 ); 256 | } 257 | 258 | 259 | static int l_proc_write( lua_State* L ) { 260 | l_proc_handle* h = luaL_checkudata( L, 1, NAME ); 261 | int rv = 0, ready = 0; 262 | int got_eof = 0; 263 | int i = 0; 264 | int top = lua_gettop( L ); 265 | if( !h->is_valid ) 266 | luaL_error( L, "attempt to use invalid process object" ); 267 | if( !h->may_write ) 268 | luaL_error( L, "process stdin is not connected to a pipe" ); 269 | for( i = 2; i <= top; ++i ) { 270 | if( lua_islightuserdata( L, i ) && 271 | lua_touserdata( L, i ) == &EOF_id ) 272 | got_eof = i; 273 | else 274 | luaL_checkstring( L, i ); 275 | if( ipc_getuservaluefield( L, 1, "iseof" ) != LUA_TNIL ) 276 | luaL_error( L, "pipe to stdin is already closed" ); 277 | lua_pop( L, 1 ); 278 | if( got_eof ) { 279 | lua_pushvalue( L, got_eof ); 280 | ipc_setuservaluefield( L, 1, "iseof" ); 281 | } else { 282 | /* append input to table in uservalue */ 283 | addtoenv( L, 1, i ); 284 | } 285 | } 286 | /* check whether we are waiting already */ 287 | rv = ipc_proc_stdinready( &h->h, &ready ); 288 | if( rv != 0 ) 289 | return pusherror( L, rv ); 290 | /* if not, start asynchronous output to child process */ 291 | if( ready ) { 292 | rv = startoutput( L, h, 1 ); 293 | if( rv != 0 ) 294 | return pusherror( L, rv ); 295 | } 296 | lua_pushboolean( L, 1 ); 297 | return 1; 298 | } 299 | 300 | 301 | static int l_proc_spawn( lua_State* L ) { 302 | FILE* cstdin = NULL; 303 | FILE* cstdout = NULL; 304 | FILE* cstderr = NULL; 305 | int pipe_stdin = 0, pipe_stdout = 0, pipe_stderr = 0; 306 | int rv = 0; 307 | l_proc_handle* h = NULL; 308 | /* check and extract arguments */ 309 | char const* cmdline = luaL_checkstring( L, 1 ); 310 | luaL_checktype( L, 2, LUA_TTABLE ); 311 | lua_settop( L, 2 ); 312 | lua_getfield( L, 2, "stdin" ); 313 | cstdin = ipc_testfile( L, -1 ); 314 | if( cstdin == NULL ) 315 | pipe_stdin = lua_toboolean( L, -1 ); 316 | lua_pop( L, 1 ); 317 | lua_getfield( L, 2, "stdout" ); 318 | cstdout = ipc_testfile( L, -1 ); 319 | if( cstdout == NULL ) 320 | pipe_stdout = lua_toboolean( L, -1 ); 321 | lua_pop( L, 1 ); 322 | lua_getfield( L, 2, "stderr" ); 323 | cstderr = ipc_testfile( L, -1 ); 324 | if( cstderr == NULL ) 325 | pipe_stderr = lua_toboolean( L, -1 ); 326 | lua_pop( L, 1 ); 327 | lua_getfield( L, 2, "callback" ); 328 | if( lua_type( L, -1 ) != LUA_TFUNCTION && 329 | (pipe_stdout || pipe_stderr) ) 330 | luaL_error( L, "'callback' field required" ); 331 | /* construct handle */ 332 | h = lua_newuserdata( L, sizeof( *h ) ); 333 | h->is_valid = 0; 334 | h->may_write = pipe_stdin; 335 | lua_newtable( L ); /* uservalue table */ 336 | lua_pushvalue( L, 3 ); 337 | lua_setfield( L, -2, "callback" ); 338 | lua_setuservalue( L, -2 ); 339 | luaL_getmetatable( L, NAME ); 340 | lua_setmetatable( L, -2 ); 341 | /* setup pipes and create child process */ 342 | rv = ipc_proc_spawn( &h->h, cmdline, cstdin, pipe_stdin, 343 | cstdout, pipe_stdout, cstderr, pipe_stderr ); 344 | if( rv != 0 ) 345 | return pusherror( L, rv ); 346 | h->is_valid = 1; 347 | return 1; 348 | } 349 | 350 | 351 | IPC_API int luaopen_ipc_proc( lua_State* L ) { 352 | luaL_Reg const methods[] = { 353 | { "kill", l_proc_kill }, 354 | { "wait", l_proc_wait }, 355 | { "write", l_proc_write }, 356 | { NULL, NULL } 357 | }; 358 | luaL_Reg const functions[] = { 359 | { "spawn", l_proc_spawn }, 360 | { NULL, NULL }, /* reserve space for proc.EOF */ 361 | { NULL, NULL } 362 | }; 363 | int rv = ipc_proc_prepare(); 364 | if( rv != 0 ) { 365 | char buf[ IPC_MAXERRMSG ]; 366 | ipc_proc_error( buf, sizeof( buf ), rv ); 367 | luaL_error( L, "ipc.proc initialization failed: %s", buf ); 368 | } 369 | if( !luaL_newmetatable( L, NAME ) ) 370 | luaL_error( L, "redefinition of metatable '%s'", NAME ); 371 | lua_pushcfunction( L, l_proc_gc ); 372 | lua_setfield( L, -2, "__gc" ); 373 | lua_pushboolean( L, 0 ); 374 | lua_setfield( L, -2, "__metatable" ); 375 | luaL_newlib( L, methods ); 376 | lua_setfield( L, -2, "__index" ); 377 | lua_pop( L, 1 ); 378 | luaL_newlib( L, functions ); 379 | lua_pushlightuserdata( L, (void*)&EOF_id ); 380 | lua_setfield( L, -2, "EOF" ); 381 | return 1; 382 | } 383 | 384 | #else /* no implementation for this platform available: */ 385 | IPC_API int luaopen_ipc_proc( lua_State* L ) { 386 | IPC_NOTIMPLEMENTED( L ); 387 | return 0; 388 | } 389 | #endif 390 | 391 | -------------------------------------------------------------------------------- /proc_posix.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | 14 | #define IPC_PROC_SIGTERM SIGTERM 15 | #define IPC_PROC_SIGKILL SIGKILL 16 | 17 | #define IPC_PROC_BUFSIZE 1024 18 | 19 | 20 | typedef struct { 21 | pid_t child; 22 | int fds[ 3 ]; 23 | int exitstatus; 24 | char const* exitkind; /* "exit" or "signal" */ 25 | char const* inbuf; 26 | size_t inlen; /* length of the string `inbuf` */ 27 | char outbuf[ IPC_PROC_BUFSIZE ]; 28 | char errbuf[ IPC_PROC_BUFSIZE ]; 29 | } ipc_proc_handle; 30 | 31 | 32 | static void ipc_proc_error( char* buf, size_t len, int code ) { 33 | if( len > 0 && strerror_r( code, buf, len ) != (int)0 ) { 34 | strncpy( buf, "unknown error", len-1 ); 35 | buf[ len-1 ] = '\0'; 36 | } 37 | } 38 | 39 | 40 | static void ipc_proc_inithandle_( ipc_proc_handle* h ) { 41 | h->child = 0; 42 | h->fds[ 0 ] = h->fds[ 1 ] = h->fds[ 2 ] = -1; 43 | h->exitstatus = 0; 44 | h->exitkind = NULL; 45 | h->inbuf = NULL; 46 | h->inlen = 0; 47 | } 48 | 49 | 50 | static int ipc_proc_waitpid_( ipc_proc_handle* h, int flags ) { 51 | int rv = 0; 52 | if( h->exitkind != NULL ) 53 | return 0; 54 | IPC_EINTR( rv, waitpid( h->child, &h->exitstatus, flags ) ); 55 | switch( rv ) { 56 | case -1: 57 | return IPC_ERR( errno ); 58 | case 0: 59 | return 0; 60 | default: 61 | if( WIFEXITED( h->exitstatus ) ) { 62 | h->exitkind = "exit"; 63 | h->exitstatus = WEXITSTATUS( h->exitstatus ); 64 | } else if( WIFSIGNALED( h->exitstatus ) ) { 65 | h->exitkind = "signal"; 66 | h->exitstatus = WTERMSIG( h->exitstatus ); 67 | } else 68 | h->exitkind = "exit"; 69 | } 70 | return 0; 71 | } 72 | 73 | 74 | static int ipc_proc_kill( ipc_proc_handle* h, int signum ) { 75 | ipc_proc_waitpid_( h, WNOHANG ); /* non-blocking */ 76 | if( h->exitkind == NULL ) { 77 | if( kill( h->child, signum ) < 0 ) 78 | return IPC_ERR( errno ); 79 | } 80 | return 0; 81 | } 82 | 83 | 84 | static int ipc_proc_wait( ipc_proc_handle* h, int* status, 85 | char const** what ) { 86 | int rv = ipc_proc_waitpid_( h, 0 ); /* blocking */ 87 | if( rv < 0 ) 88 | return IPC_ERR( rv ); 89 | *status = h->exitstatus; 90 | *what = h->exitkind; 91 | return 0; 92 | } 93 | 94 | 95 | static int ipc_proc_trywrite_( ipc_proc_handle* h, int* done ) { 96 | ssize_t nwritten = 0; 97 | IPC_EINTR( nwritten, write( h->fds[ 0 ], h->inbuf, h->inlen ) ); 98 | if( nwritten < 0 ) { 99 | *done = 0; 100 | if( errno == EAGAIN || errno == EWOULDBLOCK ) 101 | return 0; 102 | else { 103 | int saved_errno = errno; 104 | close( h->fds[ 0 ] ); 105 | h->fds[ 0 ] = -1; 106 | if( saved_errno == EPIPE ) 107 | return 0; 108 | else 109 | return IPC_ERR( saved_errno ); 110 | } 111 | } else { 112 | h->inbuf += nwritten; 113 | h->inlen -= nwritten; 114 | if( h->inlen > 0 ) 115 | *done = 0; 116 | else { 117 | h->inbuf = NULL; 118 | *done = 1; 119 | } 120 | } 121 | return 0; 122 | } 123 | 124 | 125 | static int ipc_proc_stdinready( ipc_proc_handle* h, int* ready ) { 126 | int rv = 0; 127 | if( h->fds[ 0 ] < 0 ) { 128 | *ready = 0; 129 | return 0; 130 | } 131 | if( h->inbuf == NULL ) { 132 | *ready = 1; 133 | return 0; 134 | } 135 | rv = ipc_proc_trywrite_( h, ready ); 136 | if( rv != 0 ) 137 | return IPC_ERR( rv ); 138 | return 0; 139 | } 140 | 141 | 142 | static int ipc_proc_closestdin( ipc_proc_handle* h ) { 143 | if( h->fds[ 0 ] < 0 ) 144 | return IPC_ERR( EBADF ); 145 | if( close( h->fds[ 0 ] ) < 0 ) 146 | return IPC_ERR( errno ); 147 | h->fds[ 0 ] = -1; 148 | return 0; 149 | } 150 | 151 | 152 | static int ipc_proc_write( ipc_proc_handle* h, char const* s, 153 | size_t len, int* completed ) { 154 | int rv = 0; 155 | if( h->fds[ 0 ] < 0 ) 156 | return IPC_ERR( EBADF ); 157 | if( h->inbuf != NULL ) 158 | return IPC_ERR( EBUSY ); 159 | h->inbuf = s; 160 | h->inlen = len; 161 | rv = ipc_proc_trywrite_( h, completed ); 162 | if( rv != 0 ) 163 | return IPC_ERR( rv ); 164 | return 0; 165 | } 166 | 167 | 168 | static int ipc_proc_waitio( ipc_proc_handle* h, int* child_done, 169 | int* stdin_done, int* stdout_done, 170 | int* stderr_done ) { 171 | int nfds = -1; 172 | int rv = 0; 173 | fd_set readset, writeset; 174 | fd_set* readsetp = NULL; 175 | fd_set* writesetp = NULL; 176 | *child_done = *stdin_done = *stdout_done = *stderr_done = 0; 177 | FD_ZERO( &readset ); 178 | FD_ZERO( &writeset ); 179 | #define ADD2SET( fdidx, set, extra ) \ 180 | do { \ 181 | if( h->fds[ fdidx ] >= 0 && (extra) ) { \ 182 | set ## p = &set; \ 183 | nfds = nfds > h->fds[ fdidx ] ? nfds : h->fds[ fdidx ]; \ 184 | FD_SET( h->fds[ fdidx ], set ## p ); \ 185 | } \ 186 | } while( 0 ) 187 | ADD2SET( 0, writeset, h->inbuf != NULL ); 188 | ADD2SET( 1, readset, 1 ); 189 | ADD2SET( 2, readset, 1 ); 190 | #undef ADD2SET 191 | if( nfds >= 0 ) { 192 | /* when the child dies, the select should return too */ 193 | IPC_EINTR( rv, select( nfds+1, readsetp, writesetp, NULL, NULL ) ); 194 | if( rv < 0 ) 195 | return IPC_ERR( errno ); 196 | /* check for dead child */ 197 | rv = ipc_proc_waitpid_( h, WNOHANG ); 198 | if( rv != 0 ) 199 | return IPC_ERR( rv ); 200 | if( h->exitkind != NULL ) 201 | *child_done = 1; 202 | } else { 203 | /* no I/O configured, so we just wait for the child to die! */ 204 | rv = ipc_proc_waitpid_( h, 0 ); 205 | if( rv != 0 ) 206 | return IPC_ERR( rv ); 207 | *child_done = 1; 208 | } 209 | if( writesetp != NULL && FD_ISSET( h->fds[ 0 ], writesetp ) ) 210 | *stdin_done = 1; 211 | if( readsetp != NULL && h->fds[ 1 ] >= 0 && 212 | FD_ISSET( h->fds[ 1 ], readsetp ) ) 213 | *stdout_done = 1; 214 | if( readsetp != NULL && h->fds[ 2 ] >= 0 && 215 | FD_ISSET( h->fds[ 2 ], readsetp ) ) 216 | *stderr_done = 1; 217 | return 0; 218 | } 219 | 220 | 221 | static int ipc_proc_get_( ipc_proc_handle* h, int fdindex, char* buf, 222 | char const** data, size_t* len ) { 223 | ssize_t n = 0; 224 | if( h->fds[ fdindex ] < 0 ) 225 | return IPC_ERR( EBADF ); 226 | *data = buf; 227 | *len = 0; 228 | IPC_EINTR( n, read( h->fds[ fdindex ], buf, IPC_PROC_BUFSIZE ) ); 229 | if( n < 0 ) { 230 | if( errno != EAGAIN && errno != EWOULDBLOCK ) { 231 | int saved_errno = errno; 232 | close( h->fds[ fdindex ] ); 233 | h->fds[ fdindex ] = -1; 234 | return IPC_ERR( saved_errno ); 235 | } 236 | } else if( n == 0 ) { /* EOF */ 237 | close( h->fds[ fdindex ] ); 238 | h->fds[ fdindex ] = -1; 239 | *len = 0; 240 | } else 241 | *len = n; 242 | return 0; 243 | } 244 | 245 | static int ipc_proc_stdoutready( ipc_proc_handle* h, char const** data, 246 | size_t* len ) { 247 | return ipc_proc_get_( h, 1, h->outbuf, data, len ); 248 | } 249 | 250 | static int ipc_proc_stderrready( ipc_proc_handle* h, char const** data, 251 | size_t* len ) { 252 | return ipc_proc_get_( h, 2, h->errbuf, data, len ); 253 | } 254 | 255 | 256 | static int ipc_proc_spawn( ipc_proc_handle* h, char const* cmdline, 257 | FILE* cstdin, int pstdin, 258 | FILE* cstdout, int pstdout, 259 | FILE* cstderr, int pstderr ) { 260 | int fds[] = { -1, -1, -1, -1, -1, -1 }; 261 | int stdin_fd = -1, stdout_fd = -1, stderr_fd = -1; 262 | int rv = 0; 263 | /* macro to check that the given file descriptor is valid for select 264 | * to use. cleans up and returns error code if not. */ 265 | #define CHECK_FD( fd ) \ 266 | do { \ 267 | int d = (fd); \ 268 | if( d < 0 || d >= FD_SETSIZE ) { \ 269 | size_t i = 0; \ 270 | for( i = 0; i < sizeof( fds )/sizeof( *fds ); ++i ) \ 271 | if( fds[ i ] >= 0 ) \ 272 | close( fds[ i ] ); \ 273 | return IPC_ERR( EMFILE ); \ 274 | } \ 275 | } while( 0 ) 276 | /* macro to check that a value is >= 0. cleans up and returns error 277 | * code if not. */ 278 | #define CHECK_R( v ) \ 279 | do { \ 280 | if( (v) < 0 ) { \ 281 | int saved_errno = errno; \ 282 | size_t i = 0; \ 283 | for( i = 0; i < sizeof( fds )/sizeof( *fds ); ++i ) \ 284 | if( fds[ i ] >= 0 ) \ 285 | close( fds[ i ] ); \ 286 | return IPC_ERR( saved_errno ); \ 287 | } \ 288 | } while( 0 ) 289 | ipc_proc_inithandle_( h ); 290 | /* create pipes if necessary */ 291 | if( cstdin != NULL ) { 292 | CHECK_R( stdin_fd = fileno( cstdin ) ); 293 | } else if( pstdin ) { 294 | CHECK_R( pipe( fds ) ); 295 | IPC_EINTR( rv, fcntl( fds[ 1 ], F_SETFL, O_NONBLOCK ) ); 296 | CHECK_R( rv ); 297 | CHECK_FD( h->fds[ 0 ] = fds[ 1 ] ); 298 | stdin_fd = fds[ 0 ]; 299 | } 300 | if( cstdout != NULL ) { 301 | CHECK_R( stdout_fd = fileno( cstdout ) ); 302 | } else if( pstdout ) { 303 | CHECK_R( pipe( fds+2 ) ); 304 | IPC_EINTR( rv, fcntl( fds[ 2 ], F_SETFL, O_NONBLOCK ) ); 305 | CHECK_R( rv ); 306 | CHECK_FD( h->fds[ 1 ] = fds[ 2 ] ); 307 | stdout_fd = fds[ 3 ]; 308 | } 309 | if( cstderr != NULL ) { 310 | CHECK_R( stderr_fd = fileno( cstderr ) ); 311 | } else if( pstderr ) { 312 | CHECK_R( pipe( fds+4 ) ); 313 | IPC_EINTR( rv, fcntl( fds[ 4 ], F_SETFL, O_NONBLOCK ) ); 314 | CHECK_R( rv ); 315 | CHECK_FD( h->fds[ 2 ] = fds[ 4 ] ); 316 | stderr_fd = fds[ 5 ]; 317 | } 318 | /* create child process */ 319 | CHECK_R( h->child = fork() ); 320 | if( h->child > 0 ) { /* parent */ 321 | if( fds[ 0 ] >= 0 ) 322 | close( fds[ 0 ] ); 323 | if( fds[ 3 ] >= 0 ) 324 | close( fds[ 3 ] ); 325 | if( fds[ 5 ] >= 0 ) 326 | close( fds[ 5 ] ); 327 | return 0; 328 | } 329 | #undef CHECK_FD 330 | #undef CHECK_R 331 | /* here we are the child process */ 332 | if( fds[ 1 ] >= 0 ) 333 | close( fds[ 1 ] ); 334 | if( fds[ 2 ] >= 0 ) 335 | close( fds[ 2 ] ); 336 | if( fds[ 4 ] >= 0 ) 337 | close( fds[ 4 ] ); 338 | /* setup stdin, stdout, and stderr for the child process */ 339 | if( stdin_fd >= 0 ) { 340 | IPC_EINTR( rv, dup2( stdin_fd, STDIN_FILENO ) ); 341 | close( stdin_fd ); 342 | if( rv < 0 ) 343 | _exit( 127 ); 344 | } 345 | if( stdout_fd >= 0 ) { 346 | IPC_EINTR( rv, dup2( stdout_fd, STDOUT_FILENO ) ); 347 | close( stdout_fd ); 348 | if( rv < 0 ) 349 | _exit( 127 ); 350 | } 351 | if( stderr_fd >= 0 ) { 352 | IPC_EINTR( rv, dup2( stderr_fd, STDERR_FILENO ) ); 353 | close( stderr_fd ); 354 | if( rv < 0 ) 355 | _exit( 127 ); 356 | } 357 | signal( SIGPIPE, SIG_DFL ); 358 | /* FIXME should we prevent file descriptors from leaking?! */ 359 | /* start the new process */ 360 | execl( "/bin/sh", "/bin/sh", "-c", cmdline, (char const*)NULL ); 361 | _exit( 127 ); 362 | return 0; 363 | } 364 | 365 | 366 | static int ipc_proc_prepare( void ) { 367 | if( signal( SIGPIPE, SIG_IGN ) == SIG_ERR ) 368 | return IPC_ERR( errno ); 369 | return 0; 370 | } 371 | 372 | -------------------------------------------------------------------------------- /proc_win.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #define WIN32_LEAN_AND_MEAN 7 | #include 8 | 9 | 10 | /* Windows doesn't really use signals, and it doesn't have a mechanism 11 | * to gracefully shutdown an unrelated process, so we just kill the 12 | * child process dead for all signals. */ 13 | #define IPC_PROC_SIGTERM 0 14 | #define IPC_PROC_SIGKILL 0 15 | 16 | #define IPC_PROC_BUFSIZE 1024 17 | 18 | 19 | typedef struct { 20 | HANDLE child; 21 | /* process info structure? */ 22 | int exitstatus; 23 | char const* exitkind; 24 | char const* inbuf; 25 | size_t inlen; /* length of the string `inbuf` */ 26 | char outbuf[ IPC_PROC_BUFSIZE ]; 27 | char errbuf[ IPC_PROC_BUFSIZE ]; 28 | OVERLAPPED inov; 29 | OVERLAPPED outov; 30 | OVERLAPPED errov; 31 | } ipc_proc_handle; 32 | 33 | 34 | static void ipc_proc_error( char* buf, size_t len, int code ) { 35 | if( len > 0 ) { 36 | if( 0 == FormatMessageA( FORMAT_MESSAGE_FROM_SYSTEM | 37 | FORMAT_MESSAGE_IGNORE_INSERTS, 38 | NULL, 39 | code, 40 | 0, 41 | buf, 42 | len, 43 | NULL ) ) { 44 | strncpy( buf, "unknown error", len-1 ); 45 | buf[ len-1 ] = '\0'; 46 | } else { /* Windows puts an extra newline in there! */ 47 | size_t n = strlen( buf ); 48 | while( n > 0 && isspace( (unsigned char)buf[ --n ] ) ) 49 | buf[ n ] = '\0'; 50 | } 51 | } 52 | } 53 | 54 | 55 | static int ipc_proc_waitprocess_( ipc_proc_handle* h, DWORD timeout ) { 56 | DWORD exitcode = 0; 57 | if( h->exitkind != NULL ) 58 | return 0; 59 | switch( WaitForSingleObject( h->child, timeout ) ) { 60 | case WAIT_OBJECT_0: 61 | if( !GetExitCodeProcess( h->child, &exitcode ) ) 62 | return IPC_ERR( GetLastError() ); 63 | h->exitstatus = exitcode; 64 | h->exitkind = "exit"; 65 | return 0; 66 | case WAIT_TIMEOUT: 67 | return 0; 68 | default: 69 | return IPC_ERR( GetLastError() ); 70 | } 71 | return 0; 72 | } 73 | 74 | 75 | static int ipc_proc_kill( ipc_proc_handle* h, int signum ) { 76 | (void)signum; 77 | ipc_proc_waitprocess_( h, 0 ); 78 | if( h->exitkind != 0 ) { 79 | if( !TerminateProcess( h->child, 3 ) ) 80 | return IPC_ERR( GetLastError() ); 81 | } 82 | return 0; 83 | } 84 | 85 | 86 | static int ipc_proc_wait( ipc_proc_handle* h, int* status, 87 | char const** what ) { 88 | int rv = ipc_proc_waitprocess_( h, INFINITE ); 89 | if( rv != 0 ) 90 | return IPC_ERR( rv ); 91 | *status = h->exitstatus; 92 | *what = h->exitkind; 93 | return 0; 94 | } 95 | 96 | 97 | static int ipc_proc_trywrite_( ipc_proc_handle* h, int* done ) { 98 | /* TODO */ 99 | return 0; 100 | } 101 | 102 | 103 | static int ipc_proc_stdinready( ipc_proc_handle* h, int* done ) { 104 | /* TODO */ 105 | return 0; 106 | } 107 | 108 | 109 | static int ipc_proc_closestdin( ipc_proc_handle* h ) { 110 | /* TODO */ 111 | return 0; 112 | } 113 | 114 | 115 | static int ipc_proc_write( ipc_proc_handle* h, char const* s, 116 | size_t len, int* completed ) { 117 | /* TODO */ 118 | return 0; 119 | } 120 | 121 | 122 | static int ipc_proc_waitio( ipc_proc_handle* h, int* child_complete, 123 | int* stdin_complete, int* stdout_complete, 124 | int* stderr_complete ) { 125 | /* TODO */ 126 | return 0; 127 | } 128 | 129 | 130 | static int ipc_proc_stdoutready( ipc_proc_handle* h, char const** data, 131 | size_t* len ) { 132 | /* TODO */ 133 | return 0; 134 | } 135 | 136 | static int ipc_proc_stderrready( ipc_proc_handle* h, char const** data, 137 | size_t* len ) { 138 | /* TODO */ 139 | return 0; 140 | } 141 | 142 | 143 | static int ipc_proc_spawn( ipc_proc_handle* h, char const* cmdline, 144 | FILE* cstdin, int pstdin, 145 | FILE* cstdout, int pstdout, 146 | FILE* cstderr, int pstderr ) { 147 | /* TODO */ 148 | return 0; 149 | } 150 | 151 | 152 | static int ipc_proc_prepare( void ) { 153 | /* no global preparation necessary on Windows */ 154 | return 0; 155 | } 156 | 157 | -------------------------------------------------------------------------------- /sem.c: -------------------------------------------------------------------------------- 1 | #ifndef _POSIX_C_SOURCE 2 | # define _POSIX_C_SOURCE 200112L 3 | #endif 4 | #include 5 | #include 6 | #include 7 | #include "ipc.h" 8 | 9 | 10 | /* check for POSIX */ 11 | #if defined( unix ) || defined( __unix ) || defined( __unix__ ) || \ 12 | (defined( __APPLE__ ) && defined( __MACH__ )) || \ 13 | HAVE_UNISTD_H 14 | # include 15 | # if defined( _POSIX_VERSION ) && _POSIX_VERSION >= 200112L && \ 16 | defined( _POSIX_SEMAPHORES ) && _POSIX_SEMAPHORES > 0 && \ 17 | defined( _POSIX_TIMEOUTS ) && _POSIX_TIMEOUTS > 0 18 | # define HAVE_SEM 19 | # include "sem_posix.h" 20 | # endif 21 | #endif 22 | 23 | 24 | /* OSX needs its own code, because it doesn't implement 25 | * `sem_timedwait()`! When it does, we'll gladly use the generic 26 | * POSIX code above. Until then ... */ 27 | #if !defined( HAVE_SEM ) && \ 28 | defined( __APPLE__ ) && defined( __MACH__ ) 29 | # define HAVE_SEM 30 | # include "sem_osx.h" 31 | #endif 32 | 33 | 34 | /* check for Windows */ 35 | #if !defined( HAVE_SEM ) && \ 36 | defined( _WIN32 ) && !defined( __CYGWIN__ ) 37 | # define HAVE_SEM 38 | # include "sem_win.h" 39 | #endif 40 | 41 | 42 | #ifdef HAVE_SEM 43 | 44 | #define NAME "ipc.sem" 45 | 46 | typedef struct { 47 | ipc_sem_handle h; /* platform specific data */ 48 | /* extra management info: */ 49 | char is_owner; 50 | char is_valid; 51 | } l_sem_handle; 52 | 53 | 54 | static int pusherror( lua_State* L, int code ) { 55 | char buf[ IPC_MAXERRMSG ]; 56 | ipc_sem_error( buf, sizeof( buf ), code ); 57 | lua_pushnil( L ); 58 | lua_pushstring( L, buf ); 59 | lua_pushinteger( L, code ); 60 | return 3; 61 | } 62 | 63 | 64 | static int l_sem_close( lua_State* L ) { 65 | l_sem_handle* h = luaL_checkudata( L, 1, NAME ); 66 | int rv = 0; 67 | if( !h->is_valid ) 68 | luaL_error( L, "attempt to use invalid semaphore object" ); 69 | if( h->is_owner ) 70 | rv = ipc_sem_remove( &h->h ); 71 | else 72 | rv = ipc_sem_close( &h->h ); 73 | if( rv != 0 ) 74 | return pusherror( L, rv ); 75 | h->is_valid = 0; 76 | lua_pushboolean( L, 1 ); 77 | return 1; 78 | } 79 | 80 | 81 | static int l_sem_gc( lua_State* L ) { 82 | l_sem_handle* h = lua_touserdata( L, 1 ); 83 | if( h->is_valid ) { 84 | if( h->is_owner ) 85 | ipc_sem_remove( &h->h ); 86 | else 87 | ipc_sem_close( &h->h ); 88 | } 89 | return 0; 90 | } 91 | 92 | 93 | static int l_sem_open( lua_State* L ) { 94 | char const* name = luaL_checkstring( L, 1 ); 95 | unsigned value = (unsigned)luaL_optinteger( L, 2, 0 ); 96 | l_sem_handle* h = lua_newuserdata( L, sizeof( *h ) ); 97 | int rv = 0; 98 | h->is_owner = 0; 99 | h->is_valid = 0; 100 | luaL_getmetatable( L, NAME ); 101 | lua_setmetatable( L, -2 ); 102 | if( value != 0 ) 103 | rv = ipc_sem_create( &h->h, name, value ); 104 | else 105 | rv = ipc_sem_open( &h->h, name ); 106 | if( rv != 0 ) 107 | return pusherror( L, rv ); 108 | if( value != 0 ) 109 | h->is_owner = 1; 110 | h->is_valid = 1; 111 | return 1; 112 | } 113 | 114 | 115 | static int l_sem_dec( lua_State* L ) { 116 | l_sem_handle* h = luaL_checkudata( L, 1, NAME ); 117 | lua_Integer msec = (lua_Integer)(luaL_optnumber( L, 2, -1 )*1000); 118 | int could_rec = 1; 119 | int rv = ipc_sem_dec( &h->h, msec >= 0 ? &could_rec : NULL, 120 | (unsigned)msec ); 121 | if( rv != 0 ) 122 | return pusherror( L, rv ); 123 | lua_pushboolean( L, could_rec ); 124 | return 1; 125 | } 126 | 127 | 128 | static int l_sem_inc( lua_State* L ) { 129 | l_sem_handle* h = luaL_checkudata( L, 1, NAME ); 130 | int rv = ipc_sem_inc( &h->h ); 131 | if( rv != 0 ) 132 | return pusherror( L, rv ); 133 | lua_pushboolean( L, 1 ); 134 | return 1; 135 | } 136 | 137 | 138 | IPC_API int luaopen_ipc_sem( lua_State* L ) { 139 | luaL_Reg const methods[] = { 140 | { "inc", l_sem_inc }, 141 | { "dec", l_sem_dec }, 142 | { "close", l_sem_close }, 143 | { NULL, NULL } 144 | }; 145 | luaL_Reg const functions[] = { 146 | { "open", l_sem_open }, 147 | { NULL, NULL } 148 | }; 149 | if( !luaL_newmetatable( L, NAME ) ) 150 | luaL_error( L, "redefinition of metatable '%s'", NAME ); 151 | lua_pushcfunction( L, l_sem_gc ); 152 | lua_setfield( L, -2, "__gc" ); 153 | lua_pushboolean( L, 0 ); 154 | lua_setfield( L, -2, "__metatable" ); 155 | luaL_newlib( L, methods ); 156 | lua_setfield( L, -2, "__index" ); 157 | lua_pop( L, 1 ); 158 | luaL_newlib( L, functions ); 159 | return 1; 160 | } 161 | 162 | #else /* no implementation for this platform available: */ 163 | IPC_API int luaopen_ipc_sem( lua_State* L ) { 164 | IPC_NOTIMPLEMENTED( L ); 165 | return 0; 166 | } 167 | #endif 168 | 169 | -------------------------------------------------------------------------------- /sem_osx.h: -------------------------------------------------------------------------------- 1 | /* Mac OSX doesn't have an implementation of `sem_timedwait()`, so we 2 | * provide one. This implementation was written by Keith Shortridge 3 | * at the Australian Astronomical Observatory and can be found 4 | * here[1]. 5 | * I think it contains a race condition where the monitoring thread 6 | * sends the disrupting signal before the calling thread actually 7 | * blocks on `sem_wait()` but so far I haven't found a better 8 | * solution. 9 | * 10 | * [1]: https://github.com/attie/libxbee3/blob/master/xsys_darwin/sem_timedwait.c 11 | */ 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | 19 | int ipc_sem_timedwait_( sem_t* sem, const struct timespec* abs_timeout ); 20 | #define sem_timedwait ipc_sem_timedwait_ 21 | 22 | #include "sem_posix.h" 23 | /* we include it last, because it defines some "useful" macros */ 24 | #include "osx/sem_timedwait.c" 25 | 26 | -------------------------------------------------------------------------------- /sem_posix.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | 13 | typedef struct { 14 | char name[ NAME_MAX-3 ]; 15 | sem_t* sem; 16 | } ipc_sem_handle; 17 | 18 | 19 | static void ipc_sem_error( char* buf, size_t len, int code ) { 20 | if( len > 0 && strerror_r( code, buf, len ) != (int)0 ) { 21 | strncpy( buf, "unknown error", len-1 ); 22 | buf[ len-1 ] = '\0'; 23 | } 24 | } 25 | 26 | 27 | /* the format of sem names is restricted (at least if you want to be 28 | * portable), so this functions checks the input and makes such a 29 | * portable name. */ 30 | static int ipc_sem_make_name_( char const* s, ipc_sem_handle* h ) { 31 | size_t ilen = strlen( s ); 32 | if( ilen == 0 || ilen+2 > sizeof( h->name ) ) 33 | return IPC_ERR( ENAMETOOLONG ); 34 | if( strchr( s, '/' ) != NULL ) 35 | return IPC_ERR( EINVAL ); 36 | h->name[ 0 ] = '/'; 37 | memcpy( h->name+1, s, ilen+1 ); 38 | return 0; 39 | } 40 | 41 | 42 | static int ipc_sem_create( ipc_sem_handle* h, char const* name, 43 | unsigned value ) { 44 | int rv, flags = O_CREAT|O_EXCL; 45 | rv = ipc_sem_make_name_( name, h ); 46 | if( rv != 0 ) 47 | return IPC_ERR( rv ); 48 | #ifdef O_CLOEXEC 49 | flags |= O_CLOEXEC; 50 | #endif 51 | h->sem = sem_open( h->name, flags, S_IRUSR|S_IWUSR, value ); 52 | if( h->sem == SEM_FAILED ) 53 | return IPC_ERR( errno ); 54 | return 0; 55 | } 56 | 57 | 58 | static int ipc_sem_open( ipc_sem_handle* h, char const* name ) { 59 | int rv, flags = 0; 60 | rv = ipc_sem_make_name_( name, h ); 61 | if( rv != 0 ) 62 | return IPC_ERR( rv ); 63 | #ifdef O_CLOEXEC 64 | flags |= O_CLOEXEC; 65 | #endif 66 | h->sem = sem_open( h->name, flags ); 67 | if( h->sem == SEM_FAILED ) 68 | return IPC_ERR( errno ); 69 | return 0; 70 | } 71 | 72 | 73 | static int ipc_sem_inc( ipc_sem_handle* h ) { 74 | if( sem_post( h->sem ) < 0 ) 75 | return IPC_ERR( errno ); 76 | return 0; 77 | } 78 | 79 | 80 | static int ipc_sem_dec( ipc_sem_handle* h, int* could_dec, unsigned milliseconds ) { 81 | int rv = 0; 82 | if( could_dec == NULL ) { /* blocking */ 83 | IPC_EINTR( rv, sem_wait( h->sem ) ); 84 | if( rv < 0 ) 85 | return IPC_ERR( errno ); 86 | } else if( milliseconds == 0 ) { /* peeking */ 87 | if( sem_trywait( h->sem ) < 0 ) { 88 | if( errno == EAGAIN ) { 89 | *could_dec = 0; 90 | return 0; 91 | } 92 | return IPC_ERR( errno ); 93 | } 94 | *could_dec = 1; 95 | } else { /* waiting with a timeout */ 96 | struct timespec timeout; 97 | #if defined( _POSIX_TIMERS ) && _POSIX_TIMERS > 0 98 | if( clock_gettime( CLOCK_REALTIME, &timeout ) < 0 ) 99 | return IPC_ERR( errno ); 100 | #else 101 | struct timeval tv; 102 | if( gettimeofday( &tv, NULL ) < 0 ) 103 | return IPC_ERR( errno ); 104 | timeout.tv_sec = tv.tv_sec; 105 | timeout.tv_nsec = tv.tv_usec * 1000; 106 | #endif 107 | timeout.tv_sec += milliseconds / 1000; 108 | timeout.tv_nsec += (milliseconds % 1000)*1000000; 109 | if( timeout.tv_nsec >= 1000000000L ) { 110 | timeout.tv_sec++; 111 | timeout.tv_nsec -= 1000000000L; 112 | } 113 | IPC_EINTR( rv, sem_timedwait( h->sem, &timeout ) ); 114 | if( rv < 0 ) { 115 | if( errno == ETIMEDOUT ) { 116 | *could_dec = 0; 117 | return 0; 118 | } 119 | return IPC_ERR( errno ); 120 | } 121 | *could_dec = 1; 122 | } 123 | return 0; 124 | } 125 | 126 | 127 | static int ipc_sem_close( ipc_sem_handle* h ) { 128 | if( sem_close( h->sem ) < 0 ) 129 | return IPC_ERR( errno ); 130 | return 0; 131 | } 132 | 133 | 134 | static int ipc_sem_remove( ipc_sem_handle* h ) { 135 | if( sem_close( h->sem ) < 0 ) { 136 | int saved_errno = errno; 137 | sem_unlink( h->name ); 138 | return IPC_ERR( saved_errno ); 139 | } 140 | sem_unlink( h->name ); 141 | return 0; 142 | } 143 | 144 | -------------------------------------------------------------------------------- /sem_win.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #define WIN32_LEAN_AND_MEAN 7 | #include 8 | 9 | 10 | typedef struct { 11 | HANDLE sem; 12 | } ipc_sem_handle; 13 | 14 | 15 | static void ipc_sem_error( char* buf, size_t len, int code ) { 16 | if( len > 0 ) { 17 | if( 0 == FormatMessageA( FORMAT_MESSAGE_FROM_SYSTEM | 18 | FORMAT_MESSAGE_IGNORE_INSERTS, 19 | NULL, 20 | code, 21 | 0, 22 | buf, 23 | len, 24 | NULL ) ) { 25 | strncpy( buf, "unknown error", len-1 ); 26 | buf[ len-1 ] = '\0'; 27 | } else { /* Windows puts an extra newline in there! */ 28 | size_t n = strlen( buf ); 29 | while( n > 0 && isspace( (unsigned char)buf[ --n ] ) ) 30 | buf[ n ] = '\0'; 31 | } 32 | } 33 | } 34 | 35 | 36 | static int ipc_sem_make_name_( char const* s, char** name ) { 37 | size_t len = strlen( s ); 38 | if( len == 0 || len > MAX_PATH || strcspn( s, "/\\" ) != len ) 39 | return IPC_ERR( ERROR_INVALID_NAME ); 40 | /* FIXME: should we try a "Global\\" name first? */ 41 | #define P "Local\\" 42 | *name = malloc( len + sizeof( P ) ); 43 | if( *name == NULL ) 44 | return IPC_ERR( ERROR_OUTOFMEMORY ); /* or ERROR_NOT_ENOUGH_MEMORY?! */ 45 | memcpy( *name, P, sizeof( P )-1 ); 46 | memcpy( *name+sizeof( P )-1, s, len+1 ); 47 | #undef P 48 | return 0; 49 | } 50 | 51 | 52 | static int ipc_sem_create( ipc_sem_handle* h, char const* name, 53 | unsigned value ) { 54 | char* rname = NULL; 55 | int rv = ipc_sem_make_name_( name, &rname ); 56 | if( rv != 0 ) 57 | return IPC_ERR( rv ); 58 | if( value > LONG_MAX ) { 59 | free( rname ); 60 | return IPC_ERR( ERROR_INVALID_PARAMETER ); 61 | } 62 | h->sem = CreateSemaphoreA( NULL, 63 | (LONG)value, 64 | LONG_MAX, 65 | rname ); 66 | if( h->sem == NULL ) { 67 | int saved_errno = GetLastError(); 68 | free( rname ); 69 | return IPC_ERR( saved_errno ); 70 | } else if( GetLastError() == ERROR_ALREADY_EXISTS ) { 71 | CloseHandle( h->sem ); 72 | free( rname ); 73 | return IPC_ERR( ERROR_ALREADY_EXISTS ); 74 | } 75 | /* Windows automatically handles the lifetime of the semaphore 76 | * object, so we don't have to keep track of the name! */ 77 | free( rname ); 78 | return 0; 79 | } 80 | 81 | 82 | static int ipc_sem_open( ipc_sem_handle* h, char const* name ) { 83 | char* rname = NULL; 84 | int rv = ipc_sem_make_name_( name, &rname ); 85 | if( rv != 0 ) 86 | return IPC_ERR( rv ); 87 | h->sem = OpenSemaphoreA( SEMAPHORE_ALL_ACCESS, 88 | FALSE, 89 | rname ); 90 | if( h->sem == NULL ) { 91 | int saved_errno = GetLastError(); 92 | free( rname ); 93 | return IPC_ERR( saved_errno ); 94 | } 95 | /* Windows automatically handles the lifetime of the semaphore 96 | * object, so we don't have to keep track of the name! */ 97 | free( rname ); 98 | return 0; 99 | } 100 | 101 | 102 | static int ipc_sem_inc( ipc_sem_handle* h ) { 103 | if( !ReleaseSemaphore( h->sem, 1, NULL ) ) 104 | return IPC_ERR( GetLastError() ); 105 | return 0; 106 | } 107 | 108 | 109 | static int ipc_sem_dec( ipc_sem_handle* h, int* could_dec, unsigned milliseconds ) { 110 | if( could_dec == NULL ) 111 | milliseconds = INFINITE; 112 | switch( WaitForSingleObject( h->sem, milliseconds ) ) { 113 | case WAIT_OBJECT_0: 114 | if( could_dec != NULL ) 115 | *could_dec = 1; 116 | break; 117 | case WAIT_TIMEOUT: 118 | if( could_dec != NULL ) 119 | *could_dec = 0; 120 | else 121 | return IPC_ERR( ERROR_SEM_TIMEOUT ); 122 | break; 123 | default: 124 | return IPC_ERR( GetLastError() ); 125 | } 126 | return 0; 127 | } 128 | 129 | 130 | static int ipc_sem_close( ipc_sem_handle* h ) { 131 | if( !CloseHandle( h->sem ) ) 132 | return IPC_ERR( GetLastError() ); 133 | return 0; 134 | } 135 | 136 | 137 | static int ipc_sem_remove( ipc_sem_handle* h ) { 138 | /* Windows automatically removes a semaphore object when all 139 | * processes referencing it are destroyed (or when all handles are 140 | * closed?). */ 141 | return IPC_ERR( ipc_sem_close( h ) ); 142 | } 143 | 144 | -------------------------------------------------------------------------------- /shm.c: -------------------------------------------------------------------------------- 1 | #ifndef _POSIX_C_SOURCE 2 | # define _POSIX_C_SOURCE 200112L 3 | #endif 4 | #ifndef _FILE_OFFSET_BITS 5 | # define _LARGEFILE_SOURCE 1 6 | # define _FILE_OFFSET_BITS 64 7 | #endif 8 | #include 9 | #include 10 | #include 11 | #include "memfile.h" 12 | 13 | 14 | /* check for POSIX */ 15 | #if defined( unix ) || defined( __unix ) || defined( __unix__ ) || \ 16 | (defined( __APPLE__ ) && defined( __MACH__ )) || \ 17 | HAVE_UNISTD_H 18 | # include 19 | # if defined( _POSIX_VERSION ) && _POSIX_VERSION >= 200112L && \ 20 | ((defined( _POSIX_MAPPED_FILES ) && _POSIX_MAPPED_FILES > 0 && \ 21 | defined( _POSIX_SHARED_MEMORY_OBJECTS ) && \ 22 | _POSIX_SHARED_MEMORY_OBJECTS > 0) || \ 23 | (defined( __APPLE__ ) && defined( __MACH__ ))) 24 | # define HAVE_SHM 25 | # include "shm_posix.h" 26 | # endif 27 | #endif 28 | 29 | 30 | /* check for Windows */ 31 | #if !defined( HAVE_SHM ) && \ 32 | defined( _WIN32 ) && !defined( __CYGWIN__ ) 33 | # define HAVE_SHM 34 | # include "shm_win.h" 35 | #endif 36 | 37 | 38 | #ifdef HAVE_SHM 39 | 40 | #define NAME "ipc.shm" 41 | 42 | typedef struct { 43 | ipc_shm_handle h; /* platform specific data */ 44 | /* extra management info: */ 45 | char is_owner; 46 | char is_valid; 47 | } l_shm_handle; 48 | 49 | 50 | static int pusherror( lua_State* L, int code ) { 51 | char buf[ IPC_MAXERRMSG ]; 52 | ipc_shm_error( buf, sizeof( buf ), code ); 53 | lua_pushnil( L ); 54 | lua_pushstring( L, buf ); 55 | lua_pushinteger( L, code ); 56 | return 3; 57 | } 58 | 59 | 60 | static int l_shm_close( lua_State* L ) { 61 | l_shm_handle* h = luaL_checkudata( L, 1, NAME ); 62 | int rv = 0; 63 | if( !h->is_valid ) 64 | luaL_error( L, "attempt to use invalid shm object" ); 65 | if( h->is_owner ) 66 | rv = ipc_shm_remove( &h->h ); 67 | else 68 | rv = ipc_shm_detach( &h->h ); 69 | if( rv != 0 ) 70 | return pusherror( L, rv ); 71 | h->is_valid = 0; 72 | lua_pushboolean( L, 1 ); 73 | return 1; 74 | } 75 | 76 | 77 | static int l_shm_gc( lua_State* L ) { 78 | l_shm_handle* h = lua_touserdata( L, 1 ); 79 | if( h->is_valid ) { 80 | if( h->is_owner ) 81 | ipc_shm_remove( &h->h ); 82 | else 83 | ipc_shm_detach( &h->h ); 84 | } 85 | return 0; 86 | } 87 | 88 | 89 | 90 | static int l_shm_create( lua_State* L ) { 91 | char const* name = luaL_checkstring( L, 1 ); 92 | size_t req = luaL_checkinteger( L, 2 ); 93 | l_shm_handle* h = lua_newuserdata( L, sizeof( *h ) ); 94 | int rv = 0; 95 | h->is_owner = 1; 96 | h->is_valid = 0; 97 | luaL_getmetatable( L, NAME ); 98 | lua_setmetatable( L, -2 ); 99 | rv = ipc_shm_create( &h->h, name, req ); 100 | if( rv != 0 ) 101 | return pusherror( L, rv ); 102 | h->is_valid = 1; 103 | lua_pushcfunction( L, l_shm_close ); 104 | memfile_new( L, ipc_shm_addr( &h->h ), ipc_shm_size( &h->h ), 105 | MEMFILE_RW, -2, -1, 0 ); 106 | return 1; 107 | } 108 | 109 | 110 | static int l_shm_attach( lua_State* L ) { 111 | char const* name = luaL_checkstring( L, 1 ); 112 | l_shm_handle* h = lua_newuserdata( L, sizeof( *h ) ); 113 | int rv = 0; 114 | h->is_owner = 0; 115 | h->is_valid = 0; 116 | luaL_getmetatable( L, NAME ); 117 | lua_setmetatable( L, -2 ); 118 | rv = ipc_shm_attach( &h->h, name ); 119 | if( rv != 0 ) 120 | return pusherror( L, rv ); 121 | h->is_valid = 1; 122 | lua_pushcfunction( L, l_shm_close ); 123 | memfile_new( L, ipc_shm_addr( &h->h ), ipc_shm_size( &h->h ), 124 | MEMFILE_RW, -2, -1, 0 ); 125 | return 1; 126 | } 127 | 128 | 129 | IPC_API int luaopen_ipc_shm( lua_State* L ) { 130 | luaL_Reg const functions[] = { 131 | { "create", l_shm_create }, 132 | { "attach", l_shm_attach }, 133 | { NULL, NULL } 134 | }; 135 | if( !luaL_newmetatable( L, NAME ) ) 136 | luaL_error( L, "redefinition of metatable '%s'", NAME ); 137 | lua_pushcfunction( L, l_shm_gc ); 138 | lua_setfield( L, -2, "__gc" ); 139 | lua_pop( L, 1 ); 140 | luaL_newlib( L, functions ); 141 | return 1; 142 | } 143 | 144 | #else /* no implementation for this platform available: */ 145 | IPC_API int luaopen_ipc_shm( lua_State* L ) { 146 | IPC_NOTIMPLEMENTED( L ); 147 | return 0; 148 | } 149 | #endif 150 | 151 | -------------------------------------------------------------------------------- /shm_posix.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | typedef struct { 12 | char name[ NAME_MAX ]; 13 | void* addr; 14 | size_t len; 15 | } ipc_shm_handle; 16 | 17 | static void* ipc_shm_addr( ipc_shm_handle* h ) { 18 | return h->addr; 19 | } 20 | 21 | static size_t ipc_shm_size( ipc_shm_handle* h ) { 22 | return h->len; 23 | } 24 | 25 | 26 | static void ipc_shm_error( char* buf, size_t len, int code ) { 27 | if( len > 0 && strerror_r( code, buf, len ) != (int)0 ) { 28 | strncpy( buf, "unknown error", len-1 ); 29 | buf[ len-1 ] = '\0'; 30 | } 31 | } 32 | 33 | 34 | /* the format of shm names is restricted (at least if you want to be 35 | * portable), so this functions checks the input and makes such a 36 | * portable name. */ 37 | static int ipc_shm_make_name_( char const* s, ipc_shm_handle* h ) { 38 | size_t ilen = strlen( s ); 39 | if( ilen == 0 || ilen+2 > sizeof( h->name ) ) 40 | return IPC_ERR( ENAMETOOLONG ); 41 | if( strchr( s, '/' ) != NULL ) 42 | return IPC_ERR( EINVAL ); 43 | h->name[ 0 ] = '/'; 44 | memcpy( h->name+1, s, ilen+1 ); 45 | return 0; 46 | } 47 | 48 | 49 | static int ipc_shm_create( ipc_shm_handle* h, char const* name, 50 | size_t req ) { 51 | int fd, rv, flags = O_RDWR|O_CREAT|O_EXCL; 52 | rv = ipc_shm_make_name_( name, h ); 53 | if( rv != 0 ) 54 | return IPC_ERR( rv ); 55 | if( req == 0 ) 56 | return IPC_ERR( EINVAL ); 57 | /* open(create) shm object */ 58 | #ifdef O_CLOEXEC 59 | flags |= O_CLOEXEC; 60 | #endif 61 | fd = shm_open( h->name, flags, S_IRUSR|S_IWUSR ); 62 | if( fd < 0 ) 63 | return IPC_ERR( errno ); 64 | /* resize to requested size */ 65 | if( ftruncate( fd, (off_t)req ) < 0 ) { 66 | int saved_errno = errno; 67 | close( fd ); 68 | shm_unlink( h->name ); 69 | return IPC_ERR( saved_errno ); 70 | } 71 | h->len = req; 72 | /* create mmap */ 73 | h->addr = mmap( NULL, req, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0 ); 74 | if( h->addr == MAP_FAILED ) { 75 | int saved_errno = errno; 76 | close( fd ); 77 | shm_unlink( h->name ); 78 | return IPC_ERR( saved_errno ); 79 | } 80 | close( fd ); /* we don't need it anymore! */ 81 | return 0; 82 | } 83 | 84 | 85 | static int ipc_shm_attach( ipc_shm_handle* h, char const* name ) { 86 | int fd, rv, flags = O_RDWR; 87 | struct stat buf; 88 | rv = ipc_shm_make_name_( name, h ); 89 | if( rv != 0 ) 90 | return IPC_ERR( rv ); 91 | /* open shared memory object */ 92 | #ifdef O_CLOEXEC 93 | flags |= O_CLOEXEC; 94 | #endif 95 | fd = shm_open( h->name, flags, S_IRUSR|S_IWUSR ); 96 | if( fd < 0 ) 97 | return IPC_ERR( errno ); 98 | /* figure out its size */ 99 | if( fstat( fd, &buf ) < 0 ) { 100 | int saved_errno = errno; 101 | close( fd ); 102 | return IPC_ERR( saved_errno ); 103 | } 104 | if( buf.st_size > (size_t)-1 ) { 105 | close( fd ); 106 | return IPC_ERR( EFBIG ); 107 | } 108 | h->len = buf.st_size; 109 | /* create mmap */ 110 | h->addr = mmap( NULL, h->len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0 ); 111 | if( h->addr == MAP_FAILED ) { 112 | int saved_errno = errno; 113 | close( fd ); 114 | return IPC_ERR( saved_errno ); 115 | } 116 | close( fd ); /* we don't need it anymore! */ 117 | return 0; 118 | } 119 | 120 | 121 | static int ipc_shm_detach( ipc_shm_handle* h ) { 122 | if( munmap( h->addr, h->len ) < 0 ) 123 | return IPC_ERR( errno ); 124 | return 0; 125 | } 126 | 127 | 128 | static int ipc_shm_remove( ipc_shm_handle* h ) { 129 | if( munmap( h->addr, h->len ) < 0 ) { 130 | int saved_errno = errno; 131 | shm_unlink( h->name ); /* try to delete it anyway */ 132 | return IPC_ERR( saved_errno ); 133 | } 134 | shm_unlink( h->name ); 135 | return 0; 136 | } 137 | 138 | -------------------------------------------------------------------------------- /shm_win.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #define WIN32_LEAN_AND_MEAN 6 | #include 7 | 8 | 9 | typedef struct { 10 | HANDLE h; /* creator handle */ 11 | void* addr; 12 | size_t len; 13 | } ipc_shm_handle; 14 | 15 | static void* ipc_shm_addr( ipc_shm_handle* h ) { 16 | return h->addr; 17 | } 18 | 19 | static size_t ipc_shm_size( ipc_shm_handle* h ) { 20 | return h->len; 21 | } 22 | 23 | 24 | static void ipc_shm_error( char* buf, size_t len, int code ) { 25 | if( len > 0 ) { 26 | if( 0 == FormatMessageA( FORMAT_MESSAGE_FROM_SYSTEM | 27 | FORMAT_MESSAGE_IGNORE_INSERTS, 28 | NULL, 29 | code, 30 | 0, 31 | buf, 32 | len, 33 | NULL ) ) { 34 | strncpy( buf, "unknown error", len-1 ); 35 | buf[ len-1 ] = '\0'; 36 | } else { /* Windows puts an extra newline in there! */ 37 | size_t n = strlen( buf ); 38 | while( n > 0 && isspace( (unsigned char)buf[ --n ] ) ) 39 | buf[ n ] = '\0'; 40 | } 41 | } 42 | } 43 | 44 | 45 | static int ipc_shm_make_name_( char const* s, char** name ) { 46 | size_t len = strlen( s ); 47 | if( len == 0 || strcspn( s, "/\\" ) != len ) 48 | return IPC_ERR( ERROR_INVALID_NAME ); 49 | /* FIXME: should we try a "Global\\" name first? */ 50 | #define P "Local\\" 51 | *name = malloc( len + sizeof( P ) ); 52 | if( *name == NULL ) 53 | return IPC_ERR( ERROR_OUTOFMEMORY ); /* or ERROR_NOT_ENOUGH_MEMORY?! */ 54 | memcpy( *name, P, sizeof( P )-1 ); 55 | memcpy( *name+sizeof( P )-1, s, len+1 ); 56 | #undef P 57 | return 0; 58 | } 59 | 60 | 61 | static int ipc_shm_create( ipc_shm_handle* h, char const* name, 62 | size_t req ) { 63 | char* rname = NULL; 64 | int rv = ipc_shm_make_name_( name, &rname ); 65 | if( rv != 0 ) 66 | return IPC_ERR( rv ); 67 | if( req == 0 ) { 68 | free( rname ); 69 | return IPC_ERR( ERROR_INVALID_PARAMETER ); 70 | } 71 | h->h = CreateFileMappingA( INVALID_HANDLE_VALUE, 72 | NULL, 73 | PAGE_READWRITE, 74 | (DWORD)((req >> 16) >> 16), 75 | (DWORD)req, 76 | rname ); 77 | if( h->h == NULL ) { 78 | int saved_errno = GetLastError(); 79 | free( rname ); 80 | return IPC_ERR( saved_errno ); 81 | } else if( GetLastError() == ERROR_ALREADY_EXISTS ) { 82 | /* It seems we have no way of actually removing the shared memory 83 | * object -- should we fail with EEXIST anyway?! */ 84 | CloseHandle( h->h ); 85 | free( rname ); 86 | return IPC_ERR( ERROR_ALREADY_EXISTS ); 87 | } 88 | /* Windows automatically handles the lifetime of the shared memory 89 | * object, so we don't need to keep track of the name! */ 90 | free( rname ); 91 | h->addr = MapViewOfFile( h->h, 92 | FILE_MAP_ALL_ACCESS, 93 | 0, 94 | 0, 95 | 0 ); 96 | if( h->addr == NULL ) { 97 | int saved_errno = GetLastError(); 98 | CloseHandle( h->h ); 99 | return IPC_ERR( saved_errno ); 100 | } 101 | h->len = req; 102 | return 0; 103 | } 104 | 105 | 106 | static int ipc_shm_attach( ipc_shm_handle* h, char const* name ) { 107 | HANDLE hmap; 108 | MEMORY_BASIC_INFORMATION meminfo; 109 | char* rname = NULL; 110 | int rv = ipc_shm_make_name_( name, &rname ); 111 | if( rv != 0 ) 112 | return IPC_ERR( rv ); 113 | hmap = OpenFileMappingA( FILE_MAP_ALL_ACCESS, 114 | FALSE, 115 | rname ); 116 | if( hmap == NULL ) { 117 | int saved_errno = GetLastError(); 118 | free( rname ); 119 | return IPC_ERR( saved_errno ); 120 | } 121 | free( rname ); 122 | h->addr = MapViewOfFile( hmap, 123 | FILE_MAP_ALL_ACCESS, 124 | 0, 125 | 0, 126 | 0 ); 127 | if( h->addr == NULL ) { 128 | int saved_errno = GetLastError(); 129 | CloseHandle( hmap ); 130 | return IPC_ERR( saved_errno ); 131 | } 132 | CloseHandle( hmap ); 133 | if( VirtualQuery( h->addr, &meminfo, sizeof( meminfo ) ) == 0 ) { 134 | int saved_errno = GetLastError(); 135 | UnmapViewOfFile( h->addr ); 136 | return IPC_ERR( saved_errno ); 137 | } 138 | h->len = meminfo.RegionSize; 139 | return 0; 140 | } 141 | 142 | 143 | static int ipc_shm_detach( ipc_shm_handle* h ) { 144 | if( !UnmapViewOfFile( h->addr ) ) 145 | return IPC_ERR( GetLastError() ); 146 | return 0; 147 | } 148 | 149 | 150 | static int ipc_shm_remove( ipc_shm_handle* h ) { 151 | if( !UnmapViewOfFile( h->addr ) ) { 152 | int saved_errno = GetLastError(); 153 | CloseHandle( h->h ); 154 | return IPC_ERR( saved_errno ); 155 | } 156 | CloseHandle( h->h ); 157 | return 0; 158 | } 159 | 160 | -------------------------------------------------------------------------------- /strfile.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "ipc.h" 5 | #include "memfile.h" 6 | 7 | 8 | static int str_open( lua_State* L ) { 9 | size_t len = 0; 10 | char const* str = luaL_checklstring( L, 1, &len ); 11 | memfile_new( L, (void*)str, len, MEMFILE_R, 1, 0, 0 ); 12 | return 1; 13 | } 14 | 15 | 16 | IPC_API int luaopen_ipc_strfile( lua_State* L ) { 17 | luaL_Reg const functions[] = { 18 | { "open", str_open }, 19 | { NULL, NULL } 20 | }; 21 | luaL_newlib( L, functions ); 22 | return 1; 23 | } 24 | 25 | -------------------------------------------------------------------------------- /tests/mmapcat.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/lua 2 | 3 | package.cpath = "../?.so;../?.dll;"..package.cpath 4 | local mmap = require( "ipc.mmap" ) 5 | print( "pagesize is", mmap.pagesize ) 6 | 7 | -- open the file to mmap 8 | local mem = assert( mmap.open( "mmapcat.lua", "r" ) ) 9 | -- writing should not be allowed for that memory mapping: 10 | print( mem:write( "xxx" ) ) 11 | -- `cat` the file to stdout 12 | for s in mem:lines( 10 ) do 13 | io.stdout:write( s ) 14 | end 15 | mem:close() 16 | 17 | -------------------------------------------------------------------------------- /tests/mmaprev.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/lua 2 | 3 | local FILE = "data.txt" 4 | local N = 3 5 | 6 | package.cpath = "../?.so;../?.dll;"..package.cpath 7 | local mmap = require( "ipc.mmap" ) 8 | print( "pagesize is", mmap.pagesize ) 9 | 10 | local function cat( path ) 11 | io.write( "Contents of '", path, "':\n" ) 12 | local f = assert( io.open( path, "rb" ) ) 13 | for l in f:lines() do 14 | io.write( l, "\n" ) 15 | end 16 | f:close() 17 | io.flush() 18 | end 19 | 20 | -- create a new file 21 | local f = assert( io.open( FILE, "wb" ) ) 22 | f:write( (" "):rep( mmap.pagesize ) ) 23 | for i = 1, N do 24 | f:write( "0123456789" ) 25 | end 26 | f:write( "\n" ) 27 | f:close() 28 | 29 | -- make sure that it has the contents above 30 | cat( FILE ) 31 | 32 | -- open the file to mmap 33 | local mem = assert( mmap.open( FILE, "w", mmap.pagesize ) ) 34 | print( "pointer:", mem:addr(), "size:", mem:size() ) 35 | -- modify the contents 36 | for i = 1, N do 37 | mem:write( "9876543210" ) 38 | end 39 | mem:close() 40 | 41 | -- make sure that is has the new contents 42 | io.write( "Data should be reversed now!\n" ) 43 | cat( FILE ) 44 | 45 | -------------------------------------------------------------------------------- /tests/proctest.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/lua 2 | 3 | package.cpath = "../?.so;../?.dll;"..package.cpath 4 | local proc = require( "ipc.proc" ) 5 | 6 | local lua_command, i = arg[ -1 ], -2 7 | while arg[ i ] ~= nil do 8 | lua_command, i = arg[ i ], i - 1 9 | end 10 | 11 | local function ___() 12 | io.stdout:write( ("="):rep( 70 ), "\n" ) 13 | io.stdout:flush() 14 | end 15 | 16 | local options = { 17 | stdin = true, 18 | stdout = true, 19 | stderr = true, 20 | callback = function( stream, data, msg, errno ) 21 | if data then 22 | io.write( stream.."\t", data:upper() ) 23 | else 24 | io.stderr( "ERROR", tostring( stream ), tostring( data ), 25 | tostring( msg ), tostring( errno ) ) 26 | end 27 | end, 28 | } 29 | 30 | ___'' 31 | local h = assert( proc.spawn( lua_command..' procwork.lua 1', options ) ) 32 | print( h:wait() ) 33 | 34 | ___'' 35 | h = assert( proc.spawn( lua_command..' procwork.lua 2', options ) ) 36 | print( h:wait() ) 37 | 38 | ___'' 39 | h = assert( proc.spawn( lua_command..' procwork.lua 3', options ) ) 40 | print( h:wait() ) 41 | 42 | ___'' 43 | h = assert( proc.spawn( lua_command..' procwork.lua 4', options ) ) 44 | print( h:wait() ) 45 | 46 | ___'' 47 | h = assert( proc.spawn( lua_command..' procwork.lua 5', options ) ) 48 | print( h:wait() ) 49 | 50 | ___'' 51 | h = assert( proc.spawn( lua_command..' procwork.lua 6', options ) ) 52 | assert( h:write( "sending input\n", proc.EOF ) ) 53 | print( h:wait() ) 54 | 55 | ___'' 56 | h = assert( proc.spawn( lua_command..' procwork.lua 7', options ) ) 57 | assert( h:write( "hallo welt,\n", "ich bin da.\n", "hallo welt,\n", 58 | "alles klar?\n", proc.EOF ) ) 59 | print( h:wait() ) 60 | 61 | ___'' 62 | h = assert( proc.spawn( lua_command..' procwork.lua 7', options ) ) 63 | assert( h:kill( "term" ) ) 64 | print( h:wait() ) 65 | 66 | ___'' 67 | local options2 = { 68 | stdin = assert( io.open( "procwork.lua", "r" ) ), 69 | stdout = assert( io.open( "out.txt", "w" ) ), 70 | } 71 | h = assert( proc.spawn( lua_command..' procwork.lua 7', options2 ) ) 72 | options2.stdin:close() 73 | options2.stdout:close() 74 | print( h:wait() ) 75 | 76 | ___'' 77 | 78 | -------------------------------------------------------------------------------- /tests/procwork.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/lua 2 | 3 | if arg[ 1 ] == "1" then 4 | io.stdout:write( "hello world\n" ) 5 | elseif arg[ 1 ] == "2" then 6 | io.stderr:write( "hello world\n" ) 7 | elseif arg[ 1 ] == "3" then 8 | io.stdout:write( "hello world\n" ) 9 | io.stderr:write( "hello world\n" ) 10 | elseif arg[ 1 ] == "4" then 11 | error( "an error message" ) 12 | elseif arg[ 1 ] == "5" then 13 | -- no input/output! 14 | elseif arg[ 1 ] == "6" then 15 | local l = io.read() 16 | io.write( l, "\n" ) 17 | elseif arg[ 1 ] == "7" then 18 | for l in io.lines() do 19 | io.write( l, "\n" ) 20 | end 21 | end 22 | 23 | -------------------------------------------------------------------------------- /tests/semtest.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/lua 2 | 3 | local dirsep = assert( package.config:match( "^([^\n]*)" ) ) 4 | package.cpath = "../?.so;../?.dll;"..package.cpath 5 | local sem = require( "ipc.sem" ) 6 | local socket_ok, socket = pcall( require, "socket" ) 7 | 8 | local lua_command, i = arg[ -1 ], -2 9 | while arg[ i ] ~= nil do 10 | lua_command, i = arg[ i ], i - 1 11 | end 12 | 13 | local function lua_spawn( s ) 14 | if dirsep == "\\" then -- Windows 15 | return os.execute( 'start "'..s..'" "'..lua_command..'" "'..s..'"' ) 16 | else -- assume some form of POSIX 17 | return os.execute( "'"..lua_command.."' '"..s.."' &" ) 18 | end 19 | end 20 | 21 | if socket_ok then 22 | function sleep( secs ) 23 | socket.select( nil, nil, secs ) 24 | end 25 | else 26 | function sleep( secs ) 27 | local start = os.clock() 28 | while os.clock()-start < secs do 29 | end 30 | end 31 | end 32 | 33 | local s = assert( sem.open( "luasem", 3 ) ); 34 | 35 | for i = 1, 10 do 36 | lua_spawn( "semwork.lua" ) 37 | end 38 | sleep( 10 ) 39 | 40 | s:close() 41 | 42 | -------------------------------------------------------------------------------- /tests/semwork.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/lua 2 | 3 | package.cpath = "../?.so;../?.dll;"..package.cpath 4 | local sem = require( "ipc.sem" ) 5 | local socket_ok, socket = pcall( require, "socket" ) 6 | 7 | if socket_ok then 8 | function sleep( secs ) 9 | socket.select( nil, nil, secs ) 10 | end 11 | else 12 | function sleep( secs ) 13 | local start = os.clock() 14 | while os.clock()-start < secs do 15 | end 16 | end 17 | end 18 | 19 | local s = assert( sem.open( "luasem" ) ) 20 | local ok, msg, errno = s:dec( 0 ) 21 | io.stdout:write( "peeking: ", tostring( ok ), ", ", tostring( msg ), ", ", tostring( errno ), "\n" ) 22 | if not ok then 23 | ok, msg, errno = s:dec( 3 ) 24 | io.stdout:write( "waiting: ", tostring( ok ), ", ", tostring( msg ), ", ", tostring( errno ), "\n" ) 25 | end 26 | if not ok then 27 | ok, msg, errno = s:dec() 28 | io.stdout:write( "blocking: ", tostring( ok ), ", ", tostring( msg ), ", ", tostring( errno ), "\n" ) 29 | end 30 | io.stdout:write( "decremented!\n" ) 31 | io.stdout:flush() 32 | sleep( 2 ) 33 | io.stdout:write( "incrementing!\n" ) 34 | io.stdout:flush() 35 | s:inc() 36 | s:close() 37 | -- XXX You may want to uncomment this if you test on Windows: 38 | --io.read() 39 | 40 | -------------------------------------------------------------------------------- /tests/shmread.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/lua 2 | 3 | package.cpath = "../?.so;../?.dll;"..package.cpath 4 | local shm = require( "ipc.shm" ) 5 | local fl = require( "ipc.filelock" ) 6 | local socket_ok, socket = pcall( require, "socket" ) 7 | 8 | local lockfile = assert( io.open( "shm.lck", "w+" ) ) 9 | local function lock() 10 | local ok, msg, code = fl.trylock( lockfile, "r" ) 11 | print( "trylock", ok, msg, code ) 12 | if not ok then 13 | ok, msg, code = assert( fl.lock( lockfile, "r" ) ) 14 | print( "lock", ok, msg, code ) 15 | end 16 | end 17 | local function unlock() 18 | assert( fl.unlock( lockfile ) ) 19 | end 20 | 21 | if socket_ok then 22 | function sleep( secs ) 23 | socket.select( nil, nil, secs ) 24 | end 25 | else 26 | function sleep( secs ) 27 | local start = os.clock() 28 | while os.clock() - start < secs do end 29 | end 30 | end 31 | 32 | -- create the shared memory segment 33 | local shm = assert( shm.create( "luashm", 250 ) ) 34 | 35 | local generation = "0" 36 | shm:write( generation ) 37 | 38 | while generation ~= "3" do 39 | lock() 40 | shm:seek( "set", 0 ) 41 | local newgeneration = shm:read( 1 ) 42 | if newgeneration ~= generation then 43 | generation = newgeneration 44 | local line = shm:read( "l" ) 45 | print( "Read:", line ) 46 | end 47 | unlock() 48 | sleep( 1 ) 49 | end 50 | shm:close() 51 | lockfile:close() 52 | 53 | print( "Done!" ) 54 | 55 | -------------------------------------------------------------------------------- /tests/shmtest.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/lua 2 | 3 | local dirsep = assert( package.config:match( "^([^\n]*)" ) ) 4 | local socket_ok, socket = pcall( require, "socket" ) 5 | 6 | if socket_ok then 7 | function sleep( secs ) 8 | socket.select( nil, nil, secs ) 9 | end 10 | else 11 | function sleep( secs ) 12 | local start = os.clock() 13 | while os.clock()-start < secs do 14 | end 15 | end 16 | end 17 | 18 | local lua_command, i = arg[ -1 ], -2 19 | while arg[ i ] ~= nil do 20 | lua_command, i = arg[ i ], i - 1 21 | end 22 | 23 | local function lua_spawn( s ) 24 | if dirsep == "\\" then -- Windows 25 | return os.execute( 'start "'..s..'" "'..lua_command..'" "'..s..'"' ) 26 | else -- assume some form of POSIX 27 | return os.execute( "'"..lua_command.."' '"..s.."' &" ) 28 | end 29 | end 30 | 31 | lua_spawn( "shmread.lua" ) 32 | sleep( 2 ) 33 | dofile( "shmwrite.lua" ) 34 | sleep( 2 ) 35 | 36 | 37 | -------------------------------------------------------------------------------- /tests/shmwrite.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/lua 2 | 3 | package.cpath = "../?.so;../?.dll;"..package.cpath 4 | local shm = require( "ipc.shm" ) 5 | local fl = require( "ipc.filelock" ) 6 | local socket_ok, socket = pcall( require, "socket" ) 7 | 8 | local lockfile = assert( io.open( "shm.lck", "w+" ) ) 9 | local function lock() 10 | assert( fl.lock( lockfile, "w" ) ) 11 | end 12 | local function unlock() 13 | assert( fl.unlock( lockfile ) ) 14 | end 15 | 16 | if socket_ok then 17 | function sleep( secs ) 18 | socket.select( nil, nil, secs ) 19 | end 20 | else 21 | function sleep( secs ) 22 | local start = os.clock() 23 | while os.clock() - start < secs do end 24 | end 25 | end 26 | 27 | -- attach to the shared memory segment 28 | local shm = assert( shm.attach( "luashm" ) ) 29 | shm:truncate( 200 ) -- we "guess" the correct size 30 | print( shm:addr(), shm:size() ) 31 | 32 | for i = 1, 3 do 33 | repeat 34 | sleep( 0.5 ) 35 | lock() 36 | shm:seek( "set", 0 ) 37 | local generation = assert( shm:read( 1 ) ) 38 | unlock() 39 | local gennum = tonumber( generation ) 40 | until gennum ~= nil and gennum < i 41 | lock() 42 | print( "Writing ..." ) 43 | shm:seek( "set", 0 ) 44 | shm:write( i ) 45 | shm:write( "hello world no. ", i, "\n" ) 46 | sleep( 3 ) 47 | unlock() 48 | end 49 | shm:close() 50 | lockfile:close() 51 | 52 | print( "Done!" ) 53 | 54 | -------------------------------------------------------------------------------- /tests/str.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/lua 2 | 3 | package.cpath = "../?.so;../?.dll;"..package.cpath 4 | local ms = require( "ipc.strfile" ) 5 | local s = [[ 6 | Hallo Welt, 7 | ich bin da! 8 | Hallo Welt, 9 | alles klar?! 10 | Ich bin hier 11 | wie bestellt, 12 | was sagst Du nun, 13 | hallo Welt?! 14 | ]] 15 | local f = assert( ms.open( s ) ) 16 | 17 | print( f:write( "xxx" ) ) 18 | print( f:addr(), f:size(), #s ) 19 | for l in f:lines() do 20 | print( ">"..l.."<" ) 21 | end 22 | print( f:seek( "end" ) ) 23 | print( f:seek( "set" ) ) 24 | local s2 = f:read( "*a" ) 25 | print( s == s2 ) 26 | 27 | --------------------------------------------------------------------------------