├── .gitignore ├── CHANGELOG ├── COPYRIGHT ├── ISSUES ├── Makefile ├── README ├── README.md ├── bin └── .gitkeep ├── src ├── lpsched.c ├── lpsched.h ├── luaproc.c └── luaproc.h └── tests └── hello.lua /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.o 3 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | *** CHANGELOG *** 2 | 3 | * Fixed send/receive to handle integers and floats properly in Lua 5.3. Bug 4 | reported by luafox. 5 | 6 | [v1.0-4] 7 | 8 | * Changed luaproc.newproc to be able to receive a Lua function as an argument. 9 | Suggested by Mason Bogue. 10 | 11 | * Fixed luaproc.newproc to handle binary strings properly. Calling 12 | luaproc.newproc(string.dump(f)) now works fine. Bug reported by Mason Bogue. 13 | 14 | * Minor Makefile improvements. 15 | 16 | * Documentation (README) format improvements. 17 | 18 | * Created an ISSUES file to describe known issues. 19 | 20 | * Fixed standard library load in Lua processes in Lua 5.3 to include the UTF8 21 | library. 22 | 23 | * Fixed luaproc library load function (luaopen_luaproc) to prevent problems when 24 | require'ing luaproc from within a Lua process. Although it is not necessary to 25 | require luaproc from within Lua processes, doing so would cause a deadlock. 26 | 27 | [v1.0-3] 28 | 29 | * Fixed standard library load in new Lua processes. In Lua 5.2 the bit32 30 | standard library must also be loaded. In Lua 5.2 and 5.3 the coroutine 31 | standard library must also be loaded. 32 | 33 | [v1.0-2] 34 | 35 | * Added macros for compatibility with Lua 5.2 and 5.3. 36 | 37 | * Removed global name registration in parent when loading luaproc. 38 | 39 | [v1.0-1] 40 | 41 | * Major code revision by Pablo Musa: 42 | - Internal data structures are now stored in Lua tables, removing the need 43 | for memory management in C; 44 | - Messages can contain strings, numbers, booleans and nil; 45 | - Improved error handling in Lua processes; 46 | - Functions luaproc.createworker and luaproc.destroyworker replaced by 47 | function luaproc.setnumworkers; 48 | - Added function luaproc.getnumworkers; 49 | - Code clean-up. 50 | 51 | * Added support to send and receive messages to/from main Lua script, which 52 | also resulted in a unified API for both the main (parent) Lua script and the 53 | child Lua processes (created with luaproc.newproc). 54 | 55 | * Removed function luaproc.exit. 56 | 57 | * Added function luaproc.wait, which waits until all Lua processes have 58 | finished executing and can be used for synchronization. Unlike luaproc.exit, 59 | which had to be the last function to be called, luaproc.wait can be called at 60 | any point in a script. It is always implicitly called when the main Lua 61 | script finishes executing. 62 | 63 | * Fixed a race condition that was trigged by the main Lua script exiting while 64 | luaproc workers were active. When Lua unloads dynamic libraries with 65 | dlclose() while it is exiting, pthreads is unloaded while workers are still 66 | active, causing a segmentation fault. Workers (pthreads) are now joined, via 67 | a finalizer, when luaproc is unloaded. 68 | 69 | * Fixed a race condition when creating new Lua processes with recycling. 70 | 71 | * Fixed include guard conflict in sched.h (now lpsched.h). 72 | 73 | * Included release in LuaRocks. 74 | 75 | [v1.0b1] 76 | 77 | * First public beta release. 78 | 79 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | luaproc License 2 | --------------- 3 | 4 | luaproc is licensed under the terms of the MIT license reproduced below. 5 | This means that luaproc is free software and can be used for both academic 6 | and commercial purposes at absolutely no cost. 7 | 8 | =============================================================================== 9 | 10 | Copyright (C) 2008-2015 Alexandre Skyrme, Noemi Rodriguez, Roberto Ierusalimschy 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining a copy 13 | of this software and associated documentation files (the "Software"), to deal 14 | in the Software without restriction, including without limitation the rights 15 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 | copies of the Software, and to permit persons to whom the Software is 17 | furnished to do so, subject to the following conditions: 18 | 19 | The above copyright notice and this permission notice shall be included in 20 | all copies or substantial portions of the Software. 21 | 22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 28 | THE SOFTWARE. 29 | 30 | =============================================================================== 31 | 32 | (end of COPYRIGHT) 33 | -------------------------------------------------------------------------------- /ISSUES: -------------------------------------------------------------------------------- 1 | *** ISSUES *** 2 | 3 | Using luaproc together with luasocket can result in segmentation faults. 4 | Apparently that is caused neither by bugs in luaproc, nor in luasocket, but 5 | rather by the concurrent execution of some glibc functions that are called by 6 | Lua when these libraries are used. Since the Lua interpreter binary is not 7 | linked to pthreads, memory management is not handled properly by glibc and leads 8 | to corruption. 9 | 10 | There are at least two possible workarounds: 11 | 12 | 1 - Pre-load (LD_PRELOAD) pthreads when executing the Lua interpreter to run the 13 | script that uses luaproc and luasocket. 14 | 15 | 2 - Compile a Lua interpreter binary that is linked to pthreads. 16 | 17 | In particular, we observed this issue is likely to happen when the getaddrinfo 18 | glibc function is called from luasocket functions being executed within Lua 19 | processes. Hence, another possible workaround would be to not resolve hostnames 20 | within Lua processes but rather (a) use IP addresses or (b) have the main Lua 21 | script resolve hostnames and send the corresponding IP addresses to Lua 22 | processes via message passing. 23 | 24 | For an old report that was helpful to understand this issue, check: 25 | https://sourceware.org/bugzilla/show_bug.cgi?id=10652. 26 | 27 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for building luaproc 2 | 3 | # lua version 4 | LUA_VERSION=5.1 5 | # path to lua header files 6 | LUA_INCDIR=/usr/include/lua${LUA_VERSION} 7 | # path to lua library 8 | LUA_LIBDIR=/usr/lib/x86_64-linux-gnu/ 9 | # path to install library 10 | LUA_CPATH=/usr/lib/lua/${LUA_VERSION} 11 | 12 | # standard makefile variables 13 | CC=gcc 14 | SRCDIR=src 15 | BINDIR=bin 16 | CFLAGS=-c -O2 -Wall -fPIC -I${LUA_INCDIR} 17 | # MacOS X users should replace LIBFLAG with the following definition 18 | # LIBFLAG=-bundle -undefined dynamic_lookup 19 | LIBFLAG=-shared 20 | # 21 | LDFLAGS=${LIBFLAG} -L${LUA_LIBDIR} -lpthread 22 | SOURCES=${SRCDIR}/lpsched.c ${SRCDIR}/luaproc.c 23 | OBJECTS=${SOURCES:.c=.o} 24 | 25 | # luaproc specific variables 26 | LIBNAME=luaproc 27 | LIB=${LIBNAME}.so 28 | 29 | # build targets 30 | all: ${BINDIR}/${LIB} 31 | 32 | ${BINDIR}/${LIB}: ${OBJECTS} 33 | ${CC} $^ -o $@ ${LDFLAGS} 34 | 35 | lpsched.o: lpsched.c lpsched.h luaproc.h 36 | ${CC} ${CFLAGS} $^ 37 | 38 | luaproc.o: luaproc.c luaproc.h lpsched.h 39 | ${CC} ${CFLAGS} $^ 40 | 41 | install: 42 | cp -v ${BINDIR}/${LIB} ${LUA_CPATH} 43 | 44 | clean: 45 | rm -f ${OBJECTS} ${BINDIR}/${LIB} 46 | 47 | # list targets that do not create files (but not all makes understand .PHONY) 48 | .PHONY: clean install 49 | 50 | # (end of Makefile) 51 | 52 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | # luaproc - A concurrent programming library for Lua 2 | 3 | ## Introduction 4 | 5 | luaproc is a Lua (http://www.lua.org) extension library for concurrent 6 | programming. This text provides some background information and also serves as s 7 | reference manual for the library. The library is available under the same terms 8 | and conditions (http://www.lua.org/copyright.html) as the Lua language, the MIT 9 | license. The idea is that if you can use Lua in a project, you should also be 10 | able to use luaproc. 11 | 12 | Lua natively supports cooperative multithreading by means of coroutines. 13 | However, coroutines in Lua cannot be executed in parallel. luaproc overcomes 14 | that restriction by building on the proposal and sample implementation presented 15 | in Programming in Lua (http://www.inf.puc-rio.br/~roberto/pil2) (chapter 30). 16 | It uses coroutines and multiple independent states in Lua to implement Lua 17 | processes, which are user threads comprised of Lua code that have no shared 18 | data. Lua processes are executed by workers, which are system threads 19 | implemented with POSIX threads (pthreads), and thus can run in parallel. 20 | 21 | Communication between Lua processes relies exclusively on message passing. Each 22 | message can carry a tuple of atomic Lua values (strings, numbers, booleans and 23 | nil). More complex types must be encoded somehow -- for instance by using 24 | strings of Lua code that when executed return such a type. Message addressing is 25 | based on communication channels, which are decoupled from Lua processes and must 26 | be explicitly created. 27 | 28 | Sending a message is always a synchronous operation, i.e., the send operation 29 | only returns after a message has been received by another Lua process or if an 30 | error occurs (such as trying to send a message to a non-existent channel). 31 | Receiving a message, on the other hand, can be a synchronous or asynchronous 32 | operation. In synchronous mode, a call to the receive operation only returns 33 | after a message has been received or if an error occurs. In asynchronous mode, a 34 | call to the receive operation returns immediately and indicates if a message was 35 | received or not. 36 | 37 | If a Lua process tries to send a message to a channel where there are no Lua 38 | processes waiting to receive a message, its execution is suspended until a 39 | matching receive occurs or the channel is destroyed. The same happens if a Lua 40 | process tries to synchronously receive a message from a channel where there are 41 | no Lua processes waiting to send a message. 42 | 43 | luaproc also offers an optional facility to recycle Lua processes. Recycling 44 | consists of reusing states from finished Lua processes, instead of destroying 45 | them. When recycling is enabled, a new Lua process can be created by loading its 46 | code in a previously used state from a finished Lua process, instead of creating 47 | a new state. 48 | 49 | ## Compatibility 50 | 51 | luaproc is compatible with Lua 5.1, 5.2 and 5.3. 52 | 53 | ## API 54 | 55 | `luaproc.newproc( string lua_code )` 56 | `luaproc.newproc( function f )` 57 | 58 | Creates a new Lua process to run the specified string of Lua code or the 59 | specified Lua function. Returns true if successful or nil and an error message 60 | if failed. The only libraries loaded in new Lua processes are luaproc itself and 61 | the standard Lua base and package libraries. The remaining standard Lua 62 | libraries (io, os, table, string, math, debug, coroutine and utf8) are 63 | pre-registered and can be loaded with a call to the standard Lua function 64 | `require`. 65 | 66 | `luaproc.setnumworkers( int number_of_workers )` 67 | 68 | Sets the number of active workers (pthreads) to n (default = 1, minimum = 1). 69 | Creates and destroys workers as needed, depending on the current number of 70 | active workers. No return, raises error if worker could not be created. 71 | 72 | `luaproc.getnumworkers( )` 73 | 74 | Returns the number of active workers (pthreads). 75 | 76 | `luaproc.wait( )` 77 | 78 | Waits until all Lua processes have finished, then continues program execution. 79 | It only makes sense to call this function from the main Lua script. Moreover, 80 | this function is implicitly called when the main Lua script finishes executing. 81 | No return. 82 | 83 | `luaproc.recycle( int maxrecycle )` 84 | 85 | Sets the maximum number of Lua processes to recycle. Returns true if successful 86 | or nil and an error message if failed. The default number is zero, i.e., no Lua 87 | processes are recycled. 88 | 89 | `luaproc.send( string channel_name, msg1, [msg2], [msg3], [...] )` 90 | 91 | Sends a message (tuple of boolean, nil, number or string values) to a channel. 92 | Returns true if successful or nil and an error message if failed. Suspends 93 | execution of the calling Lua process if there is no matching receive. 94 | 95 | `luaproc.receive( string channel_name, [boolean asynchronous] )` 96 | 97 | Receives a message (tuple of boolean, nil, number or string values) from a 98 | channel. Returns received values if successful or nil and an error message if 99 | failed. Suspends execution of the calling Lua process if there is no matching 100 | receive and the async (boolean) flag is not set. The async flag, by default, is 101 | not set. 102 | 103 | `luaproc.newchannel( string channel_name )` 104 | 105 | Creates a new channel identified by string name. Returns true if successful or 106 | nil and an error message if failed. 107 | 108 | `luaproc.delchannel( string channel_name )` 109 | 110 | Destroys a channel identified by string name. Returns true if successful or nil 111 | and an error message if failed. Lua processes waiting to send or receive 112 | messages on destroyed channels have their execution resumed and receive an error 113 | message indicating the channel was destroyed. 114 | 115 | ## References 116 | 117 | A paper about luaproc -- Exploring Lua for Concurrent Programming -- was 118 | published in the Journal of Universal Computer Science and is available 119 | here (http://www.jucs.org/jucs_14_21/exploring_lua_for_concurrent) and 120 | here (http://www.inf.puc-rio.br/~roberto/docs/ry08-05.pdf). Some information in 121 | the paper is already outdated, but it still provides a good overview of the 122 | library and some of its design choices. 123 | 124 | A tech report about concurrency in Lua, which uses luaproc as part of a case 125 | study, is also available 126 | here (ftp://ftp.inf.puc-rio.br/pub/docs/techreports/11_13_skyrme.pdf). 127 | 128 | Finally, a paper about an experiment to port luaproc to use Transactional Memory 129 | instead of the standard POSIX Threads synchronization constructs, published as a 130 | part of the 8th ACM SIGPLAN Workshop on Transactional Computing, can be found 131 | here (http://transact2013.cse.lehigh.edu/skyrme.pdf). 132 | 133 | ## Download 134 | 135 | GitHub source repository: https://github.com/askyrme/luaproc. 136 | 137 | ## License 138 | 139 | Copyright © 2008-2015 Alexandre Skyrme, Noemi Rodriguez, Roberto Ierusalimschy. 140 | All rights reserved. 141 | 142 | Permission is hereby granted, free of charge, to any person obtaining a copy of 143 | this software and associated documentation files (the "Software"), to deal in 144 | the Software without restriction, including without limitation the rights to 145 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 146 | the Software, and to permit persons to whom the Software is furnished to do so, 147 | subject to the following conditions: 148 | 149 | The above copyright notice and this permission notice shall be included in all 150 | copies or substantial portions of the Software. 151 | 152 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 153 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 154 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 155 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 156 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 157 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 158 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # luaproc - A concurrent programming library for Lua 2 | 3 | ## Introduction 4 | 5 | *luaproc* is a [Lua](http://www.lua.org) extension library for concurrent 6 | programming. This text provides some background information and also serves as s 7 | reference manual for the library. The library is available under the same [terms 8 | and conditions](http://www.lua.org/copyright.html) as the Lua language, the MIT 9 | license. The idea is that if you can use Lua in a project, you should also be 10 | able to use *luaproc*. 11 | 12 | Lua natively supports cooperative multithreading by means of coroutines. 13 | However, coroutines in Lua cannot be executed in parallel. *luaproc* overcomes 14 | that restriction by building on the proposal and sample implementation presented 15 | in [Programming in Lua](http://www.inf.puc-rio.br/~roberto/pil2) (chapter 30). 16 | It uses coroutines and multiple independent states in Lua to implement *Lua 17 | processes*, which are user threads comprised of Lua code that have no shared 18 | data. Lua processes are executed by *workers*, which are system threads 19 | implemented with POSIX threads (pthreads), and thus can run in parallel. 20 | 21 | Communication between Lua processes relies exclusively on message passing. Each 22 | message can carry a tuple of atomic Lua values (strings, numbers, booleans and 23 | nil). More complex types must be encoded somehow -- for instance by using 24 | strings of Lua code that when executed return such a type. Message addressing is 25 | based on communication channels, which are decoupled from Lua processes and must 26 | be explicitly created. 27 | 28 | Sending a message is always a synchronous operation, i.e., the send operation 29 | only returns after a message has been received by another Lua process or if an 30 | error occurs (such as trying to send a message to a non-existent channel). 31 | Receiving a message, on the other hand, can be a synchronous or asynchronous 32 | operation. In synchronous mode, a call to the receive operation only returns 33 | after a message has been received or if an error occurs. In asynchronous mode, a 34 | call to the receive operation returns immediately and indicates if a message was 35 | received or not. 36 | 37 | If a Lua process tries to send a message to a channel where there are no Lua 38 | processes waiting to receive a message, its execution is suspended until a 39 | matching receive occurs or the channel is destroyed. The same happens if a Lua 40 | process tries to synchronously receive a message from a channel where there are 41 | no Lua processes waiting to send a message. 42 | 43 | *luaproc* also offers an optional facility to recycle Lua processes. Recycling 44 | consists of reusing states from finished Lua processes, instead of destroying 45 | them. When recycling is enabled, a new Lua process can be created by loading its 46 | code in a previously used state from a finished Lua process, instead of creating 47 | a new state. 48 | 49 | ## Compatibility 50 | 51 | *luaproc* is compatible with Lua 5.1, 5.2 and 5.3. 52 | 53 | ## API 54 | 55 | **`luaproc.newproc( string lua_code )`** 56 | 57 | **`luaproc.newproc( function f )`** 58 | 59 | Creates a new Lua process to run the specified string of Lua code or the 60 | specified Lua function. Returns true if successful or nil and an error message 61 | if failed. The only libraries loaded in new Lua processes are luaproc itself and 62 | the standard Lua base and package libraries. The remaining standard Lua 63 | libraries (io, os, table, string, math, debug, coroutine and utf8) are 64 | pre-registered and can be loaded with a call to the standard Lua function 65 | `require`. 66 | 67 | **`luaproc.setnumworkers( int number_of_workers )`** 68 | 69 | Sets the number of active workers (pthreads) to n (default = 1, minimum = 1). 70 | Creates and destroys workers as needed, depending on the current number of 71 | active workers. No return, raises error if worker could not be created. 72 | 73 | **`luaproc.getnumworkers( )`** 74 | 75 | Returns the number of active workers (pthreads). 76 | 77 | **`luaproc.wait( )`** 78 | 79 | Waits until all Lua processes have finished, then continues program execution. 80 | It only makes sense to call this function from the main Lua script. Moreover, 81 | this function is implicitly called when the main Lua script finishes executing. 82 | No return. 83 | 84 | **`luaproc.recycle( int maxrecycle )`** 85 | 86 | Sets the maximum number of Lua processes to recycle. Returns true if successful 87 | or nil and an error message if failed. The default number is zero, i.e., no Lua 88 | processes are recycled. 89 | 90 | **`luaproc.send( string channel_name, msg1, [msg2], [msg3], [...] )`** 91 | 92 | Sends a message (tuple of boolean, nil, number or string values) to a channel. 93 | Returns true if successful or nil and an error message if failed. Suspends 94 | execution of the calling Lua process if there is no matching receive. 95 | 96 | **`luaproc.receive( string channel_name, [boolean asynchronous] )`** 97 | 98 | Receives a message (tuple of boolean, nil, number or string values) from a 99 | channel. Returns received values if successful or nil and an error message if 100 | failed. Suspends execution of the calling Lua process if there is no matching 101 | receive and the async (boolean) flag is not set. The async flag, by default, is 102 | not set. 103 | 104 | **`luaproc.newchannel( string channel_name )`** 105 | 106 | Creates a new channel identified by string name. Returns true if successful or 107 | nil and an error message if failed. 108 | 109 | **`luaproc.delchannel( string channel_name )`** 110 | 111 | Destroys a channel identified by string name. Returns true if successful or nil 112 | and an error message if failed. Lua processes waiting to send or receive 113 | messages on destroyed channels have their execution resumed and receive an error 114 | message indicating the channel was destroyed. 115 | 116 | ## References 117 | 118 | A paper about luaproc -- *Exploring Lua for Concurrent Programming* -- was 119 | published in the Journal of Universal Computer Science and is available 120 | [here](http://www.jucs.org/jucs_14_21/exploring_lua_for_concurrent) and 121 | [here](http://www.inf.puc-rio.br/~roberto/docs/ry08-05.pdf). Some information in 122 | the paper is already outdated, but it still provides a good overview of the 123 | library and some of its design choices. 124 | 125 | A tech report about concurrency in Lua, which uses luaproc as part of a case 126 | study, is also available 127 | [here](ftp://ftp.inf.puc-rio.br/pub/docs/techreports/11_13_skyrme.pdf). 128 | 129 | Finally, a paper about an experiment to port luaproc to use Transactional Memory 130 | instead of the standard POSIX Threads synchronization constructs, published as a 131 | part of the 8th ACM SIGPLAN Workshop on Transactional Computing, can be found 132 | [here](http://transact2013.cse.lehigh.edu/skyrme.pdf). 133 | 134 | ## Download 135 | 136 | GitHub source repository: 137 | [https://github.com/askyrme/luaproc](https://github.com/askyrme/luaproc) 138 | 139 | ## License 140 | 141 | Copyright © 2008-2015 Alexandre Skyrme, Noemi Rodriguez, Roberto Ierusalimschy. 142 | All rights reserved. 143 | 144 | Permission is hereby granted, free of charge, to any person obtaining a copy of 145 | this software and associated documentation files (the "Software"), to deal in 146 | the Software without restriction, including without limitation the rights to 147 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 148 | the Software, and to permit persons to whom the Software is furnished to do so, 149 | subject to the following conditions: 150 | 151 | The above copyright notice and this permission notice shall be included in all 152 | copies or substantial portions of the Software. 153 | 154 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 155 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 156 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 157 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 158 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 159 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 160 | -------------------------------------------------------------------------------- /bin/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/askyrme/luaproc/990ecf6ae7cd700afeacb98e18b9c37e1f078935/bin/.gitkeep -------------------------------------------------------------------------------- /src/lpsched.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** scheduler module for executing lua processes 3 | ** See Copyright Notice in luaproc.h 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "lpsched.h" 14 | #include "luaproc.h" 15 | 16 | #define FALSE 0 17 | #define TRUE !FALSE 18 | #define LUAPROC_SCHED_WORKERS_TABLE "workertb" 19 | 20 | #if (LUA_VERSION_NUM >= 502) 21 | #define luaproc_resume( L, from, nargs ) lua_resume( L, from, nargs ) 22 | #else 23 | #define luaproc_resume( L, from, nargs ) lua_resume( L, nargs ) 24 | #endif 25 | 26 | /******************** 27 | * global variables * 28 | *******************/ 29 | 30 | /* ready process list */ 31 | list ready_lp_list; 32 | 33 | /* ready process queue access mutex */ 34 | pthread_mutex_t mutex_sched = PTHREAD_MUTEX_INITIALIZER; 35 | 36 | /* active luaproc count access mutex */ 37 | pthread_mutex_t mutex_lp_count = PTHREAD_MUTEX_INITIALIZER; 38 | 39 | /* wake worker up conditional variable */ 40 | pthread_cond_t cond_wakeup_worker = PTHREAD_COND_INITIALIZER; 41 | 42 | /* no active luaproc conditional variable */ 43 | pthread_cond_t cond_no_active_lp = PTHREAD_COND_INITIALIZER; 44 | 45 | /* lua_State used to store workers hash table */ 46 | static lua_State *workerls = NULL; 47 | 48 | int lpcount = 0; /* number of active luaprocs */ 49 | int workerscount = 0; /* number of active workers */ 50 | int destroyworkers = 0; /* number of workers to destroy */ 51 | 52 | /*********************** 53 | * register prototypes * 54 | ***********************/ 55 | 56 | static void sched_dec_lpcount( void ); 57 | 58 | /******************************* 59 | * worker thread main function * 60 | *******************************/ 61 | 62 | /* worker thread main function */ 63 | void *workermain( void *args ) { 64 | 65 | luaproc *lp; 66 | int procstat; 67 | 68 | /* main worker loop */ 69 | while ( TRUE ) { 70 | /* 71 | wait until instructed to wake up (because there's work to do 72 | or because workers must be destroyed) 73 | */ 74 | pthread_mutex_lock( &mutex_sched ); 75 | while (( list_count( &ready_lp_list ) == 0 ) && ( destroyworkers <= 0 )) { 76 | pthread_cond_wait( &cond_wakeup_worker, &mutex_sched ); 77 | } 78 | 79 | if ( destroyworkers > 0 ) { /* check whether workers should be destroyed */ 80 | 81 | destroyworkers--; /* decrease workers to be destroyed count */ 82 | workerscount--; /* decrease active workers count */ 83 | 84 | /* remove worker from workers table */ 85 | lua_getglobal( workerls, LUAPROC_SCHED_WORKERS_TABLE ); 86 | lua_pushlightuserdata( workerls, (void *)pthread_self( )); 87 | lua_pushnil( workerls ); 88 | lua_rawset( workerls, -3 ); 89 | lua_pop( workerls, 1 ); 90 | 91 | pthread_cond_signal( &cond_wakeup_worker ); /* wake other workers up */ 92 | pthread_mutex_unlock( &mutex_sched ); 93 | pthread_exit( NULL ); /* destroy itself */ 94 | } 95 | 96 | /* remove lua process from the ready queue */ 97 | lp = list_remove( &ready_lp_list ); 98 | pthread_mutex_unlock( &mutex_sched ); 99 | 100 | /* execute the lua code specified in the lua process struct */ 101 | procstat = luaproc_resume( luaproc_get_state( lp ), NULL, 102 | luaproc_get_numargs( lp )); 103 | /* reset the process argument count */ 104 | luaproc_set_numargs( lp, 0 ); 105 | 106 | /* has the lua process sucessfully finished its execution? */ 107 | if ( procstat == 0 ) { 108 | luaproc_set_status( lp, LUAPROC_STATUS_FINISHED ); 109 | luaproc_recycle_insert( lp ); /* try to recycle finished lua process */ 110 | sched_dec_lpcount(); /* decrease active lua process count */ 111 | } 112 | 113 | /* has the lua process yielded? */ 114 | else if ( procstat == LUA_YIELD ) { 115 | 116 | /* yield attempting to send a message */ 117 | if ( luaproc_get_status( lp ) == LUAPROC_STATUS_BLOCKED_SEND ) { 118 | luaproc_queue_sender( lp ); /* queue lua process on channel */ 119 | /* unlock channel */ 120 | luaproc_unlock_channel( luaproc_get_channel( lp )); 121 | } 122 | 123 | /* yield attempting to receive a message */ 124 | else if ( luaproc_get_status( lp ) == LUAPROC_STATUS_BLOCKED_RECV ) { 125 | luaproc_queue_receiver( lp ); /* queue lua process on channel */ 126 | /* unlock channel */ 127 | luaproc_unlock_channel( luaproc_get_channel( lp )); 128 | } 129 | 130 | /* yield on explicit coroutine.yield call */ 131 | else { 132 | /* re-insert the job at the end of the ready process queue */ 133 | pthread_mutex_lock( &mutex_sched ); 134 | list_insert( &ready_lp_list, lp ); 135 | pthread_mutex_unlock( &mutex_sched ); 136 | } 137 | } 138 | 139 | /* or was there an error executing the lua process? */ 140 | else { 141 | /* print error message */ 142 | fprintf( stderr, "close lua_State (error: %s)\n", 143 | luaL_checkstring( luaproc_get_state( lp ), -1 )); 144 | lua_close( luaproc_get_state( lp )); /* close lua state */ 145 | sched_dec_lpcount(); /* decrease active lua process count */ 146 | } 147 | } 148 | } 149 | 150 | /*********************** 151 | * auxiliary functions * 152 | **********************/ 153 | 154 | /* decrease active lua process count */ 155 | static void sched_dec_lpcount( void ) { 156 | pthread_mutex_lock( &mutex_lp_count ); 157 | lpcount--; 158 | /* if count reaches zero, signal there are no more active processes */ 159 | if ( lpcount == 0 ) { 160 | pthread_cond_signal( &cond_no_active_lp ); 161 | } 162 | pthread_mutex_unlock( &mutex_lp_count ); 163 | } 164 | 165 | /********************** 166 | * exported functions * 167 | **********************/ 168 | 169 | /* increase active lua process count */ 170 | void sched_inc_lpcount( void ) { 171 | pthread_mutex_lock( &mutex_lp_count ); 172 | lpcount++; 173 | pthread_mutex_unlock( &mutex_lp_count ); 174 | } 175 | 176 | /* local scheduler initialization */ 177 | int sched_init( void ) { 178 | 179 | int i; 180 | pthread_t worker; 181 | 182 | /* initialize ready process list */ 183 | list_init( &ready_lp_list ); 184 | 185 | /* initialize workers table and lua_State used to store it */ 186 | workerls = luaL_newstate(); 187 | lua_newtable( workerls ); 188 | lua_setglobal( workerls, LUAPROC_SCHED_WORKERS_TABLE ); 189 | 190 | /* get ready to access worker threads table */ 191 | lua_getglobal( workerls, LUAPROC_SCHED_WORKERS_TABLE ); 192 | 193 | /* create default number of initial worker threads */ 194 | for ( i = 0; i < LUAPROC_SCHED_DEFAULT_WORKER_THREADS; i++ ) { 195 | 196 | if ( pthread_create( &worker, NULL, workermain, NULL ) != 0 ) { 197 | lua_pop( workerls, 1 ); /* pop workers table from stack */ 198 | return LUAPROC_SCHED_PTHREAD_ERROR; 199 | } 200 | 201 | /* store worker thread id in a table */ 202 | lua_pushlightuserdata( workerls, (void *)worker ); 203 | lua_pushboolean( workerls, TRUE ); 204 | lua_rawset( workerls, -3 ); 205 | 206 | workerscount++; /* increase active workers count */ 207 | } 208 | 209 | lua_pop( workerls, 1 ); /* pop workers table from stack */ 210 | 211 | return LUAPROC_SCHED_OK; 212 | } 213 | 214 | /* set number of active workers */ 215 | int sched_set_numworkers( int numworkers ) { 216 | 217 | int i, delta; 218 | pthread_t worker; 219 | 220 | pthread_mutex_lock( &mutex_sched ); 221 | 222 | /* calculate delta between existing workers and set number of workers */ 223 | delta = numworkers - workerscount; 224 | 225 | /* create additional workers */ 226 | if ( numworkers > workerscount ) { 227 | 228 | /* get ready to access worker threads table */ 229 | lua_getglobal( workerls, LUAPROC_SCHED_WORKERS_TABLE ); 230 | 231 | /* create additional workers */ 232 | for ( i = 0; i < delta; i++ ) { 233 | 234 | if ( pthread_create( &worker, NULL, workermain, NULL ) != 0 ) { 235 | pthread_mutex_unlock( &mutex_sched ); 236 | lua_pop( workerls, 1 ); /* pop workers table from stack */ 237 | return LUAPROC_SCHED_PTHREAD_ERROR; 238 | } 239 | 240 | /* store worker thread id in a table */ 241 | lua_pushlightuserdata( workerls, (void *)worker ); 242 | lua_pushboolean( workerls, TRUE ); 243 | lua_rawset( workerls, -3 ); 244 | 245 | workerscount++; /* increase active workers count */ 246 | } 247 | 248 | lua_pop( workerls, 1 ); /* pop workers table from stack */ 249 | } 250 | /* destroy existing workers */ 251 | else if ( numworkers < workerscount ) { 252 | destroyworkers = destroyworkers + numworkers; 253 | } 254 | 255 | pthread_mutex_unlock( &mutex_sched ); 256 | 257 | return LUAPROC_SCHED_OK; 258 | } 259 | 260 | /* return the number of active workers */ 261 | int sched_get_numworkers( void ) { 262 | 263 | int numworkers; 264 | 265 | pthread_mutex_lock( &mutex_sched ); 266 | numworkers = workerscount; 267 | pthread_mutex_unlock( &mutex_sched ); 268 | 269 | return numworkers; 270 | } 271 | 272 | /* insert lua process in ready queue */ 273 | void sched_queue_proc( luaproc *lp ) { 274 | pthread_mutex_lock( &mutex_sched ); 275 | list_insert( &ready_lp_list, lp ); /* add process to ready queue */ 276 | /* set process status ready */ 277 | luaproc_set_status( lp, LUAPROC_STATUS_READY ); 278 | pthread_cond_signal( &cond_wakeup_worker ); /* wake worker up */ 279 | pthread_mutex_unlock( &mutex_sched ); 280 | } 281 | 282 | /* join worker threads (called when Lua exits). not joining workers causes a 283 | race condition since lua_close unregisters dynamic libs with dlclose and 284 | thus libpthreads can be unloaded while there are workers that are still 285 | alive. */ 286 | void sched_join_workers( void ) { 287 | 288 | lua_State *L = luaL_newstate(); 289 | const char *wtb = "workerstbcopy"; 290 | 291 | /* wait for all running lua processes to finish */ 292 | sched_wait(); 293 | 294 | /* initialize new state and create table to copy worker ids */ 295 | lua_newtable( L ); 296 | lua_setglobal( L, wtb ); 297 | lua_getglobal( L, wtb ); 298 | 299 | pthread_mutex_lock( &mutex_sched ); 300 | 301 | /* determine remaining active worker threads and copy their ids */ 302 | lua_getglobal( workerls, LUAPROC_SCHED_WORKERS_TABLE ); 303 | lua_pushnil( workerls ); 304 | while ( lua_next( workerls, -2 ) != 0 ) { 305 | lua_pushlightuserdata( L, lua_touserdata( workerls, -2 )); 306 | lua_pushboolean( L, TRUE ); 307 | lua_rawset( L, -3 ); 308 | /* pop value, leave key for next iteration */ 309 | lua_pop( workerls, 1 ); 310 | } 311 | 312 | /* pop workers copy table name from stack */ 313 | lua_pop( L, 1 ); 314 | 315 | /* set all workers to be destroyed */ 316 | destroyworkers = workerscount; 317 | 318 | /* wake workers up */ 319 | pthread_cond_signal( &cond_wakeup_worker ); 320 | pthread_mutex_unlock( &mutex_sched ); 321 | 322 | /* join with worker threads (read ids from local table copy ) */ 323 | lua_getglobal( L, wtb ); 324 | lua_pushnil( L ); 325 | while ( lua_next( L, -2 ) != 0 ) { 326 | pthread_join(( pthread_t )lua_touserdata( L, -2 ), NULL ); 327 | /* pop value, leave key for next iteration */ 328 | lua_pop( L, 1 ); 329 | } 330 | lua_pop( L, 1 ); 331 | 332 | lua_close( workerls ); 333 | lua_close( L ); 334 | } 335 | 336 | /* wait until there are no more active lua processes and active workers. */ 337 | void sched_wait( void ) { 338 | 339 | /* wait until there are not more active lua processes */ 340 | pthread_mutex_lock( &mutex_lp_count ); 341 | if( lpcount != 0 ) { 342 | pthread_cond_wait( &cond_no_active_lp, &mutex_lp_count ); 343 | } 344 | pthread_mutex_unlock( &mutex_lp_count ); 345 | 346 | } 347 | -------------------------------------------------------------------------------- /src/lpsched.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** scheduler module for executing lua processes 3 | ** See Copyright Notice in luaproc.h 4 | */ 5 | 6 | #ifndef _LUA_LUAPROC_SCHED_H_ 7 | #define _LUA_LUAPROC_SCHED_H_ 8 | 9 | #include "luaproc.h" 10 | 11 | /**************************************** 12 | * scheduler functions return constants * 13 | ***************************************/ 14 | 15 | /* scheduler function return constants */ 16 | #define LUAPROC_SCHED_OK 0 17 | #define LUAPROC_SCHED_PTHREAD_ERROR -1 18 | 19 | /************************************* 20 | * default number of initial workers * 21 | ************************************/ 22 | 23 | /* scheduler default number of worker threads */ 24 | #define LUAPROC_SCHED_DEFAULT_WORKER_THREADS 1 25 | 26 | /*********************** 27 | * function prototypes * 28 | **********************/ 29 | 30 | /* initialize scheduler */ 31 | int sched_init( void ); 32 | /* join workers */ 33 | void sched_join_workers( void ); 34 | /* wait until there are no more active lua processes */ 35 | void sched_wait( void ); 36 | /* move process to ready queue (ie, schedule process) */ 37 | void sched_queue_proc( luaproc *lp ); 38 | /* increase active luaproc count */ 39 | void sched_inc_lpcount( void ); 40 | /* set number of active workers (creates and destroys accordingly) */ 41 | int sched_set_numworkers( int numworkers ); 42 | /* return the number of active workers */ 43 | int sched_get_numworkers( void ); 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /src/luaproc.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** luaproc API 3 | ** See Copyright Notice in luaproc.h 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "luaproc.h" 13 | #include "lpsched.h" 14 | 15 | #define FALSE 0 16 | #define TRUE !FALSE 17 | #define LUAPROC_CHANNELS_TABLE "channeltb" 18 | #define LUAPROC_RECYCLE_MAX 0 19 | 20 | #if (LUA_VERSION_NUM == 501) 21 | 22 | #define lua_pushglobaltable( L ) lua_pushvalue( L, LUA_GLOBALSINDEX ) 23 | #define luaL_newlib( L, funcs ) { lua_newtable( L ); \ 24 | luaL_register( L, NULL, funcs ); } 25 | #define isequal( L, a, b ) lua_equal( L, a, b ) 26 | #define requiref( L, modname, f, glob ) {\ 27 | lua_pushcfunction( L, f ); /* push module load function */ \ 28 | lua_pushstring( L, modname ); /* argument to module load function */ \ 29 | lua_call( L, 1, 1 ); /* call 'f' to load module */ \ 30 | /* register module in package.loaded table in case 'f' doesn't do so */ \ 31 | lua_getfield( L, LUA_GLOBALSINDEX, LUA_LOADLIBNAME );\ 32 | if ( lua_type( L, -1 ) == LUA_TTABLE ) {\ 33 | lua_getfield( L, -1, "loaded" );\ 34 | if ( lua_type( L, -1 ) == LUA_TTABLE ) {\ 35 | lua_getfield( L, -1, modname );\ 36 | if ( lua_type( L, -1 ) == LUA_TNIL ) {\ 37 | lua_pushvalue( L, 1 );\ 38 | lua_setfield( L, -3, modname );\ 39 | }\ 40 | lua_pop( L, 1 );\ 41 | }\ 42 | lua_pop( L, 1 );\ 43 | }\ 44 | lua_pop( L, 1 );\ 45 | if ( glob ) { /* set global name? */ \ 46 | lua_setglobal( L, modname );\ 47 | } else {\ 48 | lua_pop( L, 1 );\ 49 | }\ 50 | } 51 | 52 | #else 53 | 54 | #define isequal( L, a, b ) lua_compare( L, a, b, LUA_OPEQ ) 55 | #define requiref( L, modname, f, glob ) \ 56 | { luaL_requiref( L, modname, f, glob ); lua_pop( L, 1 ); } 57 | 58 | #endif 59 | 60 | #if (LUA_VERSION_NUM >= 503) 61 | #define dump( L, writer, data, strip ) lua_dump( L, writer, data, strip ) 62 | #define copynumber( Lto, Lfrom, i ) {\ 63 | if ( lua_isinteger( Lfrom, i )) {\ 64 | lua_pushinteger( Lto, lua_tonumber( Lfrom, i ));\ 65 | } else {\ 66 | lua_pushnumber( Lto, lua_tonumber( Lfrom, i ));\ 67 | }\ 68 | } 69 | #else 70 | #define dump( L, writer, data, strip ) lua_dump( L, writer, data ) 71 | #define copynumber( Lto, Lfrom, i ) \ 72 | lua_pushnumber( Lto, lua_tonumber( Lfrom, i )) 73 | #endif 74 | 75 | /******************** 76 | * global variables * 77 | *******************/ 78 | 79 | /* channel list mutex */ 80 | static pthread_mutex_t mutex_channel_list = PTHREAD_MUTEX_INITIALIZER; 81 | 82 | /* recycle list mutex */ 83 | static pthread_mutex_t mutex_recycle_list = PTHREAD_MUTEX_INITIALIZER; 84 | 85 | /* recycled lua process list */ 86 | static list recycle_list; 87 | 88 | /* maximum lua processes to recycle */ 89 | static int recyclemax = LUAPROC_RECYCLE_MAX; 90 | 91 | /* lua_State used to store channel hash table */ 92 | static lua_State *chanls = NULL; 93 | 94 | /* lua process used to wrap main state. allows main state to be queued in 95 | channels when sending and receiving messages */ 96 | static luaproc mainlp; 97 | 98 | /* main state matched a send/recv operation conditional variable */ 99 | pthread_cond_t cond_mainls_sendrecv = PTHREAD_COND_INITIALIZER; 100 | 101 | /* main state communication mutex */ 102 | static pthread_mutex_t mutex_mainls = PTHREAD_MUTEX_INITIALIZER; 103 | 104 | /*********************** 105 | * register prototypes * 106 | ***********************/ 107 | 108 | static void luaproc_openlualibs( lua_State *L ); 109 | static int luaproc_create_newproc( lua_State *L ); 110 | static int luaproc_wait( lua_State *L ); 111 | static int luaproc_send( lua_State *L ); 112 | static int luaproc_receive( lua_State *L ); 113 | static int luaproc_create_channel( lua_State *L ); 114 | static int luaproc_destroy_channel( lua_State *L ); 115 | static int luaproc_set_numworkers( lua_State *L ); 116 | static int luaproc_get_numworkers( lua_State *L ); 117 | static int luaproc_recycle_set( lua_State *L ); 118 | LUALIB_API int luaopen_luaproc( lua_State *L ); 119 | static int luaproc_loadlib( lua_State *L ); 120 | 121 | /*********** 122 | * structs * 123 | ***********/ 124 | 125 | /* lua process */ 126 | struct stluaproc { 127 | lua_State *lstate; 128 | int status; 129 | int args; 130 | channel *chan; 131 | luaproc *next; 132 | }; 133 | 134 | /* communication channel */ 135 | struct stchannel { 136 | list send; 137 | list recv; 138 | pthread_mutex_t mutex; 139 | pthread_cond_t can_be_used; 140 | }; 141 | 142 | /* luaproc function registration array */ 143 | static const struct luaL_Reg luaproc_funcs[] = { 144 | { "newproc", luaproc_create_newproc }, 145 | { "wait", luaproc_wait }, 146 | { "send", luaproc_send }, 147 | { "receive", luaproc_receive }, 148 | { "newchannel", luaproc_create_channel }, 149 | { "delchannel", luaproc_destroy_channel }, 150 | { "setnumworkers", luaproc_set_numworkers }, 151 | { "getnumworkers", luaproc_get_numworkers }, 152 | { "recycle", luaproc_recycle_set }, 153 | { NULL, NULL } 154 | }; 155 | 156 | /****************** 157 | * list functions * 158 | ******************/ 159 | 160 | /* insert a lua process in a (fifo) list */ 161 | void list_insert( list *l, luaproc *lp ) { 162 | if ( l->head == NULL ) { 163 | l->head = lp; 164 | } else { 165 | l->tail->next = lp; 166 | } 167 | l->tail = lp; 168 | lp->next = NULL; 169 | l->nodes++; 170 | } 171 | 172 | /* remove and return the first lua process in a (fifo) list */ 173 | luaproc *list_remove( list *l ) { 174 | if ( l->head != NULL ) { 175 | luaproc *lp = l->head; 176 | l->head = lp->next; 177 | l->nodes--; 178 | return lp; 179 | } else { 180 | return NULL; /* if list is empty, return NULL */ 181 | } 182 | } 183 | 184 | /* return a list's node count */ 185 | int list_count( list *l ) { 186 | return l->nodes; 187 | } 188 | 189 | /* initialize an empty list */ 190 | void list_init( list *l ) { 191 | l->head = NULL; 192 | l->tail = NULL; 193 | l->nodes = 0; 194 | } 195 | 196 | /********************* 197 | * channel functions * 198 | *********************/ 199 | 200 | /* create a new channel and insert it into channels table */ 201 | static channel *channel_create( const char *cname ) { 202 | 203 | channel *chan; 204 | 205 | /* get exclusive access to channels list */ 206 | pthread_mutex_lock( &mutex_channel_list ); 207 | 208 | /* create new channel and register its name */ 209 | lua_getglobal( chanls, LUAPROC_CHANNELS_TABLE ); 210 | chan = (channel *)lua_newuserdata( chanls, sizeof( channel )); 211 | lua_setfield( chanls, -2, cname ); 212 | lua_pop( chanls, 1 ); /* remove channel table from stack */ 213 | 214 | /* initialize channel struct */ 215 | list_init( &chan->send ); 216 | list_init( &chan->recv ); 217 | pthread_mutex_init( &chan->mutex, NULL ); 218 | pthread_cond_init( &chan->can_be_used, NULL ); 219 | 220 | /* release exclusive access to channels list */ 221 | pthread_mutex_unlock( &mutex_channel_list ); 222 | 223 | return chan; 224 | } 225 | 226 | /* 227 | return a channel (if not found, return null). 228 | caller function MUST lock 'mutex_channel_list' before calling this function. 229 | */ 230 | static channel *channel_unlocked_get( const char *chname ) { 231 | 232 | channel *chan; 233 | 234 | lua_getglobal( chanls, LUAPROC_CHANNELS_TABLE ); 235 | lua_getfield( chanls, -1, chname ); 236 | chan = (channel *)lua_touserdata( chanls, -1 ); 237 | lua_pop( chanls, 2 ); /* pop userdata and channel */ 238 | 239 | return chan; 240 | } 241 | 242 | /* 243 | return a channel (if not found, return null) with its (mutex) lock set. 244 | caller function should unlock channel's (mutex) lock after calling this 245 | function. 246 | */ 247 | static channel *channel_locked_get( const char *chname ) { 248 | 249 | channel *chan; 250 | 251 | /* get exclusive access to channels list */ 252 | pthread_mutex_lock( &mutex_channel_list ); 253 | 254 | /* 255 | try to get channel and lock it; if lock fails, release external 256 | lock ('mutex_channel_list') to try again when signaled -- this avoids 257 | keeping the external lock busy for too long. during the release, 258 | the channel may be destroyed, so it must try to get it again. 259 | */ 260 | while ((( chan = channel_unlocked_get( chname )) != NULL ) && 261 | ( pthread_mutex_trylock( &chan->mutex ) != 0 )) { 262 | pthread_cond_wait( &chan->can_be_used, &mutex_channel_list ); 263 | } 264 | 265 | /* release exclusive access to channels list */ 266 | pthread_mutex_unlock( &mutex_channel_list ); 267 | 268 | return chan; 269 | } 270 | 271 | /******************************** 272 | * exported auxiliary functions * 273 | ********************************/ 274 | 275 | /* unlock access to a channel and signal it can be used */ 276 | void luaproc_unlock_channel( channel *chan ) { 277 | 278 | /* get exclusive access to channels list */ 279 | pthread_mutex_lock( &mutex_channel_list ); 280 | /* release exclusive access to operate on a particular channel */ 281 | pthread_mutex_unlock( &chan->mutex ); 282 | /* signal that a particular channel can be used */ 283 | pthread_cond_signal( &chan->can_be_used ); 284 | /* release exclusive access to channels list */ 285 | pthread_mutex_unlock( &mutex_channel_list ); 286 | 287 | } 288 | 289 | /* insert lua process in recycle list */ 290 | void luaproc_recycle_insert( luaproc *lp ) { 291 | 292 | /* get exclusive access to recycled lua processes list */ 293 | pthread_mutex_lock( &mutex_recycle_list ); 294 | 295 | /* is recycle list full? */ 296 | if ( list_count( &recycle_list ) >= recyclemax ) { 297 | /* destroy state */ 298 | lua_close( luaproc_get_state( lp )); 299 | } else { 300 | /* insert lua process in recycle list */ 301 | list_insert( &recycle_list, lp ); 302 | } 303 | 304 | /* release exclusive access to recycled lua processes list */ 305 | pthread_mutex_unlock( &mutex_recycle_list ); 306 | } 307 | 308 | /* queue a lua process that tried to send a message */ 309 | void luaproc_queue_sender( luaproc *lp ) { 310 | list_insert( &lp->chan->send, lp ); 311 | } 312 | 313 | /* queue a lua process that tried to receive a message */ 314 | void luaproc_queue_receiver( luaproc *lp ) { 315 | list_insert( &lp->chan->recv, lp ); 316 | } 317 | 318 | /******************************** 319 | * internal auxiliary functions * 320 | ********************************/ 321 | static void luaproc_loadbuffer( lua_State *parent, luaproc *lp, 322 | const char *code, size_t len ) { 323 | 324 | /* load lua process' lua code */ 325 | int ret = luaL_loadbuffer( lp->lstate, code, len, code ); 326 | 327 | /* in case of errors, close lua_State and push error to parent */ 328 | if ( ret != 0 ) { 329 | lua_pushstring( parent, lua_tostring( lp->lstate, -1 )); 330 | lua_close( lp->lstate ); 331 | luaL_error( parent, lua_tostring( parent, -1 )); 332 | } 333 | } 334 | 335 | /* copies values between lua states' stacks */ 336 | static int luaproc_copyvalues( lua_State *Lfrom, lua_State *Lto ) { 337 | 338 | int i; 339 | int n = lua_gettop( Lfrom ); 340 | const char *str; 341 | size_t len; 342 | 343 | /* ensure there is space in the receiver's stack */ 344 | if ( lua_checkstack( Lto, n ) == 0 ) { 345 | lua_pushnil( Lto ); 346 | lua_pushstring( Lto, "not enough space in the stack" ); 347 | lua_pushnil( Lfrom ); 348 | lua_pushstring( Lfrom, "not enough space in the receiver's stack" ); 349 | return FALSE; 350 | } 351 | 352 | /* test each value's type and, if it's supported, copy value */ 353 | for ( i = 2; i <= n; i++ ) { 354 | switch ( lua_type( Lfrom, i )) { 355 | case LUA_TBOOLEAN: 356 | lua_pushboolean( Lto, lua_toboolean( Lfrom, i )); 357 | break; 358 | case LUA_TNUMBER: 359 | copynumber( Lto, Lfrom, i ); 360 | break; 361 | case LUA_TSTRING: { 362 | str = lua_tolstring( Lfrom, i, &len ); 363 | lua_pushlstring( Lto, str, len ); 364 | break; 365 | } 366 | case LUA_TNIL: 367 | lua_pushnil( Lto ); 368 | break; 369 | default: /* value type not supported: table, function, userdata, etc. */ 370 | lua_settop( Lto, 1 ); 371 | lua_pushnil( Lto ); 372 | lua_pushfstring( Lto, "failed to receive value of unsupported type " 373 | "'%s'", luaL_typename( Lfrom, i )); 374 | lua_pushnil( Lfrom ); 375 | lua_pushfstring( Lfrom, "failed to send value of unsupported type " 376 | "'%s'", luaL_typename( Lfrom, i )); 377 | return FALSE; 378 | } 379 | } 380 | return TRUE; 381 | } 382 | 383 | /* return the lua process associated with a given lua state */ 384 | static luaproc *luaproc_getself( lua_State *L ) { 385 | 386 | luaproc *lp; 387 | 388 | lua_getfield( L, LUA_REGISTRYINDEX, "LUAPROC_LP_UDATA" ); 389 | lp = (luaproc *)lua_touserdata( L, -1 ); 390 | lua_pop( L, 1 ); 391 | 392 | return lp; 393 | } 394 | 395 | /* create new lua process */ 396 | static luaproc *luaproc_new( lua_State *L ) { 397 | 398 | luaproc *lp; 399 | lua_State *lpst = luaL_newstate(); /* create new lua state */ 400 | 401 | /* store the lua process in its own lua state */ 402 | lp = (luaproc *)lua_newuserdata( lpst, sizeof( struct stluaproc )); 403 | lua_setfield( lpst, LUA_REGISTRYINDEX, "LUAPROC_LP_UDATA" ); 404 | luaproc_openlualibs( lpst ); /* load standard libraries and luaproc */ 405 | /* register luaproc's own functions */ 406 | requiref( lpst, "luaproc", luaproc_loadlib, TRUE ); 407 | lp->lstate = lpst; /* insert created lua state into lua process struct */ 408 | 409 | return lp; 410 | } 411 | 412 | /* join schedule workers (called before exiting Lua) */ 413 | static int luaproc_join_workers( lua_State *L ) { 414 | sched_join_workers(); 415 | lua_close( chanls ); 416 | return 0; 417 | } 418 | 419 | /* writer function for lua_dump */ 420 | static int luaproc_buff_writer( lua_State *L, const void *buff, size_t size, 421 | void *ud ) { 422 | (void)L; 423 | luaL_addlstring((luaL_Buffer *)ud, (const char *)buff, size ); 424 | return 0; 425 | } 426 | 427 | /* copies upvalues between lua states' stacks */ 428 | static int luaproc_copyupvalues( lua_State *Lfrom, lua_State *Lto, 429 | int funcindex ) { 430 | 431 | int i = 1; 432 | const char *str; 433 | size_t len; 434 | 435 | /* test the type of each upvalue and, if it's supported, copy it */ 436 | while ( lua_getupvalue( Lfrom, funcindex, i ) != NULL ) { 437 | switch ( lua_type( Lfrom, -1 )) { 438 | case LUA_TBOOLEAN: 439 | lua_pushboolean( Lto, lua_toboolean( Lfrom, -1 )); 440 | break; 441 | case LUA_TNUMBER: 442 | copynumber( Lto, Lfrom, -1 ); 443 | break; 444 | case LUA_TSTRING: { 445 | str = lua_tolstring( Lfrom, -1, &len ); 446 | lua_pushlstring( Lto, str, len ); 447 | break; 448 | } 449 | case LUA_TNIL: 450 | lua_pushnil( Lto ); 451 | break; 452 | /* if upvalue is a table, check whether it is the global environment 453 | (_ENV) from the source state Lfrom. in case so, push in the stack of 454 | the destination state Lto its own global environment to be set as the 455 | corresponding upvalue; otherwise, treat it as a regular non-supported 456 | upvalue type. */ 457 | case LUA_TTABLE: 458 | lua_pushglobaltable( Lfrom ); 459 | if ( isequal( Lfrom, -1, -2 )) { 460 | lua_pop( Lfrom, 1 ); 461 | lua_pushglobaltable( Lto ); 462 | break; 463 | } 464 | lua_pop( Lfrom, 1 ); 465 | /* FALLTHROUGH */ 466 | default: /* value type not supported: table, function, userdata, etc. */ 467 | lua_pushnil( Lfrom ); 468 | lua_pushfstring( Lfrom, "failed to copy upvalue of unsupported type " 469 | "'%s'", luaL_typename( Lfrom, -2 )); 470 | return FALSE; 471 | } 472 | lua_pop( Lfrom, 1 ); 473 | if ( lua_setupvalue( Lto, 1, i ) == NULL ) { 474 | lua_pushnil( Lfrom ); 475 | lua_pushstring( Lfrom, "failed to set upvalue" ); 476 | return FALSE; 477 | } 478 | i++; 479 | } 480 | return TRUE; 481 | } 482 | 483 | 484 | /********************* 485 | * library functions * 486 | *********************/ 487 | 488 | /* set maximum number of lua processes in the recycle list */ 489 | static int luaproc_recycle_set( lua_State *L ) { 490 | 491 | luaproc *lp; 492 | 493 | /* validate parameter is a non negative number */ 494 | lua_Integer max = luaL_checkinteger( L, 1 ); 495 | luaL_argcheck( L, max >= 0, 1, "recycle limit must be positive" ); 496 | 497 | /* get exclusive access to recycled lua processes list */ 498 | pthread_mutex_lock( &mutex_recycle_list ); 499 | 500 | recyclemax = max; /* set maximum number */ 501 | 502 | /* remove extra nodes and destroy each lua processes */ 503 | while ( list_count( &recycle_list ) > recyclemax ) { 504 | lp = list_remove( &recycle_list ); 505 | lua_close( lp->lstate ); 506 | } 507 | /* release exclusive access to recycled lua processes list */ 508 | pthread_mutex_unlock( &mutex_recycle_list ); 509 | 510 | return 0; 511 | } 512 | 513 | /* wait until there are no more active lua processes */ 514 | static int luaproc_wait( lua_State *L ) { 515 | sched_wait(); 516 | return 0; 517 | } 518 | 519 | /* set number of workers (creates or destroys accordingly) */ 520 | static int luaproc_set_numworkers( lua_State *L ) { 521 | 522 | /* validate parameter is a positive number */ 523 | lua_Integer numworkers = luaL_checkinteger( L, -1 ); 524 | luaL_argcheck( L, numworkers > 0, 1, "number of workers must be positive" ); 525 | 526 | /* set number of threads; signal error on failure */ 527 | if ( sched_set_numworkers( numworkers ) == LUAPROC_SCHED_PTHREAD_ERROR ) { 528 | luaL_error( L, "failed to create worker" ); 529 | } 530 | 531 | return 0; 532 | } 533 | 534 | /* return the number of active workers */ 535 | static int luaproc_get_numworkers( lua_State *L ) { 536 | lua_pushnumber( L, sched_get_numworkers( )); 537 | return 1; 538 | } 539 | 540 | /* create and schedule a new lua process */ 541 | static int luaproc_create_newproc( lua_State *L ) { 542 | 543 | size_t len; 544 | luaproc *lp; 545 | luaL_Buffer buff; 546 | const char *code; 547 | int d; 548 | int lt = lua_type( L, 1 ); 549 | 550 | /* check function argument type - must be function or string; in case it is 551 | a function, dump it into a binary string */ 552 | if ( lt == LUA_TFUNCTION ) { 553 | lua_settop( L, 1 ); 554 | luaL_buffinit( L, &buff ); 555 | d = dump( L, luaproc_buff_writer, &buff, FALSE ); 556 | if ( d != 0 ) { 557 | lua_pushnil( L ); 558 | lua_pushfstring( L, "error %d dumping function to binary string", d ); 559 | return 2; 560 | } 561 | luaL_pushresult( &buff ); 562 | lua_insert( L, 1 ); 563 | } else if ( lt != LUA_TSTRING ) { 564 | lua_pushnil( L ); 565 | lua_pushfstring( L, "cannot use '%s' to create a new process", 566 | luaL_typename( L, 1 )); 567 | return 2; 568 | } 569 | 570 | /* get pointer to code string */ 571 | code = lua_tolstring( L, 1, &len ); 572 | 573 | /* get exclusive access to recycled lua processes list */ 574 | pthread_mutex_lock( &mutex_recycle_list ); 575 | 576 | /* check if a lua process can be recycled */ 577 | if ( recyclemax > 0 ) { 578 | lp = list_remove( &recycle_list ); 579 | /* otherwise create a new lua process */ 580 | if ( lp == NULL ) { 581 | lp = luaproc_new( L ); 582 | } 583 | } else { 584 | lp = luaproc_new( L ); 585 | } 586 | 587 | /* release exclusive access to recycled lua processes list */ 588 | pthread_mutex_unlock( &mutex_recycle_list ); 589 | 590 | /* init lua process */ 591 | lp->status = LUAPROC_STATUS_IDLE; 592 | lp->args = 0; 593 | lp->chan = NULL; 594 | 595 | /* load code in lua process */ 596 | luaproc_loadbuffer( L, lp, code, len ); 597 | 598 | /* if lua process is being created from a function, copy its upvalues and 599 | remove dumped binary string from stack */ 600 | if ( lt == LUA_TFUNCTION ) { 601 | if ( luaproc_copyupvalues( L, lp->lstate, 2 ) == FALSE ) { 602 | luaproc_recycle_insert( lp ); 603 | return 2; 604 | } 605 | lua_pop( L, 1 ); 606 | } 607 | 608 | sched_inc_lpcount(); /* increase active lua process count */ 609 | sched_queue_proc( lp ); /* schedule lua process for execution */ 610 | lua_pushboolean( L, TRUE ); 611 | 612 | return 1; 613 | } 614 | 615 | /* send a message to a lua process */ 616 | static int luaproc_send( lua_State *L ) { 617 | 618 | int ret; 619 | channel *chan; 620 | luaproc *dstlp, *self; 621 | const char *chname = luaL_checkstring( L, 1 ); 622 | 623 | chan = channel_locked_get( chname ); 624 | /* if channel is not found, return an error to lua */ 625 | if ( chan == NULL ) { 626 | lua_pushnil( L ); 627 | lua_pushfstring( L, "channel '%s' does not exist", chname ); 628 | return 2; 629 | } 630 | 631 | /* remove first lua process, if any, from channel's receive list */ 632 | dstlp = list_remove( &chan->recv ); 633 | 634 | if ( dstlp != NULL ) { /* found a receiver? */ 635 | /* try to move values between lua states' stacks */ 636 | ret = luaproc_copyvalues( L, dstlp->lstate ); 637 | /* -1 because channel name is on the stack */ 638 | dstlp->args = lua_gettop( dstlp->lstate ) - 1; 639 | if ( dstlp->lstate == mainlp.lstate ) { 640 | /* if sending process is the parent (main) Lua state, unblock it */ 641 | pthread_mutex_lock( &mutex_mainls ); 642 | pthread_cond_signal( &cond_mainls_sendrecv ); 643 | pthread_mutex_unlock( &mutex_mainls ); 644 | } else { 645 | /* schedule receiving lua process for execution */ 646 | sched_queue_proc( dstlp ); 647 | } 648 | /* unlock channel access */ 649 | luaproc_unlock_channel( chan ); 650 | if ( ret == TRUE ) { /* was send successful? */ 651 | lua_pushboolean( L, TRUE ); 652 | return 1; 653 | } else { /* nil and error msg already in stack */ 654 | return 2; 655 | } 656 | 657 | } else { 658 | if ( L == mainlp.lstate ) { 659 | /* sending process is the parent (main) Lua state - block it */ 660 | mainlp.chan = chan; 661 | luaproc_queue_sender( &mainlp ); 662 | luaproc_unlock_channel( chan ); 663 | pthread_mutex_lock( &mutex_mainls ); 664 | pthread_cond_wait( &cond_mainls_sendrecv, &mutex_mainls ); 665 | pthread_mutex_unlock( &mutex_mainls ); 666 | return mainlp.args; 667 | } else { 668 | /* sending process is a standard luaproc - set status, block and yield */ 669 | self = luaproc_getself( L ); 670 | if ( self != NULL ) { 671 | self->status = LUAPROC_STATUS_BLOCKED_SEND; 672 | self->chan = chan; 673 | } 674 | /* yield. channel will be unlocked by the scheduler */ 675 | return lua_yield( L, lua_gettop( L )); 676 | } 677 | } 678 | } 679 | 680 | /* receive a message from a lua process */ 681 | static int luaproc_receive( lua_State *L ) { 682 | 683 | int ret, nargs; 684 | channel *chan; 685 | luaproc *srclp, *self; 686 | const char *chname = luaL_checkstring( L, 1 ); 687 | 688 | /* get number of arguments passed to function */ 689 | nargs = lua_gettop( L ); 690 | 691 | chan = channel_locked_get( chname ); 692 | /* if channel is not found, return an error to Lua */ 693 | if ( chan == NULL ) { 694 | lua_pushnil( L ); 695 | lua_pushfstring( L, "channel '%s' does not exist", chname ); 696 | return 2; 697 | } 698 | 699 | /* remove first lua process, if any, from channels' send list */ 700 | srclp = list_remove( &chan->send ); 701 | 702 | if ( srclp != NULL ) { /* found a sender? */ 703 | /* try to move values between lua states' stacks */ 704 | ret = luaproc_copyvalues( srclp->lstate, L ); 705 | if ( ret == TRUE ) { /* was receive successful? */ 706 | lua_pushboolean( srclp->lstate, TRUE ); 707 | srclp->args = 1; 708 | } else { /* nil and error_msg already in stack */ 709 | srclp->args = 2; 710 | } 711 | if ( srclp->lstate == mainlp.lstate ) { 712 | /* if sending process is the parent (main) Lua state, unblock it */ 713 | pthread_mutex_lock( &mutex_mainls ); 714 | pthread_cond_signal( &cond_mainls_sendrecv ); 715 | pthread_mutex_unlock( &mutex_mainls ); 716 | } else { 717 | /* otherwise, schedule process for execution */ 718 | sched_queue_proc( srclp ); 719 | } 720 | /* unlock channel access */ 721 | luaproc_unlock_channel( chan ); 722 | /* disconsider channel name, async flag and any other args passed 723 | to the receive function when returning its results */ 724 | return lua_gettop( L ) - nargs; 725 | 726 | } else { /* otherwise test if receive was synchronous or asynchronous */ 727 | if ( lua_toboolean( L, 2 )) { /* asynchronous receive */ 728 | /* unlock channel access */ 729 | luaproc_unlock_channel( chan ); 730 | /* return an error */ 731 | lua_pushnil( L ); 732 | lua_pushfstring( L, "no senders waiting on channel '%s'", chname ); 733 | return 2; 734 | } else { /* synchronous receive */ 735 | if ( L == mainlp.lstate ) { 736 | /* receiving process is the parent (main) Lua state - block it */ 737 | mainlp.chan = chan; 738 | luaproc_queue_receiver( &mainlp ); 739 | luaproc_unlock_channel( chan ); 740 | pthread_mutex_lock( &mutex_mainls ); 741 | pthread_cond_wait( &cond_mainls_sendrecv, &mutex_mainls ); 742 | pthread_mutex_unlock( &mutex_mainls ); 743 | return mainlp.args; 744 | } else { 745 | /* receiving process is a standard luaproc - set status, block and 746 | yield */ 747 | self = luaproc_getself( L ); 748 | if ( self != NULL ) { 749 | self->status = LUAPROC_STATUS_BLOCKED_RECV; 750 | self->chan = chan; 751 | } 752 | /* yield. channel will be unlocked by the scheduler */ 753 | return lua_yield( L, lua_gettop( L )); 754 | } 755 | } 756 | } 757 | } 758 | 759 | /* create a new channel */ 760 | static int luaproc_create_channel( lua_State *L ) { 761 | 762 | const char *chname = luaL_checkstring( L, 1 ); 763 | 764 | channel *chan = channel_locked_get( chname ); 765 | if (chan != NULL) { /* does channel exist? */ 766 | /* unlock the channel mutex locked by channel_locked_get */ 767 | luaproc_unlock_channel( chan ); 768 | /* return an error to lua */ 769 | lua_pushnil( L ); 770 | lua_pushfstring( L, "channel '%s' already exists", chname ); 771 | return 2; 772 | } else { /* create channel */ 773 | channel_create( chname ); 774 | lua_pushboolean( L, TRUE ); 775 | return 1; 776 | } 777 | } 778 | 779 | /* destroy a channel */ 780 | static int luaproc_destroy_channel( lua_State *L ) { 781 | 782 | channel *chan; 783 | list *blockedlp; 784 | luaproc *lp; 785 | const char *chname = luaL_checkstring( L, 1 ); 786 | 787 | /* get exclusive access to channels list */ 788 | pthread_mutex_lock( &mutex_channel_list ); 789 | 790 | /* 791 | try to get channel and lock it; if lock fails, release external 792 | lock ('mutex_channel_list') to try again when signaled -- this avoids 793 | keeping the external lock busy for too long. during this release, 794 | the channel may have been destroyed, so it must try to get it again. 795 | */ 796 | while ((( chan = channel_unlocked_get( chname )) != NULL ) && 797 | ( pthread_mutex_trylock( &chan->mutex ) != 0 )) { 798 | pthread_cond_wait( &chan->can_be_used, &mutex_channel_list ); 799 | } 800 | 801 | if ( chan == NULL ) { /* found channel? */ 802 | /* release exclusive access to channels list */ 803 | pthread_mutex_unlock( &mutex_channel_list ); 804 | /* return an error to lua */ 805 | lua_pushnil( L ); 806 | lua_pushfstring( L, "channel '%s' does not exist", chname ); 807 | return 2; 808 | } 809 | 810 | /* remove channel from table */ 811 | lua_getglobal( chanls, LUAPROC_CHANNELS_TABLE ); 812 | lua_pushnil( chanls ); 813 | lua_setfield( chanls, -2, chname ); 814 | lua_pop( chanls, 1 ); 815 | 816 | pthread_mutex_unlock( &mutex_channel_list ); 817 | 818 | /* 819 | wake up workers there are waiting to use the channel. 820 | they will not find the channel, since it was removed, 821 | and will not get this condition anymore. 822 | */ 823 | pthread_cond_broadcast( &chan->can_be_used ); 824 | 825 | /* 826 | dequeue lua processes waiting on the channel, return an error message 827 | to each of them indicating channel was destroyed and schedule them 828 | for execution (unblock them). 829 | */ 830 | if ( chan->send.head != NULL ) { 831 | lua_pushfstring( L, "channel '%s' destroyed while waiting for receiver", 832 | chname ); 833 | blockedlp = &chan->send; 834 | } else { 835 | lua_pushfstring( L, "channel '%s' destroyed while waiting for sender", 836 | chname ); 837 | blockedlp = &chan->recv; 838 | } 839 | while (( lp = list_remove( blockedlp )) != NULL ) { 840 | /* return an error to each process */ 841 | lua_pushnil( lp->lstate ); 842 | lua_pushstring( lp->lstate, lua_tostring( L, -1 )); 843 | lp->args = 2; 844 | sched_queue_proc( lp ); /* schedule process for execution */ 845 | } 846 | 847 | /* unlock channel mutex and destroy both mutex and condition */ 848 | pthread_mutex_unlock( &chan->mutex ); 849 | pthread_mutex_destroy( &chan->mutex ); 850 | pthread_cond_destroy( &chan->can_be_used ); 851 | 852 | lua_pushboolean( L, TRUE ); 853 | return 1; 854 | } 855 | 856 | /*********************** 857 | * get'ers and set'ers * 858 | ***********************/ 859 | 860 | /* return the channel where a lua process is blocked at */ 861 | channel *luaproc_get_channel( luaproc *lp ) { 862 | return lp->chan; 863 | } 864 | 865 | /* return a lua process' status */ 866 | int luaproc_get_status( luaproc *lp ) { 867 | return lp->status; 868 | } 869 | 870 | /* set lua a process' status */ 871 | void luaproc_set_status( luaproc *lp, int status ) { 872 | lp->status = status; 873 | } 874 | 875 | /* return a lua process' state */ 876 | lua_State *luaproc_get_state( luaproc *lp ) { 877 | return lp->lstate; 878 | } 879 | 880 | /* return the number of arguments expected by a lua process */ 881 | int luaproc_get_numargs( luaproc *lp ) { 882 | return lp->args; 883 | } 884 | 885 | /* set the number of arguments expected by a lua process */ 886 | void luaproc_set_numargs( luaproc *lp, int n ) { 887 | lp->args = n; 888 | } 889 | 890 | /********************************** 891 | * register structs and functions * 892 | **********************************/ 893 | 894 | static void luaproc_reglualib( lua_State *L, const char *name, 895 | lua_CFunction f ) { 896 | lua_getglobal( L, "package" ); 897 | lua_getfield( L, -1, "preload" ); 898 | lua_pushcfunction( L, f ); 899 | lua_setfield( L, -2, name ); 900 | lua_pop( L, 2 ); 901 | } 902 | 903 | static void luaproc_openlualibs( lua_State *L ) { 904 | requiref( L, "_G", luaopen_base, FALSE ); 905 | requiref( L, "package", luaopen_package, TRUE ); 906 | luaproc_reglualib( L, "io", luaopen_io ); 907 | luaproc_reglualib( L, "os", luaopen_os ); 908 | luaproc_reglualib( L, "table", luaopen_table ); 909 | luaproc_reglualib( L, "string", luaopen_string ); 910 | luaproc_reglualib( L, "math", luaopen_math ); 911 | luaproc_reglualib( L, "debug", luaopen_debug ); 912 | #if (LUA_VERSION_NUM == 502) 913 | luaproc_reglualib( L, "bit32", luaopen_bit32 ); 914 | #endif 915 | #if (LUA_VERSION_NUM >= 502) 916 | luaproc_reglualib( L, "coroutine", luaopen_coroutine ); 917 | #endif 918 | #if (LUA_VERSION_NUM >= 503) 919 | luaproc_reglualib( L, "utf8", luaopen_utf8 ); 920 | #endif 921 | 922 | } 923 | 924 | LUALIB_API int luaopen_luaproc( lua_State *L ) { 925 | 926 | /* register luaproc functions */ 927 | luaL_newlib( L, luaproc_funcs ); 928 | 929 | /* wrap main state inside a lua process */ 930 | mainlp.lstate = L; 931 | mainlp.status = LUAPROC_STATUS_IDLE; 932 | mainlp.args = 0; 933 | mainlp.chan = NULL; 934 | mainlp.next = NULL; 935 | /* initialize recycle list */ 936 | list_init( &recycle_list ); 937 | /* initialize channels table and lua_State used to store it */ 938 | chanls = luaL_newstate(); 939 | lua_newtable( chanls ); 940 | lua_setglobal( chanls, LUAPROC_CHANNELS_TABLE ); 941 | /* create finalizer to join workers when Lua exits */ 942 | lua_newuserdata( L, 0 ); 943 | lua_setfield( L, LUA_REGISTRYINDEX, "LUAPROC_FINALIZER_UDATA" ); 944 | luaL_newmetatable( L, "LUAPROC_FINALIZER_MT" ); 945 | lua_pushliteral( L, "__gc" ); 946 | lua_pushcfunction( L, luaproc_join_workers ); 947 | lua_rawset( L, -3 ); 948 | lua_pop( L, 1 ); 949 | lua_getfield( L, LUA_REGISTRYINDEX, "LUAPROC_FINALIZER_UDATA" ); 950 | lua_getfield( L, LUA_REGISTRYINDEX, "LUAPROC_FINALIZER_MT" ); 951 | lua_setmetatable( L, -2 ); 952 | lua_pop( L, 1 ); 953 | /* initialize scheduler */ 954 | if ( sched_init() == LUAPROC_SCHED_PTHREAD_ERROR ) { 955 | luaL_error( L, "failed to create worker" ); 956 | } 957 | 958 | return 1; 959 | } 960 | 961 | static int luaproc_loadlib( lua_State *L ) { 962 | 963 | /* register luaproc functions */ 964 | luaL_newlib( L, luaproc_funcs ); 965 | 966 | return 1; 967 | } 968 | -------------------------------------------------------------------------------- /src/luaproc.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** Auxiliary functions from luaproc API 3 | ** See Copyright Notice at the end of this file 4 | */ 5 | 6 | #ifndef _LUA_LUAPROC_H_ 7 | #define _LUA_LUAPROC_H_ 8 | 9 | /************************************* 10 | * execution status of lua processes * 11 | ************************************/ 12 | 13 | #define LUAPROC_STATUS_IDLE 0 14 | #define LUAPROC_STATUS_READY 1 15 | #define LUAPROC_STATUS_BLOCKED_SEND 2 16 | #define LUAPROC_STATUS_BLOCKED_RECV 3 17 | #define LUAPROC_STATUS_FINISHED 4 18 | 19 | /******************* 20 | * structure types * 21 | ******************/ 22 | 23 | typedef struct stluaproc luaproc; /* lua process */ 24 | 25 | typedef struct stchannel channel; /* communication channel */ 26 | 27 | /* linked (fifo) list */ 28 | typedef struct stlist { 29 | luaproc *head; 30 | luaproc *tail; 31 | int nodes; 32 | } list; 33 | 34 | /*********************** 35 | * function prototypes * 36 | **********************/ 37 | 38 | /* unlock access to a channel */ 39 | void luaproc_unlock_channel( channel *chan ); 40 | 41 | /* return a channel where a lua process is blocked at */ 42 | channel *luaproc_get_channel( luaproc *lp ); 43 | 44 | /* queue a lua process that tried to send a message */ 45 | void luaproc_queue_sender( luaproc *lp ); 46 | 47 | /* queue a lua process that tried to receive a message */ 48 | void luaproc_queue_receiver( luaproc *lp ); 49 | 50 | /* add a lua process to the recycle list */ 51 | void luaproc_recycle_insert( luaproc *lp ); 52 | 53 | /* return a lua process' status */ 54 | int luaproc_get_status( luaproc *lp ); 55 | 56 | /* set a lua process' status */ 57 | void luaproc_set_status( luaproc *lp, int status ); 58 | 59 | /* return a lua process' lua state */ 60 | lua_State *luaproc_get_state( luaproc *lp ); 61 | 62 | /* return the number of arguments expected by a lua process */ 63 | int luaproc_get_numargs( luaproc *lp ); 64 | 65 | /* set the number of arguments expected by a lua process */ 66 | void luaproc_set_numargs( luaproc *lp, int n ); 67 | 68 | /* initialize an empty list */ 69 | void list_init( list *l ); 70 | 71 | /* insert a lua process in a list */ 72 | void list_insert( list *l, luaproc *lp ); 73 | 74 | /* remove and return the first lua process in a list */ 75 | luaproc* list_remove( list *l ); 76 | 77 | /* return a list's node count */ 78 | int list_count( list *l ); 79 | 80 | /* }====================================================================== */ 81 | 82 | 83 | /****************************************************************************** 84 | * Copyright 2008-2015 Alexandre Skyrme, Noemi Rodriguez, Roberto Ierusalimschy 85 | * 86 | * Permission is hereby granted, free of charge, to any person obtaining 87 | * a copy of this software and associated documentation files (the 88 | * "Software"), to deal in the Software without restriction, including 89 | * without limitation the rights to use, copy, modify, merge, publish, 90 | * distribute, sublicense, and/or sell copies of the Software, and to 91 | * permit persons to whom the Software is furnished to do so, subject to 92 | * the following conditions: 93 | * 94 | * The above copyright notice and this permission notice shall be 95 | * included in all copies or substantial portions of the Software. 96 | * 97 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 98 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 99 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 100 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 101 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 102 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 103 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 104 | ******************************************************************************/ 105 | 106 | #endif 107 | -------------------------------------------------------------------------------- /tests/hello.lua: -------------------------------------------------------------------------------- 1 | -- load luaproc 2 | luaproc = require "luaproc" 3 | 4 | -- create an additional worker 5 | luaproc.setnumworkers( 2 ) 6 | 7 | -- create a new lua process 8 | luaproc.newproc( [[ 9 | -- create a communication channel 10 | luaproc.newchannel( "achannel" ) 11 | -- create a sender lua process 12 | luaproc.newproc( [=[ 13 | -- send a message 14 | luaproc.send( "achannel", "hello world from luaproc" ) 15 | ]=] ) 16 | -- create a receiver lua process 17 | luaproc.newproc( [=[ 18 | -- receive and print a message 19 | print( luaproc.receive( "achannel" )) 20 | ]=] ) 21 | ]] ) 22 | 23 | --------------------------------------------------------------------------------