summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ABOUT17
-rw-r--r--BUGS6
-rw-r--r--CHANGES72
-rw-r--r--CMakeLists.txt86
-rw-r--r--COPYRIGHT31
-rw-r--r--Makefile229
-rw-r--r--README106
-rw-r--r--TODO4
-rw-r--r--dist.cmake130
-rw-r--r--dist.info14
-rw-r--r--docs/Lua multithreading choices.grafflebin0 -> 2290 bytes
-rw-r--r--docs/Lua multithreading choices.svg15
-rw-r--r--docs/comparison.html297
-rw-r--r--docs/index.html951
-rw-r--r--docs/multi.pngbin0 -> 4657 bytes
-rw-r--r--docs/performance.odsbin0 -> 66817 bytes
-rw-r--r--make-vc.cmd274
-rw-r--r--setup-vc.cmd90
-rw-r--r--src/Makefile176
-rw-r--r--src/keeper.lua244
-rw-r--r--src/lanes.c1849
-rw-r--r--src/lanes.lua611
-rw-r--r--src/threading.c721
-rw-r--r--src/threading.h196
-rw-r--r--src/tools.c1198
-rw-r--r--src/tools.h72
-rw-r--r--tests/argtable.lua38
-rw-r--r--tests/assert.lua318
-rw-r--r--tests/atomic.lua18
-rw-r--r--tests/basic.lua331
-rw-r--r--tests/cyclic.lua64
-rw-r--r--tests/ehynes.lua52
-rw-r--r--tests/error.lua47
-rw-r--r--tests/fibonacci.lua75
-rw-r--r--tests/fifo.lua43
-rw-r--r--tests/finalizer.lua81
-rw-r--r--tests/hangtest.lua26
-rw-r--r--tests/irayo_closure.lua35
-rw-r--r--tests/irayo_recursive.lua18
-rw-r--r--tests/keeper.lua47
-rw-r--r--tests/launchtest.lua78
-rw-r--r--tests/objects.lua76
-rw-r--r--tests/perftest.lua184
-rw-r--r--tests/recursive.lua21
-rw-r--r--tests/require.lua30
-rw-r--r--tests/timer.lua93
-rw-r--r--tools/bin2c.lua131
47 files changed, 9195 insertions, 0 deletions
diff --git a/ABOUT b/ABOUT
new file mode 100644
index 0000000..81cf640
--- /dev/null
+++ b/ABOUT
@@ -0,0 +1,17 @@
1
2Lua Lanes
3---------
4
5Lanes is a lightweight, native, lazy evaluating multithreading library for
6Lua 5.1. It allows efficient use of multicore processors in Lua, by passing
7function calls into separate OS threads, and separate Lua states.
8
9No locking of the threads is needed, only launching and waiting for (with an
10optional timeout) a thread to get ready. Efficient communications between the
11running threads are possible either using message passing or shared state
12models. Values passed can be anything but coroutines (see detailed limitations
13in the manual).
14
15Lua Lanes has been optimized for performance, and provides around 50-60%
16speed increase when running heavily threaded applications on dual core
17processors (compared to running a non-threaded plain Lua implementation).
diff --git a/BUGS b/BUGS
new file mode 100644
index 0000000..d25cc0e
--- /dev/null
+++ b/BUGS
@@ -0,0 +1,6 @@
1
2BUGS:
3
4- tests/irayo_closure.lua fails (trouble with setting globals right
5 for functions carried over to another Lua state)
6
diff --git a/CHANGES b/CHANGES
new file mode 100644
index 0000000..ae4da3c
--- /dev/null
+++ b/CHANGES
@@ -0,0 +1,72 @@
1
2CHANGES:
3
4CHANGE X:
5
6CHANGE 12 (bug fix on Windows, 2.0.3) AKa 25-Jan-2009:
7 Did CHANGE 9 the way it should be done.
8
9CHANGE 11 (new feature, 2.0.3) AKa 23-Jan-2009:
10 Finalizers ('set_finalizer()') for being able to do cleanup of a lane's
11 resources, whether it returned succesfully or via an error.
12
13CHANGE 10 (new feature, 2.0.3) AKa 23-Jan-2009:
14 Call stack showing where an error occurred is not merged with the error
15 message, but delivered as a separate stack table ({ "filename:line" [, ...] }).
16 Getting call stacks of errorred lanes is now possible.
17
18CHANGE 9 (bug fix on Windows) AKa 10-Dec-2008 (> 2.0.2):
19 Applied patch from Kriss Daniels to avoid issues on 'now_time()' in Win32
20 (http://luaforge.net/forum/forum.php?thread_id=22704&forum_id=1781).
21
22CHANGE 8 (bug fix) AKa 26-Oct-2008:
23 Avoids occasional segfault at process exit (on multicore CPUs). Does this
24 by keeping track of "free running" threads (s.a. the time thread) and
25 cancelling them at process exit.
26
27 Tested (2.0.2) on Linux 64,x86, OS X, WinXP.
28
29CHANGE 7 (bug fix) AKa 15-Oct-2008:
30 Recursive functions that use themselves as direct upvalue can now be
31 passed to other lanes, and used as a lane function.
32
33CHANGE 6 (bug fix) AKa 15-Oct-2008:
34 Added local caches of the following to src/lanes.lua (was otherwise getting
35 errors at least in 'tests/irayo_recursive.lua').
36
37 local assert= assert
38 local string_gmatch= assert( string.gmatch )
39 local select= assert( select )
40 local type= assert( type )
41 local pairs= assert( pairs )
42 local tostring= assert( tostring )
43 local error= assert( error )
44 local setmetatable= assert( setmetatable )
45 local rawget= assert( rawget )
46
47 Thanks to Irayo for detecting and reporting this.
48
49CHANGE 5 (new feature):
50 Modifying Makefile so it's better suited to LuaRocks.
51
52CHANGE 4 (new feature):
53 Metatable copying, allowing Lua objects to be copied across lanes.
54
55CHANGE 3 (bug fix) AKa 5-Aug-2008:
56 The '__gc' method was not tied to thread userdata, at all. Caused memory
57 lifespan problems at least on OS X when threads were cancelled (EINVAL).
58
59CHANGE 2 (bug fix) AKa 5-Aug-2008:
60 Better calculation of timeouts, always making them absolute (even in Win32)
61 to allow for events that wake the lane up but don't read/write the Linda
62 key that it was observing.
63
64CHANGE 1 (bug fix) AKa 4-Aug-2008:
65 Signalling woke up only one waiting thread, not all. This caused i.e.
66 receive to not wake up if there was another thread waiting on the same
67 Linda object.
68
69 PThread fix: using 'pthread_cond_broadcast()' instead of 'pthread_cond_signal()'
70 Win32 fix: using manual events and 'PulseEvent()'
71
72(end)
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..1799c01
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,86 @@
1# Copyright (C) 2007-2009 LuaDist.
2# Created by Peter Kapec
3# Redistribution and use of this file is allowed according to the terms of the MIT license.
4# For details see the COPYRIGHT file distributed with LuaDist.
5# Please note that the package source code is licensed under its own license.
6
7PROJECT(lanes C)
8CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
9INCLUDE(dist.cmake)
10
11#2DO - patch threading.c to suppot cygwin.
12# The following values are just a guess.
13# WARNING: test segfault under Cygwin
14IF(CYGWIN)
15 ADD_DEFINITIONS(-D_PRIO_MODE=SCHED_FIFO)
16 ADD_DEFINITIONS(-D_PRIO_HI=15) # maximum that doesn't crash
17 ADD_DEFINITIONS(-D_PRIO_0=0)
18 ADD_DEFINITIONS(-D_PRIO_LO=-15) # ???
19 ADD_DEFINITIONS(-Dpthread_yield=sched_yield)
20ENDIF(CYGWIN)
21
22#2DO - use provided bin2c
23# Compile Lua bytecode to C
24ADD_CUSTOM_COMMAND(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/keeper.lch
25 DEPENDS src/keeper.lua
26 COMMAND "${LUAC}" "-o" "${CMAKE_CURRENT_BINARY_DIR}/keeper.lo"
27 "${CMAKE_CURRENT_SOURCE_DIR}/src/keeper.lua"
28 COMMAND "${LUA}" "${CMAKE_CURRENT_SOURCE_DIR}/tools/bin2c.lua"
29 "${CMAKE_CURRENT_BINARY_DIR}/keeper.lo"
30 "-o" "${CMAKE_CURRENT_BINARY_DIR}/keeper.lch")
31SET_SOURCE_FILES_PROPERTIES(src/lanes.c PROPERTIES OBJECT_DEPENDS
32 ${CMAKE_CURRENT_BINARY_DIR}/keeper.lch)
33INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
34
35
36# Build
37INCLUDE_DIRECTORIES(src)
38ADD_LIBRARY(lua51-lanes MODULE src/lanes.c src/threading.c src/tools.c)
39
40IF(UNIX AND NOT CYGWIN)
41 SET(LIBS pthread)
42ENDIF(UNIX AND NOT CYGWIN)
43
44IF(MINGW)
45#~ FIND_FILE(MSVCR80 NAMES msvcr80.dll msvcr90.dll)
46#~ SET(LIBS gcc ${MSVCR80})
47#~ IF(MSVC90)
48
49#~ from InstallRequiredSystemLibraries.cmake
50
51 IF(CMAKE_CL_64)
52 SET(CMAKE_MSVC_ARCH amd64)
53 ELSE(CMAKE_CL_64)
54 SET(CMAKE_MSVC_ARCH x86)
55 ENDIF(CMAKE_CL_64)
56
57 GET_FILENAME_COMPONENT(devenv_dir "${CMAKE_MAKE_PROGRAM}" PATH)
58 GET_FILENAME_COMPONENT(base_dir "${devenv_dir}/../.." ABSOLUTE)
59
60 # Find the runtime library redistribution directory.
61 FIND_PATH(MSVC90_REDIST_DIR NAMES ${CMAKE_MSVC_ARCH}/Microsoft.VC90.CRT/Microsoft.VC90.CRT.manifest
62 PATHS
63 "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\9.0;InstallDir]/../../VC/redist"
64 "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VCExpress\\9.0;InstallDir]/../../VC/redist"
65 "${base_dir}/VC/redist"
66 )
67 SET(MSVC90_CRT_DIR "${MSVC90_REDIST_DIR}/${CMAKE_MSVC_ARCH}/Microsoft.VC90.CRT")
68 SET(LIBS gcc msvcr90 "${MSVC90_CRT_DIR}/msvcr90.dll")
69
70#~ ENDIF()
71
72ENDIF()
73
74
75
76
77TARGET_LINK_LIBRARIES(lua51-lanes ${LUA_LIBRARY} ${LIBS})
78SET_TARGET_PROPERTIES(lua51-lanes PROPERTIES PREFIX "")
79
80# Install all files and documentation
81INSTALL (TARGETS lua51-lanes DESTINATION ${INSTALL_CMOD})
82INSTALL (FILES src/lanes.lua DESTINATION ${INSTALL_LMOD})
83
84INSTALL (FILES ABOUT BUGS COPYRIGHT CHANGES README TODO DESTINATION ${INSTALL_DATA})
85INSTALL (DIRECTORY docs/ DESTINATION ${INSTALL_DOC})
86INSTALL (DIRECTORY tests/ DESTINATION ${INSTALL_TEST})
diff --git a/COPYRIGHT b/COPYRIGHT
new file mode 100644
index 0000000..2930f19
--- /dev/null
+++ b/COPYRIGHT
@@ -0,0 +1,31 @@
1
2Lua Lanes is licensed under the same MIT license as the Lua 5.1 source code,
3reproduced below.
4
5For details and rationale, see http://www.lua.org/license.html
6
7===============================================================================
8
9Copyright (C) 2007-09 Asko Kauppi, <akauppi@gmail.com>
10
11Permission is hereby granted, free of charge, to any person obtaining a copy
12of this software and associated documentation files (the "Software"), to deal
13in the Software without restriction, including without limitation the rights
14to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15copies of the Software, and to permit persons to whom the Software is
16furnished to do so, subject to the following conditions:
17
18The above copyright notice and this permission notice shall be included in
19all copies or substantial portions of the Software.
20
21THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27THE SOFTWARE.
28
29===============================================================================
30
31(end of COPYRIGHT)
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..4c0ff4b
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,229 @@
1#
2# Lanes/Makefile
3#
4# make
5# make test
6# make basic|fifo|keeper|...
7#
8# make perftest[-odd|-even|-plain]
9# make launchtest
10#
11# make install DESTDIR=path
12# make tar|tgz VERSION=x.x
13# make clean
14#
15
16MODULE = lanes
17
18N=1000
19
20_SO=.so
21_TARGET_SO=src/lua51-lanes.so
22TIME=time
23
24ifeq "$(findstring MINGW32,$(shell uname -s))" "MINGW32"
25 # MinGW MSYS on XP
26 #
27 LUA=lua
28 LUAC=luac
29 _SO=.dll
30 _TARGET_SO=./lua51-lanes.dll
31 TIME=timeit.exe
32else
33 # Autodetect LUA & LUAC
34 #
35 LUA=$(word 1,$(shell which lua5.1) $(shell which lua51) lua)
36 LUAC=$(word 1,$(shell which luac5.1) $(shell which luac51) luac)
37endif
38
39_PREFIX=LUA_CPATH=./src/?$(_SO) LUA_PATH="src/?.lua;./tests/?.lua"
40
41#---
42all: $(_TARGET_SO)
43
44$(_TARGET_SO): src/*.lua src/*.c src/*.h
45 cd src && $(MAKE) LUA=$(LUA) LUAC=$(LUAC)
46
47clean:
48 cd src && $(MAKE) clean
49
50debug:
51 $(MAKE) clean
52 cd src && $(MAKE) LUA=$(LUA) LUAC=$(LUAC) OPT_FLAGS="-O0 -g"
53 @echo ""
54 @echo "** Now, try 'make repetitive' or something and if it crashes, 'gdb $(LUA) ...core file...'"
55 @echo " Then 'bt' for a backtrace."
56 @echo ""
57 @echo " You have enabled core, no? 'ulimit -c unlimited'"
58 @echo " On OS X, core files are under '/cores/'"
59 @echo ""
60
61gdb:
62 @echo "echo *** To start debugging: 'run tests/basic.lua' ***\n\n" > .gdb.cmd
63 $(_PREFIX) gdb -x .gdb.cmd $(LUA)
64
65#--- LuaRocks automated build ---
66#
67rock:
68 cd src && $(MAKE) LUAROCKS=1 CFLAGS="$(CFLAGS)" LIBFLAG="$(LIBFLAG)" LUA=$(LUA) LUAC=$(LUAC)
69
70
71#--- Testing ---
72#
73test:
74 $(MAKE) irayo_recursive
75# $(MAKE) irayo_closure
76 $(MAKE) basic
77 $(MAKE) fifo
78 $(MAKE) keeper
79 $(MAKE) timer
80 $(MAKE) atomic
81 $(MAKE) cyclic
82 $(MAKE) objects
83 $(MAKE) fibonacci
84 $(MAKE) recursive
85
86basic: tests/basic.lua $(_TARGET_SO)
87 $(_PREFIX) $(LUA) $<
88
89#
90# This tries to show out a bug which happens in lane cleanup (multicore CPU's only)
91#
92REP_ARGS=-llanes -e "print'say aaa'; for i=1,10 do print(i) end"
93repetitive: $(_TARGET_SO)
94 for i in 1 2 3 4 5 6 7 8 9 10 a b c d e f g h i j k l m n o p q r s t u v w x y z; \
95 do $(_PREFIX) $(LUA) $(REP_ARGS); \
96 done
97
98repetitive1: $(_TARGET_SO)
99 $(_PREFIX) $(LUA) $(REP_ARGS)
100
101fifo: tests/fifo.lua $(_TARGET_SO)
102 $(_PREFIX) $(LUA) $<
103
104keeper: tests/keeper.lua $(_TARGET_SO)
105 $(_PREFIX) $(LUA) $<
106
107fibonacci: tests/fibonacci.lua $(_TARGET_SO)
108 $(_PREFIX) $(LUA) $<
109
110timer: tests/timer.lua $(_TARGET_SO)
111 $(_PREFIX) $(LUA) $<
112
113atomic: tests/atomic.lua $(_TARGET_SO)
114 $(_PREFIX) $(LUA) $<
115
116cyclic: tests/cyclic.lua $(_TARGET_SO)
117 $(_PREFIX) $(LUA) $<
118
119recursive: tests/recursive.lua $(_TARGET_SO)
120 $(_PREFIX) $(LUA) $<
121
122hangtest: tests/hangtest.lua $(_TARGET_SO)
123 $(_PREFIX) $(LUA) $<
124
125ehynes: tests/ehynes.lua $(_TARGET_SO)
126 $(_PREFIX) $(LUA) $<
127
128#require: tests/require.lua $(_TARGET_SO)
129# $(_PREFIX) $(LUA) $<
130
131objects: tests/objects.lua $(_TARGET_SO)
132 $(_PREFIX) $(LUA) $<
133
134irayo_recursive: tests/irayo_recursive.lua $(_TARGET_SO)
135 $(_PREFIX) $(LUA) $<
136
137irayo_closure: tests/irayo_closure.lua $(_TARGET_SO)
138 $(_PREFIX) $(LUA) $<
139
140finalizer: tests/finalizer.lua $(_TARGET_SO)
141 $(_PREFIX) $(LUA) $<
142
143error-test: tests/error.lua $(_TARGET_SO)
144 $(_PREFIX) $(LUA) $<
145
146#---
147perftest-plain: tests/perftest.lua $(_TARGET_SO)
148 $(MAKE) _perftest ARGS="$< $(N) -plain"
149
150perftest: tests/perftest.lua $(_TARGET_SO)
151 $(MAKE) _perftest ARGS="$< $(N)"
152
153perftest-odd: tests/perftest.lua $(_TARGET_SO)
154 $(MAKE) _perftest ARGS="$< $(N) -prio=+2"
155
156perftest-even: tests/perftest.lua $(_TARGET_SO)
157 $(MAKE) _perftest ARGS="$< $(N) -prio=-2"
158
159#---
160launchtest: tests/launchtest.lua $(_TARGET_SO)
161 $(MAKE) _perftest ARGS="$< $(N)"
162
163_perftest:
164 $(_PREFIX) $(TIME) $(LUA) $(ARGS)
165
166
167#--- Installing ---
168#
169# This is for LuaRocks automatic install, mainly
170#
171# LUA_LIBDIR and LUA_SHAREDIR are used by the .rockspec (don't change the names!)
172#
173DESTDIR=/usr/local
174LUA_LIBDIR=$(DESTDIR)/lib/lua/5.1
175LUA_SHAREDIR=$(DESTDIR)/share/lua/5.1
176
177#
178# AKa 17-Oct: changed to use 'install -m 644' and 'cp -p'
179#
180install: $(_TARGET_SO) src/lanes.lua
181 mkdir -p $(LUA_LIBDIR) $(LUA_SHAREDIR)
182 install -m 644 $(_TARGET_SO) $(LUA_LIBDIR)
183 cp -p src/lanes.lua $(LUA_SHAREDIR)
184
185
186#--- Packaging ---
187#
188# Make a folder of the same name as tgz, good manners (for the manual
189# expander)
190#
191# "make tgz VERSION=yyyymmdd"
192#
193VERSION=
194
195tar tgz:
196ifeq "$(VERSION)" ""
197 echo "Usage: make tar VERSION=x.x"; false
198else
199 $(MAKE) clean
200 -rm -rf $(MODULE)-$(VERSION)
201 mkdir $(MODULE)-$(VERSION)
202 tar c * --exclude=.svn --exclude=.DS_Store --exclude="_*" \
203 --exclude="*.tgz" --exclude="*.rockspec" \
204 --exclude=lanes.dev --exclude="$(MODULE)-*" --exclude=xcode \
205 --exclude="*.obj" --exclude="*.dll" --exclude=timeit.dat \
206 | (cd $(MODULE)-$(VERSION) && tar x)
207 tar czvf $(MODULE)-$(VERSION).tgz $(MODULE)-$(VERSION)
208 rm -rf $(MODULE)-$(VERSION)
209 md5sum $(MODULE)-$(VERSION).tgz
210endif
211
212
213#--- Undocumented ---
214#
215
216# 2.0.1: Running this (instant exit of the main Lua state) occasionally
217# segfaults (1:15 or so on OS X PowerPC G4).
218#
219require: $(_TARGET_SO)
220 $(_PREFIX) $(LUA) -e "require '$(MODULE)'"
221
222run: $(_TARGET_SO)
223 $(_PREFIX) $(LUA) -e "require '$(MODULE)'" -i
224
225echo:
226 @echo $(PROGRAMFILES:C=X)
227
228.PROXY: all clean test require debug _nodemo _notest
229
diff --git a/README b/README
new file mode 100644
index 0000000..b29d43d
--- /dev/null
+++ b/README
@@ -0,0 +1,106 @@
1
2=====================
3 Usage on Windows:
4=====================
5
6For once, Win32 thread prioritazion scheme is actually a good one, and
7it works. :) Windows users, feel yourself as VIP citizens!!
8
9-------------------
10 Windows / MSYS:
11-------------------
12
13On MSYS, 'stderr' output seems to be buffered. You might want to make
14it auto-flush, to help you track & debug your scripts. Like this:
15
16 io.stderr:setvbuf "no"
17
18Even after this, MSYS insists on linewise buffering; it will flush at
19each newline only.
20
21
22===================
23 Usage on Linux:
24===================
25
26Linux NTPL 2.5 (Ubuntu 7.04) was used in the testing of Lua Lanes.
27
28This document (http://www.net.in.tum.de/~gregor/docs/pthread-scheduling.html)
29describes fairly well, what (all) is wrong with Linux threading, even today.
30
31For other worthy links:
32 http://kerneltrap.org/node/6080
33 http://en.wikipedia.org/wiki/Native_POSIX_Thread_Library
34
35In short, you cannot use thread prioritation in Linux. Unless you run as
36root, and I _truly_ would not recommend that. Lobby for yet-another thread
37implementation for Linux, and mail -> akauppi@gmail.com about it. :)
38
39
40======================
41 Usage on Mac OS X:
42======================
43
44No real problems in OS X, _once_ everything is set up right...
45
46In short, have your Lua core compiled with LUA_USE_DLOPEN and LUA_USE_POSIX
47instead of the (default as of 5.1) LUA_DL_DYLD and LUA_USE_MACOSX. This is
48crucial to have each module loaded only once (even if initialized separately
49for each thread) and for the static & global variables within the modules to
50actually be process-wide. Lua Lanes cannot live without (and hopefully,
51LUA_DL_DYLD is long gone by Lua 5.2)...
52
53Another issue is making sure you only have _one_ Lua core. Your 'lua' binary
54must link dynamically to a .dylib, it must _not_ carry a personal copy of Lua
55core with itself. If it does, you will gain many mysterious malloc errors
56when entering multithreading realm.
57
58<<
59lua-xcode2(9473,0xa000ed88) malloc: *** Deallocation of a pointer not malloced: 0xe9fc0; This could be a double free(), or free() called with the middle of an allocated block; Try setting environment variable MallocHelp to see tools to help debug
60<<
61
62rm lua.o luac.o
63gcc -dynamiclib -install_name /usr/local/lib/liblua.5.1.dylib \
64 -compatibility_version 5.1 -current_version 5.1.2 \
65 -o liblua.5.1.2.dylib *.o
66
67gcc -fno-common -DLUA_USE_POSIX -DLUA_USE_DLOPEN -DLUA_USE_READLINE -lreadline -L. -llua.5.1.2 lua.c -o lua
68
69That should be it. :)
70
71Fink 'lua51' packaging has the necessary changes since 5.1.2-3.
72
73
74=====================
75 Usage on FreeBSD:
76=====================
77
78Unlike in Linux, also the Lua engine used with Lanes needs to be compiled with
79'-lpthread'. Otherwise, the following malloc errors are received:
80
81 <<
82 lua in free(): warning: recursive call
83 PANIC: unprotected error in call to Lua API (not enough memory)
84 <<
85
86Here are the Lua compilation steps that proved to work (FreeBSD 6.2 i386):
87
88 gmake freebsd
89 rm lua.o luac.o liblua.a
90 gcc -shared -lm -Wl,-E -o liblua.5.1.2.so *.o
91 gcc -O2 -Wall -DLUA_USE_LINUX -lm -Wl,-E -lpthread -lreadline -L. -llua.5.1.2 lua.c -o lua
92
93To compile Lanes, use 'gmake' or simply:
94
95 cc -O2 -Wall -llua.5.1.2 -lpthread -shared -o out/bsd-x86/lua51-lanes.so \
96 -DGLUA_LUA51 gluax.c lanes.c
97
98To place Lua into ~/local, set the following environment variables (this is
99just a reminder for myself...):
100
101 export CPATH=.../local/include
102 export LIBRARY_PATH=.../local/lib
103 export LD_LIBRARY_PATH=.../local/lib
104
105
106(end)
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..7c4c3d8
--- /dev/null
+++ b/TODO
@@ -0,0 +1,4 @@
1
2TODO:
3
4- Testing Lane killing (not cancellation, but actual killing)
diff --git a/dist.cmake b/dist.cmake
new file mode 100644
index 0000000..95928b2
--- /dev/null
+++ b/dist.cmake
@@ -0,0 +1,130 @@
1# LuaDist CMake utility library.
2# Provides variables and utility functions common to LuaDist CMake builds.
3#
4# Copyright (C) 2007-2010 LuaDist.
5# by David Manura, Peter Drahos
6# Redistribution and use of this file is allowed according to the terms of the MIT license.
7# For details see the COPYRIGHT file distributed with LuaDist.
8# Please note that the package source code is licensed under its own license.
9
10# Few convinence settings
11SET (CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS true)
12SET (CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_MODULE_PATH})
13
14# Where to install module parts:
15set(INSTALL_BIN bin CACHE PATH "Where to install binaries to.")
16set(INSTALL_LIB lib CACHE PATH "Where to install libraries to.")
17set(INSTALL_INC include CACHE PATH "Where to install headers to.")
18set(INSTALL_ETC etc CACHE PATH "Where to store configuration files")
19set(INSTALL_LMOD share/lua/lmod CACHE PATH "Directory to install Lua modules.")
20set(INSTALL_CMOD share/lua/cmod CACHE PATH "Directory to install Lua binary modules.")
21set(INSTALL_DATA share/${PROJECT_NAME} CACHE PATH "Directory the package can store documentation, tests or other data in.")
22set(INSTALL_DOC ${INSTALL_DATA}/doc CACHE PATH "Recommended directory to install documentation into.")
23set(INSTALL_EXAMPLE ${INSTALL_DATA}/example CACHE PATH "Recommended directory to install examples into.")
24set(INSTALL_TEST ${INSTALL_DATA}/test CACHE PATH "Recommended directory to install tests into.")
25set(INSTALL_FOO ${INSTALL_DATA}/etc CACHE PATH "Where to install additional files")
26
27
28# In MSVC, prevent warnings that can occur when using standard libraries.
29if(MSVC)
30 add_definitions(-D_CRT_SECURE_NO_WARNINGS)
31endif(MSVC)
32
33# Adds Lua shared library module target `_target`.
34# Additional sources to build the module are listed after `_target`.
35macro(add_lua_module _target)
36 find_package(Lua51 REQUIRED)
37 include_directories(${LUA_INCLUDE_DIR}) #2DO: somehow apply only to _target?
38
39 add_library(${_target} MODULE ${ARGN})
40 set_target_properties(${_target} PROPERTIES PREFIX "")
41 target_link_libraries(${_target} ${LUA_LIBRARY})
42
43 IF(WIN32)
44 set_target_properties(${_target} PROPERTIES LINK_FLAGS "-Wl,--enable-auto-import")
45 ENDIF()
46
47endmacro(add_lua_module)
48
49# Runs Lua script `_testfile` under CTest tester.
50# Optional argument `_testcurrentdir` is current working directory to run test under
51# (defaults to ${CMAKE_CURRENT_BINARY_DIR}).
52# Both paths, if relative, are relative to ${CMAKE_CURRENT_SOURCE_DIR}.
53# Under LuaDist, set test=true in config.lua to enable testing.
54macro(add_lua_test _testfile)
55 include(CTest)
56 if(BUILD_TESTING)
57 find_program(LUA NAMES lua lua.bat)
58 get_filename_component(TESTFILEABS ${_testfile} ABSOLUTE)
59 get_filename_component(TESTFILENAME ${_testfile} NAME)
60 get_filename_component(TESTFILEBASE ${_testfile} NAME_WE)
61
62 # Write wrapper script.
63 set(TESTWRAPPER ${CMAKE_CURRENT_BINARY_DIR}/${TESTFILENAME})
64 set(TESTWRAPPERSOURCE
65"package.path = '${CMAKE_CURRENT_BINARY_DIR}/?.lua\;${CMAKE_CURRENT_SOURCE_DIR}/?.lua\;' .. package.path
66package.cpath = '${CMAKE_CURRENT_BINARY_DIR}/?.so\;${CMAKE_CURRENT_BINARY_DIR}/?.dll\;' .. package.cpath
67return dofile '${TESTFILEABS}'
68" )
69 if(${ARGC} GREATER 1)
70 set(_testcurrentdir ${ARGV1})
71 get_filename_component(TESTCURRENTDIRABS ${_testcurrentdir} ABSOLUTE)
72 set(TESTWRAPPERSOURCE
73"require 'lfs'
74lfs.chdir('${TESTCURRENTDIRABS}')
75${TESTWRAPPERSOURCE}")
76 endif()
77 FILE(WRITE ${TESTWRAPPER} ${TESTWRAPPERSOURCE})
78
79 add_test(${TESTFILEBASE} ${LUA} ${TESTWRAPPER})
80 endif(BUILD_TESTING)
81
82 # see also http://gdcm.svn.sourceforge.net/viewvc/gdcm/Sandbox/CMakeModules/UsePythonTest.cmake
83endmacro(add_lua_test)
84
85# Converts Lua source file `_source` to binary string embedded in C source
86# file `_target`. Optionally compiles Lua source to byte code (not available
87# under LuaJIT2, which doesn't have a bytecode loader). Additionally, Lua
88# versions of bin2c [1] and luac [2] may be passed respectively as additional
89# arguments.
90#
91# [1] http://lua-users.org/wiki/BinToCee
92# [2] http://lua-users.org/wiki/LuaCompilerInLua
93function(add_lua_bin2c _target _source)
94 find_program(LUA NAMES lua lua.bat)
95 execute_process(COMMAND ${LUA} -e "string.dump(function()end)" RESULT_VARIABLE _LUA_DUMP_RESULT ERROR_QUIET)
96 if (NOT ${_LUA_DUMP_RESULT})
97 SET(HAVE_LUA_DUMP true)
98 endif()
99 message("-- string.dump=${HAVE_LUA_DUMP}")
100
101 if (ARGV2)
102 get_filename_component(BIN2C ${ARGV2} ABSOLUTE)
103 set(BIN2C ${LUA} ${BIN2C})
104 else()
105 find_program(BIN2C NAMES bin2c bin2c.bat)
106 endif()
107 if (HAVE_LUA_DUMP)
108 if (ARGV3)
109 get_filename_component(LUAC ${ARGV3} ABSOLUTE)
110 set(LUAC ${LUA} ${LUAC})
111 else()
112 find_program(LUAC NAMES luac luac.bat)
113 endif()
114 endif (HAVE_LUA_DUMP)
115 message("-- bin2c=${BIN2C}")
116 message("-- luac=${LUAC}")
117
118 get_filename_component(SOURCEABS ${_source} ABSOLUTE)
119 if (HAVE_LUA_DUMP)
120 get_filename_component(SOURCEBASE ${_source} NAME_WE)
121 add_custom_command(
122 OUTPUT ${_target} DEPENDS ${_source}
123 COMMAND ${LUAC} -o ${CMAKE_CURRENT_BINARY_DIR}/${SOURCEBASE}.lo ${SOURCEABS}
124 COMMAND ${BIN2C} ${CMAKE_CURRENT_BINARY_DIR}/${SOURCEBASE}.lo ">${_target}" )
125 else()
126 add_custom_command(
127 OUTPUT ${_target} DEPENDS ${SOURCEABS}
128 COMMAND ${BIN2C} ${_source} ">${_target}" )
129 endif()
130endfunction(add_lua_bin2c)
diff --git a/dist.info b/dist.info
new file mode 100644
index 0000000..a6553b0
--- /dev/null
+++ b/dist.info
@@ -0,0 +1,14 @@
1--- This file is part of LuaDist project
2
3name = "lanes"
4version = "2.0.3"
5
6desc = "Lanes is a lightweight, native, lazy evaluating multithreading library for Lua 5.1."
7author = "Asko Kauppi"
8license = "MIT"
9url = "http://luaforge.net/projects/lanes"
10maintainer = "Peter Kapec"
11
12depends = {
13 "lua ~> 5.1"
14}
diff --git a/docs/Lua multithreading choices.graffle b/docs/Lua multithreading choices.graffle
new file mode 100644
index 0000000..2bd4cb4
--- /dev/null
+++ b/docs/Lua multithreading choices.graffle
Binary files differ
diff --git a/docs/Lua multithreading choices.svg b/docs/Lua multithreading choices.svg
new file mode 100644
index 0000000..8a09698
--- /dev/null
+++ b/docs/Lua multithreading choices.svg
@@ -0,0 +1,15 @@
1<?xml version="1.0"?>
2<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
3<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="0 0 813.60004 566.95" width="813.60004pt" height="566.95pt"><metadata xmlns:dc="http://purl.org/dc/elements/1.1/"><dc:date>2008-07-25 19:33Z</dc:date><!-- Produced by OmniGraffle Professional 4.2.1 --></metadata><defs><filter id="Shadow" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" result="blur" stdDeviation="3.488"/><feOffset in="blur" result="offset" dx="0" dy="4"/><feFlood flood-color="black" flood-opacity=".75" result="flood"/><feComposite in="flood" in2="offset" operator="in"/></filter><font-face font-family="Helvetica" font-size="18" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="555.55554" cap-height="722.22223" ascent="770.01953" descent="-229.98047" font-weight="500"><!--NSCTFontDescriptor &lt;0x7988130&gt; = {
4 NSFontNameAttribute = Helvetica;
5 NSFontSizeAttribute = 18;
6}--><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="black"><g><path d="M 8 0 L 0 -3 L 0 3 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><font-face font-family="Courier" font-size="18" units-per-em="1000" underline-position="-178.22266" underline-thickness="57.617188" slope="0" x-height="444.44446" cap-height="611.11115" ascent="753.90625" descent="-246.09375" font-weight="500"><!--NSCTFontDescriptor &lt;0x79dbbf0&gt; = {
7 NSFontNameAttribute = Courier;
8 NSFontSizeAttribute = 18;
9}--><font-face-src><font-face-name name="Courier"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="16" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="531.25" cap-height="718.75" ascent="770.01953" descent="-229.98047" font-weight="500"><!--NSCTFontDescriptor &lt;0x79dde00&gt; = {
10 NSFontNameAttribute = Helvetica;
11 NSFontSizeAttribute = 16;
12}--><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><font-face font-family="Times" font-size="24" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="458.33334" cap-height="666.6667" ascent="750" descent="-250" font-weight="500"><!--NSCTFontDescriptor &lt;0x79df960&gt; = {
13 NSFontNameAttribute = "Times-Roman";
14 NSFontSizeAttribute = 24;
15}--><font-face-src><font-face-name name="Times-Roman"/></font-face-src></font-face></defs><g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"><title>Canvas 1</title><rect fill="white" width="813.60004" height="566.95"/><g><title>Layer 1</title><g><use xl:href="#id4_Graphic" filter="url(#Shadow)"/><use xl:href="#id2_Graphic" filter="url(#Shadow)"/><use xl:href="#id33_Graphic" filter="url(#Shadow)"/><use xl:href="#id36_Graphic" filter="url(#Shadow)"/></g><g id="id4_Graphic"><path d="M 162.15001 63.999992 L 484.84998 63.999992 C 523.0208 63.999992 554 94.02719 554 131.02499 C 554 168.0228 523.0208 198.04999 484.84998 198.04999 L 162.15001 198.04999 C 123.9792 198.04999 93 168.0228 93 131.02499 C 93 94.02719 123.9792 63.999992 162.15001 63.999992" fill="#ffff51"/><path d="M 162.15001 63.999992 L 484.84998 63.999992 C 523.0208 63.999992 554 94.02719 554 131.02499 C 554 168.0228 523.0208 198.04999 484.84998 198.04999 L 162.15001 198.04999 C 123.9792 198.04999 93 168.0228 93 131.02499 C 93 94.02719 123.9792 63.999992 162.15001 63.999992" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(144.10001 120.024994)" fill="black"><tspan font-family="Helvetica" font-size="18" font-weight="500" x="154.878525" y="18" textLength="49.04297">Lanes</tspan></text></g><line x1="67" y1="508" x2="733.09998" y2="508" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="67" y1="508" x2="67" y2="68.90001" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(613 516)" fill="black"><tspan font-family="Courier" font-size="18" font-weight="500" x="0" y="18" textLength="118.819336">distributed</tspan></text><text transform="translate(104.9992 516)" fill="black"><tspan font-family="Courier" font-size="18" font-weight="500" x="0" y="18" textLength="118.819336">shared data</tspan></text><text transform="translate(323.5 516)" fill="black"><tspan font-family="Courier" font-size="18" font-weight="500" x="0" y="18" textLength="140.42285">isolated data</tspan></text><text transform="translate(21.501301 355) rotate(-90)" fill="black"><tspan font-family="Courier" font-size="18" font-weight="500" x="0" y="18" textLength="108.01758">coroutines</tspan></text><text transform="translate(21.501297 187.5) rotate(-90)" fill="black"><tspan font-family="Courier" font-size="18" font-weight="500" x="0" y="18" textLength="108.01758">OS threads</tspan></text><g id="id2_Graphic"><ellipse cx="164.4995" cy="312.5" rx="71.49962" ry="67.025116" fill="white"/><ellipse cx="164.4995" cy="312.5" rx="71.49962" ry="67.025116" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(112.299896 293.50003)" fill="black"><tspan font-family="Helvetica" font-size="16" font-weight="500" x="38.85194" y="15" textLength="26.695312">Lua</tspan><tspan font-family="Helvetica" font-size="16" font-weight="500" x="15.28944" y="34" textLength="73.820312">coroutines</tspan></text></g><text transform="translate(21.501301 504) rotate(-90)" fill="black"><tspan font-family="Courier" font-size="18" font-weight="500" x="0" y="18" textLength="97.21582">core mods</tspan></text><g id="id33_Graphic"><ellipse cx="164.49921" cy="441.975" rx="71.49961" ry="67.02514" fill="#ff7695"/><ellipse cx="164.49921" cy="441.975" rx="71.49961" ry="67.02514" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(112.2996 432.47504)" fill="black"><tspan font-family="Helvetica" font-size="16" font-weight="500" x="13.504284" y="15" textLength="77.390625">LuaThread</tspan></text></g><text transform="translate(518 26)" fill="black"><tspan font-family="Times" font-size="24" font-weight="500" x="0" y="23" textLength="262.58203">Lua multithreading choices</tspan></text><g id="id36_Graphic"><path d="M 345.15002 245.475 L 667.84998 245.475 C 706.0208 245.475 737 275.5022 737 312.5 C 737 349.4978 706.0208 379.525 667.84998 379.525 L 345.15002 379.525 C 306.97919 379.525 276 349.4978 276 312.5 C 276 275.5022 306.97919 245.475 345.15002 245.475" fill="#69b1ff"/><path d="M 345.15002 245.475 L 667.84998 245.475 C 706.0208 245.475 737 275.5022 737 312.5 C 737 349.4978 706.0208 379.525 667.84998 379.525 L 345.15002 379.525 C 306.97919 379.525 276 349.4978 276 312.5 C 276 275.5022 306.97919 245.475 345.15002 245.475" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(327.1 301.5)" fill="black"><tspan font-family="Helvetica" font-size="18" font-weight="500" x="119.8629" y="18" textLength="119.07422">ConcurrentLua</tspan></text></g></g></g></svg>
diff --git a/docs/comparison.html b/docs/comparison.html
new file mode 100644
index 0000000..84ef9ca
--- /dev/null
+++ b/docs/comparison.html
@@ -0,0 +1,297 @@
1<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
2<!--
3 Comparison of Lua Lanes with other approaches
4-->
5
6<html>
7<head>
8 <meta name="description" content="Lua Lanes - Comparison" />
9 <meta name="keywords" content="Lua, Library, Multithreading, Threads, Rocks" />
10
11 <title>Lua Lanes - Comparison</title>
12</head>
13
14<body>
15
16<!-- comparisons +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
17<hr/>
18<h2 id="comparisons">Comparisons to other threading kits</h2>
19
20<p>
21A short comparison of Lanes with other existing Lua multithreading kits.
22</p>
23
24<table><tr><td width=40>
25 <td bgcolor="#ffffe0">
26<pre>
27=============
28 Lua Lanes
29=============
30
31With the introduction of Lindas (Jun-2008), Lua Lanes simplifies its API while
32simultaneously adding more power and speed.
33
34Pros:
35 - regular Lua 5.1 module
36 - completely separate Lua states, one per OS thread
37 - message passing, or shared data using Lindas
38 - no application level locking, ever
39 - scales well, up to 1000's of threads
40 - priorities (-2..+2) for launched threads
41 - threads are cancellable (with complete cleanup)
42 - timeouts on all pending operations
43 - thread contents are given as regular Lua functions; syntax checked early on,
44 syntax highlighting works
45 - standard libraries opened to subthreads can be granually selected
46 - fast stack-to-stack copies, via hidden "keeper states". No serialization needed.
47 - protects calls to 'require', allowing wide compatibility with existing
48 modules (and all, with minor changes)
49
50Cons:
51 - requires OS threads
52 - not utilizing network parallelism (all threads on one CPU)
53
54Sample:
55<<
56 require "lanes"
57
58 local function calculate(a,b,c)
59 if not a then
60 error "sample error; propagated to main lane when reading results"
61 end
62 return a+b+c
63 end
64
65 local h1= lanes.new(calculate)(1,2,3)
66 local h2= lanes.new(calculate)(10,20,30)
67 local h3= lanes.new(calculate)(100,200,300)
68
69 print( h1[1], h2[1], h3[1] ) -- pends for the results, or propagates error
70<<
71
72
73==================
74 Lua coroutines (by Lua authors)
75==================
76
77<A HREF="http://www.lua.org/manual/5.1/manual.html#2.11">http://www.lua.org/manual/5.1/manual.html#2.11</A>
78<A HREF="http://lua-users.org/wiki/CoroutinesTutorial">http://lua-users.org/wiki/CoroutinesTutorial</A>
79
80Lua coroutines is an integral part of Lua 5 itself. It is listed here, since
81it should be the _first_ multitasking mechanism to consider. It can also be
82used together with Lanes, or the other mechanisms listed below.
83
84Coroutines are very usable in provider/consumer situations, allowing you to
85queue Lua functions on an as-needed dataflow basis with each other.
86
87Pros:
88 - works with plain Lua (no extensions)
89 - works on any platform
90 - lightweight (no OS level threading or locking involved)
91
92Cons:
93 - co-operative, meaning your code will need to decide, who gets to run
94 - does not utilize multiple CPUs/cores
95
96Sample:
97
98 ..TBD: sample code doing the "child" "parent" output here (see below)..
99
100
101=============
102 LuaThread (by Diego Nehab)
103=============
104
105<A HREF="http://www.cs.princeton.edu/~diego/professional/luathread/">http://www.cs.princeton.edu/~diego/professional/luathread/</A>
106
107LuaThread provides thread creation, mutexes, condition variables, and inter-
108thread queues to the Lua scripts. It takes a C-kind of approach, where Lua
109globals are shared by the threads running, and need therefore to be guarded
110against multithreading conflicts.
111
112Whether this is exactly what you want, or whether a more loosely implemented
113multithreading (s.a. Lanes) would be better, is up to you. One can argue that
114a loose implementation is easier for the developer, since no application level
115lockings need to be considered.
116
117Pros:
118 - no marshalling overhead, since threads share the same Lua state
119
120Cons:
121 - requires a modified Lua core
122 - application level locking required
123
124Sample:
125&lt;&lt;
126 local function flood(output, word)
127 while 1 do
128 output:lock()
129 io.write(word, ", ")
130 output:unlock()
131 end
132 end
133
134 local output = thread.newmutex()
135 thread.newthread(flood, {output, "child"})
136 flood(output, "parent")
137&lt;&lt;
138
139
140=============
141 Lua Rings (by Roberto Ierusalimschy &amp; Tom&aacute;s Guisasola)
142=============
143
144<A HREF="http://www.keplerproject.org/rings/">http://www.keplerproject.org/rings/</A>
145
146".. library which provides a way to create new Lua states from within Lua.
147It also offers a simple way to communicate between the creator (master) and
148the created (slave) states."
149
150".. master can execute code in any of its slaves but each slave only has
151access to its master (or its own slaves)."
152
153Rings offers separate Lua states, but no multithreading. This makes it simple,
154but it won't use more than one CPU core. Other differences include:
155
156 - opens all Lua standard libraries for subthreads
157 (Lanes opens the needed ones)
158
159 - marshalls numbers, strings, booleans, userdata
160 (Lanes marshalls also non-cyclic tables)
161
162 - "remotedostring" allows executing code in the master state
163 (Lanes does _not_ allow subthreads to trouble/modify master automatically,
164 to allow effective sandboxing. The same can be achieved by sending code
165 between the threads, but master needs to explicitly allow this = receive
166 a function and execute it)
167
168 - offers "Stable, a very simple API to manage a shared table at the master
169 state"
170 (Lanes 2008 offers keeper tables)
171
172Pros:
173 - "offers Stable, a very simple API to manage a shared table at the master
174 state"
175
176Cons:
177 - thread contents defined as strings, not Lua source as such; does not
178 give syntax check at file parsing, does not allow syntax highlight
179
180Sample:
181&lt;&lt;
182 require"rings"
183 S = rings.new ()
184
185 data = { 12, 13, 14, }
186 print (S:dostring ([[
187 aux = {}
188 for i, v in ipairs (arg) do
189 table.insert (aux, 1, v)
190 end
191 return unpack (aux)]], unpack (data))) -- true, 14, 13, 12
192
193 S:close ()
194&lt;&lt;
195
196
197==========================
198 Helper Threads Toolkit (by Javier Guerra G.)
199==========================
200
201<A HREF="http://luaforge.net/projects/helper-threads/">http://luaforge.net/projects/helper-threads/</A>
202
203"Provides a consistent framework to write non-blocking C libraries, with a Lua
204interface for starting tasks and managing the Futures, Queues and Threads."
205
206This seems like a companion of the "occasional threading" model (see below);
207Lua side is kept clear of multithreading, while C side can be "spawn" off to
208do things on the background.
209
210Pros:
211 - provides an "update" mechanism, allowing the (C) thread and controlling
212 Lua to interact during execution of the thread
213 - ...
214
215Cons:
216 - thread is "only for C code and it can't touch or access the Lua state",
217 in other words there is no Lua-side multithreading concept (by design)
218
219
220========================
221 Occasional threading (by Russ Cox)
222========================
223
224<A HREF="http://lua-users.org/lists/lua-l/2006-11/msg00368.html">http://lua-users.org/lists/lua-l/2006-11/msg00368.html</A>
225
226".. able to have certain C calls run in parallel with the [Lua] VM, but
227otherwise keep the VM single-threaded."
228
229That pretty much says it all.
230
231Pros:
232 - simple, yet providing for the "occasional" need to run really multithreaded
233 - can be made into a loadable module (says the message)
234
235Cons:
236 - only for occasional usage, the programming paradigm is still essentially
237 singlethreaded (by definition)
238 - not a real project, just a message on the Lua list (but a good one!)
239
240
241=================
242 ConcurrentLua
243=================
244
245<A TARGET="_blank" HREF="http://concurrentlua.luaforge.net/index.html"
246>http://concurrentlua.luaforge.net/index.html</A>
247
248ConcurrentLua is based on the Lua model for concurrency, namely coroutines, and
249extends this model by providing message-passing primitives.
250
251".. implementation of the share-nothing asynchronous message-passing model"
252
253".. process can check its mailbox for new messages at any time, and if there
254are any, they can be read in the order of arrival."
255
256".. processes in the system are implemented with Lua coroutines"
257
258".. still based on the cooperative multithreading model that Lua uses"
259
260Recent, released on 21 June 2008.
261
262Pros:
263 - From ground up designed for distributed computing (multiple computers,
264 not only CPU cores)
265 - Does not require a pre-emptive operating system
266
267Cons:
268 - Serialization must degrade raw performance in one-computer scenarios
269 (vs. stack-to-stack copying ala Lanes)
270 - Depends on LuaSocket and Copas modules.
271 - Each thread has a single mailbox tied to it (vs. separating threads and
272 connection resources)
273
274</pre>
275</td></tr></table>
276
277
278<!-- footnotes +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
279<hr/>
280
281<p>For feedback, questions and suggestions:
282<UL>
283 <li><A HREF="http://luaforge.net/projects/lanes">Lanes @ LuaForge</A></li>
284 <li><A HREF="mailto:akauppi@gmail.com">the author</A></li>
285</UL>
286</p>
287
288<!--
289<font size="-1">
290<p>
2911) ...
292</p>
293</font>
294-->
295
296</body>
297</html>
diff --git a/docs/index.html b/docs/index.html
new file mode 100644
index 0000000..956e691
--- /dev/null
+++ b/docs/index.html
@@ -0,0 +1,951 @@
1<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
2<!--
3 Documentation for Lua Lanes
4-->
5
6<html>
7<head>
8 <meta name="description" content="Lua Lanes - multithreading in Lua" />
9 <meta name="keywords" content="Lua, Library, Multithreading, Threads, Rocks" />
10
11 <title>Lua Lanes - multithreading in Lua</title>
12</head>
13
14<body>
15<div class="header">
16<hr />
17
18<center>
19<table summary="Lua logo">
20 <tbody>
21 <tr>
22 <td align="center">
23 <a href="http://www.lua.org">
24 <img src="http://akauppi.googlepages.com/multi.png" alt="Lua" align="middle" border="0" height="120" width="128" />
25 <img src="http://akauppi.googlepages.com/multi.png" alt="Lua" align="middle" border="0" height="120" width="128" />
26 <img src="http://akauppi.googlepages.com/multi.png" alt="Lua" align="middle" border="0" height="120" width="128" />
27 <img src="http://akauppi.googlepages.com/multi.png" alt="Lua" align="middle" border="0" height="120" width="128" />
28 <img src="http://akauppi.googlepages.com/multi.png" alt="Lua" align="middle" border="0" height="120" width="128" />
29 </a></td>
30 </tr>
31 <tr>
32 <td align="center" valign="top"><h1>Lua Lanes - multithreading in Lua</h1>
33 </td>
34 </tr>
35 </tbody>
36</table>
37
38<p class="bar">
39 <a href="#description">Description</a> &middot;
40 <a href="#systems">Supported systems</a> &middot;
41 <a href="#installing">Building and Installing</a>
42</p><p class="bar">
43 <a href="#creation">Creation</a> &middot;
44 <a href="#status">Status</a> &middot;
45 <a href="#results">Results and errors</a>
46</p><p class="bar">
47 <a href="#cancelling">Cancelling</a> &middot;
48 <a href="#finalizers">Finalizers</a> &middot;
49 <a href="#lindas">Lindas</a> &middot;
50 <a href="#timers">Timers</a> &middot;
51 <a href="#locks">Locks etc.</a>
52</p><p class="bar">
53 <a href="#other">Other issues</a> &middot;
54 <a href="#changes">Change log</a>
55 <!-- ... -->
56
57<p><br/><font size="-1"><i>Copyright &copy; 2007-08 Asko Kauppi. All rights reserved.</i>
58 <br>Lua Lanes is published under the same <A HREF="http://en.wikipedia.org/wiki/MIT_License">MIT license</A> as Lua 5.1.
59 </p><p>This document was revised on 23-Jan-09, and applies to version 2.0.3.
60</font></p>
61
62</center>
63</div>
64
65
66<!-- description +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
67<hr/>
68<h2 id="description">Description</h2>
69
70<p>Lua Lanes is a Lua extension library providing
71 the possibility to run multiple Lua states in parallel. It is intended to
72 be used for optimizing performance on multicore CPU's and to study ways to make Lua programs naturally parallel to begin with.
73</p><p>
74 Lanes is included into your software by the regular
75 <tt>require "lanes"</tt> method. No C side programming is needed; all APIs are Lua side, and most existing extension modules should
76 work seamlessly together with the multiple lanes.
77</p><p>
78 See <A HREF="comparison.html">comparison</A> of Lua Lanes with other Lua multithreading solutions.
79</p><p>
80 <h3>Features:</h3>
81
82 <ul>
83 <li>Lanes have separated data, by default. Shared data is possible with Linda objects.
84 </li>
85 <li>Communications is separate of threads, using Linda objects.
86 </li>
87 <li>Data passing uses fast inter-state copies (no serialization required)</li>
88 </li>
89 <li>"Deep userdata" concept, for sharing userdata over multiple lanes
90 </li>
91 <li>Millisecond level timers, integrated with the Linda system.
92 </li>
93 <li>Threads can be given priorities -2..+2 (default is 0).
94 </li>
95 <li>Lanes are cancellable, with proper cleanup.
96 </li>
97 <li>No application level locking - ever!
98 </li>
99 </ul>
100
101
102<h3>Limitations:</h3>
103
104 <ul><li>coroutines are not passed between states
105 </li>
106 <li>sharing full userdata between states needs special C side
107 preparations (-&gt; <A HREF="#deep_userdata">deep userdata</A>)
108 </li>
109 <li>network level parallelism not included
110 </li>
111 </ul>
112</p>
113
114
115<!-- systems +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
116<hr/>
117<h2 id="systems">Supported systems</h2>
118
119<p>Lua Lanes supports the following operating systems:
120
121 <ul>
122 <li>Mac OS X PowerPC / Intel (10.4 and later)</li>
123 <li>Linux x86</li>
124 <li>Windows 2000/XP and later <font size="-1">(MinGW or Visual C++ 2005/2008)</font></li>
125<!--
126 Other OS'es here once people help test them. (and the tester's name)
127
128 Win64, BSD, Linux x64, Linux embedded, QNX, Solaris, ...
129-->
130 </ul>
131
132 <p>The underlying threading code can be compiled either towards Win32 API
133 or <a TARGET="_blank" HREF="http://en.wikipedia.org/wiki/POSIX_Threads">Pthreads</a>. Unfortunately, thread prioritation under Pthreads is a JOKE,
134 requiring OS specific tweaks and guessing undocumented behaviour. Other
135 features should be portable to any modern platform.
136 </p>
137</p>
138
139
140<!-- installing +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
141<hr/>
142<h2 id="installing">Building and Installing</h2>
143
144<p>Lua Lanes is built simply by <tt>make</tt> on the supported platforms
145(<tt>make-vc</tt> for Visual C++). See <tt>README</tt> for system specific
146details and limitations.
147</p>
148
149<p>To install Lanes, all you need are the <tt>lanes.lua</tt> and <tt>lua51-lanes.so|dll</tt>
150files to be reachable by Lua (see LUA_PATH, LUA_CPATH).
151
152Or use <A HREF="http://www.luarocks.org" TARGET="_blank">Lua Rocks</A> package management.
153</p>
154
155<pre>
156 > luarocks search lanes
157 ... output listing Lua Lanes is there ...
158
159 > luarocks install lanes
160 ... output ...
161</pre>
162
163
164<!-- launching +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
165<hr/>
166<h2 id="creation">Creation</h2>
167
168<p>The following sample shows preparing a function for parallel calling, and
169calling it with varying arguments. Each of the two results is calculated in
170a separate OS thread, parallel to the calling one. Reading the results
171joins the threads, waiting for any results not already there.
172</p>
173
174<table border=1 bgcolor="#FFFFE0" width=500><tr><td>
175<pre>
176 require "lanes"
177
178 f= lanes.gen( function(n) return 2*n end )
179 a= f(1)
180 b= f(2)
181
182 print( a[1], b[1] ) -- 2 4
183</pre>
184</table>
185
186<p>
187<table border=1 bgcolor="#E0E0FF" cellpadding=10><tr><td>
188 <code>func= lanes.gen( [libs_str | opt_tbl [, ...],] lane_func )
189 <br/><br/>
190 lane_h= func( ... )</code>
191</table>
192</p>
193</p><p>
194 The function returned by <tt>lanes.gen()</tt> is a "generator" for
195 launching any number of lanes. They will share code, options, initial globals,
196 but the particular arguments may vary. Only calling the generator function
197 actually launches a lane, and provides a handle for controlling it.
198<!--
199</p>
200<p>This prepares <tt>lane_func</tt> to be called in parallel. It does not yet start
201anything, but merely returns a <i>generator function</i> that can be called
202any number of times, with varying parameters. Each call will spawn a new lane.
203-->
204</p><p>
205Lanes automatically copies upvalues over to the new lanes, so you
206need not wrap all the required elements into one 'wrapper' function. If
207<tt>lane_func</tt> uses some local values, or local functions, they will be there
208also in the new lanes.
209</p><p>
210 <code>libs_str</code> defines the standard libraries made available to the
211 new Lua state:
212 <table>
213 <tr><td/><td>(nothing)</td><td>no standard libraries (default)</td></tr>
214 <tr><td width=40><td><tt>"base"</tt> or <tt>""</tt></td>
215 <td>root level names, <tt>print</tt>, <tt>assert</tt>, <tt>unpack</tt> etc.</td></tr>
216 <tr><td/><td><tt>"coroutine"</tt></td><td><tt>coroutine.*</tt> namespace <font size="-1">(part of base in Lua 5.1)</font></td></tr>
217 <tr><td/><td><tt>"debug"</tt></td><td><tt>debug.*</tt> namespace</td></tr>
218 <tr><td/><td><tt>"io"</tt></td><td><tt>io.*</tt> namespace</td></tr>
219 <tr><td/><td><tt>"math"</tt></td><td><tt>math.*</tt> namespace</td></tr>
220 <tr><td/><td><tt>"os"</tt></td><td><tt>os.*</tt> namespace</td></tr>
221 <tr><td/><td><tt>"package"</tt></td><td><tt>package.*</tt> namespace and <tt>require</tt></td></tr>
222 <tr><td/><td><tt>"string"</tt></td><td><tt>string.*</tt> namespace</td></tr>
223 <tr><td/><td><tt>"table"</tt></td><td><tt>table.*</tt> namespace</td></tr>
224 <br/>
225 <tr><td/><td><tt>"*"</tt></td><td>all standard libraries</td></tr>
226 </table>
227
228</p><p>
229 Initializing the standard libs takes a bit of time at each lane invocation.
230 This is the main reason why "no libraries" is the default.
231</p><p>
232
233 <code>opt_tbl</code> is a collection of named options to control the way
234 lanes are run:
235</p><p>
236 <table>
237 <tr valign=top><td/><td>
238 <code>.cancelstep</code> <br/><nobr>N / true</nobr></td>
239 <td>
240 By default, lanes are only cancellable when they enter a pending
241 <tt>:receive()</tt> or <tt>:send()</tt> call.
242 With this option, one can set cancellation check to occur every <tt>N</tt>
243 Lua statements. The value <tt>true</tt> uses a default value (100).
244 </td></tr>
245
246 <tr valign=top><td/><td>
247 <code>.globals</code> <br/>globals_tbl</td>
248 <td>
249 Sets the globals table for the launched threads. This can be used for giving
250 them constants.
251 </p><p>
252 The global values of different lanes are in no manner connected;
253 modifying one will only affect the particular lane.
254 </td></tr>
255
256 <tr valign=top><td width=40><td>
257 <code>.priority</code> <br/><nobr>-2..+2</nobr></td>
258 <td>The priority of lanes generated. -2 is lowest, +2 is highest.
259 <p>
260 Implementation and dependability of priorities varies
261 by platform. Especially Linux kernel 2.6 is not supporting priorities in user mode.
262 </td></tr>
263 </table>
264
265</p>
266
267<h3>Free running lanes</h3>
268
269<p>
270The lane handles are allowed to be 'let loose'; in other words you may execute
271a lane simply by:
272
273<pre>
274 lanes.gen( function() ... end ) ()
275</pre>
276
277Normally, this kind of lanes will be in an eternal loop handling messages.
278Since the lane handle is gone,
279there is no way to control such a lane from the outside, nor read its potential
280return values. Then again, such a lane does not even normally return.
281</p>
282
283
284<!-- status +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
285<hr/>
286<h2 id="status">Status</h2>
287
288<table border=1 bgcolor="#E0E0FF" cellpadding=10><tr><td>
289 <code>str= lane_h.status</code>
290</table>
291
292<p>The current execution state of a lane can be read via its <tt>status</tt>
293member, providing one of these values: <sup>(<a href="#2">2</a></sup>
294
295 <table>
296 <tr><td width=40><td><tt>"pending"</tt></td><td>not started, yet</td></tr>
297 <tr><td/><td><tt>"running"</tt></td><td>running</td></tr>
298 <tr><td/><td><tt>"waiting"</tt></td><td>waiting at a Linda <tt>:receive()</tt> or <tt>:send()</tt></td></tr>
299 <tr><td/><td><tt>"done"</tt></td><td>finished executing (results are ready)</td></tr>
300 <tr><td/><td><tt>"error"</tt></td><td>met an error (reading results will propagate it)</td></tr>
301 <tr><td/><td><tt>"cancelled"</tt></td><td>received cancellation and finished itself</td></tr>
302 </table>
303</p><p>
304 This is similar to <tt>coroutine.status</tt>, which has: <tt>"running"</tt> /
305 <tt>"suspended"</tt> / <tt>"normal"</tt> / <tt>"dead"</tt>. Not using the
306 exact same names is intentional.
307</p>
308
309
310<!-- results +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
311<hr/>
312<h2 id="results">Results and errors</h2>
313
314<p>A lane can be waited upon by simply reading its results. This can be done
315in two ways.
316</p><p>
317
318<table border=1 bgcolor="#E0E0FF" cellpadding=10><tr><td>
319 <code>[val]= lane_h[1]</code>
320</table>
321<p>
322Makes sure lane has finished, and gives its first (maybe only) return value.
323Other return values will be available in other <tt>lane_h</tt> indices.
324</p><p>
325If the lane ended in an error, it is propagated to master state at this place.
326</p>
327
328<table border=1 bgcolor="#E0E0FF" cellpadding=10><tr><td>
329 <code>[...]|[nil,err,stack_tbl]= lane_h:join( [timeout_secs] )</code>
330</table>
331<p>
332Waits until the lane finishes, or <tt>timeout</tt> seconds have passed.
333Returns <tt>nil</tt> on timeout, <tt>nil,err,stack_tbl</tt> if the lane hit an error,
334or the return values of the lane. Unlike in reading the results in table
335fashion, errors are not propagated.
336</p><p>
337<tt>stack_tbl</tt> is an array of "&lt;filename&gt;:&lt;line&gt;" strings,
338describing where the error was thrown. Use <tt>table.concat()</tt> to format
339it to your liking (or just ignore it).
340</p><p>
341If you use <tt>:join</tt>, make sure your lane main function returns
342a non-nil value so you can tell timeout and error cases apart from succesful
343return (using the <tt>.status</tt> property may be risky, since it might change
344between a timed out join and the moment you read it).
345</p><p>
346
347<table border=1 bgcolor="#FFFFE0" width=500><tr><td>
348<pre>
349 require "lanes"
350
351 f= lanes.gen( function() error "!!!" end )
352 a= f(1)
353
354 --print( a[1] ) -- propagates error
355
356 v,err= a:join() -- no propagation
357 if v==nil then
358 error( "'a' faced error"..tostring(err) ) -- manual propagation
359 end
360</pre>
361</table>
362
363
364<!-- cancelling +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
365<hr/>
366<h2 id="cancelling">Cancelling</h2>
367
368<table border=1 bgcolor="#E0E0FF" cellpadding=10><tr><td>
369 <code>bool= lane_h:cancel( [timeout_secs=0.0,] [force_kill_bool=false] )</code>
370</table>
371
372<p>Sends a cancellation request to the lane. If <tt>timeout_secs</tt> is non-zero, waits
373for the request to be processed, or a timeout to occur.
374Returns <tt>true</tt> if the lane was already done (in <tt>"done"</tt>, <tt>"error"</tt> or <tt>"cancelled"</tt> status)
375or if the cancellation was fruitful within timeout period.
376</p><p>
377If the lane is still running and <tt>force_kill</tt> is <tt>true</tt>, the
378OS thread running the lane is forcefully killed. This means no GC, and should
379generally be the last resort.
380</p>
381<p>Cancellation is tested before going to sleep in <tt>receive()</tt> or <tt>send()</tt> calls
382and after executing <tt>cancelstep</tt> Lua statements. A currently pending <tt>receive</tt>
383or <tt>send</tt> call is currently not awakened, and may be a reason for a non-detected cancel.
384</p>
385
386
387<!-- finalizers +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
388<hr/>
389<h2 id="finalizers">Finalizers</h2>
390
391<table border=1 bgcolor="#E0E0FF" cellpadding=10><tr><td>
392 <code>set_finalizer( finalizer_func )</code>
393 <br/><br/>
394 <code>void= finalizer_func( [error] )</code>
395</table>
396
397<p>The <tt>error</tt> call is used for throwing exceptions in Lua. What Lua
398does not offer, however, is scoped <a href="http://en.wikipedia.org/wiki/Finalizer">finalizers</a>
399that would get called when a certain block of instructions gets exited, whether
400through peaceful return or abrupt <tt>error</tt>.
401</p>
402<p>Since 2.0.3, Lanes prepares a function <tt>set_finalizer</tt> for doing this.
403Any functions given to it will be called in the lane Lua state, just prior to
404closing it. They are not called in any particular order.
405</p>
406<p>An error in a finalizer itself overrides the state of the regular chunk
407(in practise, it would be highly preferable <i>not</i> to have errors in finalizers).
408If one finalizer errors, the others may not get called.
409</p>
410
411
412<!-- lindas +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
413<hr/>
414<h2 id="lindas">Lindas</h2>
415
416<p>Communications between lanes is completely detached from the lane handles
417themselves. By itself, a lane can only provide return values once it's finished,
418or throw an error. Needs to communicate during runtime are handled by <A HREF="http://en.wikipedia.org/wiki/Linda_%28coordination_language%29" TARGET="_blank">Linda objects</A>, which are
419<A HREF="#deep_userdata">deep userdata</A> instances. They can be provided to a lane
420as startup parameters, upvalues or in some other Linda's message.
421</p><p>
422Access to a Linda object means a lane can read or write to any of its data
423slots. Multiple lanes can be accessing the same Linda in parallel. No application
424level locking is required; each Linda operation is atomic.
425</p><p>
426
427<table border=1 bgcolor="#FFFFE0" width=500><tr><td>
428<pre>
429 require "lanes"
430
431 local linda= lanes.linda()
432
433 local function loop( max )
434 for i=1,max do
435 print( "sending: "..i )
436 linda:send( "x", i ) -- linda as upvalue
437 end
438 end
439
440 a= lanes.gen("",loop)( 10000 )
441
442 while true do
443 local val= linda:receive( 3.0, "x" ) -- timeout in seconds&nbsp;
444 if val==nil then
445 print( "timed out" )
446 break
447 end
448 print( "received: "..val )
449 end
450</pre>
451</table>
452
453</p>
454<p>Characteristics of the Lanes implementation of Lindas are:
455
456<ul>
457 <li>keys can be of number, string or boolean type
458 </li>
459 <li>values can be any type supported by inter-state copying (same limits
460 as for function parameters and upvalues)
461 </li>
462 <li>consuming method is <tt>:receive</tt> (not in)
463 </li>
464 <li>non-consuming method is <tt>:get</tt> (not rd)
465 </li>
466 <li>two producer-side methods: <tt>:send</tt> and <tt>:set</tt> (not out)
467 </li>
468 <li><tt>send</tt> allows for sending multiple values -atomically- to a
469 given key
470 </li>
471 <li><tt>receive</tt> can wait for multiple keys at once
472 </li>
473 <li>individual keys' queue length can be limited, balancing speed differences
474 in a producer/consumer scenario (making <tt>:send</tt> wait)
475 </li>
476</ul>
477</p>
478
479<p>
480<table border=1 bgcolor="#E0E0FF" cellpadding=10><tr><td>
481 <code>h= lanes.linda()</code>
482 <br/><br/>
483 <code>bool= h:send( [timeout_secs,] key, ... )</code>
484 <br/>
485 <code>[val, key]= h:receive( [timeout_secs,] key [, ...] )</code>
486 <br/><br/>
487 <code>= h:limit( key, n_uint )</code>
488</table>
489
490<p>The <tt>send</tt> and <tt>receive</tt> methods use Linda keys as FIFO stacks
491(first in, first out). Timeouts are given in seconds (millisecond accuracy).
492If using numbers as the first Linda key, one must explicitly give <tt>nil</tt>
493as the timeout parameter to avoid ambiguities.
494</p><p>
495By default, stack sizes are unlimited but limits can be
496enforced using the <tt>limit</tt> method. This can be useful to balance execution
497speeds in a producer/consumer scenario.
498</p><p>
499Note that any number of lanes can be reading or writing a Linda. There can be
500many producers, and many consumers. It's up to you.
501</p>
502<p><tt>send</tt> returns <tt>true</tt> if the sending succeeded, and <tt>false</tt>
503if the queue limit was met, and the queue did not empty enough during the given
504timeout.
505</p><p>
506Equally, <tt>receive</tt> returns a value and the key that provided the value,
507or nothing for timeout. Note that <tt>nil</tt>s can be sent and received;
508the <tt>key</tt> value will tell it apart from a timeout.
509</p><p>
510Multiple values can be sent to a given key at once, atomically (the send will
511fail unless all the values fit within the queue limit). This can be useful for
512multiple producer scenarios, if the protocols used are giving data in streams
513of multiple units. Atomicity avoids the producers from garbling each others
514messages, which could happen if the units were sent individually.
515</p><p>
516
517When receiving from multiple slots, the keys are checked in order, which can
518be used for making priority queues.
519</p><p>
520
521<table border=1 bgcolor="#E0E0FF" cellpadding=10><tr><td>
522 <code>linda_h:set( key, [val] )</code>
523 <br/>
524 <code>[val]= linda_h:get( key )</code>
525</table>
526
527</p><p>
528The table access methods are for accessing a slot without queuing or consuming.
529They can be used for making shared tables of storage among the lanes.
530</p><p>
531Writing to a slot overwrites existing value, and clears any possible queued
532entries. Table access and <tt>send</tt>/<tt>receive</tt> can be used together;
533reading a slot essentially peeks the next outcoming value of a queue.
534</p>
535
536<!--
537<p>
538<table border=1 bgcolor="#E0E0FF" cellpadding=10><tr><td>
539 <code>lightuserdata= linda_h:deep()</code>
540</table>
541
542<p>There is one more method that is not required in applications, but
543discussing it is good for a preview of how deep userdata works.
544</p><p>
545Because proxy objects (<tt>linda_h</tt>) are just pointers to the real, deep
546userdata, they cannot be used to identify a certain Linda from the others.
547The internal timer system needs to do this, and the <tt>:deep()</tt> method
548has been added for its use. It returns a light userdata pointing to the
549<i>actual</i> deep object, and thus can be used for seeing, which proxies actually
550mean the same underlying object. You might or might not need a similar system
551with your own deep userdata.
552</p>
553-->
554
555
556<h3>Granularity of using Lindas</h3>
557
558<p>A single Linda object provides an infinite number of slots, so why would
559you want to use several?
560</p><p>There are some important reasons:
561
562<ul>
563 <li>Access control. If you don't trust certain code completely, or just
564 to modularize your design, use one Linda for one usage and another one
565 for the other. This keeps your code clear and readable. You can pass
566 multiple Linda handles to a lane with practically no added cost.
567 </li>
568
569 <li>Namespace control. Linda keys have a "flat" namespace, so collisions
570 are possible if you try to use the same Linda for too many separate uses.
571 </li>
572
573 <li>Performance. Changing any slot in a Linda causes all pending threads
574 for that Linda to be momentarily awakened (at least in the C level).
575 This can degrade performance due to unnecessary OS level context switches.
576 </li>
577</ul>
578
579On the other side, you need to use a common Linda for waiting for multiple
580keys. You cannot wait for keys from two separate Linda objects at the same
581time.
582</p><p>
583<font size="-1">Actually, you can. Make separate lanes to wait each, and then multiplex those
584events to a common Linda, but... :).</font>
585</p>
586
587
588<!-- timers +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
589<hr/>
590<h2 id="timers">Timers</h2>
591
592<table border=1 bgcolor="#E0E0FF" cellpadding=10><tr><td>
593 <code>= lanes.timer( linda_h, key, date_tbl|first_secs [,period_secs] )</code>
594</table>
595
596<p>
597Timers can be run once, or in a reoccurring fashion (<tt>period_secs > 0</tt>).
598The first occurrence can be given either as a date or as a relative delay in seconds.
599The <tt>date</tt> table is like what <tt>os.date("*t")</tt> returns, in the
600local time zone.
601</p><p>
602Once a timer expires, the <tt>key</tt> is set with the current time
603(in seconds, same offset as <tt>os.time()</tt> but with millisecond accuracy).
604The key can be waited upon using the regular Linda <tt>:receive()</tt>
605method.
606</p><p>
607A timer can be stopped simply by <tt>first_secs=0</tt> and no period.
608</p><p>
609
610<table border=1 bgcolor="#FFFFE0" width=500><tr><td>
611<pre>
612 require "lanes"
613
614 local linda= lanes.linda()
615
616 -- First timer once a second, not synchronized to wall clock
617 --
618 lanes.timer( linda, "sec", 1, 1 )
619
620 -- Timer to a future event (next even minute); wall clock synchronized&nbsp;
621 --
622 local t= os.date( "*t", os.time()+60 ) -- now + 1min
623 t.sec= 0
624
625 lanes.timer( linda, "min", t, 60 ) -- reoccur every minute (sharp)
626
627 while true do
628 local v,key= linda:receive( "sec", "min" )
629 print( "Timer "..key..": "..v )
630 end
631</pre>
632</table>
633
634</p><p>
635NOTE: Timer keys are set, not queued, so missing a beat is possible especially
636if the timer cycle is extremely small. The key value can be used to know the
637actual time passed.
638</p><p>
639<table>
640 <tr><td valign=top><nobr><i>Design note:</i></nobr>&nbsp;</td>
641 <td>
642<font size="-1">
643Having the API as <tt>lanes.timer()</tt> is intentional. Another
644alternative would be <tt>linda_h:timer()</tt> but timers are not traditionally
645seen to be part of Lindas. Also, it would mean any lane getting a Linda handle
646would be able to modify timers on it. A third choice could
647be abstracting the timers out of Linda realm altogether (<tt>timer_h= lanes.timer( date|first_secs, period_secs )</tt>)
648but that would mean separate waiting functions for timers, and lindas. Even if
649a linda object and key was returned, that key couldn't be waited upon simultaneously
650with one's general linda events.
651The current system gives maximum capabilities with minimum API, and any smoothenings
652can easily be crafted in Lua at the application level.
653</font>
654 </td>
655 </tr>
656</table>
657</p>
658
659
660<!-- locks +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
661<hr/>
662<h2 id="locks">Locks etc.</h2>
663
664<p>
665Lanes does not generally require locks or critical sections to be used, at all.
666If necessary, a limited queue can be used to emulate them. <tt>lanes.lua</tt>
667offers some sugar to make it easy:
668</p><p>
669
670<table border=1 bgcolor="#E0E0FF" cellpadding=10><tr><td><pre>
671 lock_func= lanes.genlock( linda_h, key [,N_uint=1] )
672
673 lock_func( M_uint ) -- acquire
674 ..
675 lock_func( -M_uint ) -- release
676</table>
677</p><p>
678
679The generated function acquires M entries from the N available, or releases
680them if the value is negative. The acquiring call will suspend the lane, if necessary.
681Use <tt>M=N=1</tt> for a critical section lock (only one lane allowed to enter).
682</p><p>
683
684Note: The locks generated are <u>not recursive</u>. That would need another
685kind of generator, which is currently not implemented.
686</p><p>
687
688Similar sugar exists for atomic counters:
689</p><p>
690
691<table border=1 bgcolor="#E0E0FF" cellpadding=10><tr><td><pre>
692 atomic_func= lanes.genatomic( linda_h, key [,initial_num=0.0] )
693
694 new_num= atomic_func( [diff_num=+1.0] )
695</table>
696</p><p>
697
698Each time called, the generated function will change <tt>linda[key]</tt>
699atomically, without other lanes being able to interfere. The new value is
700returned. You can use either <tt>diff 0.0</tt> or <tt>get</tt> to just read the current
701value.
702</p><p>
703
704Note that the generated functions can be passed on to other lanes.
705</p>
706
707
708<!-- others +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
709<hr/>
710<h2 id="other">Other issues</h2>
711
712<h3>Limitations on data passing</h3>
713
714<p>Data passed between lanes (either as starting parameters, return values, upvalues or via Lindas) must conform to the following:
715</p>
716<p><ul>
717 <li>Booleans, numbers, strings, light userdata, Lua functions and tables of such can always be passed.
718 </li>
719 <li>Cyclic tables and/or duplicate references are allowed and reproduced appropriately,
720 but only <u>within the same transmission</u>.
721 <ul>
722 <li>using the same source table in multiple Linda messages keeps no ties between the tables
723 </li>
724 </ul>
725 </li>
726 <li>Objects (tables with a metatable) are copyable between lanes.
727 <ul>
728 <li>metatables are assumed to be immutable; they are internally indexed and only copied once
729 per each type of objects per lane
730 </li>
731 </ul>
732 </li>
733 <li>C functions (<tt>lua_CFunction</tt>) referring to <tt>LUA_ENVIRONINDEX</tt> or <tt>LUA_REGISTRYINDEX</tt> might not
734 work right in the target
735 <ul>
736 <li>rather completely re-initialize a module with <tt>require</tt> in the target lane
737 </li>
738 </ul>
739 </li>
740 <li>Full userdata can be passed only if it's prepared using the <A HREF="#deep_userdata">deep userdata</A>
741 system, which handles its lifespan management
742 <ul>
743 <li>in particular, lane handles cannot be passed between lanes
744 </li>
745 </ul>
746 </li>
747 <li>coroutines cannot be passed
748 </li>
749</ul>
750</p>
751
752
753<h3>Required of module makers</h3>
754
755<p>
756Most Lua extension modules should work unaltered with Lanes.
757If the module simply ties C side features to Lua, everything is fine without
758alterations. The <tt>luaopen_...()</tt> entry point will be called separately for each
759lane, where the module is <tt>require</tt>'d from.
760</p><p>
761If it, however, also does one-time C side initializations, these
762should be covered into a one-time-only construct such as below.
763</p><p>
764
765<table><tr><td width=40>
766 <td bgcolor="#ffffe0">
767<pre>
768 int luaopen_module( lua_State *L )
769 {
770 static char been_here; /* 0 by ANSI C */
771
772 /* Calls to 'require' serialized by Lanes; this is safe.&nbsp;&nbsp;
773 */
774 if (!been_here) {
775 been_here= 1;
776 ... one time initializations ...
777 }
778
779 ... binding to Lua ...
780 }
781</pre>
782</td></tr></table>
783</p>
784
785
786<h3 id="shared_userdata">Deep userdata in your own apps</h3>
787
788<p>
789The mechanism Lanes uses for sharing Linda handles between separate Lua states
790can be used for custom userdata as well. Here's what to do.
791</p>
792<ol>
793 <li>Provide an <i>identity function</i> for your userdata, in C. This function is
794used for creation and deletion of your deep userdata (the shared resource),
795and for making metatables for the state-specific proxies for accessing it.
796Take a look at <tt>linda_id</tt> in <tt>lanes.c</tt>.
797 </li>
798 <li>Create your userdata using <tt>luaG_deep_userdata()</tt>, which is
799 a Lua-callable function. Given an <tt>idfunc</tt>, it sets up the support
800 structures and returns a state-specific proxy userdata for accessing your
801 data. This proxy can also be copied over to other lanes.
802 </li>
803 <li>Accessing the deep userdata from your C code, use <tt>luaG_todeep()</tt>
804 instead of the regular <tt>lua_touserdata()</tt>.
805 </li>
806</ol>
807
808<p>Deep userdata management will take care of tying to <tt>__gc</tt> methods,
809and doing reference counting to see how many proxies are still there for
810accessing the data. Once there are none, the data will be freed through a call
811to the <tt>idfunc</tt> you provided.
812</p>
813<p><b>NOTE</b>: The lifespan of deep userdata may exceed that of the Lua state
814that created it. The allocation of the data storage should not be tied to
815the Lua state used. In other words, use <tt>malloc</tt>/<tt>free</tt> or
816similar memory handling mechanism.
817</p>
818
819
820<h3>Lane handles don't travel</h3>
821
822<p>
823Lane handles are not implemented as deep userdata, and cannot thus be
824copied across lanes. This is intentional; problems would occur at least when
825multiple lanes were to wait upon one to get ready. Also, it is a matter of
826design simplicity.
827</p><p>
828The same benefits can be achieved by having a single worker lane spawn all
829the sublanes, and keep track of them. Communications to and from this lane
830can be handled via a Linda.
831</p>
832
833
834<h3>Beware with print and file output</h3>
835
836<p>
837In multithreaded scenarios, giving multiple parameters to <tt>print()</tt>
838or <tt>file:write()</tt> may cause them to be overlapped in the output,
839something like this:
840
841<pre>
842 A: print( 1, 2, 3, 4 )
843 B: print( 'a', 'b', 'c', 'd' )
844
845 1 a b 2 3 c d 4
846</pre>
847
848Lanes does not protect you from this behaviour. The thing to do is either to
849concentrate your output to a certain lane per stream, or to concatenate output
850into a single string before you call the output function.
851</p>
852
853
854<h3 id="performance">Performance considerations</h3>
855
856<p>
857Lanes is about making multithreading easy, and natural in the Lua state of mind.
858Expect performance not to be an issue, if your program is logically built.
859Here are some things one should consider, if best performance is vital:
860</p><p>
861<ul>
862 <li>Data passing (parameters, upvalues, Linda messages) is generally fast,
863 doing two binary state-to-state copies (from source state to hidden state,
864 hidden state to target state). Remember that not only the function you
865 specify but also its upvalues, their upvalues, etc. etc. will get copied.
866 </li>
867 <li>Lane startup is fast (1000's of lanes a second), depending on the
868 number of standard libraries initialized. Initializing all standard libraries
869 is about 3-4 times slower than having no standard libraries at all. If you
870 throw in a lot of lanes per second, make sure you give them minimal necessary
871 set of libraries.
872 </li>
873 <li>Waiting Lindas are woken up (and execute some hidden Lua code) each
874 time <u>any</u> key in the Lindas they are waiting for are changed. This
875 may give essential slow-down (not measured, just a gut feeling) if a lot
876 of Linda keys are used. Using separate Linda objects for logically separate
877 issues will help (which is good practise anyhow).
878 </li>
879 <li>Linda objects are light. The memory footprint is two OS-level signalling
880 objects (<tt>HANDLE</tt> or <tt>pthread_cond_t</tt>) for each, plus one
881 C pointer for the proxies per each Lua state using the Linda. Barely nothing.
882 </li>
883 <li>Timers are light. You can probably expect timers up to 0.01 second
884 resolution to be useful, but that is very system specific. All timers are
885 merged into one main timer state (see <tt>timer.lua</tt>); no OS side
886 timers are utilized.
887 </li>
888 <li>Lindas are hashed to a fixed number of "keeper states", which are a locking entity.
889 If you are using a lot of Linda objects,
890 it may be useful to try having more of these keeper states. By default,
891 only one is used (see <tt>KEEPER_STATES_N</tt>), but this is an implementation detail.
892 </li>
893</ul>
894</p>
895
896
897<h3 id="cancelling_cancel">Cancelling cancel</h3>
898
899<p>
900Cancellation of lanes uses the Lua error mechanism with a special lightuserdata
901error sentinel.
902If you use <tt>pcall</tt> in code that needs to be cancellable
903from the outside, the special error might not get through to Lanes, thus
904preventing the Lane from being cleanly cancelled. You should throw any
905lightuserdata error further.
906</p><p>
907This system can actually be used by application to detect cancel, do your own
908cancellation duties, and pass on the error so Lanes will get it. If it does
909not get a clean cancellation from a lane in due time,
910it may forcefully kill the lane.
911</p><p>
912The sentinel is exposed as <tt>lanes.cancel_error</tt>, if you wish to use
913its actual value.
914</p>
915
916
917
918<!-- change log +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
919<hr/>
920<h2 id="changes">Change log</h2>
921
922<p>
923Jan-2009 (2.0.3):
924<ul>
925 <li>Added 'finalizer' to lane options. (TBD: not implemented yet!)
926 </li>
927 <li>Added call stack to errors coming from a lane.
928 </li>
929</ul>
930
931Jul-2008 (2.0):
932<ul>
933 <li>Too many changes to list (you'll need to re-read this manual)
934 </li>
935</ul>
936</p>
937
938<!-- footnotes +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
939<hr/>
940
941<p>For feedback, questions and suggestions:
942<UL>
943 <li><A HREF="http://luaforge.net/projects/lanes">Lanes @ LuaForge</A></li>
944 <li><A HREF="mailto:akauppi@gmail.com">the author</A></li>
945</UL>
946</p>
947
948<p><br/></p>
949
950</body>
951</html>
diff --git a/docs/multi.png b/docs/multi.png
new file mode 100644
index 0000000..f527aff
--- /dev/null
+++ b/docs/multi.png
Binary files differ
diff --git a/docs/performance.ods b/docs/performance.ods
new file mode 100644
index 0000000..541cc8e
--- /dev/null
+++ b/docs/performance.ods
Binary files differ
diff --git a/make-vc.cmd b/make-vc.cmd
new file mode 100644
index 0000000..2b4a7f6
--- /dev/null
+++ b/make-vc.cmd
@@ -0,0 +1,274 @@
1@REM
2@REM make-vc.cmd to build Lanes on Visual C++ 2005/08
3@REM
4@REM Requires: Windows XP or later (cmd.exe)
5@REM Visual C++ 2005/2008 (Express)
6@REM LuaBinaries 5.1.3 or Lua for Windows 5.1.3
7@REM
8
9@setlocal
10@set LUA_PATH=src\?.lua;tests\?.lua
11
12@if not "%LUA51%"=="" (
13 @goto LUA_OK
14)
15
16@REM *** Lua for Windows >=5.1.3.14 (%LUA_DEV%) ***
17@REM
18@if exist "%LUA_DEV%\lua.exe" (
19 @set LUA51=%LUA_DEV%
20 @goto LUA_OK
21)
22
23@REM *** Lua for Windows (default path) ***
24@REM
25@if exist "%ProgramFiles%\Lua\5.1\lua.exe" (
26 @set LUA51=%ProgramFiles:~0,2%\Progra~1\Lua\5.1
27 @goto LUA_OK
28)
29
30@REM *** LuaBinaries (default path) ***
31@REM
32@if exist "%ProgramFiles%\Lua5.1\lua5.1.exe" (
33 @set LUA51=%ProgramFiles:~0,2%\Progra~1\Lua5.1
34 @goto LUA_OK
35)
36
37goto ERR_NOLUA
38:LUA_OK
39
40@REM ---
41@REM %LUA_EXE% = %LUA51%\lua[5.1].exe
42@REM %LUAC_EXE% = %LUA51%\luac[5.1].exe
43@REM %LUA_LIB% = %LUA51%[\lib]
44@REM ---
45
46@set LUA_EXE=%LUA51%\lua5.1.exe
47@if exist "%LUA_EXE%" goto LUA_EXE_OK
48@set LUA_EXE=%LUA51%\lua.exe
49@if exist "%LUA_EXE%" goto LUA_EXE_OK
50@echo "Cannot find %LUA51%\lua[5.1].exe
51@goto EXIT
52:LUA_EXE_OK
53
54@set LUAC_EXE=%LUA51%\luac5.1.exe
55@if exist "%LUAC_EXE%" goto LUAC_EXE_OK
56@set LUAC_EXE=%LUA51%\luac.exe
57@if exist "%LUAC_EXE%" goto LUAC_EXE_OK
58@echo "Cannot find %LUA51%\luac[5.1].exe
59@goto EXIT
60:LUAC_EXE_OK
61
62
63@if "%1"=="" goto BUILD
64@if "%1"=="clean" goto CLEAN
65@if "%1"=="test" goto TEST
66@if "%1"=="launchtest" goto LAUNCHTEST
67@if "%1"=="perftest" goto PERFTEST
68@if "%1"=="perftest-plain" goto PERFTEST-PLAIN
69@if "%1"=="stress" goto STRESS
70@if "%1"=="basic" goto BASIC
71@if "%1"=="fifo" goto FIFO
72@if "%1"=="keeper" goto KEEPER
73@if "%1"=="atomic" goto ATOMIC
74@if "%1"=="cyclic" goto CYCLIC
75@if "%1"=="timer" goto TIMER
76@if "%1"=="recursive" goto RECURSIVE
77@if "%1"=="fibonacci" goto FIBONACCI
78@if "%1"=="hangtest" goto HANGTEST
79@if "%1"=="require" goto REQUIRE
80
81@echo Unknown target: %1
82@echo.
83@goto EXIT
84
85:BUILD
86@REM LuaBinaries:
87@REM The current build system does not show 'lua51-lanes.dll' to
88@REM be dependent on more than 'KERNEL32.DLL'. Good.
89@REM
90@REM Lua for Windows:
91@REM Depends on KERNEL32.DLL and LUA5.1.DLL. Good?
92
93@set LUA_LIB=%LUA51%
94@if exist "%LUA_LIB%\lua5.1.lib" (
95 @echo.
96 @echo ***
97 @echo *** Using Lua from: %LUA51%
98 @echo ***
99 @echo.
100 @goto LUA_LIB_OK
101)
102
103@set LUA_LIB=%LUA51%\lib
104@if exist "%LUA_LIB%\lua5.1.lib" (
105 @echo.
106 @echo ***
107 @echo *** Using Lua from: %LUA51%
108 @echo ***
109 @echo.
110 @goto LUA_LIB_OK
111)
112@echo Cannot find %LUA51%\[lib\]lua5.1.lib
113@goto EXIT
114:LUA_LIB_OK
115
116@REM
117@REM Precompile src/.lua -> .lch
118@REM
119@REM Note: we cannot use piping in Windows since we need binary output.
120@REM
121"%LUAC_EXE%" -o delme src/keeper.lua
122"%LUA_EXE%" tools/bin2c.lua -o src/keeper.lch delme
123@del delme
124
125@if "%VCINSTALLDIR%"=="" goto ERR_NOVC
126
127@REM
128@REM Win32 (Visual C++ 2005/08 Express) build commands
129@REM
130@REM MS itself has warnings in stdlib.h (4255), winbase.h (4668), several (4820, 4826)
131@REM 4054: "type cast from function pointer to data pointer"
132@REM 4127: "conditional expression is constant"
133@REM 4711: ".. selected for automatic inline expansion"
134@REM
135@set WARN=/Wall /wd4054 /wd4127 /wd4255 /wd4668 /wd4711 /wd4820 /wd4826
136
137@REM /LDd: debug DLL
138@REM /O2 /LD: release DLL
139@REM
140@set FLAGS=/O2 /LD
141
142cl %WARN% %FLAGS% /I "%LUA51%\include" /Felua51-lanes.dll src\*.c "%LUA_LIB%\lua5.1.lib"
143@REM cl %WARN% %FLAGS% /I "%LUA51%\include" /Felua51-lanes.dll src\*.c "%LUA_LIB%\lua5.1.lib" /link /NODEFAULTLIB:libcmt
144
145@del lua51-lanes.lib
146@del lua51-lanes.exp
147@goto EXIT
148
149:CLEAN
150if exist *.dll del *.dll
151if exist delme del delme
152@goto EXIT
153
154:TEST
155@REM "make test" does not automatically build/update the dll. We're NOT a makefile. :!
156@REM
157"%LUA_EXE%" tests\basic.lua
158@IF errorlevel 1 goto EXIT
159
160"%LUA_EXE%" tests\fifo.lua
161@IF errorlevel 1 goto EXIT
162
163"%LUA_EXE%" tests\keeper.lua
164@IF errorlevel 1 goto EXIT
165
166"%LUA_EXE%" tests\fibonacci.lua
167@IF errorlevel 1 goto EXIT
168
169"%LUA_EXE%" tests\timer.lua
170@IF errorlevel 1 goto EXIT
171
172"%LUA_EXE%" tests\atomic.lua
173@IF errorlevel 1 goto EXIT
174
175"%LUA_EXE%" tests\cyclic.lua
176@IF errorlevel 1 goto EXIT
177
178"%LUA_EXE%" tests\recursive.lua
179@IF errorlevel 1 goto EXIT
180
181@goto EXIT
182
183:BASIC
184"%LUA_EXE%" tests\basic.lua
185@goto EXIT
186
187:FIFO
188"%LUA_EXE%" tests\fifo.lua
189@goto EXIT
190
191:KEEPER
192"%LUA_EXE%" tests\keeper.lua
193@goto EXIT
194
195:ATOMIC
196"%LUA_EXE%" tests\atomic.lua
197@goto EXIT
198
199:CYCLIC
200"%LUA_EXE%" tests\cyclic.lua
201@goto EXIT
202
203:TIMER
204"%LUA_EXE%" tests\timer.lua
205@goto EXIT
206
207:RECURSIVE
208"%LUA_EXE%" tests\recursive.lua
209@goto EXIT
210
211:FIBONACCI
212"%LUA_EXE%" tests\fibonacci.lua
213@goto EXIT
214
215:HANGTEST
216"%LUA_EXE%" tests\hangtest.lua
217@goto EXIT
218
219:REQUIRE
220"%LUA_EXE%" -e "require'lanes'"
221@goto EXIT
222
223REM ---
224REM NOTE: 'timeit' is a funny thing; it does _not_ work with quoted
225REM long paths, but it _does_ work without the quotes. I have no idea,
226REM how it knows the spaces in paths apart from spaces in between
227REM parameters.
228
229:LAUNCHTEST
230timeit %LUA_EXE% tests\launchtest.lua %2 %3 %4
231@goto EXIT
232
233:PERFTEST
234timeit %LUA_EXE% tests\perftest.lua %2 %3 %4
235@goto EXIT
236
237:PERFTEST-PLAIN
238timeit %LUA_EXE% tests\perftest.lua --plain %2 %3 %4
239@goto EXIT
240
241:STRESS
242"%LUA_EXE%" tests\test.lua
243"%LUA_EXE%" tests\perftest.lua 100
244"%LUA_EXE%" tests\perftest.lua 50 -prio=-1,0
245"%LUA_EXE%" tests\perftest.lua 50 -prio=0,-1
246"%LUA_EXE%" tests\perftest.lua 50 -prio=0,2
247"%LUA_EXE%" tests\perftest.lua 50 -prio=2,0
248
249@echo All seems okay!
250@goto EXIT
251
252REM ---
253:ERR_NOLUA
254@echo ***
255@echo *** Please set LUA51 to point to either LuaBinaries or
256@echo *** Lua for Windows directory.
257@echo ***
258@echo *** http://luabinaries.luaforge.net/download.html
259@echo *** lua5_1_2_Win32_dll8_lib
260@echo *** lua5_1_2_Win32_bin
261@echo ***
262@echo *** http://luaforge.net/frs/?group_id=377&release_id=1138
263@echo ***
264@echo.
265@goto EXIT
266
267:ERR_NOVC
268@echo ***
269@echo *** VCINSTALLDIR not defined; please run 'setup-vc'
270@echo ***
271@echo.
272@goto EXIT
273
274:EXIT
diff --git a/setup-vc.cmd b/setup-vc.cmd
new file mode 100644
index 0000000..e93262e
--- /dev/null
+++ b/setup-vc.cmd
@@ -0,0 +1,90 @@
1@echo off
2REM
3REM Setting up command line to use Visual C++ 2005/2008 Express
4REM
5REM Visual C++ 2005:
6REM VCINSTALLDIR=C:\Program Files\Microsoft Visual Studio 8\VC
7REM VS80COMNTOOLS=C:\Program Files\Microsoft Visual Studio 8\Common7\Tools\
8REM VSINSTALLDIR=C:\Program Files\Microsoft Visual Studio 8
9REM
10REM Visual C++ 2008:
11REM VCINSTALLDIR=C:\Program Files\Microsoft Visual Studio 9.0\VC
12REM VS90COMNTOOLS=C:\Program Files\Microsoft Visual Studio 9.0\Common7\Tools\
13REM VSINSTALLDIR=C:\Program Files\Microsoft Visual Studio 9.0
14REM
15
16REM Test for VC++2005 FIRST, because it is the norm with Lua 5.1.4
17REM LuaBinaries and LfW. All prebuilt modules and lua.exe are built
18REM with it.
19REM
20set VSINSTALLDIR=C:\Program Files\Microsoft Visual Studio 8
21if not exist "%VSINSTALLDIR%\VC\vcvarsall.bat" goto TRY_VC9
22
23REM Win32 headers must be separately downloaded for VC++2005
24REM (VC++2008 SP1 carries an SDK with it)
25REM
26set _SDK=C:\Program Files\Microsoft Platform SDK for Windows Server 2003 R2\SetEnv.cmd
27if not exist "%_SDK%" goto ERR_NOSDK
28call "%_SDK%"
29goto FOUND_VC
30
31:TRY_VC9
32set VSINSTALLDIR=C:\Program Files\Microsoft Visual Studio 9.0
33if not exist "%VSINSTALLDIR%\VC\vcvarsall.bat" goto ERR_NOVC
34
35echo.
36echo *** Warning: Visual C++ 2008 in use ***
37echo.
38echo Using VC++2005 is recommended for runtime compatibility issues
39echo (LuaBinaries and LfW use it; if you compile everything from
40echo scratch, ignore this message)
41echo.
42
43:FOUND_VC
44set VCINSTALLDIR=%VSINSTALLDIR%\vc
45
46REM vcvars.bat sets the following values right:
47REM
48REM PATH=...
49REM INCLUDE=%VCINSTALLDIR%\ATLMFC\INCLUDE;%VCINSTALLDIR%\INCLUDE;%VCINSTALLDIR%\PlatformSDK\include;%FrameworkSDKDir%\include;%INCLUDE%
50REM LIB=%VCINSTALLDIR%\ATLMFC\LIB;%VCINSTALLDIR%\LIB;%VCINSTALLDIR%\PlatformSDK\lib;%FrameworkSDKDir%\lib;%LIB%
51REM LIBPATH=%FrameworkDir%\%FrameworkVersion%;%VCINSTALLDIR%\ATLMFC\LIB
52REM
53call "%VSINSTALLDIR%\VC\vcvarsall.bat"
54
55REM 'timeit.exe' is part of the MS Server Res Kit Tools (needed for "make perftest")
56REM
57set _RESKIT=C:\Program Files\Windows Resource Kits\Tools\
58if not exist "%_RESKIT%\timeit.exe" goto WARN_NOTIMEIT
59PATH=%PATH%;%_RESKIT%
60goto EXIT
61
62:WARN_NOTIMEIT
63echo.
64echo ** WARNING: Windows Server 2003 Resource Kit Tools - not detected
65echo You will need the 'timeit' utility to run 'make perftest'
66echo http://www.microsoft.com/downloads/details.aspx?familyid=9D467A69-57FF-4AE7-96EE-B18C4790CFFD
67echo.
68goto EXIT
69
70REM ---
71:ERR_NOVC
72echo.
73echo ** ERROR: Visual C++ 2005/08 Express - not detected
74echo You can set the environment variables separately, and run 'make-vc.cmd'
75echo or download the compiler from:
76echo http://msdn.microsoft.com/vstudio/express/downloads/
77echo.
78goto EXIT
79
80:ERR_NOSDK
81echo.
82echo ** ERROR: Windows Server 2003 Platform SDK - not detected
83echo You will need the core API's of it to compile Win32 applications.
84echo http://www.microsoft.com/downloads/details.aspx?familyid=0BAF2B35-C656-4969-ACE8-E4C0C0716ADB
85echo.
86goto EXIT
87
88:EXIT
89set _SDK=
90set _RESKIT=
diff --git a/src/Makefile b/src/Makefile
new file mode 100644
index 0000000..a17e9cd
--- /dev/null
+++ b/src/Makefile
@@ -0,0 +1,176 @@
1#
2# Lanes/src/Makefile
3#
4# make [LUA=... LUAC=...] Manual build
5# make LUAROCKS=1 CFLAGS=... LIBFLAG=... LUA=... LUAC=... LuaRocks automated build
6#
7
8MODULE=lanes
9
10SRC=lanes.c threading.c tools.c
11
12OBJ=$(SRC:.c=.o)
13
14# LuaRocks gives 'LIBFLAG' from the outside
15#
16LIBFLAG=-shared
17
18OPT_FLAGS=-O2
19 # -O0 -g
20
21LUA=lua
22LUAC=luac
23
24_SO=.so
25ifeq "$(findstring MINGW32,$(shell uname -s))" "MINGW32"
26 _SO=.dll
27endif
28
29ifeq "$(LUAROCKS)" ""
30 ifeq "$(findstring MINGW32,$(shell uname -s))" "MINGW32"
31 # MinGW MSYS on Windows
32 #
33 # - 'lua' and 'luac' expected to be on the path
34 # - %LUA_DEV% must lead to include files and libraries (Lua for Windows >= 5.1.3.14)
35 # - %MSCVR80% must be the full pathname of 'msvcr80.dll'
36 #
37 ifeq "$(LUA_DEV)" ""
38 $(error LUA_DEV not defined - try i.e. 'make LUA_DEV=/c/Program\ Files/Lua/5.1')
39 endif
40 ifeq "$(MSVCR80)" ""
41 MSVCR80:=$(LUA_DEV)/install/support/Microsoft.VC80.CRT.SP1/MSVCR80.DLL
42 ifneq '$(shell test -f "$(MSVCR80)" && echo found)' 'found'
43 $(error MSVCR80 not defined - set it to full path of msvcr80.dll')
44 endif
45 $(warning MSVCR80=$(MSVCR80))
46 endif
47 LUA_FLAGS:=-I "$(LUA_DEV)/include"
48 LUA_LIBS:="$(LUA_DEV)/lua5.1.dll" -lgcc -lmsvcr80 "$(MSVCR80)"
49 LIBFLAG=-shared -Wl,-Map,lanes.map
50 else
51 # Autodetect LUA_FLAGS and/or LUA_LIBS
52 #
53 ifneq "$(shell which pkg-config)" ""
54 ifeq "$(shell pkg-config --exists lua5.1 && echo 1)" "1"
55 LUA_FLAGS:=$(shell pkg-config --cflags lua5.1)
56 LUA_LIBS:=$(shell pkg-config --libs lua5.1)
57 #
58 # Ubuntu: -I/usr/include/lua5.1
59 # -llua5.1
60 else
61 ifeq "$(shell pkg-config --exists lua && echo 1)" "1"
62 LUA_FLAGS:=$(shell pkg-config --cflags lua)
63 LUA_LIBS:=$(shell pkg-config --libs lua)
64 #
65 # OS X fink with pkg-config:
66 # -I/sw/include
67 # -L/sw/lib -llua -lm
68 else
69 $(warning *** 'pkg-config' existed but did not know of 'lua[5.1]' - Good luck!)
70 LUA_FLAGS:=
71 LUA_LIBS:=-llua
72 endif
73 endif
74 else
75 # No 'pkg-config'; try defaults
76 #
77 ifeq "$(shell uname -s)" "Darwin"
78 $(warning *** Assuming 'fink' at default path)
79 LUA_FLAGS:=-I/sw/include
80 LUA_LIBS:=-L/sw/lib -llua
81 else
82 $(warning *** Assuming an arbitrary Lua installation; try installing 'pkg-config')
83 LUA_FLAGS:=
84 LUA_LIBS:=-llua
85 endif
86 endif
87 endif
88
89 ifeq "$(shell uname -s)" "Darwin"
90 # Some machines need 'MACOSX_DEPLOYMENT_TARGET=10.3' for using '-undefined dynamic_lookup'
91 # (at least PowerPC running 10.4.11); does not harm the others
92 #
93 CC = MACOSX_DEPLOYMENT_TARGET=10.3 gcc
94 LIBFLAG = -bundle -undefined dynamic_lookup
95 endif
96
97 CFLAGS=-Wall -Werror $(OPT_FLAGS) $(LUA_FLAGS)
98 LIBS=$(LUA_LIBS)
99endif
100
101#---
102# PThread platform specifics
103#
104ifeq "$(shell uname -s)" "Linux"
105 # -D_GNU_SOURCE needed for 'pthread_mutexattr_settype'
106 CFLAGS += -D_GNU_SOURCE -fPIC
107
108 # Use of -DUSE_PTHREAD_TIMEDJOIN is possible, but not recommended (slower & keeps threads
109 # unreleased somewhat longer)
110 #CFLAGS += -DUSE_PTHREAD_TIMEDJOIN
111
112 LIBS += -lpthread
113endif
114
115ifeq "$(shell uname -s)" "BSD"
116 LIBS += -lpthread
117endif
118
119#---
120all: lua51-$(MODULE)$(_SO)
121
122%.o: %.c *.h Makefile
123
124# Note: Don't put $(LUA_LIBS) ahead of $^; MSYS will not like that (I think)
125#
126lua51-$(MODULE)$(_SO): $(OBJ)
127 $(CC) $(LIBFLAG) $(LIBS) $^ $(LUA_LIBS) -o $@
128
129clean:
130 -rm -rf lua51-$(MODULE)$(_SO) *.lch *.o *.tmp *.map
131
132lanes.o: keeper.lch
133
134# Note: 'luac -o -' could be used on systems other than Windows (where pipes
135# are binary). We need to support MinGW as well, so a temporary file.
136#
137%.lch: %.lua
138 $(LUAC) -o $@.tmp $<
139 $(LUA) ../tools/bin2c.lua $@.tmp -o $@
140 -rm $@.tmp
141
142#---
143# NSLU2 "slug" Linux ARM
144#
145nslu2:
146 $(MAKE) all CFLAGS="$(CFLAGS) -I/opt/include -L/opt/lib -D_GNU_SOURCE -lpthread"
147
148#---
149# Cross compiling to Win32 (MinGW on OS X Intel)
150#
151# Point WIN32_LUA51 to an extraction of LuaBinaries dll8 and dev packages.
152#
153# Note: Only works on platforms with same endianess (i.e. not from PowerPC OS X,
154# since 'luac' uses the host endianess)
155#
156# EXPERIMENTAL; NOT TESTED OF LATE.
157#
158MINGW_GCC=mingw32-gcc
159 # i686-pc-mingw32-gcc
160
161win32: $(WIN32_LUA51)/include/lua.h
162 $(MAKE) build CC=$(MINGW_GCC) \
163 LUA_FLAGS=-I$(WIN32_LUA51)/include \
164 LUA_LIBS="-L$(WIN32_LUA51) -llua51" \
165 _SO=.dll \
166 SO_FLAGS=-shared \
167 LUA=lua51 \
168 LUAC=luac51
169
170$(WIN32_LUA51)/include/lua.h:
171 @echo "Usage: make win32 WIN32_LUA51=<path of extracted LuaBinaries dll8 and dev packages>"
172 @echo " [MINGW_GCC=...mingw32-gcc]"
173 @false
174
175.PROXY: all clean nslu2 win32
176
diff --git a/src/keeper.lua b/src/keeper.lua
new file mode 100644
index 0000000..f76173b
--- /dev/null
+++ b/src/keeper.lua
@@ -0,0 +1,244 @@
1--
2-- KEEPER.LUA
3--
4-- Keeper state logic
5--
6-- This code is read in for each "keeper state", which are the hidden, inter-
7-- mediate data stores used by Lanes inter-state communication objects.
8--
9-- Author: Asko Kauppi <akauppi@gmail.com>
10--
11--[[
12===============================================================================
13
14Copyright (C) 2008 Asko Kauppi <akauppi@gmail.com>
15
16Permission is hereby granted, free of charge, to any person obtaining a copy
17of this software and associated documentation files (the "Software"), to deal
18in the Software without restriction, including without limitation the rights
19to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
20copies of the Software, and to permit persons to whom the Software is
21furnished to do so, subject to the following conditions:
22
23The above copyright notice and this permission notice shall be included in
24all copies or substantial portions of the Software.
25
26THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
29AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
31OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
32THE SOFTWARE.
33
34===============================================================================
35]]--
36
37-- unique key instead of 'nil' in queues
38--
39assert( nil_sentinel )
40
41-- We only need to have base and table libraries (and io for debugging)
42--
43local table_remove= assert( table.remove )
44local table_concat= assert( table.concat )
45
46local function WR(...)
47 if io then
48 io.stderr:write( table_concat({...},'\t').."\n" )
49 end
50end
51
52-----
53-- Actual data store
54--
55-- { [linda_deep_ud]= { key= val [, ...] }
56-- ...
57-- }
58--
59local _data= {}
60
61-----
62-- Entries queued for use when the existing 'data[ud][key]' entry is consumed.
63--
64-- { [linda_deep_ud]= { key= { val [, ... } [, ...] }
65-- ...
66-- }
67--
68local _incoming= {}
69
70-----
71-- Length limits (if any) for queues
72--
73-- 0: don't queue values at all; ':send()' waits if the slot is not vacant
74-- N: allow N values to be queued (slot itself + N-1); wait if full
75-- nil: no limits, '_incoming' may grow endlessly
76--
77local _limits= {}
78
79-----
80-- data_tbl, incoming_tbl, limits_tbl = tables( linda_deep_ud )
81--
82-- Gives appropriate tables for a certain Linda (creates them if needed)
83--
84local function tables( ud )
85 -- tables are created either all or nothing
86 --
87 if not _data[ud] then
88 _data[ud]= {}
89 _incoming[ud]= {}
90 _limits[ud]= {}
91 end
92 return _data[ud], _incoming[ud], _limits[ud]
93end
94
95
96local function DEBUG(title,ud,key)
97 assert( title and ud and key )
98
99 local data,incoming,_= tables(ud)
100
101 local s= tostring(data[key])
102 for _,v in ipairs( incoming[key] or {} ) do
103 s= s..", "..tostring(v)
104 end
105 WR( "*** "..title.." ("..tostring(key).."): ", s )
106end
107
108
109-----
110-- bool= send( linda_deep_ud, key, ... )
111--
112-- Send new data (1..N) to 'key' slot. This send is atomic; all the values
113-- end up one after each other (this is why having possibility for sending
114-- multiple values in one call is deemed important).
115--
116-- If the queue has a limit, values are sent only if all of them fit in.
117--
118-- Returns: 'true' if all the values were placed
119-- 'false' if sending would exceed the queue limit (wait & retry)
120--
121function send( ud, key, ... )
122
123 local data,incoming,limits= tables(ud)
124
125 local n= select('#',...)
126 if n==0 then return true end -- nothing to send
127
128 -- Initialize queue for all keys that have been used with ':send()'
129 --
130 if incoming[key]==nil then
131 incoming[key]= {}
132 end
133
134 local len= data[key] and 1+#incoming[key] or 0
135 local m= limits[key]
136
137 if m and len+n > m then
138 return false -- would exceed the limit; try again later
139 end
140
141 for i=1,n do
142 local val= select(i,...)
143
144 -- 'nil' in the data replaced by sentinel
145 if val==nil then
146 val= nil_sentinel
147 end
148
149 if len==0 then
150 data[key]= val
151 len= 1
152 else
153 incoming[key][len]= val
154 len= len+1
155 end
156 end
157 return true
158end
159
160
161-----
162-- [val, key]= receive( linda_deep_ud, key [, ...] )
163--
164-- Read any of the given keys, consuming the data found. Keys are read in
165-- order.
166--
167function receive( ud, ... )
168
169 local data,incoming,_= tables(ud)
170
171 for i=1,select('#',...) do
172 local key= select(i,...)
173 local val= data[key]
174
175 if val~=nil then
176 if incoming[key] and incoming[key][1]~=nil then
177 -- pop [1] from 'incoming[key]' into the actual slot
178 data[key]= table_remove( incoming[key], 1 )
179 else
180 data[key]= nil -- empty the slot
181 end
182 if val==nil_sentinel then
183 val= nil
184 end
185 return val, key
186 end
187 end
188 --return nil
189end
190
191
192-----
193-- = limit( linda_deep_ud, key, uint )
194--
195function limit( ud, key, n )
196
197 local _,_,limits= tables(ud)
198
199 limits[key]= n
200end
201
202
203-----
204-- void= set( linda_deep_ud, key, [val] )
205--
206function set( ud, key, val )
207
208 local data,incoming,_= tables(ud)
209
210 -- Setting a key to 'nil' really clears it; only queing uses sentinels.
211 --
212 data[key]= val
213 incoming[key]= nil
214end
215
216
217-----
218-- [val]= get( linda_deep_ud, key )
219--
220function get( ud, key )
221
222 local data,_,_= tables(ud)
223
224 local val= data[key]
225 if val==nil_sentinel then
226 val= nil
227 end
228 return val
229end
230
231
232-----
233-- void= clear( linda_deep_ud )
234--
235-- Clear the data structures used for a Linda (at its destructor)
236--
237function clear( ud )
238
239 _data[ud]= nil
240 _incoming[ud]= nil
241 _limits[ud]= nil
242end
243
244
diff --git a/src/lanes.c b/src/lanes.c
new file mode 100644
index 0000000..9b36e4d
--- /dev/null
+++ b/src/lanes.c
@@ -0,0 +1,1849 @@
1/*
2 * LANES.C Copyright (c) 2007-08, Asko Kauppi
3 *
4 * Multithreading in Lua.
5 *
6 * History:
7 * 20-Oct-08 (2.0.2): Added closing of free-running threads, but it does
8 * not seem to eliminate the occasional segfaults at process
9 * exit.
10 * ...
11 * 24-Jun-08 .. 14-Aug-08 AKa: Major revise, Lanes 2008 version (2.0 rc1)
12 * ...
13 * 18-Sep-06 AKa: Started the module.
14 *
15 * Platforms (tested internally):
16 * OS X (10.5.4 PowerPC/Intel)
17 * Linux x86 (Ubuntu 8.04)
18 * Win32 (Windows XP Home SP2, Visual C++ 2005/2008 Express)
19 * PocketPC (TBD)
20 *
21 * Platforms (tested externally):
22 * Win32 (MSYS) by Ross Berteig.
23 *
24 * Platforms (testers appreciated):
25 * Win64 - should work???
26 * Linux x64 - should work
27 * FreeBSD - should work
28 * QNX - porting shouldn't be hard
29 * Sun Solaris - porting shouldn't be hard
30 *
31 * References:
32 * "Porting multithreaded applications from Win32 to Mac OS X":
33 * <http://developer.apple.com/macosx/multithreadedprogramming.html>
34 *
35 * Pthreads:
36 * <http://vergil.chemistry.gatech.edu/resources/programming/threads.html>
37 *
38 * MSDN: <http://msdn2.microsoft.com/en-us/library/ms686679.aspx>
39 *
40 * <http://ridiculousfish.com/blog/archives/2007/02/17/barrier>
41 *
42 * Defines:
43 * -DLINUX_SCHED_RR: all threads are lifted to SCHED_RR category, to
44 * allow negative priorities (-2,-1) be used. Even without this,
45 * using priorities will require 'sudo' privileges on Linux.
46 *
47 * -DUSE_PTHREAD_TIMEDJOIN: use 'pthread_timedjoin_np()' for waiting
48 * for threads with a timeout. This changes the thread cleanup
49 * mechanism slightly (cleans up at the join, not once the thread
50 * has finished). May or may not be a good idea to use it.
51 * Available only in selected operating systems (Linux).
52 *
53 * Bugs:
54 *
55 * To-do:
56 *
57 * ...
58 */
59
60const char *VERSION= "2.0.3";
61
62/*
63===============================================================================
64
65Copyright (C) 2007-08 Asko Kauppi <akauppi@gmail.com>
66
67Permission is hereby granted, free of charge, to any person obtaining a copy
68of this software and associated documentation files (the "Software"), to deal
69in the Software without restriction, including without limitation the rights
70to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
71copies of the Software, and to permit persons to whom the Software is
72furnished to do so, subject to the following conditions:
73
74The above copyright notice and this permission notice shall be included in
75all copies or substantial portions of the Software.
76
77THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
78IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
79FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
80AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
81LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
82OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
83THE SOFTWARE.
84
85===============================================================================
86*/
87#include <string.h>
88#include <stdio.h>
89#include <ctype.h>
90#include <stdlib.h>
91
92#include "lua.h"
93#include "lauxlib.h"
94
95#include "threading.h"
96#include "tools.h"
97
98#if !((defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC))
99# include <sys/time.h>
100#endif
101
102/* geteuid() */
103#ifdef PLATFORM_LINUX
104# include <unistd.h>
105# include <sys/types.h>
106#endif
107
108/* The selected number is not optimal; needs to be tested. Even using just
109* one keeper state may be good enough (depends on the number of Lindas used
110* in the applications).
111*/
112#define KEEPER_STATES_N 1 // 6
113
114/* Do you want full call stacks, or just the line where the error happened?
115*
116* TBD: The full stack feature does not seem to work (try 'make error').
117*/
118#define ERROR_FULL_STACK
119
120#ifdef ERROR_FULL_STACK
121# define STACK_TRACE_KEY ((void*)lane_error) // used as registry key
122#endif
123
124/*
125* Lua code for the keeper states (baked in)
126*/
127static char keeper_chunk[]=
128#include "keeper.lch"
129
130struct s_lane;
131static bool_t cancel_test( lua_State *L );
132static void cancel_error( lua_State *L );
133
134#define CANCEL_TEST_KEY ((void*)cancel_test) // used as registry key
135#define CANCEL_ERROR ((void*)cancel_error) // 'cancel_error' sentinel
136
137/*
138* registry[FINALIZER_REG_KEY] is either nil (no finalizers) or a table
139* of functions that Lanes will call after the executing 'pcall' has ended.
140*
141* We're NOT using the GC system for finalizer mainly because providing the
142* error (and maybe stack trace) parameters to the finalizer functions would
143* anyways complicate that approach.
144*/
145#define FINALIZER_REG_KEY ((void*)LG_set_finalizer)
146
147struct s_Linda;
148
149#if 1
150# define DEBUG_SIGNAL( msg, signal_ref ) /* */
151#else
152# define DEBUG_SIGNAL( msg, signal_ref ) \
153 { int i; unsigned char *ptr; char buf[999]; \
154 sprintf( buf, ">>> " msg ": %p\t", (signal_ref) ); \
155 ptr= (unsigned char *)signal_ref; \
156 for( i=0; i<sizeof(*signal_ref); i++ ) { \
157 sprintf( strchr(buf,'\0'), "%02x %c ", ptr[i], ptr[i] ); \
158 } \
159 fprintf( stderr, "%s\n", buf ); \
160 }
161#endif
162
163static bool_t thread_cancel( struct s_lane *s, double secs, bool_t force );
164
165
166/*
167* Push a table stored in registry onto Lua stack.
168*
169* If there is no existing table, create one if 'create' is TRUE.
170*
171* Returns: TRUE if a table was pushed
172* FALSE if no table found, not created, and nothing pushed
173*/
174static bool_t push_registry_table( lua_State *L, void *key, bool_t create ) {
175
176 STACK_GROW(L,3);
177
178 lua_pushlightuserdata( L, key );
179 lua_gettable( L, LUA_REGISTRYINDEX );
180
181 if (lua_isnil(L,-1)) {
182 lua_pop(L,1);
183
184 if (!create) return FALSE; // nothing pushed
185
186 lua_newtable(L);
187 lua_pushlightuserdata( L, key );
188 lua_pushvalue(L,-2); // duplicate of the table
189 lua_settable( L, LUA_REGISTRYINDEX );
190
191 // [-1]: table that's also bound in registry
192 }
193 return TRUE; // table pushed
194}
195
196
197/*---=== Serialize require ===---
198*/
199
200static MUTEX_T require_cs;
201
202//---
203// [val]= new_require( ... )
204//
205// Call 'old_require' but only one lane at a time.
206//
207// Upvalues: [1]: original 'require' function
208//
209static int new_require( lua_State *L ) {
210 int rc;
211 int args= lua_gettop(L);
212
213 STACK_GROW(L,1);
214 STACK_CHECK(L)
215
216 // Using 'lua_pcall()' to catch errors; otherwise a failing 'require' would
217 // leave us locked, blocking any future 'require' calls from other lanes.
218 //
219 MUTEX_LOCK( &require_cs );
220 {
221 lua_pushvalue( L, lua_upvalueindex(1) );
222 lua_insert( L, 1 );
223
224 rc= lua_pcall( L, args, 1 /*retvals*/, 0 /*errfunc*/ );
225 //
226 // LUA_ERRRUN / LUA_ERRMEM
227 }
228 MUTEX_UNLOCK( &require_cs );
229
230 if (rc) lua_error(L); // error message already at [-1]
231
232 STACK_END(L,0)
233 return 1;
234}
235
236/*
237* Serialize calls to 'require', if it exists
238*/
239static
240void serialize_require( lua_State *L ) {
241
242 STACK_GROW(L,1);
243 STACK_CHECK(L)
244
245 // Check 'require' is there; if not, do nothing
246 //
247 lua_getglobal( L, "require" );
248 if (lua_isfunction( L, -1 )) {
249 // [-1]: original 'require' function
250
251 lua_pushcclosure( L, new_require, 1 /*upvalues*/ );
252 lua_setglobal( L, "require" );
253
254 } else {
255 // [-1]: nil
256 lua_pop(L,1);
257 }
258
259 STACK_END(L,0)
260}
261
262
263/*---=== Keeper states ===---
264*/
265
266/*
267* Pool of keeper states
268*
269* Access to keeper states is locked (only one OS thread at a time) so the
270* bigger the pool, the less chances of unnecessary waits. Lindas map to the
271* keepers randomly, by a hash.
272*/
273struct s_Keeper {
274 MUTEX_T lock_;
275 lua_State *L;
276} keeper[ KEEPER_STATES_N ];
277
278/* We could use an empty table in 'keeper.lua' as the sentinel, but maybe
279* checking for a lightuserdata is faster.
280*/
281static bool_t nil_sentinel;
282
283/*
284* Initialize keeper states
285*
286* If there is a problem, return an error message (NULL for okay).
287*
288* Note: Any problems would be design flaws; the created Lua state is left
289* unclosed, because it does not really matter. In production code, this
290* function never fails.
291*/
292static const char *init_keepers(void) {
293 unsigned int i;
294 for( i=0; i<KEEPER_STATES_N; i++ ) {
295
296 // Initialize Keeper states with bare minimum of libs (those required
297 // by 'keeper.lua')
298 //
299 lua_State *L= luaL_newstate();
300 if (!L) return "out of memory";
301
302 luaG_openlibs( L, "io,table" ); // 'io' for debugging messages
303
304 lua_pushlightuserdata( L, &nil_sentinel );
305 lua_setglobal( L, "nil_sentinel" );
306
307 // Read in the preloaded chunk (and run it)
308 //
309 if (luaL_loadbuffer( L, keeper_chunk, sizeof(keeper_chunk), "=lanes_keeper" ))
310 return "luaL_loadbuffer() failed"; // LUA_ERRMEM
311
312 if (lua_pcall( L, 0 /*args*/, 0 /*results*/, 0 /*errfunc*/ )) {
313 // LUA_ERRRUN / LUA_ERRMEM / LUA_ERRERR
314 //
315 const char *err= lua_tostring(L,-1);
316 assert(err);
317 return err;
318 }
319
320 MUTEX_INIT( &keeper[i].lock_ );
321 keeper[i].L= L;
322 }
323 return NULL; // ok
324}
325
326static
327struct s_Keeper *keeper_acquire( const void *ptr ) {
328 /*
329 * Any hashing will do that maps pointers to 0..KEEPER_STATES_N-1
330 * consistently.
331 *
332 * Pointers are often aligned by 8 or so - ignore the low order bits
333 */
334 unsigned int i= ((unsigned long)(ptr) >> 3) % KEEPER_STATES_N;
335 struct s_Keeper *K= &keeper[i];
336
337 MUTEX_LOCK( &K->lock_ );
338 return K;
339}
340
341static
342void keeper_release( struct s_Keeper *K ) {
343 MUTEX_UNLOCK( &K->lock_ );
344}
345
346/*
347* Call a function ('func_name') in the keeper state, and pass on the returned
348* values to 'L'.
349*
350* 'linda': deep Linda pointer (used only as a unique table key, first parameter)
351* 'starting_index': first of the rest of parameters (none if 0)
352*
353* Returns: number of return values (pushed to 'L')
354*/
355static
356int keeper_call( lua_State* K, const char *func_name,
357 lua_State *L, struct s_Linda *linda, uint_t starting_index ) {
358
359 int args= starting_index ? (lua_gettop(L) - starting_index +1) : 0;
360 int Ktos= lua_gettop(K);
361 int retvals;
362
363 lua_getglobal( K, func_name );
364 ASSERT_L( lua_isfunction(K,-1) );
365
366 STACK_GROW( K, 1 );
367 lua_pushlightuserdata( K, linda );
368
369 luaG_inter_copy( L,K, args ); // L->K
370 lua_call( K, 1+args, LUA_MULTRET );
371
372 retvals= lua_gettop(K) - Ktos;
373
374 luaG_inter_move( K,L, retvals ); // K->L
375 return retvals;
376}
377
378
379/*---=== Linda ===---
380*/
381
382/*
383* Actual data is kept within a keeper state, which is hashed by the 's_Linda'
384* pointer (which is same to all userdatas pointing to it).
385*/
386struct s_Linda {
387 SIGNAL_T read_happened;
388 SIGNAL_T write_happened;
389};
390
391static int LG_linda_id( lua_State* );
392
393#define lua_toLinda(L,n) ((struct s_Linda *)luaG_todeep( L, LG_linda_id, n ))
394
395
396/*
397* bool= linda_send( linda_ud, [timeout_secs=-1,] key_num|str|bool|lightuserdata, ... )
398*
399* Send one or more values to a Linda. If there is a limit, all values must fit.
400*
401* Returns: 'true' if the value was queued
402* 'false' for timeout (only happens when the queue size is limited)
403*/
404LUAG_FUNC( linda_send ) {
405 struct s_Linda *linda= lua_toLinda( L, 1 );
406 bool_t ret;
407 bool_t cancel= FALSE;
408 struct s_Keeper *K;
409 time_d timeout= -1.0;
410 uint_t key_i= 2; // index of first key, if timeout not there
411
412 if (lua_isnumber(L,2)) {
413 timeout= SIGNAL_TIMEOUT_PREPARE( lua_tonumber(L,2) );
414 key_i++;
415 } else if (lua_isnil(L,2))
416 key_i++;
417
418 if (lua_isnil(L,key_i))
419 luaL_error( L, "nil key" );
420
421 STACK_GROW(L,1);
422
423 K= keeper_acquire( linda );
424 {
425 lua_State *KL= K->L; // need to do this for 'STACK_CHECK'
426STACK_CHECK(KL)
427 while(TRUE) {
428 int pushed;
429
430STACK_MID(KL,0)
431 pushed= keeper_call( K->L, "send", L, linda, key_i );
432 ASSERT_L( pushed==1 );
433
434 ret= lua_toboolean(L,-1);
435 lua_pop(L,1);
436
437 if (ret) {
438 // Wake up ALL waiting threads
439 //
440 SIGNAL_ALL( &linda->write_happened );
441 break;
442
443 } else if (timeout==0.0) {
444 break; /* no wait; instant timeout */
445
446 } else {
447 /* limit faced; push until timeout */
448
449 cancel= cancel_test( L ); // testing here causes no delays
450 if (cancel) break;
451
452 // K lock will be released for the duration of wait and re-acquired
453 //
454 if (!SIGNAL_WAIT( &linda->read_happened, &K->lock_, timeout ))
455 break; // timeout
456 }
457 }
458STACK_END(KL,0)
459 }
460 keeper_release(K);
461
462 if (cancel)
463 cancel_error(L);
464
465 lua_pushboolean( L, ret );
466 return 1;
467}
468
469
470/*
471* [val, key]= linda_receive( linda_ud, [timeout_secs_num=-1], key_num|str|bool|lightuserdata [, ...] )
472*
473* Receive a value from Linda, consuming it.
474*
475* Returns: value received (which is consumed from the slot)
476* key which had it
477*/
478LUAG_FUNC( linda_receive ) {
479 struct s_Linda *linda= lua_toLinda( L, 1 );
480 int pushed;
481 bool_t cancel= FALSE;
482 struct s_Keeper *K;
483 time_d timeout= -1.0;
484 uint_t key_i= 2;
485
486 if (lua_isnumber(L,2)) {
487 timeout= SIGNAL_TIMEOUT_PREPARE( lua_tonumber(L,2) );
488 key_i++;
489 } else if (lua_isnil(L,2))
490 key_i++;
491
492 K= keeper_acquire( linda );
493 {
494 while(TRUE) {
495 pushed= keeper_call( K->L, "receive", L, linda, key_i );
496 if (pushed) {
497 ASSERT_L( pushed==2 );
498
499 // To be done from within the 'K' locking area
500 //
501 SIGNAL_ALL( &linda->read_happened );
502 break;
503
504 } else if (timeout==0.0) {
505 break; /* instant timeout */
506
507 } else { /* nothing received; wait until timeout */
508
509 cancel= cancel_test( L ); // testing here causes no delays
510 if (cancel) break;
511
512 // Release the K lock for the duration of wait, and re-acquire
513 //
514 if (!SIGNAL_WAIT( &linda->write_happened, &K->lock_, timeout ))
515 break;
516 }
517 }
518 }
519 keeper_release(K);
520
521 if (cancel)
522 cancel_error(L);
523
524 return pushed;
525}
526
527
528/*
529* = linda_set( linda_ud, key_num|str|bool|lightuserdata [,value] )
530*
531* Set a value to Linda.
532*
533* Existing slot value is replaced, and possible queue entries removed.
534*/
535LUAG_FUNC( linda_set ) {
536 struct s_Linda *linda= lua_toLinda( L, 1 );
537 bool_t has_value= !lua_isnil(L,3);
538
539 struct s_Keeper *K= keeper_acquire( linda );
540 {
541 int pushed= keeper_call( K->L, "set", L, linda, 2 );
542 ASSERT_L( pushed==0 );
543
544 /* Set the signal from within 'K' locking.
545 */
546 if (has_value) {
547 SIGNAL_ALL( &linda->write_happened );
548 }
549 }
550 keeper_release(K);
551
552 return 0;
553}
554
555
556/*
557* [val]= linda_get( linda_ud, key_num|str|bool|lightuserdata )
558*
559* Get a value from Linda.
560*/
561LUAG_FUNC( linda_get ) {
562 struct s_Linda *linda= lua_toLinda( L, 1 );
563 int pushed;
564
565 struct s_Keeper *K= keeper_acquire( linda );
566 {
567 pushed= keeper_call( K->L, "get", L, linda, 2 );
568 ASSERT_L( pushed==0 || pushed==1 );
569 }
570 keeper_release(K);
571
572 return pushed;
573}
574
575
576/*
577* = linda_limit( linda_ud, key_num|str|bool|lightuserdata, uint [, ...] )
578*
579* Set limits to 1 or more Linda keys.
580*/
581LUAG_FUNC( linda_limit ) {
582 struct s_Linda *linda= lua_toLinda( L, 1 );
583
584 struct s_Keeper *K= keeper_acquire( linda );
585 {
586 int pushed= keeper_call( K->L, "limit", L, linda, 2 );
587 ASSERT_L( pushed==0 );
588 }
589 keeper_release(K);
590
591 return 0;
592}
593
594
595/*
596* lightuserdata= linda_deep( linda_ud )
597*
598* Return the 'deep' userdata pointer, identifying the Linda.
599*
600* This is needed for using Lindas as key indices (timer system needs it);
601* separately created proxies of the same underlying deep object will have
602* different userdata and won't be known to be essentially the same deep one
603* without this.
604*/
605LUAG_FUNC( linda_deep ) {
606 struct s_Linda *linda= lua_toLinda( L, 1 );
607 lua_pushlightuserdata( L, linda ); // just the address
608 return 1;
609}
610
611
612/*
613* Identity function of a shared userdata object.
614*
615* lightuserdata= linda_id( "new" [, ...] )
616* = linda_id( "delete", lightuserdata )
617*
618* Creation and cleanup of actual 'deep' objects. 'luaG_...' will wrap them into
619* regular userdata proxies, per each state using the deep data.
620*
621* tbl= linda_id( "metatable" )
622*
623* Returns a metatable for the proxy objects ('__gc' method not needed; will
624* be added by 'luaG_...')
625*
626* = linda_id( str, ... )
627*
628* For any other strings, the ID function must not react at all. This allows
629* future extensions of the system.
630*/
631LUAG_FUNC( linda_id ) {
632 const char *which= lua_tostring(L,1);
633
634 if (strcmp( which, "new" )==0) {
635 struct s_Linda *s;
636
637 /* We don't use any parameters, but one could (they're at [2..TOS])
638 */
639 ASSERT_L( lua_gettop(L)==1 );
640
641 /* The deep data is allocated separately of Lua stack; we might no
642 * longer be around when last reference to it is being released.
643 * One can use any memory allocation scheme.
644 */
645 s= (struct s_Linda *) malloc( sizeof(struct s_Linda) );
646 ASSERT_L(s);
647
648 SIGNAL_INIT( &s->read_happened );
649 SIGNAL_INIT( &s->write_happened );
650
651 lua_pushlightuserdata( L, s );
652 return 1;
653
654 } else if (strcmp( which, "delete" )==0) {
655 struct s_Keeper *K;
656 struct s_Linda *s= lua_touserdata(L,2);
657 ASSERT_L(s);
658
659 /* Clean associated structures in the keeper state.
660 */
661 K= keeper_acquire(s);
662 {
663 keeper_call( K->L, "clear", L, s, 0 );
664 }
665 keeper_release(K);
666
667 /* There aren't any lanes waiting on these lindas, since all proxies
668 * have been gc'ed. Right?
669 */
670 SIGNAL_FREE( &s->read_happened );
671 SIGNAL_FREE( &s->write_happened );
672 free(s);
673
674 return 0;
675
676 } else if (strcmp( which, "metatable" )==0) {
677
678 STACK_CHECK(L)
679 lua_newtable(L);
680 lua_newtable(L);
681 //
682 // [-2]: linda metatable
683 // [-1]: metatable's to-be .__index table
684
685 lua_pushcfunction( L, LG_linda_send );
686 lua_setfield( L, -2, "send" );
687
688 lua_pushcfunction( L, LG_linda_receive );
689 lua_setfield( L, -2, "receive" );
690
691 lua_pushcfunction( L, LG_linda_limit );
692 lua_setfield( L, -2, "limit" );
693
694 lua_pushcfunction( L, LG_linda_set );
695 lua_setfield( L, -2, "set" );
696
697 lua_pushcfunction( L, LG_linda_get );
698 lua_setfield( L, -2, "get" );
699
700 lua_pushcfunction( L, LG_linda_deep );
701 lua_setfield( L, -2, "deep" );
702
703 lua_setfield( L, -2, "__index" );
704 STACK_END(L,1)
705
706 return 1;
707 }
708
709 return 0; // unknown request, be quiet
710}
711
712
713/*---=== Finalizer ===---
714*/
715
716//---
717// void= finalizer( finalizer_func )
718//
719// finalizer_func( [err, stack_tbl] )
720//
721// Add a function that will be called when exiting the lane, either via
722// normal return or an error.
723//
724LUAG_FUNC( set_finalizer )
725{
726 STACK_GROW(L,3);
727
728 // Get the current finalizer table (if any)
729 //
730 push_registry_table( L, FINALIZER_REG_KEY, TRUE /*do create if none*/ );
731
732 lua_pushinteger( L, lua_objlen(L,-1)+1 );
733 lua_pushvalue( L, 1 ); // copy of the function
734 lua_settable( L, -3 );
735
736 lua_pop(L,1);
737 return 0;
738}
739
740
741//---
742// Run finalizers - if any - with the given parameters
743//
744// If 'rc' is nonzero, error message and stack index are available as:
745// [-1]: stack trace (table)
746// [-2]: error message (any type)
747//
748// Returns:
749// 0 if finalizers were run without error (or there were none)
750// LUA_ERRxxx return code if any of the finalizers failed
751//
752// TBD: should we add stack trace on failing finalizer, wouldn't be hard..
753//
754static int run_finalizers( lua_State *L, int lua_rc )
755{
756 unsigned error_index, tbl_index;
757 unsigned n;
758 int rc= 0;
759
760 if (!push_registry_table(L, FINALIZER_REG_KEY, FALSE /*don't create one*/))
761 return 0; // no finalizers
762
763 tbl_index= lua_gettop(L);
764 error_index= (lua_rc!=0) ? tbl_index-1 : 0; // absolute indices
765
766 STACK_GROW(L,4);
767
768 // [-1]: { func [, ...] }
769 //
770 for( n= lua_objlen(L,-1); n>0; n-- ) {
771 unsigned args= 0;
772 lua_pushinteger( L,n );
773 lua_gettable( L, -2 );
774
775 // [-1]: function
776 // [-2]: finalizers table
777
778 if (error_index) {
779 lua_pushvalue( L, error_index );
780 lua_pushvalue( L, error_index+1 ); // stack trace
781 args= 2;
782 }
783
784 rc= lua_pcall( L, args, 0 /*retvals*/, 0 /*no errfunc*/ );
785 //
786 // LUA_ERRRUN / LUA_ERRMEM
787
788 if (rc!=0) {
789 // [-1]: error message
790 //
791 // If one finalizer fails, don't run the others. Return this
792 // as the 'real' error, preceding that we could have had (or not)
793 // from the actual code.
794 //
795 break;
796 }
797 }
798
799 lua_remove(L,tbl_index); // take finalizer table out of stack
800
801 return rc;
802}
803
804
805/*---=== Threads ===---
806*/
807
808// NOTE: values to be changed by either thread, during execution, without
809// locking, are marked "volatile"
810//
811struct s_lane {
812 THREAD_T thread;
813 //
814 // M: sub-thread OS thread
815 // S: not used
816
817 lua_State *L;
818 //
819 // M: prepares the state, and reads results
820 // S: while S is running, M must keep out of modifying the state
821
822 volatile enum e_status status;
823 //
824 // M: sets to PENDING (before launching)
825 // S: updates -> RUNNING/WAITING -> DONE/ERROR_ST/CANCELLED
826
827 volatile bool_t cancel_request;
828 //
829 // M: sets to FALSE, flags TRUE for cancel request
830 // S: reads to see if cancel is requested
831
832#if !( (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN) )
833 SIGNAL_T done_signal_;
834 //
835 // M: Waited upon at lane ending (if Posix with no PTHREAD_TIMEDJOIN)
836 // S: sets the signal once cancellation is noticed (avoids a kill)
837
838 MUTEX_T done_lock_;
839 //
840 // Lock required by 'done_signal' condition variable, protecting
841 // lane status changes to DONE/ERROR_ST/CANCELLED.
842#endif
843
844 volatile enum {
845 NORMAL, // normal master side state
846 KILLED // issued an OS kill
847 } mstatus;
848 //
849 // M: sets to NORMAL, if issued a kill changes to KILLED
850 // S: not used
851
852 struct s_lane * volatile selfdestruct_next;
853 //
854 // M: sets to non-NULL if facing lane handle '__gc' cycle but the lane
855 // is still running
856 // S: cleans up after itself if non-NULL at lane exit
857};
858
859static MUTEX_T selfdestruct_cs;
860 //
861 // Protects modifying the selfdestruct chain
862
863#define SELFDESTRUCT_END ((struct s_lane *)(-1))
864 //
865 // The chain is ended by '(struct s_lane*)(-1)', not NULL:
866 // 'selfdestruct_first -> ... -> ... -> (-1)'
867
868struct s_lane * volatile selfdestruct_first= SELFDESTRUCT_END;
869
870/*
871* Add the lane to selfdestruct chain; the ones still running at the end of the
872* whole process will be cancelled.
873*/
874static void selfdestruct_add( struct s_lane *s ) {
875
876 MUTEX_LOCK( &selfdestruct_cs );
877 {
878 assert( s->selfdestruct_next == NULL );
879
880 s->selfdestruct_next= selfdestruct_first;
881 selfdestruct_first= s;
882 }
883 MUTEX_UNLOCK( &selfdestruct_cs );
884}
885
886/*
887* A free-running lane has ended; remove it from selfdestruct chain
888*/
889static void selfdestruct_remove( struct s_lane *s ) {
890
891 MUTEX_LOCK( &selfdestruct_cs );
892 {
893 // Make sure (within the MUTEX) that we actually are in the chain
894 // still (at process exit they will remove us from chain and then
895 // cancel/kill).
896 //
897 if (s->selfdestruct_next != NULL) {
898 struct s_lane **ref= (struct s_lane **) &selfdestruct_first;
899 bool_t found= FALSE;
900
901 while( *ref != SELFDESTRUCT_END ) {
902 if (*ref == s) {
903 *ref= s->selfdestruct_next;
904 s->selfdestruct_next= NULL;
905 found= TRUE;
906 break;
907 }
908 ref= (struct s_lane **) &((*ref)->selfdestruct_next);
909 }
910 assert( found );
911 }
912 }
913 MUTEX_UNLOCK( &selfdestruct_cs );
914}
915
916/*
917* Process end; cancel any still free-running threads
918*/
919static void selfdestruct_atexit( void ) {
920
921 if (selfdestruct_first == SELFDESTRUCT_END) return; // no free-running threads
922
923 // Signal _all_ still running threads to exit
924 //
925 MUTEX_LOCK( &selfdestruct_cs );
926 {
927 struct s_lane *s= selfdestruct_first;
928 while( s != SELFDESTRUCT_END ) {
929 s->cancel_request= TRUE;
930 s= s->selfdestruct_next;
931 }
932 }
933 MUTEX_UNLOCK( &selfdestruct_cs );
934
935 // When noticing their cancel, the lanes will remove themselves from
936 // the selfdestruct chain.
937
938 // TBD: Not sure if Windows (multi core) will require the timed approach,
939 // or single Yield. I don't have machine to test that (so leaving
940 // for timed approach). -- AKa 25-Oct-2008
941
942#ifdef PLATFORM_LINUX
943 // It seems enough for Linux to have a single yield here, which allows
944 // other threads (timer lane) to proceed. Without the yield, there is
945 // segfault.
946 //
947 YIELD();
948#else
949 // OS X 10.5 (Intel) needs more to avoid segfaults.
950 //
951 // "make test" is okay. 100's of "make require" are okay.
952 //
953 // Tested on MacBook Core Duo 2GHz and 10.5.5:
954 // -- AKa 25-Oct-2008
955 //
956 #ifndef ATEXIT_WAIT_SECS
957 # define ATEXIT_WAIT_SECS (0.1)
958 #endif
959 {
960 double t_until= now_secs() + ATEXIT_WAIT_SECS;
961
962 while( selfdestruct_first != SELFDESTRUCT_END ) {
963 YIELD(); // give threads time to act on their cancel
964
965 if (now_secs() >= t_until) break;
966 }
967 }
968#endif
969
970 //---
971 // Kill the still free running threads
972 //
973 if ( selfdestruct_first != SELFDESTRUCT_END ) {
974 unsigned n=0;
975 MUTEX_LOCK( &selfdestruct_cs );
976 {
977 struct s_lane *s= selfdestruct_first;
978 while( s != SELFDESTRUCT_END ) {
979 n++;
980 s= s->selfdestruct_next;
981 }
982 }
983 MUTEX_UNLOCK( &selfdestruct_cs );
984
985 // Linux (at least 64-bit): CAUSES A SEGFAULT IF THIS BLOCK IS ENABLED
986 // and works without the block (so let's leave those lanes running)
987 //
988#if 1
989 // 2.0.2: at least timer lane is still here
990 //
991 //fprintf( stderr, "Left %d lane(s) with cancel request at process end.\n", n );
992#else
993 MUTEX_LOCK( &selfdestruct_cs );
994 {
995 struct s_lane *s= selfdestruct_first;
996 while( s != SELFDESTRUCT_END ) {
997 struct s_lane *next_s= s->selfdestruct_next;
998 s->selfdestruct_next= NULL; // detach from selfdestruct chain
999
1000 THREAD_KILL( &s->thread );
1001 s= next_s;
1002 n++;
1003 }
1004 selfdestruct_first= SELFDESTRUCT_END;
1005 }
1006 MUTEX_UNLOCK( &selfdestruct_cs );
1007
1008 fprintf( stderr, "Killed %d lane(s) at process end.\n", n );
1009#endif
1010 }
1011}
1012
1013
1014// To allow free-running threads (longer lifespan than the handle's)
1015// 'struct s_lane' are malloc/free'd and the handle only carries a pointer.
1016// This is not deep userdata since the handle's not portable among lanes.
1017//
1018#define lua_toLane(L,i) (* ((struct s_lane**) lua_touserdata(L,i)))
1019
1020
1021/*
1022* Check if the thread in question ('L') has been signalled for cancel.
1023*
1024* Called by cancellation hooks and/or pending Linda operations (because then
1025* the check won't affect performance).
1026*
1027* Returns TRUE if any locks are to be exited, and 'cancel_error()' called,
1028* to make execution of the lane end.
1029*/
1030static bool_t cancel_test( lua_State *L ) {
1031 struct s_lane *s;
1032
1033 STACK_GROW(L,1);
1034
1035 STACK_CHECK(L)
1036 lua_pushlightuserdata( L, CANCEL_TEST_KEY );
1037 lua_rawget( L, LUA_REGISTRYINDEX );
1038 s= lua_touserdata( L, -1 ); // lightuserdata (true 's_lane' pointer) / nil
1039 lua_pop(L,1);
1040 STACK_END(L,0)
1041
1042 // 's' is NULL for the original main state (no-one can cancel that)
1043 //
1044 return s && s->cancel_request;
1045}
1046
1047static void cancel_error( lua_State *L ) {
1048 STACK_GROW(L,1);
1049 lua_pushlightuserdata( L, CANCEL_ERROR ); // special error value
1050 lua_error(L); // no return
1051}
1052
1053static void cancel_hook( lua_State *L, lua_Debug *ar ) {
1054 (void)ar;
1055 if (cancel_test(L)) cancel_error(L);
1056}
1057
1058
1059//---
1060// = _single( [cores_uint=1] )
1061//
1062// Limits the process to use only 'cores' CPU cores. To be used for performance
1063// testing on multicore devices. DEBUGGING ONLY!
1064//
1065LUAG_FUNC( _single ) {
1066 uint_t cores= luaG_optunsigned(L,1,1);
1067
1068#ifdef PLATFORM_OSX
1069 #ifdef _UTILBINDTHREADTOCPU
1070 if (cores > 1) luaL_error( L, "Limiting to N>1 cores not possible." );
1071 // requires 'chudInitialize()'
1072 utilBindThreadToCPU(0); // # of CPU to run on (we cannot limit to 2..N CPUs?)
1073 #else
1074 luaL_error( L, "Not available: compile with _UTILBINDTHREADTOCPU" );
1075 #endif
1076#else
1077 luaL_error( L, "not implemented!" );
1078#endif
1079 (void)cores;
1080
1081 return 0;
1082}
1083
1084
1085/*
1086* str= lane_error( error_val|str )
1087*
1088* Called if there's an error in some lane; add call stack to error message
1089* just like 'lua.c' normally does.
1090*
1091* ".. will be called with the error message and its return value will be the
1092* message returned on the stack by lua_pcall."
1093*
1094* Note: Rather than modifying the error message itself, it would be better
1095* to provide the call stack (as string) completely separated. This would
1096* work great with non-string error values as well (current system does not).
1097* (This is NOT possible with the Lua 5.1 'lua_pcall()'; we could of course
1098* implement a Lanes-specific 'pcall' of our own that does this). TBD!!! :)
1099* --AKa 22-Jan-2009
1100*/
1101#ifdef ERROR_FULL_STACK
1102
1103static int lane_error( lua_State *L ) {
1104 lua_Debug ar;
1105 unsigned lev,n;
1106
1107 // [1]: error message (any type)
1108
1109 assert( lua_gettop(L)==1 );
1110
1111 // Don't do stack survey for cancelled lanes.
1112 //
1113#if 1
1114 if (lua_touserdata(L,1) == CANCEL_ERROR)
1115 return 1; // just pass on
1116#endif
1117
1118 // Place stack trace at 'registry[lane_error]' for the 'luc_pcall()'
1119 // caller to fetch. This bypasses the Lua 5.1 limitation of only one
1120 // return value from error handler to 'lua_pcall()' caller.
1121
1122 // It's adequate to push stack trace as a table. This gives the receiver
1123 // of the stack best means to format it to their liking. Also, it allows
1124 // us to add more stack info later, if needed.
1125 //
1126 // table of { "sourcefile.lua:<line>", ... }
1127 //
1128 STACK_GROW(L,3);
1129 lua_newtable(L);
1130
1131 // Best to start from level 1, but in some cases it might be a C function
1132 // and we don't get '.currentline' for that. It's okay - just keep level
1133 // and table index growing separate. --AKa 22-Jan-2009
1134 //
1135 lev= 0;
1136 n=1;
1137 while( lua_getstack(L, ++lev, &ar ) ) {
1138 lua_getinfo(L, "Sl", &ar);
1139 if (ar.currentline > 0) {
1140 lua_pushinteger( L, n++ );
1141 lua_pushfstring( L, "%s:%d", ar.short_src, ar.currentline );
1142 lua_settable( L, -3 );
1143 }
1144 }
1145
1146 lua_pushlightuserdata( L, STACK_TRACE_KEY );
1147 lua_insert(L,-2);
1148 lua_settable( L, LUA_REGISTRYINDEX );
1149
1150 assert( lua_gettop(L)== 1 );
1151
1152 return 1; // the untouched error value
1153}
1154#endif
1155
1156
1157//---
1158#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC)
1159 static THREAD_RETURN_T __stdcall lane_main( void *vs )
1160#else
1161 static THREAD_RETURN_T lane_main( void *vs )
1162#endif
1163{
1164 struct s_lane *s= (struct s_lane *)vs;
1165 int rc, rc2;
1166 lua_State *L= s->L;
1167
1168 s->status= RUNNING; // PENDING -> RUNNING
1169
1170 // Tie "set_finalizer()" to the state
1171 //
1172 lua_pushcfunction( L, LG_set_finalizer );
1173 lua_setglobal( L, "set_finalizer" );
1174
1175#ifdef ERROR_FULL_STACK
1176 STACK_GROW( L, 1 );
1177 lua_pushcfunction( L, lane_error );
1178 lua_insert( L, 1 );
1179
1180 // [1]: error handler
1181 // [2]: function to run
1182 // [3..top]: parameters
1183 //
1184 rc= lua_pcall( L, lua_gettop(L)-2, LUA_MULTRET, 1 /*error handler*/ );
1185 // 0: no error
1186 // LUA_ERRRUN: a runtime error (error pushed on stack)
1187 // LUA_ERRMEM: memory allocation error
1188 // LUA_ERRERR: error while running the error handler (if any)
1189
1190 assert( rc!=LUA_ERRERR ); // since we've authored it
1191
1192 lua_remove(L,1); // remove error handler
1193
1194 // Lua 5.1 error handler is limited to one return value; taking stack trace
1195 // via registry
1196 //
1197 if (rc!=0) {
1198 STACK_GROW(L,1);
1199 lua_pushlightuserdata( L, STACK_TRACE_KEY );
1200 lua_gettable(L, LUA_REGISTRYINDEX);
1201
1202 // For cancellation, a stack trace isn't placed
1203 //
1204 assert( lua_istable(L,2) || (lua_touserdata(L,1)==CANCEL_ERROR) );
1205
1206 // Just leaving the stack trace table on the stack is enough to get
1207 // it through to the master.
1208 }
1209
1210#else
1211 // This code does not use 'lane_error'
1212 //
1213 // [1]: function to run
1214 // [2..top]: parameters
1215 //
1216 rc= lua_pcall( L, lua_gettop(L)-1, LUA_MULTRET, 0 /*no error handler*/ );
1217 // 0: no error
1218 // LUA_ERRRUN: a runtime error (error pushed on stack)
1219 // LUA_ERRMEM: memory allocation error
1220#endif
1221
1222//STACK_DUMP(L);
1223 // Call finalizers, if the script has set them up.
1224 //
1225 rc2= run_finalizers(L,rc);
1226 if (rc2!=0) {
1227 // Error within a finalizer!
1228 //
1229 // [-1]: error message
1230
1231 rc= rc2; // we're overruling the earlier script error or normal return
1232
1233 lua_insert( L,1 ); // make error message [1]
1234 lua_settop( L,1 ); // remove all rest
1235
1236 // Place an empty stack table just to keep the API simple (always when
1237 // there's an error, there's also stack table - though it may be empty).
1238 //
1239 lua_newtable(L);
1240 }
1241
1242 if (s->selfdestruct_next != NULL) {
1243 // We're a free-running thread and no-one's there to clean us up.
1244 //
1245 lua_close( s->L );
1246 L= 0;
1247
1248 #if !( (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN) )
1249 SIGNAL_FREE( &s->done_signal_ );
1250 MUTEX_FREE( &s->done_lock_ );
1251 #endif
1252 selfdestruct_remove(s); // away from selfdestruct chain
1253 free(s);
1254
1255 } else {
1256 // leave results (1..top) or error message + stack trace (1..2) on the stack - master will copy them
1257
1258 enum e_status st=
1259 (rc==0) ? DONE
1260 : (lua_touserdata(L,1)==CANCEL_ERROR) ? CANCELLED
1261 : ERROR_ST;
1262
1263 // Posix no PTHREAD_TIMEDJOIN:
1264 // 'done_lock' protects the -> DONE|ERROR_ST|CANCELLED state change
1265 //
1266 #if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN)
1267 s->status= st;
1268 #else
1269 MUTEX_LOCK( &s->done_lock_ );
1270 {
1271 s->status= st;
1272 SIGNAL_ONE( &s->done_signal_ ); // wake up master (while 's->done_lock' is on)
1273 }
1274 MUTEX_UNLOCK( &s->done_lock_ );
1275 #endif
1276 }
1277
1278 return 0; // ignored
1279}
1280
1281
1282//---
1283// lane_ud= thread_new( function, [libs_str],
1284// [cancelstep_uint=0],
1285// [prio_int=0],
1286// [globals_tbl],
1287// [... args ...] )
1288//
1289// Upvalues: metatable to use for 'lane_ud'
1290//
1291LUAG_FUNC( thread_new )
1292{
1293 lua_State *L2;
1294 struct s_lane *s;
1295 struct s_lane **ud;
1296
1297 const char *libs= lua_tostring( L, 2 );
1298 uint_t cs= luaG_optunsigned( L, 3,0);
1299 int prio= luaL_optinteger( L, 4,0);
1300 uint_t glob= luaG_isany(L,5) ? 5:0;
1301
1302 #define FIXED_ARGS (5)
1303 uint_t args= lua_gettop(L) - FIXED_ARGS;
1304
1305 if (prio < THREAD_PRIO_MIN || prio > THREAD_PRIO_MAX) {
1306 luaL_error( L, "Priority out of range: %d..+%d (%d)",
1307 THREAD_PRIO_MIN, THREAD_PRIO_MAX, prio );
1308 }
1309
1310 /* --- Create and prepare the sub state --- */
1311
1312 L2 = luaL_newstate(); // uses standard 'realloc()'-based allocator,
1313 // sets the panic callback
1314
1315 if (!L2) luaL_error( L, "'luaL_newstate()' failed; out of memory" );
1316
1317 STACK_GROW( L,2 );
1318
1319 // Setting the globals table (needs to be done before loading stdlibs,
1320 // and the lane function)
1321 //
1322 if (glob!=0) {
1323STACK_CHECK(L)
1324 if (!lua_istable(L,glob))
1325 luaL_error( L, "Expected table, got %s", luaG_typename(L,glob) );
1326
1327 lua_pushvalue( L, glob );
1328 luaG_inter_move( L,L2, 1 ); // moves the table to L2
1329
1330 // L2 [-1]: table of globals
1331
1332 // "You can change the global environment of a Lua thread using lua_replace"
1333 // (refman-5.0.pdf p. 30)
1334 //
1335 lua_replace( L2, LUA_GLOBALSINDEX );
1336STACK_END(L,0)
1337 }
1338
1339 // Selected libraries
1340 //
1341 if (libs) {
1342 const char *err= luaG_openlibs( L2, libs );
1343 ASSERT_L( !err ); // bad libs should have been noticed by 'lanes.lua'
1344
1345 serialize_require( L2 );
1346 }
1347
1348 // Lane main function
1349 //
1350STACK_CHECK(L)
1351 lua_pushvalue( L, 1 );
1352 luaG_inter_move( L,L2, 1 ); // L->L2
1353STACK_MID(L,0)
1354
1355 ASSERT_L( lua_gettop(L2) == 1 );
1356 ASSERT_L( lua_isfunction(L2,1) );
1357
1358 // revive arguments
1359 //
1360 if (args) luaG_inter_copy( L,L2, args ); // L->L2
1361STACK_MID(L,0)
1362
1363ASSERT_L( (uint_t)lua_gettop(L2) == 1+args );
1364ASSERT_L( lua_isfunction(L2,1) );
1365
1366 // 's' is allocated from heap, not Lua, since its life span may surpass
1367 // the handle's (if free running thread)
1368 //
1369 ud= lua_newuserdata( L, sizeof(struct s_lane*) );
1370 ASSERT_L(ud);
1371
1372 s= *ud= malloc( sizeof(struct s_lane) );
1373 ASSERT_L(s);
1374
1375 //memset( s, 0, sizeof(struct s_lane) );
1376 s->L= L2;
1377 s->status= PENDING;
1378 s->cancel_request= FALSE;
1379
1380#if !( (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN) )
1381 MUTEX_INIT( &s->done_lock_ );
1382 SIGNAL_INIT( &s->done_signal_ );
1383#endif
1384 s->mstatus= NORMAL;
1385 s->selfdestruct_next= NULL;
1386
1387 // Set metatable for the userdata
1388 //
1389 lua_pushvalue( L, lua_upvalueindex(1) );
1390 lua_setmetatable( L, -2 );
1391STACK_MID(L,1)
1392
1393 // Place 's' to registry, for 'cancel_test()' (even if 'cs'==0 we still
1394 // do cancel tests at pending send/receive).
1395 //
1396 lua_pushlightuserdata( L2, CANCEL_TEST_KEY );
1397 lua_pushlightuserdata( L2, s );
1398 lua_rawset( L2, LUA_REGISTRYINDEX );
1399
1400 if (cs) {
1401 lua_sethook( L2, cancel_hook, LUA_MASKCOUNT, cs );
1402 }
1403
1404 THREAD_CREATE( &s->thread, lane_main, s, prio );
1405STACK_END(L,1)
1406
1407 return 1;
1408}
1409
1410
1411//---
1412// = thread_gc( lane_ud )
1413//
1414// Cleanup for a thread userdata. If the thread is still executing, leave it
1415// alive as a free-running thread (will clean up itself).
1416//
1417// * Why NOT cancel/kill a loose thread:
1418//
1419// At least timer system uses a free-running thread, they should be handy
1420// and the issue of cancelling/killing threads at gc is not very nice, either
1421// (would easily cause waits at gc cycle, which we don't want).
1422//
1423// * Why YES kill a loose thread:
1424//
1425// Current way causes segfaults at program exit, if free-running threads are
1426// in certain stages. Details are not clear, but this is the core reason.
1427// If gc would kill threads then at process exit only one thread would remain.
1428//
1429// Todo: Maybe we should have a clear #define for selecting either behaviour.
1430//
1431LUAG_FUNC( thread_gc ) {
1432 struct s_lane *s= lua_toLane(L,1);
1433
1434 // We can read 's->status' without locks, but not wait for it
1435 //
1436 if (s->status < DONE) {
1437 //
1438 selfdestruct_add(s);
1439 assert( s->selfdestruct_next );
1440 return 0;
1441
1442 } else if (s->mstatus==KILLED) {
1443 // Make sure a kill has proceeded, before cleaning up the data structure.
1444 //
1445 // If not doing 'THREAD_WAIT()' we should close the Lua state here
1446 // (can it be out of order, since we killed the lane abruptly?)
1447 //
1448#if 0
1449 lua_close( s->L );
1450#else
1451fprintf( stderr, "** Joining with a killed thread (needs testing) **" );
1452#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN)
1453 THREAD_WAIT( &s->thread, -1 );
1454#else
1455 THREAD_WAIT( &s->thread, &s->done_signal_, &s->done_lock_, &s->status, -1 );
1456#endif
1457fprintf( stderr, "** Joined ok **" );
1458#endif
1459 }
1460
1461 // Clean up after a (finished) thread
1462 //
1463#if (! ((defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN)))
1464 SIGNAL_FREE( &s->done_signal_ );
1465 MUTEX_FREE( &s->done_lock_ );
1466 free(s);
1467#endif
1468
1469 return 0;
1470}
1471
1472
1473//---
1474// = thread_cancel( lane_ud [,timeout_secs=0.0] [,force_kill_bool=false] )
1475//
1476// The originator thread asking us specifically to cancel the other thread.
1477//
1478// 'timeout': <0: wait forever, until the lane is finished
1479// 0.0: just signal it to cancel, no time waited
1480// >0: time to wait for the lane to detect cancellation
1481//
1482// 'force_kill': if true, and lane does not detect cancellation within timeout,
1483// it is forcefully killed. Using this with 0.0 timeout means just kill
1484// (unless the lane is already finished).
1485//
1486// Returns: true if the lane was already finished (DONE/ERROR_ST/CANCELLED) or if we
1487// managed to cancel it.
1488// false if the cancellation timed out, or a kill was needed.
1489//
1490LUAG_FUNC( thread_cancel )
1491{
1492 struct s_lane *s= lua_toLane(L,1);
1493 double secs= 0.0;
1494 uint_t force_i=2;
1495 bool_t force, done= TRUE;
1496
1497 if (lua_isnumber(L,2)) {
1498 secs= lua_tonumber(L,2);
1499 force_i++;
1500 } else if (lua_isnil(L,2))
1501 force_i++;
1502
1503 force= lua_toboolean(L,force_i); // FALSE if nothing there
1504
1505 // We can read 's->status' without locks, but not wait for it (if Posix no PTHREAD_TIMEDJOIN)
1506 //
1507 if (s->status < DONE) {
1508 s->cancel_request= TRUE; // it's now signalled to stop
1509
1510 done= thread_cancel( s, secs, force );
1511 }
1512
1513 lua_pushboolean( L, done );
1514 return 1;
1515}
1516
1517static bool_t thread_cancel( struct s_lane *s, double secs, bool_t force )
1518{
1519 bool_t done=
1520#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN)
1521 THREAD_WAIT( &s->thread, secs );
1522#else
1523 THREAD_WAIT( &s->thread, &s->done_signal_, &s->done_lock_, &s->status, secs );
1524#endif
1525
1526 if ((!done) && force) {
1527 // Killing is asynchronous; we _will_ wait for it to be done at
1528 // GC, to make sure the data structure can be released (alternative
1529 // would be use of "cancellation cleanup handlers" that at least
1530 // PThread seems to have).
1531 //
1532 THREAD_KILL( &s->thread );
1533 s->mstatus= KILLED; // mark 'gc' to wait for it
1534 }
1535 return done;
1536}
1537
1538
1539//---
1540// str= thread_status( lane_ud )
1541//
1542// Returns: "pending" not started yet
1543// -> "running" started, doing its work..
1544// <-> "waiting" blocked in a receive()
1545// -> "done" finished, results are there
1546// / "error" finished at an error, error value is there
1547// / "cancelled" execution cancelled by M (state gone)
1548//
1549LUAG_FUNC( thread_status )
1550{
1551 struct s_lane *s= lua_toLane(L,1);
1552 enum e_status st= s->status; // read just once (volatile)
1553 const char *str;
1554
1555 if (s->mstatus == KILLED)
1556 st= CANCELLED;
1557
1558 str= (st==PENDING) ? "pending" :
1559 (st==RUNNING) ? "running" : // like in 'co.status()'
1560 (st==WAITING) ? "waiting" :
1561 (st==DONE) ? "done" :
1562 (st==ERROR_ST) ? "error" :
1563 (st==CANCELLED) ? "cancelled" : NULL;
1564 ASSERT_L(str);
1565
1566 lua_pushstring( L, str );
1567 return 1;
1568}
1569
1570
1571//---
1572// [...] | [nil, err_any, stack_tbl]= thread_join( lane_ud [, wait_secs=-1] )
1573//
1574// timeout: returns nil
1575// done: returns return values (0..N)
1576// error: returns nil + error value + stack table
1577// cancelled: returns nil
1578//
1579LUAG_FUNC( thread_join )
1580{
1581 struct s_lane *s= lua_toLane(L,1);
1582 double wait_secs= luaL_optnumber(L,2,-1.0);
1583 lua_State *L2= s->L;
1584 int ret;
1585
1586 bool_t done=
1587#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN)
1588 THREAD_WAIT( &s->thread, wait_secs );
1589#else
1590 THREAD_WAIT( &s->thread, &s->done_signal_, &s->done_lock_, &s->status, wait_secs );
1591#endif
1592 if (!done)
1593 return 0; // timeout: pushes none, leaves 'L2' alive
1594
1595 // Thread is DONE/ERROR_ST/CANCELLED; all ours now
1596
1597 STACK_GROW( L, 1 );
1598
1599 switch( s->status ) {
1600 case DONE: {
1601 uint_t n= lua_gettop(L2); // whole L2 stack
1602 luaG_inter_move( L2,L, n );
1603 ret= n;
1604 } break;
1605
1606 case ERROR_ST:
1607 lua_pushnil(L);
1608 luaG_inter_move( L2,L, 2 ); // error message at [-2], stack trace at [-1]
1609 ret= 3;
1610 break;
1611
1612 case CANCELLED:
1613 ret= 0;
1614 break;
1615
1616 default:
1617 fprintf( stderr, "Status: %d\n", s->status );
1618 ASSERT_L( FALSE ); ret= 0;
1619 }
1620 lua_close(L2);
1621
1622 return ret;
1623}
1624
1625
1626/*---=== Timer support ===---
1627*/
1628
1629/*
1630* Push a timer gateway Linda object; only one deep userdata is
1631* created for this, each lane will get its own proxy.
1632*
1633* Note: this needs to be done on the C side; Lua wouldn't be able
1634* to even see, when we've been initialized for the very first
1635* time (with us, they will be).
1636*/
1637static
1638void push_timer_gateway( lua_State *L ) {
1639
1640 /* No need to lock; 'static' is just fine
1641 */
1642 static DEEP_PRELUDE *p; // = NULL
1643
1644 STACK_CHECK(L)
1645 if (!p) {
1646 // Create the Linda (only on first time)
1647 //
1648 // proxy_ud= deep_userdata( idfunc )
1649 //
1650 lua_pushcfunction( L, luaG_deep_userdata );
1651 lua_pushcfunction( L, LG_linda_id );
1652 lua_call( L, 1 /*args*/, 1 /*retvals*/ );
1653
1654 ASSERT_L( lua_isuserdata(L,-1) );
1655
1656 // Proxy userdata contents is only a 'DEEP_PRELUDE*' pointer
1657 //
1658 p= * (DEEP_PRELUDE**) lua_touserdata( L, -1 );
1659 ASSERT_L(p && p->refcount==1 && p->deep);
1660
1661 // [-1]: proxy for accessing the Linda
1662
1663 } else {
1664 /* Push a proxy based on the deep userdata we stored.
1665 */
1666 luaG_push_proxy( L, LG_linda_id, p );
1667 }
1668 STACK_END(L,1)
1669}
1670
1671/*
1672* secs= now_secs()
1673*
1674* Returns the current time, as seconds (millisecond resolution).
1675*/
1676LUAG_FUNC( now_secs )
1677{
1678 lua_pushnumber( L, now_secs() );
1679 return 1;
1680}
1681
1682/*
1683* wakeup_at_secs= wakeup_conv( date_tbl )
1684*/
1685LUAG_FUNC( wakeup_conv )
1686{
1687 int year, month, day, hour, min, sec, isdst;
1688 struct tm tm= {0};
1689 //
1690 // .year (four digits)
1691 // .month (1..12)
1692 // .day (1..31)
1693 // .hour (0..23)
1694 // .min (0..59)
1695 // .sec (0..61)
1696 // .yday (day of the year)
1697 // .isdst (daylight saving on/off)
1698
1699 STACK_CHECK(L)
1700 lua_getfield( L, 1, "year" ); year= lua_tointeger(L,-1); lua_pop(L,1);
1701 lua_getfield( L, 1, "month" ); month= lua_tointeger(L,-1); lua_pop(L,1);
1702 lua_getfield( L, 1, "day" ); day= lua_tointeger(L,-1); lua_pop(L,1);
1703 lua_getfield( L, 1, "hour" ); hour= lua_tointeger(L,-1); lua_pop(L,1);
1704 lua_getfield( L, 1, "min" ); min= lua_tointeger(L,-1); lua_pop(L,1);
1705 lua_getfield( L, 1, "sec" ); sec= lua_tointeger(L,-1); lua_pop(L,1);
1706
1707 // If Lua table has '.isdst' we trust that. If it does not, we'll let
1708 // 'mktime' decide on whether the time is within DST or not (value -1).
1709 //
1710 lua_getfield( L, 1, "isdst" );
1711 isdst= lua_isboolean(L,-1) ? lua_toboolean(L,-1) : -1;
1712 lua_pop(L,1);
1713 STACK_END(L,0)
1714
1715 tm.tm_year= year-1900;
1716 tm.tm_mon= month-1; // 0..11
1717 tm.tm_mday= day; // 1..31
1718 tm.tm_hour= hour; // 0..23
1719 tm.tm_min= min; // 0..59
1720 tm.tm_sec= sec; // 0..60
1721 tm.tm_isdst= isdst; // 0/1/negative
1722
1723 lua_pushnumber( L, (double) mktime( &tm ) ); // ms=0
1724 return 1;
1725}
1726
1727
1728/*---=== Module linkage ===---
1729*/
1730
1731#define REG_FUNC( name ) \
1732 lua_pushcfunction( L, LG_##name ); \
1733 lua_setglobal( L, #name )
1734
1735#define REG_FUNC2( name, val ) \
1736 lua_pushcfunction( L, val ); \
1737 lua_setglobal( L, #name )
1738
1739#define REG_STR2( name, val ) \
1740 lua_pushstring( L, val ); \
1741 lua_setglobal( L, #name )
1742
1743#define REG_INT2( name, val ) \
1744 lua_pushinteger( L, val ); \
1745 lua_setglobal( L, #name )
1746
1747
1748int
1749#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC)
1750__declspec(dllexport)
1751#endif
1752 luaopen_lanes( lua_State *L ) {
1753 const char *err;
1754 static volatile char been_here; // =0
1755
1756 // One time initializations:
1757 //
1758 if (!been_here) {
1759 been_here= TRUE;
1760
1761#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC)
1762 now_secs(); // initialize 'now_secs()' internal offset
1763#endif
1764
1765#if (defined PLATFORM_OSX) && (defined _UTILBINDTHREADTOCPU)
1766 chudInitialize();
1767#endif
1768
1769 // Locks for 'tools.c' inc/dec counters
1770 //
1771 MUTEX_INIT( &deep_lock );
1772 MUTEX_INIT( &mtid_lock );
1773
1774 // Serialize calls to 'require' from now on, also in the primary state
1775 //
1776 MUTEX_RECURSIVE_INIT( &require_cs );
1777
1778 serialize_require( L );
1779
1780 // Selfdestruct chain handling
1781 //
1782 MUTEX_INIT( &selfdestruct_cs );
1783 atexit( selfdestruct_atexit );
1784
1785 //---
1786 // Linux needs SCHED_RR to change thread priorities, and that is only
1787 // allowed for sudo'ers. SCHED_OTHER (default) has no priorities.
1788 // SCHED_OTHER threads are always lower priority than SCHED_RR.
1789 //
1790 // ^-- those apply to 2.6 kernel. IF **wishful thinking** these
1791 // constraints will change in the future, non-sudo priorities can
1792 // be enabled also for Linux.
1793 //
1794#ifdef PLATFORM_LINUX
1795 sudo= geteuid()==0; // we are root?
1796
1797 // If lower priorities (-2..-1) are wanted, we need to lift the main
1798 // thread to SCHED_RR and 50 (medium) level. Otherwise, we're always below
1799 // the launched threads (even -2).
1800 //
1801 #ifdef LINUX_SCHED_RR
1802 if (sudo) {
1803 struct sched_param sp= {0}; sp.sched_priority= _PRIO_0;
1804 PT_CALL( pthread_setschedparam( pthread_self(), SCHED_RR, &sp) );
1805 }
1806 #endif
1807#endif
1808 err= init_keepers();
1809 if (err)
1810 luaL_error( L, "Unable to initialize: %s", err );
1811 }
1812
1813 // Linda identity function
1814 //
1815 REG_FUNC( linda_id );
1816
1817 // metatable for threads
1818 //
1819 lua_newtable( L );
1820 lua_pushcfunction( L, LG_thread_gc );
1821 lua_setfield( L, -2, "__gc" );
1822
1823 lua_pushcclosure( L, LG_thread_new, 1 ); // metatable as closure param
1824 lua_setglobal( L, "thread_new" );
1825
1826 REG_FUNC( thread_status );
1827 REG_FUNC( thread_join );
1828 REG_FUNC( thread_cancel );
1829
1830 REG_STR2( _version, VERSION );
1831 REG_FUNC( _single );
1832
1833 REG_FUNC2( _deep_userdata, luaG_deep_userdata );
1834
1835 REG_FUNC( now_secs );
1836 REG_FUNC( wakeup_conv );
1837
1838 push_timer_gateway(L);
1839 lua_setglobal( L, "timer_gateway" );
1840
1841 REG_INT2( max_prio, THREAD_PRIO_MAX );
1842
1843 lua_pushlightuserdata( L, CANCEL_ERROR );
1844 lua_setglobal( L, "cancel_error" );
1845
1846 return 0;
1847}
1848
1849
diff --git a/src/lanes.lua b/src/lanes.lua
new file mode 100644
index 0000000..c68506d
--- /dev/null
+++ b/src/lanes.lua
@@ -0,0 +1,611 @@
1--
2-- LANES.LUA
3--
4-- Multithreading and -core support for Lua
5--
6-- Author: Asko Kauppi <akauppi@gmail.com>
7--
8-- History:
9-- Jun-08 AKa: major revise
10-- 15-May-07 AKa: pthread_join():less version, some speedup & ability to
11-- handle more threads (~ 8000-9000, up from ~ 5000)
12-- 26-Feb-07 AKa: serialization working (C side)
13-- 17-Sep-06 AKa: started the module (serialization)
14--
15--[[
16===============================================================================
17
18Copyright (C) 2007-08 Asko Kauppi <akauppi@gmail.com>
19
20Permission is hereby granted, free of charge, to any person obtaining a copy
21of this software and associated documentation files (the "Software"), to deal
22in the Software without restriction, including without limitation the rights
23to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
24copies of the Software, and to permit persons to whom the Software is
25furnished to do so, subject to the following conditions:
26
27The above copyright notice and this permission notice shall be included in
28all copies or substantial portions of the Software.
29
30THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
31IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
32FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
33AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
34LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
35OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
36THE SOFTWARE.
37
38===============================================================================
39]]--
40
41module( "lanes", package.seeall )
42
43require "lua51-lanes"
44assert( type(lanes)=="table" )
45
46local mm= lanes
47
48local linda_id= assert( mm.linda_id )
49
50local thread_new= assert(mm.thread_new)
51local thread_status= assert(mm.thread_status)
52local thread_join= assert(mm.thread_join)
53local thread_cancel= assert(mm.thread_cancel)
54
55local _single= assert(mm._single)
56local _version= assert(mm._version)
57
58local _deep_userdata= assert(mm._deep_userdata)
59
60local now_secs= assert( mm.now_secs )
61local wakeup_conv= assert( mm.wakeup_conv )
62local timer_gateway= assert( mm.timer_gateway )
63
64local max_prio= assert( mm.max_prio )
65
66-- This check is for sublanes requiring Lanes
67--
68-- TBD: We could also have the C level expose 'string.gmatch' for us. But this is simpler.
69--
70if not string then
71 error( "To use 'lanes', you will also need to have 'string' available.", 2 )
72end
73
74--
75-- Cache globals for code that might run under sandboxing
76--
77local assert= assert
78local string_gmatch= assert( string.gmatch )
79local select= assert( select )
80local type= assert( type )
81local pairs= assert( pairs )
82local tostring= assert( tostring )
83local error= assert( error )
84local setmetatable= assert( setmetatable )
85local rawget= assert( rawget )
86
87ABOUT=
88{
89 author= "Asko Kauppi <akauppi@gmail.com>",
90 description= "Running multiple Lua states in parallel",
91 license= "MIT/X11",
92 copyright= "Copyright (c) 2007-08, Asko Kauppi",
93 version= _version,
94}
95
96
97-- Making copies of necessary system libs will pass them on as upvalues;
98-- only the first state doing "require 'lanes'" will need to have 'string'
99-- and 'table' visible.
100--
101local function WR(str)
102 io.stderr:write( str.."\n" )
103end
104
105local function DUMP( tbl )
106 if not tbl then return end
107 local str=""
108 for k,v in pairs(tbl) do
109 str= str..k.."="..tostring(v).."\n"
110 end
111 WR(str)
112end
113
114
115---=== Laning ===---
116
117-- lane_h[1..n]: lane results, same as via 'lane_h:join()'
118-- lane_h[0]: can be read to make sure a thread has finished (always gives 'true')
119-- lane_h[-1]: error message, without propagating the error
120--
121-- Reading a Lane result (or [0]) propagates a possible error in the lane
122-- (and execution does not return). Cancelled lanes give 'nil' values.
123--
124-- lane_h.state: "pending"/"running"/"waiting"/"done"/"error"/"cancelled"
125--
126local lane_mt= {
127 __index= function( me, k )
128 if type(k) == "number" then
129 -- 'me[0]=true' marks we've already taken in the results
130 --
131 if not rawget( me, 0 ) then
132 -- Wait indefinately; either propagates an error or
133 -- returns the return values
134 --
135 me[0]= true -- marker, even on errors
136
137 local t= { thread_join(me._ud) } -- wait indefinate
138 --
139 -- { ... } "done": regular return, 0..N results
140 -- { } "cancelled"
141 -- { nil, err_str, stack_tbl } "error"
142
143 local st= thread_status(me._ud)
144 if st=="done" then
145 -- Use 'pairs' and not 'ipairs' so that nil holes in
146 -- the returned values are tolerated.
147 --
148 for i,v in pairs(t) do
149 me[i]= v
150 end
151 elseif st=="error" then
152 assert( t[1]==nil and t[2] and type(t[3])=="table" )
153 me[-1]= t[2]
154 -- me[-2] could carry the stack table, but even
155 -- me[-1] is rather unnecessary (and undocumented);
156 -- use ':join()' instead. --AKa 22-Jan-2009
157 elseif st=="cancelled" then
158 -- do nothing
159 else
160 error( "Unexpected status: "..st )
161 end
162 end
163
164 -- Check errors even if we'd first peeked them via [-1]
165 -- and then came for the actual results.
166 --
167 local err= rawget(me, -1)
168 if err~=nil and k~=-1 then
169 -- Note: Lua 5.1 interpreter is not prepared to show
170 -- non-string errors, so we use 'tostring()' here
171 -- to get meaningful output. --AKa 22-Jan-2009
172 --
173 -- Also, the stack dump we get is no good; it only
174 -- lists our internal Lanes functions. There seems
175 -- to be no way to switch it off, though.
176
177 -- Level 3 should show the line where 'h[x]' was read
178 -- but this only seems to work for string messages
179 -- (Lua 5.1.4). No idea, why. --AKa 22-Jan-2009
180 --
181 error( tostring(err), 3 ) -- level 3 should show the line where 'h[x]' was read
182 end
183 return rawget( me, k )
184 --
185 elseif k=="status" then -- me.status
186 return thread_status(me._ud)
187 --
188 else
189 error( "Unknown key: "..k )
190 end
191 end
192 }
193
194-----
195-- h= lanes.gen( [libs_str|opt_tbl [, ...],] lane_func ) ( [...] )
196--
197-- 'libs': nil: no libraries available (default)
198-- "": only base library ('assert', 'print', 'unpack' etc.)
199-- "math,os": math + os + base libraries (named ones + base)
200-- "*": all standard libraries available
201--
202-- 'opt': .priority: int (-2..+2) smaller is lower priority (0 = default)
203--
204-- .cancelstep: bool | uint
205-- false: cancellation check only at pending Linda operations
206-- (send/receive) so no runtime performance penalty (default)
207-- true: adequate cancellation check (same as 100)
208-- >0: cancellation check every x Lua lines (small number= faster
209-- reaction but more performance overhead)
210--
211-- .globals: table of globals to set for a new thread (passed by value)
212--
213-- ... (more options may be introduced later) ...
214--
215-- Calling with a function parameter ('lane_func') ends the string/table
216-- modifiers, and prepares a lane generator. One can either finish here,
217-- and call the generator later (maybe multiple times, with different parameters)
218-- or add on actual thread arguments to also ignite the thread on the same call.
219--
220local lane_proxy
221
222local valid_libs= {
223 ["package"]= true,
224 ["table"]= true,
225 ["io"]= true,
226 ["os"]= true,
227 ["string"]= true,
228 ["math"]= true,
229 ["debug"]= true,
230 --
231 ["base"]= true,
232 ["coroutine"]= true,
233 ["*"]= true
234}
235
236function gen( ... )
237 local opt= {}
238 local libs= nil
239 local lev= 2 -- level for errors
240
241 local n= select('#',...)
242
243 if n==0 then
244 error( "No parameters!" )
245 end
246
247 for i=1,n-1 do
248 local v= select(i,...)
249 if type(v)=="string" then
250 libs= libs and libs..","..v or v
251 elseif type(v)=="table" then
252 for k,vv in pairs(v) do
253 opt[k]= vv
254 end
255 elseif v==nil then
256 -- skip
257 else
258 error( "Bad parameter: "..tostring(v) )
259 end
260 end
261
262 local func= select(n,...)
263 if type(func)~="function" then
264 error( "Last parameter not function: "..tostring(func) )
265 end
266
267 -- Check 'libs' already here, so the error goes in the right place
268 -- (otherwise will be noticed only once the generator is called)
269 --
270 if libs then
271 for s in string_gmatch(libs, "[%a*]+") do
272 if not valid_libs[s] then
273 error( "Bad library name: "..s )
274 end
275 end
276 end
277
278 local prio, cs, g_tbl
279
280 for k,v in pairs(opt) do
281 if k=="priority" then prio= v
282 elseif k=="cancelstep" then cs= (v==true) and 100 or
283 (v==false) and 0 or
284 type(v)=="number" and v or
285 error( "Bad cancelstep: "..tostring(v), lev )
286 elseif k=="globals" then g_tbl= v
287 --..
288 elseif k==1 then error( "unkeyed option: ".. tostring(v), lev )
289 else error( "Bad option: ".. tostring(k), lev )
290 end
291 end
292
293 -- Lane generator
294 --
295 return function(...)
296 return lane_proxy( thread_new( func, libs, cs, prio, g_tbl,
297 ... ) ) -- args
298 end
299end
300
301lane_proxy= function( ud )
302 local proxy= {
303 _ud= ud,
304
305 -- void= me:cancel()
306 --
307 cancel= function(me) thread_cancel(me._ud) end,
308
309 -- [...] | [nil,err,stack_tbl]= me:join( [wait_secs=-1] )
310 --
311 join= function( me, wait )
312 return thread_join( me._ud, wait )
313 end,
314 }
315 assert( proxy._ud )
316 setmetatable( proxy, lane_mt )
317
318 return proxy
319end
320
321
322---=== Lindas ===---
323
324-- We let the C code attach methods to userdata directly
325
326-----
327-- linda_ud= lanes.linda()
328--
329function linda()
330 local proxy= _deep_userdata( linda_id )
331 assert( (type(proxy) == "userdata") and getmetatable(proxy) )
332 return proxy
333end
334
335
336---=== Timers ===---
337
338--
339-- On first 'require "lanes"', a timer lane is spawned that will maintain
340-- timer tables and sleep in between the timer events. All interaction with
341-- the timer lane happens via a 'timer_gateway' Linda, which is common to
342-- all that 'require "lanes"'.
343--
344-- Linda protocol to timer lane:
345--
346-- TGW_KEY: linda_h, key, [wakeup_at_secs], [repeat_secs]
347--
348local TGW_KEY= "(timer control)" -- the key does not matter, a 'weird' key may help debugging
349local first_time_key= "first time"
350
351local first_time= timer_gateway:get(first_time_key) == nil
352timer_gateway:set(first_time_key,true)
353
354--
355-- Timer lane; initialize only on the first 'require "lanes"' instance (which naturally
356-- has 'table' always declared)
357--
358if first_time then
359 local table_remove= assert( table.remove )
360 local table_insert= assert( table.insert )
361
362 --
363 -- { [deep_linda_lightuserdata]= { [deep_linda_lightuserdata]=linda_h,
364 -- [key]= { wakeup_secs [,period_secs] } [, ...] },
365 -- }
366 --
367 -- Collection of all running timers, indexed with linda's & key.
368 --
369 -- Note that we need to use the deep lightuserdata identifiers, instead
370 -- of 'linda_h' themselves as table indices. Otherwise, we'd get multiple
371 -- entries for the same timer.
372 --
373 -- The 'hidden' reference to Linda proxy is used in 'check_timers()' but
374 -- also important to keep the Linda alive, even if all outside world threw
375 -- away pointers to it (which would ruin uniqueness of the deep pointer).
376 -- Now we're safe.
377 --
378 local collection= {}
379
380 --
381 -- set_timer( linda_h, key [,wakeup_at_secs [,period_secs]] )
382 --
383 local function set_timer( linda, key, wakeup_at, period )
384
385 assert( wakeup_at==nil or wakeup_at>0.0 )
386 assert( period==nil or period>0.0 )
387
388 local linda_deep= linda:deep()
389 assert( linda_deep )
390
391 -- Find or make a lookup for this timer
392 --
393 local t1= collection[linda_deep]
394 if not t1 then
395 t1= { [linda_deep]= linda } -- proxy to use the Linda
396 collection[linda_deep]= t1
397 end
398
399 if wakeup_at==nil then
400 -- Clear the timer
401 --
402 t1[key]= nil
403
404 -- Remove empty tables from collection; speeds timer checks and
405 -- lets our 'safety reference' proxy be gc:ed as well.
406 --
407 local empty= true
408 for k,_ in pairs(t1) do
409 if k~= linda_deep then
410 empty= false; break
411 end
412 end
413 if empty then
414 collection[linda_deep]= nil
415 end
416
417 -- Note: any unread timer value is left at 'linda[key]' intensionally;
418 -- clearing a timer just stops it.
419 else
420 -- New timer or changing the timings
421 --
422 local t2= t1[key]
423 if not t2 then
424 t2= {}; t1[key]= t2
425 end
426
427 t2[1]= wakeup_at
428 t2[2]= period -- can be 'nil'
429 end
430 end
431
432 -----
433 -- [next_wakeup_at]= check_timers()
434 --
435 -- Check timers, and wake up the ones expired (if any)
436 --
437 -- Returns the closest upcoming (remaining) wakeup time (or 'nil' if none).
438 --
439 local function check_timers()
440
441 local now= now_secs()
442 local next_wakeup
443
444 for linda_deep,t1 in pairs(collection) do
445 for key,t2 in pairs(t1) do
446 --
447 if key==linda_deep then
448 -- no 'continue' in Lua :/
449 else
450 -- 't2': { wakeup_at_secs [,period_secs] }
451 --
452 local wakeup_at= t2[1]
453 local period= t2[2] -- may be 'nil'
454
455 if wakeup_at <= now then
456 local linda= t1[linda_deep]
457 assert(linda)
458
459 linda:set( key, now )
460
461 -- 'pairs()' allows the values to be modified (and even
462 -- removed) as far as keys are not touched
463
464 if not period then
465 -- one-time timer; gone
466 --
467 t1[key]= nil
468 wakeup_at= nil -- no 'continue' in Lua :/
469 else
470 -- repeating timer; find next wakeup (may jump multiple repeats)
471 --
472 repeat
473 wakeup_at= wakeup_at+period
474 until wakeup_at > now
475
476 t2[1]= wakeup_at
477 end
478 end
479
480 if wakeup_at and ((not next_wakeup) or (wakeup_at < next_wakeup)) then
481 next_wakeup= wakeup_at
482 end
483 end
484 end -- t2 loop
485 end -- t1 loop
486
487 return next_wakeup -- may be 'nil'
488 end
489
490 -----
491 -- Snore loop (run as a lane on the background)
492 --
493 -- High priority, to get trustworthy timings.
494 --
495 -- We let the timer lane be a "free running" thread; no handle to it
496 -- remains.
497 --
498 gen( "io", { priority=max_prio }, function()
499
500 while true do
501 local next_wakeup= check_timers()
502
503 -- Sleep until next timer to wake up, or a set/clear command
504 --
505 local secs= next_wakeup and (next_wakeup - now_secs()) or nil
506 local linda= timer_gateway:receive( secs, TGW_KEY )
507
508 if linda then
509 local key= timer_gateway:receive( 0.0, TGW_KEY )
510 local wakeup_at= timer_gateway:receive( 0.0, TGW_KEY )
511 local period= timer_gateway:receive( 0.0, TGW_KEY )
512 assert( key and wakeup_at and period )
513
514 set_timer( linda, key, wakeup_at, period>0 and period or nil )
515 end
516 end
517 end )()
518end
519
520-----
521-- = timer( linda_h, key_val, date_tbl|first_secs [,period_secs] )
522--
523function timer( linda, key, a, period )
524
525 if a==0.0 then
526 -- Caller expects to get current time stamp in Linda, on return
527 -- (like the timer had expired instantly); it would be good to set this
528 -- as late as possible (to give most current time) but also we want it
529 -- to precede any possible timers that might start striking.
530 --
531 linda:set( key, now_secs() )
532
533 if not period or period==0.0 then
534 timer_gateway:send( TGW_KEY, linda, key, nil, nil ) -- clear the timer
535 return -- nothing more to do
536 end
537 a= period
538 end
539
540 local wakeup_at= type(a)=="table" and wakeup_conv(a) -- given point of time
541 or now_secs()+a
542 -- queue to timer
543 --
544 timer_gateway:send( TGW_KEY, linda, key, wakeup_at, period )
545end
546
547
548---=== Lock & atomic generators ===---
549
550-- These functions are just surface sugar, but make solutions easier to read.
551-- Not many applications should even need explicit locks or atomic counters.
552
553--
554-- lock_f= lanes.genlock( linda_h, key [,N_uint=1] )
555--
556-- = lock_f( +M ) -- acquire M
557-- ...locked...
558-- = lock_f( -M ) -- release M
559--
560-- Returns an access function that allows 'N' simultaneous entries between
561-- acquire (+M) and release (-M). For binary locks, use M==1.
562--
563function genlock( linda, key, N )
564 linda:limit(key,N)
565 linda:set(key,nil) -- clears existing data
566
567 --
568 -- [true [, ...]= trues(uint)
569 --
570 local function trues(n)
571 if n>0 then return true,trues(n-1) end
572 end
573
574 return
575 function(M)
576 if M>0 then
577 -- 'nil' timeout allows 'key' to be numeric
578 linda:send( nil, key, trues(M) ) -- suspends until been able to push them
579 else
580 for i=1,-M do
581 linda:receive( key )
582 end
583 end
584 end
585end
586
587
588--
589-- atomic_f= lanes.genatomic( linda_h, key [,initial_num=0.0] )
590--
591-- int= atomic_f( [diff_num=1.0] )
592--
593-- Returns an access function that allows atomic increment/decrement of the
594-- number in 'key'.
595--
596function genatomic( linda, key, initial_val )
597 linda:limit(key,2) -- value [,true]
598 linda:set(key,initial_val or 0.0) -- clears existing data (also queue)
599
600 return
601 function(diff)
602 -- 'nil' allows 'key' to be numeric
603 linda:send( nil, key, true ) -- suspends until our 'true' is in
604 local val= linda:get(key) + (diff or 1.0)
605 linda:set( key, val ) -- releases the lock, by emptying queue
606 return val
607 end
608end
609
610
611--the end
diff --git a/src/threading.c b/src/threading.c
new file mode 100644
index 0000000..68d1e41
--- /dev/null
+++ b/src/threading.c
@@ -0,0 +1,721 @@
1/*
2 * THREADING.C Copyright (c) 2007-08, Asko Kauppi
3 *
4 * Lua Lanes OS threading specific code.
5 *
6 * References:
7 * <http://www.cse.wustl.edu/~schmidt/win32-cv-1.html>
8*/
9
10/*
11===============================================================================
12
13Copyright (C) 2007-08 Asko Kauppi <akauppi@gmail.com>
14
15Permission is hereby granted, free of charge, to any person obtaining a copy
16of this software and associated documentation files (the "Software"), to deal
17in the Software without restriction, including without limitation the rights
18to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
19copies of the Software, and to permit persons to whom the Software is
20furnished to do so, subject to the following conditions:
21
22The above copyright notice and this permission notice shall be included in
23all copies or substantial portions of the Software.
24
25THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
30OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
31THE SOFTWARE.
32
33===============================================================================
34*/
35#include <stdio.h>
36#include <stdlib.h>
37#include <assert.h>
38#include <errno.h>
39#include <math.h>
40
41#include "threading.h"
42#include "lua.h"
43
44#if !((defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC))
45# include <sys/time.h>
46#endif
47
48
49#if defined(PLATFORM_LINUX) || defined(PLATFORM_CYGWIN)
50# include <sys/types.h>
51# include <unistd.h>
52#endif
53
54/* Linux needs to check, whether it's been run as root
55*/
56#ifdef PLATFORM_LINUX
57 volatile bool_t sudo;
58#endif
59
60#ifdef _MSC_VER
61// ".. selected for automatic inline expansion" (/O2 option)
62# pragma warning( disable : 4711 )
63// ".. type cast from function pointer ... to data pointer"
64# pragma warning( disable : 4054 )
65#endif
66
67//#define THREAD_CREATE_RETRIES_MAX 20
68 // loops (maybe retry forever?)
69
70/*
71* FAIL is for unexpected API return values - essentially programming
72* error in _this_ code.
73*/
74#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC)
75static void FAIL( const char *funcname, int rc ) {
76 fprintf( stderr, "%s() failed! (%d)\n", funcname, rc );
77 abort();
78}
79#endif
80
81
82/*
83* Returns millisecond timing (in seconds) for the current time.
84*
85* Note: This function should be called once in single-threaded mode in Win32,
86* to get it initialized.
87*/
88time_d now_secs(void) {
89
90#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC)
91 /*
92 * Windows FILETIME values are "100-nanosecond intervals since
93 * January 1, 1601 (UTC)" (MSDN). Well, we'd want Unix Epoch as
94 * the offset and it seems, so would they:
95 *
96 * <http://msdn.microsoft.com/en-us/library/ms724928(VS.85).aspx>
97 */
98 SYSTEMTIME st;
99 FILETIME ft;
100 ULARGE_INTEGER uli;
101 static ULARGE_INTEGER uli_epoch; // Jan 1st 1970 0:0:0
102
103 if (uli_epoch.HighPart==0) {
104 st.wYear= 1970;
105 st.wMonth= 1; // Jan
106 st.wDay= 1;
107 st.wHour= st.wMinute= st.wSecond= st.wMilliseconds= 0;
108
109 if (!SystemTimeToFileTime( &st, &ft ))
110 FAIL( "SystemTimeToFileTime", GetLastError() );
111
112 uli_epoch.LowPart= ft.dwLowDateTime;
113 uli_epoch.HighPart= ft.dwHighDateTime;
114 }
115
116 GetSystemTime( &st ); // current system date/time in UTC
117 if (!SystemTimeToFileTime( &st, &ft ))
118 FAIL( "SystemTimeToFileTime", GetLastError() );
119
120 uli.LowPart= ft.dwLowDateTime;
121 uli.HighPart= ft.dwHighDateTime;
122
123 /* 'double' has less accuracy than 64-bit int, but if it were to degrade,
124 * it would do so gracefully. In practise, the integer accuracy is not
125 * of the 100ns class but just 1ms (Windows XP).
126 */
127# if 1
128 // >= 2.0.3 code
129 return (double) ((uli.QuadPart - uli_epoch.QuadPart)/10000) / 1000.0;
130# elif 0
131 // fix from Kriss Daniels, see:
132 // <http://luaforge.net/forum/forum.php?thread_id=22704&forum_id=1781>
133 //
134 // "seem to be getting negative numbers from the old version, probably number
135 // conversion clipping, this fixes it and maintains ms resolution"
136 //
137 // This was a bad fix, and caused timer test 5 sec timers to disappear.
138 // --AKa 25-Jan-2009
139 //
140 return ((double)((signed)((uli.QuadPart/10000) - (uli_epoch.QuadPart/10000)))) / 1000.0;
141# else
142 // <= 2.0.2 code
143 return (double)(uli.QuadPart - uli_epoch.QuadPart) / 10000000.0;
144# endif
145#else
146 struct timeval tv;
147 // {
148 // time_t tv_sec; /* seconds since Jan. 1, 1970 */
149 // suseconds_t tv_usec; /* and microseconds */
150 // };
151
152 int rc= gettimeofday( &tv, NULL /*time zone not used any more (in Linux)*/ );
153 assert( rc==0 );
154
155 return ((double)tv.tv_sec) + ((tv.tv_usec)/1000) / 1000.0;
156#endif
157}
158
159
160/*
161*/
162time_d SIGNAL_TIMEOUT_PREPARE( double secs ) {
163 if (secs<=0.0) return secs;
164 else return now_secs() + secs;
165}
166
167
168#if !((defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC))
169/*
170* Prepare 'abs_secs' kind of timeout to 'timespec' format
171*/
172static void prepare_timeout( struct timespec *ts, time_d abs_secs ) {
173 assert(ts);
174 assert( abs_secs >= 0.0 );
175
176 if (abs_secs==0.0)
177 abs_secs= now_secs();
178
179 ts->tv_sec= floor( abs_secs );
180 ts->tv_nsec= ((long)((abs_secs - ts->tv_sec) * 1000.0 +0.5)) * 1000000UL; // 1ms = 1000000ns
181}
182#endif
183
184
185/*---=== Threading ===---*/
186
187//---
188// It may be meaningful to explicitly limit the new threads' C stack size.
189// We should know how much Lua needs in the C stack, all Lua side allocations
190// are done in heap so they don't count.
191//
192// Consequence of _not_ limiting the stack is running out of virtual memory
193// with 1000-5000 threads on 32-bit systems.
194//
195// Note: using external C modules may be affected by the stack size check.
196// if having problems, set back to '0' (default stack size of the system).
197//
198// Win32: 64K (?)
199// Win64: xxx
200//
201// Linux x86: 2MB Ubuntu 7.04 via 'pthread_getstacksize()'
202// Linux x64: xxx
203// Linux ARM: xxx
204//
205// OS X 10.4.9: 512K <http://developer.apple.com/qa/qa2005/qa1419.html>
206// valid values N * 4KB
207//
208#ifndef _THREAD_STACK_SIZE
209# if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PLATFORM_CYGWIN)
210# define _THREAD_STACK_SIZE 0
211 // Win32: does it work with less?
212# elif (defined PLATFORM_OSX)
213# define _THREAD_STACK_SIZE (524288/2) // 262144
214 // OS X: "make test" works on 65536 and even below
215 // "make perftest" works on >= 4*65536 == 262144 (not 3*65536)
216# elif (defined PLATFORM_LINUX) && (defined __i386)
217# define _THREAD_STACK_SIZE (2097152/16) // 131072
218 // Linux x86 (Ubuntu 7.04): "make perftest" works on /16 (not on /32)
219# elif (defined PLATFORM_BSD) && (defined __i386)
220# define _THREAD_STACK_SIZE (1048576/8) // 131072
221 // FreeBSD 6.2 SMP i386: ("gmake perftest" works on /8 (not on /16)
222# endif
223#endif
224
225#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC)
226 //
227 void MUTEX_INIT( MUTEX_T *ref ) {
228 *ref= CreateMutex( NULL /*security attr*/, FALSE /*not locked*/, NULL );
229 if (!ref) FAIL( "CreateMutex", GetLastError() );
230 }
231 void MUTEX_FREE( MUTEX_T *ref ) {
232 if (!CloseHandle(*ref)) FAIL( "CloseHandle (mutex)", GetLastError() );
233 *ref= NULL;
234 }
235 void MUTEX_LOCK( MUTEX_T *ref ) {
236 DWORD rc= WaitForSingleObject(*ref,INFINITE);
237 if (rc!=0) FAIL( "WaitForSingleObject", rc==WAIT_FAILED ? GetLastError() : rc );
238 }
239 void MUTEX_UNLOCK( MUTEX_T *ref ) {
240 if (!ReleaseMutex(*ref))
241 FAIL( "ReleaseMutex", GetLastError() );
242 }
243 /* MSDN: "If you would like to use the CRT in ThreadProc, use the
244 _beginthreadex function instead (of CreateThread)."
245 MSDN: "you can create at most 2028 threads"
246 */
247 void
248 THREAD_CREATE( THREAD_T *ref,
249 THREAD_RETURN_T (__stdcall *func)( void * ),
250 // Note: Visual C++ requires '__stdcall' where it is
251 void *data, int prio /* -3..+3 */ ) {
252
253 HANDLE h= (HANDLE)_beginthreadex( NULL, // security
254 _THREAD_STACK_SIZE,
255 func,
256 data,
257 0, // flags (0/CREATE_SUSPENDED)
258 NULL // thread id (not used)
259 );
260
261 if (h == INVALID_HANDLE_VALUE) FAIL( "CreateThread", GetLastError() );
262
263 if (prio!= 0) {
264 int win_prio= (prio == +3) ? THREAD_PRIORITY_TIME_CRITICAL :
265 (prio == +2) ? THREAD_PRIORITY_HIGHEST :
266 (prio == +1) ? THREAD_PRIORITY_ABOVE_NORMAL :
267 (prio == -1) ? THREAD_PRIORITY_BELOW_NORMAL :
268 (prio == -2) ? THREAD_PRIORITY_LOWEST :
269 THREAD_PRIORITY_IDLE; // -3
270
271 if (!SetThreadPriority( h, win_prio ))
272 FAIL( "SetThreadPriority", GetLastError() );
273 }
274 *ref= h;
275 }
276 //
277 bool_t THREAD_WAIT( THREAD_T *ref, double secs ) {
278 long ms= (long)((secs*1000.0)+0.5);
279
280 DWORD rc= WaitForSingleObject( *ref, ms<0 ? INFINITE:ms /*timeout*/ );
281 //
282 // (WAIT_ABANDONED)
283 // WAIT_OBJECT_0 success (0)
284 // WAIT_TIMEOUT
285 // WAIT_FAILED more info via GetLastError()
286
287 if (rc == WAIT_TIMEOUT) return FALSE;
288 if (rc != 0) FAIL( "WaitForSingleObject", rc );
289 *ref= NULL; // thread no longer usable
290 return TRUE;
291 }
292 //
293 void THREAD_KILL( THREAD_T *ref ) {
294 if (!TerminateThread( *ref, 0 )) FAIL("TerminateThread", GetLastError());
295 *ref= NULL;
296 }
297 //
298 void SIGNAL_INIT( SIGNAL_T *ref ) {
299 // 'manual reset' event type selected, to be able to wake up all the
300 // waiting threads.
301 //
302 HANDLE h= CreateEvent( NULL, // security attributes
303 TRUE, // TRUE: manual event
304 FALSE, // Initial state
305 NULL ); // name
306
307 if (h == NULL) FAIL( "CreateEvent", GetLastError() );
308 *ref= h;
309 }
310 void SIGNAL_FREE( SIGNAL_T *ref ) {
311 if (!CloseHandle(*ref)) FAIL( "CloseHandle (event)", GetLastError() );
312 *ref= NULL;
313 }
314 //
315 bool_t SIGNAL_WAIT( SIGNAL_T *ref, MUTEX_T *mu_ref, time_d abs_secs ) {
316 DWORD rc;
317 long ms;
318
319 if (abs_secs<0.0)
320 ms= INFINITE;
321 else if (abs_secs==0.0)
322 ms= 0;
323 else {
324 ms= (long) ((abs_secs - now_secs())*1000.0 + 0.5);
325
326 // If the time already passed, still try once (ms==0). A short timeout
327 // may have turned negative or 0 because of the two time samples done.
328 //
329 if (ms<0) ms= 0;
330 }
331
332 // Unlock and start a wait, atomically (like condition variables do)
333 //
334 rc= SignalObjectAndWait( *mu_ref, // "object to signal" (unlock)
335 *ref, // "object to wait on"
336 ms,
337 FALSE ); // not alertable
338
339 // All waiting locks are woken here; each competes for the lock in turn.
340 //
341 // Note: We must get the lock even if we've timed out; it makes upper
342 // level code equivalent to how PThread does it.
343 //
344 MUTEX_LOCK(mu_ref);
345
346 if (rc==WAIT_TIMEOUT) return FALSE;
347 if (rc!=0) FAIL( "SignalObjectAndWait", rc );
348 return TRUE;
349 }
350 void SIGNAL_ALL( SIGNAL_T *ref ) {
351/*
352 * MSDN tries to scare that 'PulseEvent' is bad, unreliable and should not be
353 * used. Use condition variables instead (wow, they have that!?!); which will
354 * ONLY WORK on Vista and 2008 Server, it seems... so MS, isn't it.
355 *
356 * I refuse to believe that; using 'PulseEvent' is probably just as good as
357 * using Windows (XP) in the first place. Just don't use APC's (asynchronous
358 * process calls) in your C side coding.
359 */
360 // PulseEvent on manual event:
361 //
362 // Release ALL threads waiting for it (and go instantly back to unsignalled
363 // status = future threads to start a wait will wait)
364 //
365 if (!PulseEvent( *ref ))
366 FAIL( "PulseEvent", GetLastError() );
367 }
368#else
369 // PThread (Linux, OS X, ...)
370 //
371 // On OS X, user processes seem to be able to change priorities.
372 // On Linux, SCHED_RR and su privileges are required.. !-(
373 //
374 #include <errno.h>
375 #include <sys/time.h>
376 //
377 static void _PT_FAIL( int rc, const char *name, const char *file, uint_t line ) {
378 const char *why= (rc==EINVAL) ? "EINVAL" :
379 (rc==EBUSY) ? "EBUSY" :
380 (rc==EPERM) ? "EPERM" :
381 (rc==ENOMEM) ? "ENOMEM" :
382 (rc==ESRCH) ? "ESRCH" :
383 //...
384 "";
385 fprintf( stderr, "%s %d: %s failed, %d %s\n", file, line, name, rc, why );
386 abort();
387 }
388 #define PT_CALL( call ) { int rc= call; if (rc!=0) _PT_FAIL( rc, #call, __FILE__, __LINE__ ); }
389 //
390 void SIGNAL_INIT( SIGNAL_T *ref ) {
391 PT_CALL( pthread_cond_init(ref,NULL /*attr*/) );
392 }
393 void SIGNAL_FREE( SIGNAL_T *ref ) {
394 PT_CALL( pthread_cond_destroy(ref) );
395 }
396 //
397 /*
398 * Timeout is given as absolute since we may have fake wakeups during
399 * a timed out sleep. A Linda with some other key read, or just because
400 * PThread cond vars can wake up unwantedly.
401 */
402 bool_t SIGNAL_WAIT( SIGNAL_T *ref, pthread_mutex_t *mu, time_d abs_secs ) {
403 if (abs_secs<0.0) {
404 PT_CALL( pthread_cond_wait( ref, mu ) ); // infinite
405 } else {
406 int rc;
407 struct timespec ts;
408
409 assert( abs_secs != 0.0 );
410 prepare_timeout( &ts, abs_secs );
411
412 rc= pthread_cond_timedwait( ref, mu, &ts );
413
414 if (rc==ETIMEDOUT) return FALSE;
415 if (rc) { _PT_FAIL( rc, "pthread_cond_timedwait()", __FILE__, __LINE__ ); }
416 }
417 return TRUE;
418 }
419 //
420 void SIGNAL_ONE( SIGNAL_T *ref ) {
421 PT_CALL( pthread_cond_signal(ref) ); // wake up ONE (or no) waiting thread
422 }
423 //
424 void SIGNAL_ALL( SIGNAL_T *ref ) {
425 PT_CALL( pthread_cond_broadcast(ref) ); // wake up ALL waiting threads
426 }
427 //
428 void THREAD_CREATE( THREAD_T* ref,
429 THREAD_RETURN_T (*func)( void * ),
430 void *data, int prio /* -2..+2 */ ) {
431 pthread_attr_t _a;
432 pthread_attr_t *a= &_a;
433 struct sched_param sp;
434
435 PT_CALL( pthread_attr_init(a) );
436
437#ifndef PTHREAD_TIMEDJOIN
438 // We create a NON-JOINABLE thread. This is mainly due to the lack of
439 // 'pthread_timedjoin()', but does offer other benefits (s.a. earlier
440 // freeing of the thread's resources).
441 //
442 PT_CALL( pthread_attr_setdetachstate(a,PTHREAD_CREATE_DETACHED) );
443#endif
444
445 // Use this to find a system's default stack size (DEBUG)
446#if 0
447 { size_t n; pthread_attr_getstacksize( a, &n );
448 fprintf( stderr, "Getstack: %u\n", (unsigned int)n ); }
449 // 524288 on OS X
450 // 2097152 on Linux x86 (Ubuntu 7.04)
451 // 1048576 on FreeBSD 6.2 SMP i386
452#endif
453
454#if (defined _THREAD_STACK_SIZE) && (_THREAD_STACK_SIZE > 0)
455 PT_CALL( pthread_attr_setstacksize( a, _THREAD_STACK_SIZE ) );
456#endif
457
458 bool_t normal=
459#if defined(PLATFORM_LINUX) && defined(LINUX_SCHED_RR)
460 !sudo; // with sudo, even normal thread must use SCHED_RR
461#else
462 prio == 0; // create a default thread if
463#endif
464 if (!normal) {
465 // NB: PThreads priority handling is about as twisty as one can get it
466 // (and then some). DON*T TRUST ANYTHING YOU READ ON THE NET!!!
467
468 // "The specified scheduling parameters are only used if the scheduling
469 // parameter inheritance attribute is PTHREAD_EXPLICIT_SCHED."
470 //
471 PT_CALL( pthread_attr_setinheritsched( a, PTHREAD_EXPLICIT_SCHED ) );
472
473 //---
474 // "Select the scheduling policy for the thread: one of SCHED_OTHER
475 // (regular, non-real-time scheduling), SCHED_RR (real-time,
476 // round-robin) or SCHED_FIFO (real-time, first-in first-out)."
477 //
478 // "Using the RR policy ensures that all threads having the same
479 // priority level will be scheduled equally, regardless of their activity."
480 //
481 // "For SCHED_FIFO and SCHED_RR, the only required member of the
482 // sched_param structure is the priority sched_priority. For SCHED_OTHER,
483 // the affected scheduling parameters are implementation-defined."
484 //
485 // "The priority of a thread is specified as a delta which is added to
486 // the priority of the process."
487 //
488 // ".. priority is an integer value, in the range from 1 to 127.
489 // 1 is the least-favored priority, 127 is the most-favored."
490 //
491 // "Priority level 0 cannot be used: it is reserved for the system."
492 //
493 // "When you use specify a priority of -99 in a call to
494 // pthread_setschedparam(), the priority of the target thread is
495 // lowered to the lowest possible value."
496 //
497 // ...
498
499 // ** CONCLUSION **
500 //
501 // PThread priorities are _hugely_ system specific, and we need at
502 // least OS specific settings. Hopefully, Linuxes and OS X versions
503 // are uniform enough, among each other...
504 //
505#ifdef PLATFORM_OSX
506 // AK 10-Apr-07 (OS X PowerPC 10.4.9):
507 //
508 // With SCHED_RR, 26 seems to be the "normal" priority, where setting
509 // it does not seem to affect the order of threads processed.
510 //
511 // With SCHED_OTHER, the range 25..32 is normal (maybe the same 26,
512 // but the difference is not so clear with OTHER).
513 //
514 // 'sched_get_priority_min()' and '..max()' give 15, 47 as the
515 // priority limits. This could imply, user mode applications won't
516 // be able to use values outside of that range.
517 //
518 #define _PRIO_MODE SCHED_OTHER
519
520 // OS X 10.4.9 (PowerPC) gives ENOTSUP for process scope
521 //#define _PRIO_SCOPE PTHREAD_SCOPE_PROCESS
522
523 #define _PRIO_HI 32 // seems to work (_carefully_ picked!)
524 #define _PRIO_0 26 // detected
525 #define _PRIO_LO 1 // seems to work (tested)
526
527#elif defined(PLATFORM_LINUX)
528 // (based on Ubuntu Linux 2.6.15 kernel)
529 //
530 // SCHED_OTHER is the default policy, but does not allow for priorities.
531 // SCHED_RR allows priorities, all of which (1..99) are higher than
532 // a thread with SCHED_OTHER policy.
533 //
534 // <http://kerneltrap.org/node/6080>
535 // <http://en.wikipedia.org/wiki/Native_POSIX_Thread_Library>
536 // <http://www.net.in.tum.de/~gregor/docs/pthread-scheduling.html>
537 //
538 // Manuals suggest checking #ifdef _POSIX_THREAD_PRIORITY_SCHEDULING,
539 // but even Ubuntu does not seem to define it.
540 //
541 #define _PRIO_MODE SCHED_RR
542
543 // NTLP 2.5: only system scope allowed (being the basic reason why
544 // root privileges are required..)
545 //#define _PRIO_SCOPE PTHREAD_SCOPE_PROCESS
546
547 #define _PRIO_HI 99
548 #define _PRIO_0 50
549 #define _PRIO_LO 1
550
551#elif defined(PLATFORM_BSD)
552 //
553 // <http://www.net.in.tum.de/~gregor/docs/pthread-scheduling.html>
554 //
555 // "When control over the thread scheduling is desired, then FreeBSD
556 // with the libpthread implementation is by far the best choice .."
557 //
558 #define _PRIO_MODE SCHED_OTHER
559 #define _PRIO_SCOPE PTHREAD_SCOPE_PROCESS
560 #define _PRIO_HI 31
561 #define _PRIO_0 15
562 #define _PRIO_LO 1
563
564#elif defined(PLATFORM_CYGWIN)
565 //
566 // TBD: Find right values for Cygwin
567 //
568#else
569 #error "Unknown OS: not implemented!"
570#endif
571
572#ifdef _PRIO_SCOPE
573 PT_CALL( pthread_attr_setscope( a, _PRIO_SCOPE ) );
574#endif
575 PT_CALL( pthread_attr_setschedpolicy( a, _PRIO_MODE ) );
576
577#define _PRIO_AN (_PRIO_0 + ((_PRIO_HI-_PRIO_0)/2) )
578#define _PRIO_BN (_PRIO_LO + ((_PRIO_0-_PRIO_LO)/2) )
579
580 sp.sched_priority=
581 (prio == +2) ? _PRIO_HI :
582 (prio == +1) ? _PRIO_AN :
583#if defined(PLATFORM_LINUX) && defined(LINUX_SCHED_RR)
584 (prio == 0) ? _PRIO_0 :
585#endif
586 (prio == -1) ? _PRIO_BN : _PRIO_LO;
587
588 PT_CALL( pthread_attr_setschedparam( a, &sp ) );
589 }
590
591 //---
592 // Seems on OS X, _POSIX_THREAD_THREADS_MAX is some kind of system
593 // thread limit (not userland thread). Actual limit for us is way higher.
594 // PTHREAD_THREADS_MAX is not defined (even though man page refers to it!)
595 //
596# ifndef THREAD_CREATE_RETRIES_MAX
597 // Don't bother with retries; a failure is a failure
598 //
599 {
600 int rc= pthread_create( ref, a, func, data );
601 if (rc) _PT_FAIL( rc, "pthread_create()", __FILE__, __LINE__-1 );
602 }
603# else
604# error "This code deprecated"
605/*
606 // Wait slightly if thread creation has exchausted the system
607 //
608 { uint_t retries;
609 for( retries=0; retries<THREAD_CREATE_RETRIES_MAX; retries++ ) {
610
611 int rc= pthread_create( ref, a, func, data );
612 //
613 // OS X / Linux:
614 // EAGAIN: ".. lacked the necessary resources to create
615 // another thread, or the system-imposed limit on the
616 // total number of threads in a process
617 // [PTHREAD_THREADS_MAX] would be exceeded."
618 // EINVAL: attr is invalid
619 // Linux:
620 // EPERM: no rights for given parameters or scheduling (no sudo)
621 // ENOMEM: (known to fail with this code, too - not listed in man)
622
623 if (rc==0) break; // ok!
624
625 // In practise, exhaustion seems to be coming from memory, not a
626 // maximum number of threads. Keep tuning... ;)
627 //
628 if (rc==EAGAIN) {
629//fprintf( stderr, "Looping (retries=%d) ", retries ); // DEBUG
630
631 // Try again, later.
632
633 Yield();
634 } else {
635 _PT_FAIL( rc, "pthread_create()", __FILE__, __LINE__ );
636 }
637 }
638 }
639*/
640# endif
641
642 if (a) {
643 PT_CALL( pthread_attr_destroy(a) );
644 }
645 }
646 //
647 /*
648 * Wait for a thread to finish.
649 *
650 * 'mu_ref' is a lock we should use for the waiting; initially unlocked.
651 * Same lock as passed to THREAD_EXIT.
652 *
653 * Returns TRUE for succesful wait, FALSE for timed out
654 */
655#ifdef PTHREAD_TIMEDJOIN
656 bool_t THREAD_WAIT( THREAD_T *ref, double secs )
657#else
658 bool_t THREAD_WAIT( THREAD_T *ref, SIGNAL_T *signal_ref, MUTEX_T *mu_ref, volatile enum e_status *st_ref, double secs )
659#endif
660{
661 struct timespec ts_store;
662 const struct timespec *timeout= NULL;
663 bool_t done;
664
665 // Do timeout counting before the locks
666 //
667#ifdef PTHREAD_TIMEDJOIN
668 if (secs>=0.0) {
669#else
670 if (secs>0.0) {
671#endif
672 prepare_timeout( &ts_store, now_secs()+secs );
673 timeout= &ts_store;
674 }
675
676#ifdef PTHREAD_TIMEDJOIN
677 /* Thread is joinable
678 */
679 if (!timeout) {
680 PT_CALL( pthread_join( *ref, NULL /*ignore exit value*/ ));
681 done= TRUE;
682 } else {
683 int rc= PTHREAD_TIMEDJOIN( *ref, NULL, timeout );
684 if ((rc!=0) && (rc!=ETIMEDOUT)) {
685 _PT_FAIL( rc, "PTHREAD_TIMEDJOIN", __FILE__, __LINE__-2 );
686 }
687 done= rc==0;
688 }
689#else
690 /* Since we've set the thread up as PTHREAD_CREATE_DETACHED, we cannot
691 * join with it. Use the cond.var.
692 */
693 MUTEX_LOCK( mu_ref );
694
695 // 'secs'==0.0 does not need to wait, just take the current status
696 // within the 'mu_ref' locks
697 //
698 if (secs != 0.0) {
699 while( *st_ref < DONE ) {
700 if (!timeout) {
701 PT_CALL( pthread_cond_wait( signal_ref, mu_ref ));
702 } else {
703 int rc= pthread_cond_timedwait( signal_ref, mu_ref, timeout );
704 if (rc==ETIMEDOUT) break;
705 if (rc!=0) _PT_FAIL( rc, "pthread_cond_timedwait", __FILE__, __LINE__-2 );
706 }
707 }
708 }
709 done= *st_ref >= DONE; // DONE|ERROR_ST|CANCELLED
710
711 MUTEX_UNLOCK( mu_ref );
712#endif
713 return done;
714 }
715 //
716 void THREAD_KILL( THREAD_T *ref ) {
717 pthread_cancel( *ref );
718 }
719#endif
720
721static const lua_Alloc alloc_f= 0;
diff --git a/src/threading.h b/src/threading.h
new file mode 100644
index 0000000..4a83229
--- /dev/null
+++ b/src/threading.h
@@ -0,0 +1,196 @@
1/*
2* THREADING.H
3*/
4#ifndef THREADING_H
5#define THREADING_H
6
7/* Platform detection
8*/
9#ifdef _WIN32_WCE
10 #define PLATFORM_POCKETPC
11#elif (defined _WIN32)
12 #define PLATFORM_WIN32
13#elif (defined __linux__)
14 #define PLATFORM_LINUX
15#elif (defined __APPLE__) && (defined __MACH__)
16 #define PLATFORM_OSX
17#elif (defined __NetBSD__) || (defined __FreeBSD__) || (defined BSD)
18 #define PLATFORM_BSD
19#elif (defined __QNX__)
20 #define PLATFORM_QNX
21#elif (defined __CYGWIN__)
22 #define PLATFORM_CYGWIN
23#else
24 #error "Unknown platform!"
25#endif
26
27typedef int bool_t;
28#ifndef FALSE
29# define FALSE 0
30# define TRUE 1
31#endif
32
33typedef unsigned int uint_t;
34
35#if defined(PLATFORM_WIN32) && defined(__GNUC__)
36/* MinGW with MSVCR80.DLL */
37/* Do this BEFORE including time.h so that it is declaring _mktime32()
38 * as it would have declared mktime().
39 */
40# define mktime _mktime32
41#endif
42#include <time.h>
43
44/* Note: ERROR is a defined entity on Win32
45*/
46enum e_status { PENDING, RUNNING, WAITING, DONE, ERROR_ST, CANCELLED };
47
48
49/*---=== Locks & Signals ===---
50*/
51
52#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC)
53 #define WIN32_LEAN_AND_MEAN
54 // 'SignalObjectAndWait' needs this (targets Windows 2000 and above)
55 #define _WIN32_WINNT 0x0400
56 #include <windows.h>
57 #include <process.h>
58
59 // MSDN: http://msdn2.microsoft.com/en-us/library/ms684254.aspx
60 //
61 // CRITICAL_SECTION can be used for simple code protection. Mutexes are
62 // needed for use with the SIGNAL system.
63 //
64 #define MUTEX_T HANDLE
65 void MUTEX_INIT( MUTEX_T *ref );
66 #define MUTEX_RECURSIVE_INIT(ref) MUTEX_INIT(ref) /* always recursive in Win32 */
67 void MUTEX_FREE( MUTEX_T *ref );
68 void MUTEX_LOCK( MUTEX_T *ref );
69 void MUTEX_UNLOCK( MUTEX_T *ref );
70
71 typedef unsigned THREAD_RETURN_T;
72
73 #define SIGNAL_T HANDLE
74
75 #define YIELD() Sleep(0)
76#else
77 // PThread (Linux, OS X, ...)
78 //
79 #include <pthread.h>
80
81 #ifdef PLATFORM_LINUX
82 # define _MUTEX_RECURSIVE PTHREAD_MUTEX_RECURSIVE_NP
83 #else
84 /* OS X, ... */
85 # define _MUTEX_RECURSIVE PTHREAD_MUTEX_RECURSIVE
86 #endif
87
88 #define MUTEX_T pthread_mutex_t
89 #define MUTEX_INIT(ref) pthread_mutex_init(ref,NULL)
90 #define MUTEX_RECURSIVE_INIT(ref) \
91 { pthread_mutexattr_t a; pthread_mutexattr_init( &a ); \
92 pthread_mutexattr_settype( &a, _MUTEX_RECURSIVE ); \
93 pthread_mutex_init(ref,&a); pthread_mutexattr_destroy( &a ); \
94 }
95 #define MUTEX_FREE(ref) pthread_mutex_destroy(ref)
96 #define MUTEX_LOCK(ref) pthread_mutex_lock(ref)
97 #define MUTEX_UNLOCK(ref) pthread_mutex_unlock(ref)
98
99 typedef void * THREAD_RETURN_T;
100
101 typedef pthread_cond_t SIGNAL_T;
102
103 void SIGNAL_ONE( SIGNAL_T *ref );
104
105 // Yield is non-portable:
106 //
107 // OS X 10.4.8/9 has pthread_yield_np()
108 // Linux 2.4 has pthread_yield() if _GNU_SOURCE is #defined
109 // FreeBSD 6.2 has pthread_yield()
110 // ...
111 //
112 #ifdef PLATFORM_OSX
113 #define YIELD() pthread_yield_np()
114 #else
115 #define YIELD() pthread_yield()
116 #endif
117#endif
118
119void SIGNAL_INIT( SIGNAL_T *ref );
120void SIGNAL_FREE( SIGNAL_T *ref );
121void SIGNAL_ALL( SIGNAL_T *ref );
122
123/*
124* 'time_d': <0.0 for no timeout
125* 0.0 for instant check
126* >0.0 absolute timeout in secs + ms
127*/
128typedef double time_d;
129time_d now_secs(void);
130
131time_d SIGNAL_TIMEOUT_PREPARE( double rel_secs );
132
133bool_t SIGNAL_WAIT( SIGNAL_T *ref, MUTEX_T *mu, time_d timeout );
134
135
136/*---=== Threading ===---
137*/
138
139#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC)
140
141 typedef HANDLE THREAD_T;
142 //
143 void THREAD_CREATE( THREAD_T *ref,
144 THREAD_RETURN_T (__stdcall *func)( void * ),
145 void *data, int prio /* -3..+3 */ );
146
147# define THREAD_PRIO_MIN (-3)
148# define THREAD_PRIO_MAX (+3)
149
150#else
151 /* Platforms that have a timed 'pthread_join()' can get away with a simpler
152 * implementation. Others will use a condition variable.
153 */
154# ifdef USE_PTHREAD_TIMEDJOIN
155# ifdef PLATFORM_OSX
156# error "No 'pthread_timedjoin()' on this system"
157# else
158 /* Linux, ... */
159# define PTHREAD_TIMEDJOIN pthread_timedjoin_np
160# endif
161# endif
162
163 typedef pthread_t THREAD_T;
164
165 void THREAD_CREATE( THREAD_T *ref,
166 THREAD_RETURN_T (*func)( void * ),
167 void *data, int prio /* -2..+2 */ );
168
169# if defined(PLATFORM_LINUX)
170 volatile bool_t sudo;
171# ifdef LINUX_SCHED_RR
172# define THREAD_PRIO_MIN (sudo ? -2 : 0)
173# else
174# define THREAD_PRIO_MIN (0)
175# endif
176# define THREAD_PRIO_MAX (sudo ? +2 : 0)
177# else
178# define THREAD_PRIO_MIN (-2)
179# define THREAD_PRIO_MAX (+2)
180# endif
181#endif
182
183/*
184* Win32 and PTHREAD_TIMEDJOIN allow waiting for a thread with a timeout.
185* Posix without PTHREAD_TIMEDJOIN needs to use a condition variable approach.
186*/
187#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN)
188 bool_t THREAD_WAIT( THREAD_T *ref, double secs );
189#else
190 bool_t THREAD_WAIT( THREAD_T *ref, SIGNAL_T *signal_ref, MUTEX_T *mu_ref, volatile enum e_status *st_ref, double secs );
191#endif
192
193void THREAD_KILL( THREAD_T *ref );
194
195#endif
196 // THREADING_H
diff --git a/src/tools.c b/src/tools.c
new file mode 100644
index 0000000..a2ec517
--- /dev/null
+++ b/src/tools.c
@@ -0,0 +1,1198 @@
1/*
2 * TOOLS.C Copyright (c) 2002-08, Asko Kauppi
3 *
4 * Lua tools to support Lanes.
5*/
6
7/*
8===============================================================================
9
10Copyright (C) 2002-08 Asko Kauppi <akauppi@gmail.com>
11
12Permission is hereby granted, free of charge, to any person obtaining a copy
13of this software and associated documentation files (the "Software"), to deal
14in the Software without restriction, including without limitation the rights
15to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16copies of the Software, and to permit persons to whom the Software is
17furnished to do so, subject to the following conditions:
18
19The above copyright notice and this permission notice shall be included in
20all copies or substantial portions of the Software.
21
22THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
28THE SOFTWARE.
29
30===============================================================================
31*/
32
33#include "tools.h"
34
35#include "lualib.h"
36#include "lauxlib.h"
37
38#include <stdio.h>
39#include <string.h>
40#include <ctype.h>
41#include <stdlib.h>
42
43static volatile lua_CFunction hijacked_tostring; // = NULL
44
45MUTEX_T deep_lock;
46MUTEX_T mtid_lock;
47
48/*---=== luaG_dump ===---*/
49
50void luaG_dump( lua_State* L ) {
51
52 int top= lua_gettop(L);
53 int i;
54
55 fprintf( stderr, "\n\tDEBUG STACK:\n" );
56
57 if (top==0)
58 fprintf( stderr, "\t(none)\n" );
59
60 for( i=1; i<=top; i++ ) {
61 int type= lua_type( L, i );
62
63 fprintf( stderr, "\t[%d]= (%s) ", i, lua_typename(L,type) );
64
65 // Print item contents here...
66 //
67 // Note: this requires 'tostring()' to be defined. If it is NOT,
68 // enable it for more debugging.
69 //
70 STACK_CHECK(L)
71 STACK_GROW( L, 2 )
72
73 lua_getglobal( L, "tostring" );
74 //
75 // [-1]: tostring function, or nil
76
77 if (!lua_isfunction(L,-1)) {
78 fprintf( stderr, "('tostring' not available)" );
79 } else {
80 lua_pushvalue( L, i );
81 lua_call( L, 1 /*args*/, 1 /*retvals*/ );
82
83 // Don't trust the string contents
84 //
85 fprintf( stderr, "%s", lua_tostring(L,-1) );
86 }
87 lua_pop(L,1);
88 STACK_END(L,0)
89 fprintf( stderr, "\n" );
90 }
91 fprintf( stderr, "\n" );
92}
93
94
95/*---=== luaG_openlibs ===---*/
96
97static const luaL_Reg libs[] = {
98 { LUA_LOADLIBNAME, luaopen_package },
99 { LUA_TABLIBNAME, luaopen_table },
100 { LUA_IOLIBNAME, luaopen_io },
101 { LUA_OSLIBNAME, luaopen_os },
102 { LUA_STRLIBNAME, luaopen_string },
103 { LUA_MATHLIBNAME, luaopen_math },
104 { LUA_DBLIBNAME, luaopen_debug },
105 //
106 { "base", NULL }, // ignore "base" (already acquired it)
107 { "coroutine", NULL }, // part of Lua 5.1 base package
108 { NULL, NULL }
109};
110
111static bool_t openlib( lua_State *L, const char *name, size_t len ) {
112
113 unsigned i;
114 bool_t all= strncmp( name, "*", len ) == 0;
115
116 for( i=0; libs[i].name; i++ ) {
117 if (all || (strncmp(name, libs[i].name, len) ==0)) {
118 if (libs[i].func) {
119 STACK_GROW(L,2);
120 lua_pushcfunction( L, libs[i].func );
121 lua_pushstring( L, libs[i].name );
122 lua_call( L, 1, 0 );
123 }
124 if (!all) return TRUE;
125 }
126 }
127 return all;
128}
129
130/*
131* Like 'luaL_openlibs()' but allows the set of libraries be selected
132*
133* NULL no libraries, not even base
134* "" base library only
135* "io,string" named libraries
136* "*" all libraries
137*
138* Base ("unpack", "print" etc.) is always added, unless 'libs' is NULL.
139*
140* Returns NULL for ok, position of error within 'libs' on failure.
141*/
142#define is_name_char(c) (isalpha(c) || (c)=='*')
143
144const char *luaG_openlibs( lua_State *L, const char *libs ) {
145 const char *p;
146 unsigned len;
147
148 if (!libs) return NULL; // no libs, not even 'base'
149
150 // 'lua.c' stops GC during initialization so perhaps its a good idea. :)
151 //
152 lua_gc(L, LUA_GCSTOP, 0);
153
154 // Anything causes 'base' to be taken in
155 //
156 STACK_GROW(L,2);
157 lua_pushcfunction( L, luaopen_base );
158 lua_pushliteral( L, "" );
159 lua_call( L, 1, 0 );
160
161 for( p= libs; *p; p+=len ) {
162 len=0;
163 while (*p && !is_name_char(*p)) p++; // bypass delimiters
164 while (is_name_char(p[len])) len++; // bypass name
165 if (len && (!openlib( L, p, len )))
166 break;
167 }
168 lua_gc(L, LUA_GCRESTART, 0);
169
170 return *p ? p : NULL;
171}
172
173
174
175/*---=== Deep userdata ===---*/
176
177/* The deep portion must be allocated separately of any Lua state's; it's
178* lifespan may be longer than that of the creating state.
179*/
180#define DEEP_MALLOC malloc
181#define DEEP_FREE free
182
183/*
184* 'registry[REGKEY]' is a two-way lookup table for 'idfunc's and those type's
185* metatables:
186*
187* metatable -> idfunc
188* idfunc -> metatable
189*/
190#define DEEP_LOOKUP_KEY ((void*)set_deep_lookup)
191 // any unique light userdata
192
193static void push_registry_subtable( lua_State *L, void *token );
194
195/*
196* Sets up [-1]<->[-2] two-way lookups, and ensures the lookup table exists.
197* Pops the both values off the stack.
198*/
199void set_deep_lookup( lua_State *L ) {
200
201 STACK_GROW(L,3);
202
203 STACK_CHECK(L)
204#if 1
205 push_registry_subtable( L, DEEP_LOOKUP_KEY );
206#else
207 /* ..to be removed.. */
208 lua_pushlightuserdata( L, DEEP_LOOKUP_KEY );
209 lua_rawget( L, LUA_REGISTRYINDEX );
210
211 if (lua_isnil(L,-1)) {
212 // First time here; let's make the lookup
213 //
214 lua_pop(L,1);
215
216 lua_newtable(L);
217 lua_pushlightuserdata( L, DEEP_LOOKUP_KEY );
218 lua_pushvalue(L,-2);
219 //
220 // [-3]: {} (2nd ref)
221 // [-2]: DEEP_LOOKUP_KEY
222 // [-1]: {}
223
224 lua_rawset( L, LUA_REGISTRYINDEX );
225 //
226 // [-1]: lookup table (empty)
227 }
228#endif
229 STACK_MID(L,1)
230
231 lua_insert(L,-3);
232
233 // [-3]: lookup table
234 // [-2]: A
235 // [-1]: B
236
237 lua_pushvalue( L,-1 ); // B
238 lua_pushvalue( L,-3 ); // A
239 lua_rawset( L, -5 ); // B->A
240 lua_rawset( L, -3 ); // A->B
241 lua_pop( L,1 );
242
243 STACK_END(L,-2)
244}
245
246/*
247* Pops the key (metatable or idfunc) off the stack, and replaces with the
248* deep lookup value (idfunc/metatable/nil).
249*/
250void get_deep_lookup( lua_State *L ) {
251
252 STACK_GROW(L,1);
253
254 STACK_CHECK(L)
255 lua_pushlightuserdata( L, DEEP_LOOKUP_KEY );
256 lua_rawget( L, LUA_REGISTRYINDEX );
257
258 if (!lua_isnil(L,-1)) {
259 // [-2]: key (metatable or idfunc)
260 // [-1]: lookup table
261
262 lua_insert( L, -2 );
263 lua_rawget( L, -2 );
264
265 // [-2]: lookup table
266 // [-1]: value (metatable / idfunc / nil)
267 }
268 lua_remove(L,-2);
269 // remove lookup, or unused key
270 STACK_END(L,0)
271}
272
273/*
274* Return the registered ID function for 'index' (deep userdata proxy),
275* or NULL if 'index' is not a deep userdata proxy.
276*/
277static
278lua_CFunction get_idfunc( lua_State *L, int index ) {
279 lua_CFunction ret;
280
281 index= STACK_ABS(L,index);
282
283 STACK_GROW(L,1);
284
285 STACK_CHECK(L)
286 if (!lua_getmetatable( L, index ))
287 return NULL; // no metatable
288
289 // [-1]: metatable of [index]
290
291 get_deep_lookup(L);
292 //
293 // [-1]: idfunc/nil
294
295 ret= lua_tocfunction(L,-1);
296 lua_pop(L,1);
297 STACK_END(L,0)
298 return ret;
299}
300
301
302/*
303* void= mt.__gc( proxy_ud )
304*
305* End of life for a proxy object; reduce the deep reference count and clean
306* it up if reaches 0.
307*/
308static
309int deep_userdata_gc( lua_State *L ) {
310 DEEP_PRELUDE **proxy= (DEEP_PRELUDE**)lua_touserdata( L, 1 );
311 DEEP_PRELUDE *p= *proxy;
312 int v;
313
314 *proxy= 0; // make sure we don't use it any more
315
316 MUTEX_LOCK( &deep_lock );
317 v= --(p->refcount);
318 MUTEX_UNLOCK( &deep_lock );
319
320 if (v==0) {
321 int pushed;
322
323 // Call 'idfunc( "delete", deep_ptr )' to make deep cleanup
324 //
325 lua_CFunction idfunc= get_idfunc(L,1);
326 ASSERT_L(idfunc);
327
328 lua_settop(L,0); // clean stack so we can call 'idfunc' directly
329
330 // void= idfunc( "delete", lightuserdata )
331 //
332 lua_pushliteral( L, "delete" );
333 lua_pushlightuserdata( L, p->deep );
334 pushed= idfunc(L);
335
336 if (pushed)
337 luaL_error( L, "Bad idfunc on \"delete\": returned something" );
338
339 DEEP_FREE( (void*)p );
340 }
341 return 0;
342}
343
344
345/*
346* Push a proxy userdata on the stack.
347*
348* Initializes necessary structures if it's the first time 'idfunc' is being
349* used in this Lua state (metatable, registring it). Otherwise, increments the
350* reference count.
351*/
352void luaG_push_proxy( lua_State *L, lua_CFunction idfunc, DEEP_PRELUDE *prelude ) {
353 DEEP_PRELUDE **proxy;
354
355 MUTEX_LOCK( &deep_lock );
356 ++(prelude->refcount); // one more proxy pointing to this deep data
357 MUTEX_UNLOCK( &deep_lock );
358
359 STACK_GROW(L,4);
360
361 STACK_CHECK(L)
362
363 proxy= lua_newuserdata( L, sizeof( DEEP_PRELUDE* ) );
364 ASSERT_L(proxy);
365 *proxy= prelude;
366
367 // Get/create metatable for 'idfunc' (in this state)
368 //
369 lua_pushcfunction( L, idfunc ); // key
370 get_deep_lookup(L);
371 //
372 // [-2]: proxy
373 // [-1]: metatable / nil
374
375 if (lua_isnil(L,-1)) {
376 // No metatable yet; make one and register it
377 //
378 lua_pop(L,1);
379
380 // tbl= idfunc( "metatable" )
381 //
382 lua_pushcfunction( L, idfunc );
383 lua_pushliteral( L, "metatable" );
384 lua_call( L, 1 /*args*/, 1 /*results*/ );
385 //
386 // [-2]: proxy
387 // [-1]: metatable (returned by 'idfunc')
388
389 if (!lua_istable(L,-1))
390 luaL_error( L, "Bad idfunc on \"metatable\": did not return one" );
391
392 // Add '__gc' method
393 //
394 lua_pushcfunction( L, deep_userdata_gc );
395 lua_setfield( L, -2, "__gc" );
396
397 // Memorize for later rounds
398 //
399 lua_pushvalue( L,-1 );
400 lua_pushcfunction( L, idfunc );
401 //
402 // [-4]: proxy
403 // [-3]: metatable (2nd ref)
404 // [-2]: metatable
405 // [-1]: idfunc
406
407 set_deep_lookup(L);
408 }
409 STACK_MID(L,2)
410 ASSERT_L( lua_isuserdata(L,-2) );
411 ASSERT_L( lua_istable(L,-1) );
412
413 // [-2]: proxy userdata
414 // [-1]: metatable to use
415
416 lua_setmetatable( L, -2 );
417
418 STACK_END(L,1)
419 // [-1]: proxy userdata
420}
421
422
423/*
424* Create a deep userdata
425*
426* proxy_ud= deep_userdata( idfunc [, ...] )
427*
428* Creates a deep userdata entry of the type defined by 'idfunc'.
429* Other parameters are passed on to the 'idfunc' "new" invocation.
430*
431* 'idfunc' must fulfill the following features:
432*
433* lightuserdata= idfunc( "new" [, ...] ) -- creates a new deep data instance
434* void= idfunc( "delete", lightuserdata ) -- releases a deep data instance
435* tbl= idfunc( "metatable" ) -- gives metatable for userdata proxies
436*
437* Reference counting and true userdata proxying are taken care of for the
438* actual data type.
439*
440* Types using the deep userdata system (and only those!) can be passed between
441* separate Lua states via 'luaG_inter_move()'.
442*
443* Returns: 'proxy' userdata for accessing the deep data via 'luaG_todeep()'
444*/
445int luaG_deep_userdata( lua_State *L ) {
446 lua_CFunction idfunc= lua_tocfunction( L,1 );
447 int pushed;
448
449 DEEP_PRELUDE *prelude= DEEP_MALLOC( sizeof(DEEP_PRELUDE) );
450 ASSERT_L(prelude);
451
452 prelude->refcount= 0; // 'luaG_push_proxy' will lift it to 1
453
454 STACK_GROW(L,1);
455 STACK_CHECK(L)
456
457 // Replace 'idfunc' with "new" in the stack (keep possible other params)
458 //
459 lua_remove(L,1);
460 lua_pushliteral( L, "new" );
461 lua_insert(L,1);
462
463 // lightuserdata= idfunc( "new" [, ...] )
464 //
465 pushed= idfunc(L);
466
467 if ((pushed!=1) || lua_type(L,-1) != LUA_TLIGHTUSERDATA)
468 luaL_error( L, "Bad idfunc on \"new\": did not return light userdata" );
469
470 prelude->deep= lua_touserdata(L,-1);
471 ASSERT_L(prelude->deep);
472
473 lua_pop(L,1); // pop deep data
474
475 luaG_push_proxy( L, idfunc, prelude );
476 //
477 // [-1]: proxy userdata
478
479 STACK_END(L,1)
480 return 1;
481}
482
483
484/*
485* Access deep userdata through a proxy.
486*
487* Reference count is not changed, and access to the deep userdata is not
488* serialized. It is the module's responsibility to prevent conflicting usage.
489*/
490void *luaG_todeep( lua_State *L, lua_CFunction idfunc, int index ) {
491 DEEP_PRELUDE **proxy;
492
493 STACK_CHECK(L)
494 if (get_idfunc(L,index) != idfunc)
495 return NULL; // no metatable, or wrong kind
496
497 proxy= (DEEP_PRELUDE**)lua_touserdata( L, index );
498 STACK_END(L,0)
499
500 return (*proxy)->deep;
501}
502
503
504/*
505* Copy deep userdata between two separate Lua states.
506*
507* Returns:
508* the id function of the copied value, or NULL for non-deep userdata
509* (not copied)
510*/
511static
512lua_CFunction luaG_copydeep( lua_State *L, lua_State *L2, int index ) {
513 DEEP_PRELUDE **proxy;
514 DEEP_PRELUDE *p;
515
516 lua_CFunction idfunc;
517
518 idfunc= get_idfunc( L, index );
519 if (!idfunc) return NULL; // not a deep userdata
520
521 // Increment reference count
522 //
523 proxy= (DEEP_PRELUDE**)lua_touserdata( L, index );
524 p= *proxy;
525
526 luaG_push_proxy( L2, idfunc, p );
527 //
528 // L2 [-1]: proxy userdata
529
530 return idfunc;
531}
532
533
534
535/*---=== Inter-state copying ===---*/
536
537/*-- Metatable copying --*/
538
539/*
540 * 'reg[ REG_MT_KNOWN ]'= {
541 * [ table ]= id_uint,
542 * ...
543 * [ id_uint ]= table,
544 * ...
545 * }
546 */
547
548/*
549* Push a registry subtable (keyed by unique 'token') onto the stack.
550* If the subtable does not exist, it is created and chained.
551*/
552static
553void push_registry_subtable( lua_State *L, void *token ) {
554
555 STACK_GROW(L,3);
556
557 STACK_CHECK(L)
558
559 lua_pushlightuserdata( L, token );
560 lua_rawget( L, LUA_REGISTRYINDEX );
561 //
562 // [-1]: nil/subtable
563
564 if (lua_isnil(L,-1)) {
565 lua_pop(L,1);
566 lua_newtable(L); // value
567 lua_pushlightuserdata( L, token ); // key
568 lua_pushvalue(L,-2);
569 //
570 // [-3]: value (2nd ref)
571 // [-2]: key
572 // [-1]: value
573
574 lua_rawset( L, LUA_REGISTRYINDEX );
575 }
576 STACK_END(L,1)
577
578 ASSERT_L( lua_istable(L,-1) );
579}
580
581#define REG_MTID ( (void*) get_mt_id )
582
583/*
584* Get a unique ID for metatable at [i].
585*/
586static
587uint_t get_mt_id( lua_State *L, int i ) {
588 static uint_t last_id= 0;
589 uint_t id;
590
591 i= STACK_ABS(L,i);
592
593 STACK_GROW(L,3);
594
595 STACK_CHECK(L)
596 push_registry_subtable( L, REG_MTID );
597 lua_pushvalue(L, i);
598 lua_rawget( L, -2 );
599 //
600 // [-2]: reg[REG_MTID]
601 // [-1]: nil/uint
602
603 id= lua_tointeger(L,-1); // 0 for nil
604 lua_pop(L,1);
605 STACK_MID(L,1)
606
607 if (id==0) {
608 MUTEX_LOCK( &mtid_lock );
609 id= ++last_id;
610 MUTEX_UNLOCK( &mtid_lock );
611
612 /* Create two-way references: id_uint <-> table
613 */
614 lua_pushvalue(L,i);
615 lua_pushinteger(L,id);
616 lua_rawset( L, -3 );
617
618 lua_pushinteger(L,id);
619 lua_pushvalue(L,i);
620 lua_rawset( L, -3 );
621 }
622 lua_pop(L,1); // remove 'reg[REG_MTID]' reference
623
624 STACK_END(L,0)
625
626 return id;
627}
628
629
630static int buf_writer( lua_State *L, const void* b, size_t n, void* B ) {
631 (void)L;
632 luaL_addlstring((luaL_Buffer*) B, (const char *)b, n);
633 return 0;
634}
635
636
637/*
638 * Check if we've already copied the same table from 'L', and
639 * reuse the old copy. This allows table upvalues shared by multiple
640 * local functions to point to the same table, also in the target.
641 *
642 * Always pushes a table to 'L2'.
643 *
644 * Returns TRUE if the table was cached (no need to fill it!); FALSE if
645 * it's a virgin.
646 */
647static
648bool_t push_cached_table( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i ) {
649 bool_t ret;
650
651 ASSERT_L( hijacked_tostring );
652 ASSERT_L( L2_cache_i != 0 );
653
654 STACK_GROW(L,2);
655 STACK_GROW(L2,3);
656
657 // Create an identity string for table at [i]; it should stay unique at
658 // least during copying of the data (then we can clear the caches).
659 //
660 STACK_CHECK(L)
661 lua_pushcfunction( L, hijacked_tostring );
662 lua_pushvalue( L, i );
663 lua_call( L, 1 /*args*/, 1 /*retvals*/ );
664 //
665 // [-1]: "table: 0x...."
666
667 STACK_END(L,1)
668 ASSERT_L( lua_type(L,-1) == LUA_TSTRING );
669
670 // L2_cache[id_str]= [{...}]
671 //
672 STACK_CHECK(L2)
673
674 // We don't need to use the from state ('L') in ID since the life span
675 // is only for the duration of a copy (both states are locked).
676 //
677 lua_pushstring( L2, lua_tostring(L,-1) );
678 lua_pop(L,1); // remove the 'tostring(tbl)' value (in L!)
679
680//fprintf( stderr, "<< ID: %s >>\n", lua_tostring(L2,-1) );
681
682 lua_pushvalue( L2, -1 );
683 lua_rawget( L2, L2_cache_i );
684 //
685 // [-2]: identity string ("table: 0x...")
686 // [-1]: table|nil
687
688 if (lua_isnil(L2,-1)) {
689 lua_pop(L2,1);
690 lua_newtable(L2);
691 lua_pushvalue(L2,-1);
692 lua_insert(L2,-3);
693 //
694 // [-3]: new table (2nd ref)
695 // [-2]: identity string
696 // [-1]: new table
697
698 lua_rawset(L2, L2_cache_i);
699 //
700 // [-1]: new table (tied to 'L2_cache' table')
701
702 ret= FALSE; // brand new
703
704 } else {
705 lua_remove(L2,-2);
706 ret= TRUE; // from cache
707 }
708 STACK_END(L2,1)
709 //
710 // L2 [-1]: table to use as destination
711
712 ASSERT_L( lua_istable(L2,-1) );
713 return ret;
714}
715
716
717/*
718 * Check if we've already copied the same function from 'L', and reuse the old
719 * copy.
720 *
721 * Always pushes a function to 'L2'.
722 */
723static void inter_copy_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i );
724
725static
726void push_cached_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i ) {
727 // TBD: Merge this and same code for tables
728
729 ASSERT_L( hijacked_tostring );
730 ASSERT_L( L2_cache_i != 0 );
731
732 STACK_GROW(L,2);
733 STACK_GROW(L2,3);
734
735 STACK_CHECK(L)
736 lua_pushcfunction( L, hijacked_tostring );
737 lua_pushvalue( L, i );
738 lua_call( L, 1 /*args*/, 1 /*retvals*/ );
739 //
740 // [-1]: "function: 0x...."
741
742 STACK_END(L,1)
743 ASSERT_L( lua_type(L,-1) == LUA_TSTRING );
744
745 // L2_cache[id_str]= function
746 //
747 STACK_CHECK(L2)
748
749 // We don't need to use the from state ('L') in ID since the life span
750 // is only for the duration of a copy (both states are locked).
751 //
752 lua_pushstring( L2, lua_tostring(L,-1) );
753 lua_pop(L,1); // remove the 'tostring(tbl)' value (in L!)
754
755//fprintf( stderr, "<< ID: %s >>\n", lua_tostring(L2,-1) );
756
757 lua_pushvalue( L2, -1 );
758 lua_rawget( L2, L2_cache_i );
759 //
760 // [-2]: identity string ("function: 0x...")
761 // [-1]: function|nil|true (true means: we're working on it; recursive)
762
763 if (lua_isnil(L2,-1)) {
764 lua_pop(L2,1);
765
766 // Set to 'true' for the duration of creation; need to find self-references
767 // via upvalues
768 //
769 lua_pushboolean(L2,TRUE);
770 lua_setfield( L2, L2_cache_i, lua_tostring(L2,-2) );
771
772 inter_copy_func( L2, L2_cache_i, L, i ); // pushes a copy of the func
773
774 lua_pushvalue(L2,-1);
775 lua_insert(L2,-3);
776 //
777 // [-3]: function (2nd ref)
778 // [-2]: identity string
779 // [-1]: function
780
781 lua_rawset(L2,L2_cache_i);
782 //
783 // [-1]: function (tied to 'L2_cache' table')
784
785 } else if (lua_isboolean(L2,-1)) {
786 // Loop in preparing upvalues; either direct or via a table
787 //
788 // Note: This excludes the case where a function directly addresses
789 // itself as an upvalue (recursive lane creation).
790 //
791 luaL_error( L, "Recursive use of upvalues; cannot copy the function" );
792
793 } else {
794 lua_remove(L2,-2);
795 }
796 STACK_END(L2,1)
797 //
798 // L2 [-1]: function
799
800 ASSERT_L( lua_isfunction(L2,-1) );
801}
802
803
804/*
805* Copy a function over, which has not been found in the cache.
806*/
807enum e_vt {
808 VT_NORMAL, VT_KEY, VT_METATABLE
809};
810static bool_t inter_copy_one_( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i, enum e_vt value_type );
811
812static void inter_copy_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i ) {
813
814 lua_CFunction cfunc= lua_tocfunction( L,i );
815 unsigned n;
816
817 ASSERT_L( L2_cache_i != 0 );
818
819 STACK_GROW(L,2);
820
821 STACK_CHECK(L)
822 if (!cfunc) { // Lua function
823 luaL_Buffer b;
824 const char *s;
825 size_t sz;
826 int tmp;
827 const char *name= NULL;
828
829#if 0
830 // "To get information about a function you push it onto the
831 // stack and start the what string with the character '>'."
832 //
833 { lua_Debug ar;
834 lua_pushvalue( L, i );
835 lua_getinfo(L, ">n", &ar); // fills 'name' and 'namewhat', pops function
836 name= ar.namewhat;
837
838 fprintf( stderr, "NAME: %s\n", name ); // just gives NULL
839 }
840#endif
841 // 'lua_dump()' needs the function at top of stack
842 //
843 if (i!=-1) lua_pushvalue( L, i );
844
845 luaL_buffinit(L,&b);
846 tmp= lua_dump(L, buf_writer, &b);
847 ASSERT_L(tmp==0);
848 //
849 // "value returned is the error code returned by the last call
850 // to the writer" (and we only return 0)
851
852 luaL_pushresult(&b); // pushes dumped string on 'L'
853 s= lua_tolstring(L,-1,&sz);
854 ASSERT_L( s && sz );
855
856 if (i!=-1) lua_remove( L, -2 );
857
858 // Note: Line numbers seem to be taken precisely from the
859 // original function. 'name' is not used since the chunk
860 // is precompiled (it seems...).
861 //
862 // TBD: Can we get the function's original name through, as well?
863 //
864 if (luaL_loadbuffer(L2, s, sz, name) != 0) {
865 // chunk is precompiled so only LUA_ERRMEM can happen
866 // "Otherwise, it pushes an error message"
867 //
868 STACK_GROW( L,1 );
869 luaL_error( L, "%s", lua_tostring(L2,-1) );
870 }
871 lua_pop(L,1); // remove the dumped string
872 STACK_MID(L,0)
873 }
874
875 /* push over any upvalues; references to this function will come from
876 * cache so we don't end up in eternal loop.
877 */
878 for( n=0; lua_getupvalue( L, i, 1+n ) != NULL; n++ ) {
879 if ((!cfunc) && lua_equal(L,i,-1)) {
880 /* Lua closure that has a (recursive) upvalue to itself
881 */
882 lua_pushvalue( L2, -((int)n)-1 );
883 } else {
884 if (!inter_copy_one_( L2, L2_cache_i, L, lua_gettop(L), VT_NORMAL ))
885 luaL_error( L, "Cannot copy upvalue type '%s'", luaG_typename(L,-1) );
886 }
887 lua_pop(L,1);
888 }
889 // L2: function + 'n' upvalues (>=0)
890
891 STACK_MID(L,0)
892
893 if (cfunc) {
894 lua_pushcclosure( L2, cfunc, n ); // eats up upvalues
895 } else {
896 // Set upvalues (originally set to 'nil' by 'lua_load')
897 //
898 int func_index= lua_gettop(L2)-n;
899
900 for( ; n>0; n-- ) {
901 const char *rc= lua_setupvalue( L2, func_index, n );
902 //
903 // "assigns the value at the top of the stack to the upvalue and returns its name.
904 // It also pops the value from the stack."
905
906 ASSERT_L(rc); // not having enough slots?
907 }
908 }
909 STACK_END(L,0)
910}
911
912
913/*
914* Copies a value from 'L' state (at index 'i') to 'L2' state. Does not remove
915* the original value.
916*
917* NOTE: Both the states must be solely in the current OS thread's posession.
918*
919* 'i' is an absolute index (no -1, ...)
920*
921* Returns TRUE if value was pushed, FALSE if its type is non-supported.
922*/
923static bool_t inter_copy_one_( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i, enum e_vt vt )
924{
925 bool_t ret= TRUE;
926
927 STACK_GROW( L2, 1 );
928
929 STACK_CHECK(L2)
930
931 switch ( lua_type(L,i) ) {
932 /* Basic types allowed both as values, and as table keys */
933
934 case LUA_TBOOLEAN:
935 lua_pushboolean( L2, lua_toboolean(L, i) );
936 break;
937
938 case LUA_TNUMBER:
939 /* LNUM patch support (keeping integer accuracy) */
940#ifdef LUA_LNUM
941 if (lua_isinteger(L,i)) {
942 lua_pushinteger( L2, lua_tointeger(L, i) );
943 break;
944 }
945#endif
946 lua_pushnumber( L2, lua_tonumber(L, i) );
947 break;
948
949 case LUA_TSTRING: {
950 size_t len; const char *s = lua_tolstring( L, i, &len );
951 lua_pushlstring( L2, s, len );
952 } break;
953
954 case LUA_TLIGHTUSERDATA:
955 lua_pushlightuserdata( L2, lua_touserdata(L, i) );
956 break;
957
958 /* The following types are not allowed as table keys */
959
960 case LUA_TUSERDATA: if (vt==VT_KEY) { ret=FALSE; break; }
961 /* Allow only deep userdata entities to be copied across
962 */
963 if (!luaG_copydeep( L, L2, i )) {
964 // Cannot copy it full; copy as light userdata
965 //
966 lua_pushlightuserdata( L2, lua_touserdata(L, i) );
967 } break;
968
969 case LUA_TNIL: if (vt==VT_KEY) { ret=FALSE; break; }
970 lua_pushnil(L2);
971 break;
972
973 case LUA_TFUNCTION: if (vt==VT_KEY) { ret=FALSE; break; } {
974 /*
975 * Passing C functions is risky; if they refer to LUA_ENVIRONINDEX
976 * and/or LUA_REGISTRYINDEX they might work unintended (not work)
977 * at the target.
978 *
979 * On the other hand, NOT copying them causes many self tests not
980 * to work (timer, hangtest, ...)
981 *
982 * The trouble is, we cannot KNOW if the function at hand is safe
983 * or not. We cannot study it's behaviour. We could trust the user,
984 * but they might not even know they're sending lua_CFunction over
985 * (as upvalues etc.).
986 */
987#if 0
988 if (lua_iscfunction(L,i))
989 luaL_error( L, "Copying lua_CFunction between Lua states is risky, and currently disabled." );
990#endif
991 STACK_CHECK(L2)
992 push_cached_func( L2, L2_cache_i, L, i );
993 ASSERT_L( lua_isfunction(L2,-1) );
994 STACK_END(L2,1)
995 } break;
996
997 case LUA_TTABLE: if (vt==VT_KEY) { ret=FALSE; break; } {
998
999 STACK_CHECK(L)
1000 STACK_CHECK(L2)
1001
1002 /* Check if we've already copied the same table from 'L' (during this transmission), and
1003 * reuse the old copy. This allows table upvalues shared by multiple
1004 * local functions to point to the same table, also in the target.
1005 * Also, this takes care of cyclic tables and multiple references
1006 * to the same subtable.
1007 *
1008 * Note: Even metatables need to go through this test; to detect
1009 * loops s.a. those in required module tables (getmetatable(lanes).lanes == lanes)
1010 */
1011 if (push_cached_table( L2, L2_cache_i, L, i )) {
1012 ASSERT_L( lua_istable(L2, -1) ); // from cache
1013 break;
1014 }
1015 ASSERT_L( lua_istable(L2,-1) );
1016
1017 STACK_GROW( L, 2 );
1018 STACK_GROW( L2, 2 );
1019
1020 lua_pushnil(L); // start iteration
1021 while( lua_next( L, i ) ) {
1022 uint_t val_i= lua_gettop(L);
1023 uint_t key_i= val_i-1;
1024
1025 /* Only basic key types are copied over; others ignored
1026 */
1027 if (inter_copy_one_( L2, 0 /*key*/, L, key_i, VT_KEY )) {
1028 /*
1029 * Contents of metatables are copied with cache checking;
1030 * important to detect loops.
1031 */
1032 if (inter_copy_one_( L2, L2_cache_i, L, val_i, VT_NORMAL )) {
1033 ASSERT_L( lua_istable(L2,-3) );
1034 lua_rawset( L2, -3 ); // add to table (pops key & val)
1035 } else {
1036 luaL_error( L, "Unable to copy over type '%s' (in %s)",
1037 luaG_typename(L,val_i),
1038 vt==VT_NORMAL ? "table":"metatable" );
1039 }
1040 }
1041 lua_pop( L, 1 ); // pop value (next round)
1042 }
1043 STACK_MID(L,0)
1044 STACK_MID(L2,1)
1045
1046 /* Metatables are expected to be immutable, and copied only once.
1047 */
1048 if (lua_getmetatable( L, i )) {
1049 //
1050 // L [-1]: metatable
1051
1052 uint_t mt_id= get_mt_id( L, -1 ); // Unique id for the metatable
1053
1054 STACK_GROW(L2,4);
1055
1056 push_registry_subtable( L2, REG_MTID );
1057 STACK_MID(L2,2);
1058 lua_pushinteger( L2, mt_id );
1059 lua_rawget( L2, -2 );
1060 //
1061 // L2 ([-3]: copied table)
1062 // [-2]: reg[REG_MTID]
1063 // [-1]: nil/metatable pre-known in L2
1064
1065 STACK_MID(L2,3);
1066
1067 if (lua_isnil(L2,-1)) { /* L2 did not know the metatable */
1068 lua_pop(L2,1);
1069 STACK_MID(L2,2);
1070ASSERT_L( lua_istable(L,-1) );
1071 if (inter_copy_one_( L2, L2_cache_i /*for function cacheing*/, L, lua_gettop(L) /*[-1]*/, VT_METATABLE )) {
1072 //
1073 // L2 ([-3]: copied table)
1074 // [-2]: reg[REG_MTID]
1075 // [-1]: metatable (copied from L)
1076
1077 STACK_MID(L2,3);
1078 // mt_id -> metatable
1079 //
1080 lua_pushinteger(L2,mt_id);
1081 lua_pushvalue(L2,-2);
1082 lua_rawset(L2,-4);
1083
1084 // metatable -> mt_id
1085 //
1086 lua_pushvalue(L2,-1);
1087 lua_pushinteger(L2,mt_id);
1088 lua_rawset(L2,-4);
1089
1090 STACK_MID(L2,3);
1091 } else {
1092 luaL_error( L, "Error copying a metatable" );
1093 }
1094 STACK_MID(L2,3);
1095 }
1096 // L2 ([-3]: copied table)
1097 // [-2]: reg[REG_MTID]
1098 // [-1]: metatable (pre-known or copied from L)
1099
1100 lua_remove(L2,-2); // take away 'reg[REG_MTID]'
1101 //
1102 // L2: ([-2]: copied table)
1103 // [-1]: metatable for that table
1104
1105 lua_setmetatable( L2, -2 );
1106
1107 // L2: [-1]: copied table (with metatable set if source had it)
1108
1109 lua_pop(L,1); // remove source metatable (L, not L2!)
1110 }
1111 STACK_END(L2,1)
1112 STACK_END(L,0)
1113 } break;
1114
1115 /* The following types cannot be copied */
1116
1117 case LUA_TTHREAD:
1118 ret=FALSE; break;
1119 }
1120
1121 STACK_END(L2, ret? 1:0)
1122
1123 return ret;
1124}
1125
1126
1127/*
1128* Akin to 'lua_xmove' but copies values between _any_ Lua states.
1129*
1130* NOTE: Both the states must be solely in the current OS thread's posession.
1131*
1132* Note: Parameters are in this order ('L' = from first) to be same as 'lua_xmove'.
1133*/
1134void luaG_inter_copy( lua_State* L, lua_State *L2, uint_t n )
1135{
1136 uint_t top_L= lua_gettop(L);
1137 uint_t top_L2= lua_gettop(L2);
1138 uint_t i;
1139
1140 /* steal Lua library's 'luaB_tostring()' from the first call. Other calls
1141 * don't have to have access to it.
1142 *
1143 * Note: multiple threads won't come here at once; this function will
1144 * be called before there can be multiple threads (no locking needed).
1145 */
1146 if (!hijacked_tostring) {
1147 STACK_GROW( L,1 );
1148
1149 STACK_CHECK(L)
1150 lua_getglobal( L, "tostring" );
1151 //
1152 // [-1]: function|nil
1153
1154 hijacked_tostring= lua_tocfunction( L, -1 );
1155 lua_pop(L,1);
1156 STACK_END(L,0)
1157
1158 if (!hijacked_tostring) {
1159 luaL_error( L, "Need to see 'tostring()' once" );
1160 }
1161 }
1162
1163 if (n > top_L)
1164 luaL_error( L, "Not enough values: %d < %d", top_L, n );
1165
1166 STACK_GROW( L2, n+1 );
1167
1168 /*
1169 * Make a cache table for the duration of this copy. Collects tables and
1170 * function entries, avoiding the same entries to be passed on as multiple
1171 * copies. ESSENTIAL i.e. for handling upvalue tables in the right manner!
1172 */
1173 lua_newtable(L2);
1174
1175 for (i=top_L-n+1; i <= top_L; i++) {
1176 if (!inter_copy_one_( L2, top_L2+1, L, i, VT_NORMAL )) {
1177
1178 luaL_error( L, "Cannot copy type: %s", luaG_typename(L,i) );
1179 }
1180 }
1181
1182 /*
1183 * Remove the cache table. Persistant caching would cause i.e. multiple
1184 * messages passed in the same table to use the same table also in receiving
1185 * end.
1186 */
1187 lua_remove( L2, top_L2+1 );
1188
1189 ASSERT_L( (uint_t)lua_gettop(L) == top_L );
1190 ASSERT_L( (uint_t)lua_gettop(L2) == top_L2+n );
1191}
1192
1193
1194void luaG_inter_move( lua_State* L, lua_State *L2, uint_t n )
1195{
1196 luaG_inter_copy( L, L2, n );
1197 lua_pop( L,(int)n );
1198}
diff --git a/src/tools.h b/src/tools.h
new file mode 100644
index 0000000..d155c65
--- /dev/null
+++ b/src/tools.h
@@ -0,0 +1,72 @@
1/*
2* TOOLS.H
3*/
4#ifndef TOOLS_H
5#define TOOLS_H
6
7#include "lua.h"
8#include "threading.h"
9 // MUTEX_T
10
11#include <assert.h>
12
13// Note: The < -10000 test is to leave registry/global/upvalue indices untouched
14//
15#define /*int*/ STACK_ABS(L,n) \
16 ( ((n) >= 0 || (n) <= -10000) ? (n) : lua_gettop(L) +(n) +1 )
17
18#ifdef NDEBUG
19 #define _ASSERT_L(lua,c) /*nothing*/
20 #define STACK_CHECK(L) /*nothing*/
21 #define STACK_MID(L,c) /*nothing*/
22 #define STACK_END(L,c) /*nothing*/
23 #define STACK_DUMP(L) /*nothing*/
24 #define DEBUG() /*nothing*/
25#else
26 #define _ASSERT_L(lua,c) { if (!(c)) luaL_error( lua, "ASSERT failed: %s:%d '%s'", __FILE__, __LINE__, #c ); }
27 //
28 #define STACK_CHECK(L) { int _oldtop_##L = lua_gettop(L);
29 #define STACK_MID(L,change) { int a= lua_gettop(L)-_oldtop_##L; int b= (change); \
30 if (a != b) luaL_error( L, "STACK ASSERT failed (%d not %d): %s:%d", a, b, __FILE__, __LINE__ ); }
31 #define STACK_END(L,change) STACK_MID(L,change) }
32
33 #define STACK_DUMP(L) luaG_dump(L);
34 #define DEBUG() fprintf( stderr, "<<%s %d>>\n", __FILE__, __LINE__ );
35#endif
36#define ASSERT_L(c) _ASSERT_L(L,c)
37
38#define STACK_GROW(L,n) { if (!lua_checkstack(L,n)) luaL_error( L, "Cannot grow stack!" ); }
39
40#define LUAG_FUNC( func_name ) static int LG_##func_name( lua_State *L )
41
42#define luaG_optunsigned(L,i,d) ((uint_t) luaL_optinteger(L,i,d))
43#define luaG_tounsigned(L,i) ((uint_t) lua_tointeger(L,i))
44
45#define luaG_isany(L,i) (!lua_isnil(L,i))
46
47#define luaG_typename( L, index ) lua_typename( L, lua_type(L,index) )
48
49void luaG_dump( lua_State* L );
50
51const char *luaG_openlibs( lua_State *L, const char *libs );
52
53int luaG_deep_userdata( lua_State *L );
54void *luaG_todeep( lua_State *L, lua_CFunction idfunc, int index );
55
56typedef struct {
57 volatile int refcount;
58 void *deep;
59} DEEP_PRELUDE;
60
61void luaG_push_proxy( lua_State *L, lua_CFunction idfunc, DEEP_PRELUDE *deep_userdata );
62
63void luaG_inter_copy( lua_State *L, lua_State *L2, uint_t n );
64void luaG_inter_move( lua_State *L, lua_State *L2, uint_t n );
65
66// Lock for reference counter inc/dec locks (to be initialized by outside code)
67//
68extern MUTEX_T deep_lock;
69extern MUTEX_T mtid_lock;
70
71#endif
72 // TOOLS_H
diff --git a/tests/argtable.lua b/tests/argtable.lua
new file mode 100644
index 0000000..5ed5d4e
--- /dev/null
+++ b/tests/argtable.lua
@@ -0,0 +1,38 @@
1--
2-- ARGTABLE.LUA Copyright (c) 2007, Asko Kauppi <akauppi@gmail.com>
3--
4-- Command line parameter parsing
5--
6-- NOTE: Wouldn't hurt having such a service built-in to Lua...? :P
7--
8
9local m= {}
10
11-- tbl= argtable(...)
12--
13-- Returns a table with 1..N indices being 'value' parameters, and any
14-- "-flag[=xxx]" or "--flag[=xxx]" parameters set to { flag=xxx/true }.
15--
16-- In other words, makes handling command line parameters simple. :)
17--
18-- 15 --> { 15 }
19-- -20 --> { -20 }
20-- -a --> { ['a']=true }
21-- --some=15 --> { ['some']=15 }
22-- --more=big --> { ['more']='big' }
23--
24function m.argtable(...)
25 local ret= {}
26 for i=1,select('#',...) do
27 local v= select(i,...)
28 local flag,val= string.match( v, "^%-+([^=]+)%=?(.*)" )
29 if flag and not tonumber(v) then
30 ret[flag]= (val=="") and true or tonumber(val) or val
31 else
32 table.insert( ret, v ) -- 1..N
33 end
34 end
35 return ret
36end
37
38return m
diff --git a/tests/assert.lua b/tests/assert.lua
new file mode 100644
index 0000000..85febfb
--- /dev/null
+++ b/tests/assert.lua
@@ -0,0 +1,318 @@
1--
2-- ASSERT.LUA Copyright (c) 2006-07, <akauppi@gmail.com>
3--
4-- Converting the Lua 'assert' function into a namespace table (without
5-- breaking compatibility with the basic 'assert()' calling).
6--
7-- This module allows shorthand use s.a. 'assert.table()' for asserting
8-- variable types, and is also being used by Lua-super constraints system
9-- for testing function parameter & return types.
10--
11-- All in all, a worthy module and could be part of Lua future versions.
12--
13-- Note: the 'assert' table is available for your own assertions, too. Just add
14-- more functions s.a. 'assert.myobj()' to check for custom invariants.
15-- They will then be available for the constraints check, too.
16--
17-- Author: <akauppi@gmail.com>
18--
19--[[
20/******************************************************************************
21* Lua 5.1.1 support and extension functions (assert.lua)
22*
23* Copyright (C) 2006-07, Asko Kauppi.
24*
25* NOTE: This license concerns only the particular source file; not necessarily
26* the project with which it has been delivered (the project may have a more
27* restrictive license, s.a. [L]GPL).
28*
29* Permission is hereby granted, free of charge, to any person obtaining
30* a copy of this software and associated documentation files (the
31* "Software"), to deal in the Software without restriction, including
32* without limitation the rights to use, copy, modify, merge, publish,
33* distribute, sublicense, and/or sell copies of the Software, and to
34* permit persons to whom the Software is furnished to do so, subject to
35* the following conditions:
36*
37* The above copyright notice and this permission notice shall be
38* included in all copies or substantial portions of the Software.
39*
40* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
41* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
42* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
43* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
44* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
45* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
46* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
47******************************************************************************/
48]]--
49
50local m= { _info= { MODULE= "Assert.* functions for constraints, and unit testing",
51 AUTHOR= "akauppi@gmail.com",
52 VERSION= 20070603, -- last change (yyyymmdd)
53 LICENSE= "MIT/X11" } }
54
55-- Global changes:
56-- 'assert' redefined, in a backwards compatible way
57--
58-- Module functions:
59-- none
60
61assert( type(assert) == "function" ) -- system assert function
62
63-----
64-- Integer range: INT_MIN..INT_MAX
65--
66local function try_maxint( n )
67 return (n > n-1) and n-1 -- false when outside the integer range
68end
69
70local INT_MAX=
71 try_maxint( 2^64 ) or
72 try_maxint( 2^32 ) or
73 try_maxint( 2^24 ) or -- float (24-bit mantissa)
74 assert( false )
75
76local INT_MIN= -(INT_MAX+1)
77
78
79---=== assert.*() ===---
80
81local at_msg= "type assertion error" -- TBD: better messages, about that exact situation
82local av_msg= "value assertion error"
83
84-- void= _assert( val [, msg_str [, lev_uint]] )
85--
86local function _assert( cond, msg, lev )
87 -- original 'assert' provides no level override, so we use 'error'.
88 --
89 if not cond then
90 error( msg or "assertion failed!", (lev or 1)+1 )
91 end
92end
93
94-- Note: following code uses the _new_ 'assert()' by purpose, since it provides
95-- a level override (original doesn't)
96--
97local function assert_v( v0 )
98 return function(v,msg)
99 _assert( v == v0, msg or av_msg, 2 )
100 return v
101 end
102end
103local function assert_t( str )
104 return function(v,msg)
105 _assert( type(v) == str, msg or at_msg, 2 )
106 return v
107 end
108end
109local function assert_t2( str )
110 return function(v,subtype,msg)
111 local t,st= type(v)
112 _assert( t==str and ((not subtype) or (st==subtype)),
113 msg or at_msg, 2 )
114 return v
115 end
116end
117
118assert=
119 {
120 __call= function(_,v,msg) -- plain 'assert()' (compatibility)
121 if v then return v end
122 _assert( v, msg, 2 )
123 end,
124
125 -- Hopefully, Lua will allow use of 'nil', 'function' and other reserved words as table
126 -- shortcuts in the future (5.1.1 does not).
127 --
128 ["nil"]= assert_v( nil ),
129 boolean= assert_t "boolean",
130 table= assert_t2 "table",
131 ["function"]= assert_t "function",
132 userdata= assert_t2 "userdata",
133
134 string= function( v, msg )
135 local s= tostring(v)
136 _assert( s, msg or at_msg, 2 )
137 return v
138 end,
139
140 char= function( v, msg )
141 -- 'char' is _not_ doing int->string conversion
142 _assert( type(v)=="string" and v:len()==1, msg or at_msg, 2 )
143 return v
144 end,
145
146 number= function( v, msg )
147 _assert( tonumber(v), msg or at_msg, 2 )
148 return v
149 end,
150
151 int= function( v, msg )
152 local n= tonumber(v)
153 _assert( n and (n >= INT_MIN) and (n <= INT_MAX) and math.floor(n) == n,
154 msg or at_msg, 2 )
155 return v
156 end,
157
158 uint= function( v, msg )
159 local n= tonumber(v)
160 -- unsigned integer upper range is the same as integers' (there's no
161 -- real unsigned within the Lua)
162 _assert( n and (n >= 0) and (n <= INT_MAX) and math.floor(n) == n,
163 msg or at_msg, 2 )
164 return v
165 end,
166
167 ['true']= assert_v( true ),
168 ['false']= assert_v( false ),
169
170 string_or_table= function( v, msg )
171 assert( tostring(v) or type(v)=="table", msg or at_msg, 2 )
172 return v
173 end,
174
175 number_or_string= function( v, msg )
176 assert( tonumber(v) or type(v)=="table", msg or at_msg, 2 )
177 return v
178 end,
179
180 any= function( v, msg )
181 assert( v ~= nil, msg or av_msg, 2 )
182 return v
183 end,
184
185 -- Range assertion, with extra parameters per instance
186 --
187 -- Note: values may be of _any_ type that can do >=, <= comparisons.
188 --
189 range= function( lo, hi )
190 _assert( lo and hi and lo <= hi, "Bad limits", 2 )
191 -- make sure the limits make sense (just once)
192
193 return function(v,msg,lev)
194 if ( (lo and v<lo) or (hi and v>hi) ) then
195 msg= msg or "not in range: ("..(lo or "")..","..(hi or "")..")"
196 _assert( false, msg, 2 )
197 end
198 return v
199 end
200 end,
201
202 -- Table contents assertion
203 -- - no unknown (non-numeric) keys are allowed
204 -- - numeric keys are ignored
205 --
206 -- Constraints patch should point to this, when using the ":{ ... }" constraint.
207 --
208 ["{}"]= function( tbl )
209
210 -- check all keys in 't' (including numeric, if any) that they do exist,
211 -- and carry the right type
212 --
213 local function subf1(v,t,msg,lev)
214 _assert(lev)
215 for k,f in pairs(t) do
216 -- 'f' is an assert function, or subtable
217 local ft= type(f)
218 if ft=="function" then
219 f( v[k], msg, lev+1 )
220 elseif ft=="table" then
221 _assert( type(v[k])=="table", msg or "no subtable "..tostring(k), lev+1 )
222 subf1( v[k], f, msg, lev+1 )
223 else
224 error( "Bad constraints table for '"..tostring(k).."'! (not a function)", lev+1 )
225 end
226 end
227 end
228
229 -- check there are no other (non-numeric) keys in 'v'
230 local function subf2(v,t,msg,lev)
231 _assert(lev)
232 for k,vv in pairs(v) do
233 if type(k)=="number" then
234 -- skip them
235 elseif not t[k] then
236 _assert( false, msg or "extra field: '"..tostring(k).."'", lev+1 )
237 elseif type(vv)=="table" then
238 subf2( vv, t[k], msg, lev+1 )
239 end
240 end
241 end
242
243 _assert( type(tbl)=="table", "Wrong parameter to assert['{}']" )
244
245 return function( v, msg, lev )
246 lev= (lev or 1)+1
247 _assert( type(v)=="table" ,msg, lev )
248 subf1( v, tbl, msg, lev )
249 subf2( v, tbl, msg, lev )
250 return v
251 end
252 end,
253
254 -- ...
255}
256setmetatable( assert, assert )
257
258assert.void= assert["nil"]
259
260
261-----
262-- void= assert.fails( function [,err_msg_str] )
263--
264-- Special assert function, to make sure the call within it fails, and gives a
265-- specific error message (to be used in unit testing).
266--
267function assert.fails( func_block, err_msg )
268 --
269 local st,err= pcall( func_block )
270 if st then
271 _assert( false, "Block expected to fail, but didn't.", 2 )
272 elseif err_msg and err ~= err_msg then
273 _assert( false, "Failed with wrong error message: \n"..
274 "'"..err.."'\nexpected: '"..err_msg.."'", 2 )
275 end
276end
277
278
279-----
280-- void= assert.failsnot( function [,err_msg_str] )
281--
282-- Similar to 'assert.fails' but expects the code to survive.
283--
284function assert.failsnot( func_block, err_msg )
285 --
286 local st,err= pcall( func_block )
287 if not st then
288 _assert( false, "Block expected NOT to fail, but did."..
289 (err and "\n\tError: '"..err.."'" or ""), 2 )
290 end
291end
292
293
294-----
295-- void= assert.nilerr( function [,err_msg_str] )
296--
297-- Expects the function to return with 'nil,err' failure code, with
298-- optionally error string matching. Similar to --> 'assert.fails()'
299--
300function assert.nilerr( func_block, err_msg )
301 --
302 local v,err= func_block()
303 _assert( v==nil, "Expected to return nil, but didn't: "..tostring(v), 2 )
304 if err_msg and err ~= err_msg then
305 _assert( false, "Failed with wrong error message: \n"..
306 "'"..err.."'\nexpected: '"..err_msg.."'", 2 )
307 end
308end
309
310
311-- Sanity check
312--
313assert( true )
314assert.fails( function() assert( false ) end )
315assert.fails( function() assert( nil ) end )
316
317
318return m -- just info
diff --git a/tests/atomic.lua b/tests/atomic.lua
new file mode 100644
index 0000000..a027453
--- /dev/null
+++ b/tests/atomic.lua
@@ -0,0 +1,18 @@
1--
2-- ATOMIC.LUA
3--
4-- Test program for Lua Lanes
5--
6
7require "lanes"
8
9local linda= lanes.linda()
10local key= "$"
11
12local f= lanes.genatomic( linda, key, 5 )
13
14local v
15v= f(); print(v); assert(v==6)
16v= f(-0.5); print(v); assert(v==5.5)
17
18v= f(-10); print(v); assert(v==-4.5)
diff --git a/tests/basic.lua b/tests/basic.lua
new file mode 100644
index 0000000..ee31ed1
--- /dev/null
+++ b/tests/basic.lua
@@ -0,0 +1,331 @@
1--
2-- BASIC.LUA Copyright (c) 2007-08, Asko Kauppi <akauppi@gmail.com>
3--
4-- Selftests for Lua Lanes
5--
6-- To do:
7-- - ...
8--
9
10require "lanes"
11require "assert" -- assert.fails()
12
13local lanes_gen= assert( lanes.gen )
14local lanes_linda= assert( lanes.linda )
15
16local tostring= assert( tostring )
17
18local function PRINT(...)
19 local str=""
20 for i=1,select('#',...) do
21 str= str..tostring(select(i,...)).."\t"
22 end
23 if io then
24 io.stderr:write(str.."\n")
25 end
26end
27
28
29---=== Local helpers ===---
30
31local tables_match
32
33-- true if 'a' is a subtable of 'b'
34--
35local function subtable( a, b )
36 --
37 assert( type(a)=="table" and type(b)=="table" )
38
39 for k,v in pairs(b) do
40 if type(v)~=type(a[k]) then
41 return false -- not subtable (different types, or missing key)
42 elseif type(v)=="table" then
43 if not tables_match(v,a[k]) then return false end
44 else
45 if a[k] ~= v then return false end
46 end
47 end
48 return true -- is a subtable
49end
50
51-- true when contents of 'a' and 'b' are identific
52--
53tables_match= function( a, b )
54 return subtable( a, b ) and subtable( b, a )
55end
56
57
58---=== Tasking (basic) ===---
59
60local function task( a, b, c )
61 --error "111" -- testing error messages
62 assert(hey)
63 local v=0
64 for i=a,b,c do
65 v= v+i
66 end
67 return v, hey
68end
69
70local task_launch= lanes_gen( "", { globals={hey=true} }, task )
71 -- base stdlibs, normal priority
72
73-- 'task_launch' is a factory of multithreaded tasks, we can launch several:
74
75local lane1= task_launch( 100,200,3 )
76local lane2= task_launch( 200,300,4 )
77
78-- At this stage, states may be "pending", "running" or "done"
79
80local st1,st2= lane1.status, lane2.status
81PRINT(st1,st2)
82assert( st1=="pending" or st1=="running" or st1=="done" )
83assert( st2=="pending" or st2=="running" or st2=="done" )
84
85-- Accessing results ([1..N]) pends until they are available
86--
87PRINT("waiting...")
88local v1, v1_hey= lane1[1], lane1[2]
89local v2, v2_hey= lane2[1], lane2[2]
90
91PRINT( v1, v1_hey )
92assert( v1_hey == true )
93
94PRINT( v2, v2_hey )
95assert( v2_hey == true )
96
97assert( lane1.status == "done" )
98assert( lane1.status == "done" )
99
100
101---=== Tasking (cancelling) ===---
102
103local task_launch2= lanes_gen( "", { cancelstep=100, globals={hey=true} }, task )
104
105local N=999999999
106local lane9= task_launch2(1,N,1) -- huuuuuuge...
107
108-- Wait until state changes "pending"->"running"
109--
110local st
111local t0= os.time()
112while os.time()-t0 < 5 do
113 st= lane9.status
114 io.stderr:write( (i==1) and st.." " or '.' )
115 if st~="pending" then break end
116end
117PRINT(" "..st)
118
119if st=="error" then
120 local _= lane9[0] -- propagate the error here
121end
122if st=="done" then
123 error( "Looping to "..N.." was not long enough (cannot test cancellation)" )
124end
125assert( st=="running" )
126
127lane9:cancel()
128
129local t0= os.time()
130while os.time()-t0 < 5 do
131 st= lane9.status
132 io.stderr:write( (i==1) and st.." " or '.' )
133 if st~="running" then break end
134end
135PRINT(" "..st)
136assert( st == "cancelled" )
137
138
139---=== Communications ===---
140
141local function WR(...) io.stderr:write(...) end
142
143local chunk= function( linda )
144
145 local function receive() return linda:receive( "->" ) end
146 local function send(...) linda:send( "<-", ... ) end
147
148 WR( "Lane starts!\n" )
149
150 local v
151 v=receive(); WR( v.." received\n" ); assert( v==1 )
152 v=receive(); WR( v.." received\n" ); assert( v==2 )
153 v=receive(); WR( v.." received\n" ); assert( v==3 )
154
155 send( 1,2,3 ); WR( "1,2,3 sent\n" )
156 send 'a'; WR( "'a' sent\n" )
157 send { 'a', 'b', 'c', d=10 }; WR( "{'a','b','c',d=10} sent\n" )
158
159 v=receive(); WR( v.." received\n" ); assert( v==4 )
160
161 WR( "Lane ends!\n" )
162end
163
164local linda= lanes_linda()
165assert( type(linda) == "userdata" )
166 --
167 -- ["->"] master -> slave
168 -- ["<-"] slave <- master
169
170local function PEEK() return linda:get("<-") end
171local function SEND(...) linda:send( "->", ... ) end
172local function RECEIVE() return linda:receive( "<-" ) end
173
174local t= lanes_gen("io",chunk)(linda) -- prepare & launch
175
176SEND(1); WR( "1 sent\n" )
177SEND(2); WR( "2 sent\n" )
178for i=1,100 do
179 WR "."
180 assert( PEEK() == nil ) -- nothing coming in, yet
181end
182SEND(3); WR( "3 sent\n" )
183
184local a,b,c= RECEIVE(), RECEIVE(), RECEIVE()
185 WR( a..", "..b..", "..c.." received\n" )
186assert( a==1 and b==2 and c==3 )
187
188local a= RECEIVE(); WR( a.." received\n" )
189assert( a=='a' )
190
191local a= RECEIVE(); WR( type(a).." received\n" )
192assert( tables_match( a, {'a','b','c',d=10} ) )
193
194assert( PEEK() == nil )
195SEND(4)
196
197
198---=== Stdlib naming ===---
199
200local function io_os_f()
201 assert(io)
202 assert(os)
203 assert(print)
204 return true
205end
206
207local f1= lanes_gen( "io,os", io_os_f ) -- any delimiter will do
208local f2= lanes_gen( "io+os", io_os_f )
209local f3= lanes_gen( "io,os,base", io_os_f )
210
211assert.fails( function() lanes_gen( "xxx", io_os_f ) end )
212
213assert( f1()[1] )
214assert( f2()[1] )
215assert( f3()[1] )
216
217
218---=== Comms criss cross ===---
219
220-- We make two identical lanes, which are using the same Linda channel.
221--
222local tc= lanes_gen( "io",
223 function( linda, ch_in, ch_out )
224
225 local function STAGE(str)
226 io.stderr:write( ch_in..": "..str.."\n" )
227 linda:send( nil, ch_out, str )
228 local v= linda:receive( nil, ch_in )
229 assert(v==str)
230 end
231 STAGE("Hello")
232 STAGE("I was here first!")
233 STAGE("So waht?")
234 end
235)
236
237local linda= lanes_linda()
238
239local a,b= tc(linda, "A","B"), tc(linda, "B","A") -- launching two lanes, twisted comms
240
241local _= a[1],b[1] -- waits until they are both ready
242
243
244---=== Receive & send of code ===---
245
246local upvalue="123"
247
248local function chunk2( linda )
249 assert( upvalue=="123" ) -- even when running as separate thread
250
251 -- function name & line number should be there even as separate thread
252 --
253 local info= debug.getinfo(1) -- 1 = us
254 --
255 for k,v in pairs(info) do PRINT(k,v) end
256
257 assert( info.nups == 2 ) -- one upvalue + PRINT
258 assert( info.what == "Lua" )
259
260 --assert( info.name == "chunk2" ) -- name does not seem to come through
261 assert( string.match( info.source, "^@tests[/\\]basic.lua$" ) )
262 assert( string.match( info.short_src, "^tests[/\\]basic.lua$" ) )
263
264 -- These vary so let's not be picky (they're there..)
265 --
266 assert( info.linedefined > 200 ) -- start of 'chunk2'
267 assert( info.currentline > info.linedefined ) -- line of 'debug.getinfo'
268 assert( info.lastlinedefined > info.currentline ) -- end of 'chunk2'
269
270 local func,k= linda:receive( "down" )
271 assert( type(func)=="function" )
272 assert( k=="down" )
273
274 func(linda)
275
276 local str= linda:receive( "down" )
277 assert( str=="ok" )
278
279 linda:send( "up", function() return ":)" end, "ok2" )
280end
281
282local linda= lanes.linda()
283
284local t2= lanes_gen( "debug,string", chunk2 )(linda) -- prepare & launch
285
286linda:send( "down", function(linda) linda:send( "up", "ready!" ) end,
287 "ok" )
288
289-- wait to see if the tiny function gets executed
290--
291local s= linda:receive( "up" )
292PRINT(s)
293assert( s=="ready!" )
294
295-- returns of the 'chunk2' itself
296--
297local f= linda:receive( "up" )
298assert( type(f)=="function" )
299
300local s2= f()
301assert( s2==":)" )
302
303local ok2= linda:receive( "up" )
304assert( ok2 == "ok2" )
305
306
307---=== :join test ===---
308
309-- NOTE: 'unpack()' cannot be used on the lane handle; it will always return nil
310-- (unless [1..n] has been read earlier, in which case it would seemingly
311-- work).
312
313local S= lanes_gen( "table",
314 function(arg)
315 aux= {}
316 for i, v in ipairs(arg) do
317 table.insert (aux, 1, v)
318 end
319 return unpack(aux)
320end )
321
322h= S { 12, 13, 14 } -- execution starts, h[1..3] will get the return values
323
324local a,b,c,d= h:join()
325assert(a==14)
326assert(b==13)
327assert(c==12)
328assert(d==nil)
329
330--
331io.stderr:write "Done! :)\n"
diff --git a/tests/cyclic.lua b/tests/cyclic.lua
new file mode 100644
index 0000000..06452bd
--- /dev/null
+++ b/tests/cyclic.lua
@@ -0,0 +1,64 @@
1--
2-- CYCLIC.LUA
3--
4-- Test program for Lua Lanes
5--
6
7require "lanes"
8
9local table_concat= assert(table.concat)
10
11local function WR(str,...)
12 for i=1,select('#',...) do
13 str= str.."\t"..tostring( select(i,...) )
14 end
15 io.stderr:write( str..'\n' )
16end
17
18local function same(k,l)
19 return k==l and "same" or ("NOT SAME: "..k.." "..l)
20end
21
22local a= {}
23local b= {a}
24a[1]= b
25
26-- Getting the tables as upvalues should still have the <-> linkage
27--
28local function lane1()
29 WR( "Via upvalue: ", same(a,b[1]), same(a[1],b) )
30 assert( a[1]==b )
31 assert( b[1]==a )
32end
33local L1= lanes.gen( "io", lane1 )()
34 -- ...running
35
36-- Getting the tables as parameters should also keep the linkage
37--
38local function lane2( aa, bb )
39 WR( "Via parameters:", same(aa,bb[1]), same(aa[1],bb) )
40 assert( aa[1]==bb )
41 assert( bb[1]==aa )
42end
43local L2= lanes.gen( "io", lane2 )( a, b )
44 -- ...running
45
46-- Really unnecessary, but let's try a directly recursive table
47--
48c= {}
49c.a= c
50
51local function lane3( cc )
52 WR( "Directly recursive: ", same(cc, cc.a) )
53 assert( cc and cc.a==cc )
54end
55local L3= lanes.gen("io", lane3)(c)
56
57-- Without a wait, exit from the main lane will close the process
58--
59-- Waiting for multiple lanes at once could be done using a Linda
60-- (but we're okay waiting them in order)
61--
62L1:join()
63L2:join()
64L3:join()
diff --git a/tests/ehynes.lua b/tests/ehynes.lua
new file mode 100644
index 0000000..4cc370e
--- /dev/null
+++ b/tests/ehynes.lua
@@ -0,0 +1,52 @@
1--
2-- Test from <ehynes at dharmagaia.com>
3--
4require 'lanes'
5
6local function PRINT_FMT( fmt, ... )
7 io.stderr:write( string.format(fmt,...).."\n" )
8end
9
10-- a linda for sending messages
11local linda = lanes.linda()
12
13-- a linda message receiver
14local receiver_gen = lanes.gen( 'base', 'os', 'string', 'io',
15 function (message_name)
16 PRINT_FMT( 'receiver for message %s entered', message_name )
17 local n = 1
18 while linda:receive(message_name) do
19 PRINT_FMT( '%s %d receieved', message_name, n )
20 n = n + 1
21 end
22 end
23)
24
25-- create a receiver
26local receiver1 = receiver_gen('message')
27
28-- create a second receiver (a second receiver in the same linda
29-- appears to be needed to trigger the delays)
30--
31-- AKa 4-Aug-2008: No, with svn version it isn't. But it causes the 2nd
32-- message to be hanging...
33--
34local receiver2 = receiver_gen('another message')
35
36-- a function to pause and log the execution for debugging
37local function logf(s, f, ...)
38 os.execute('sleep 1')
39 PRINT_FMT( "*** %s", s )
40 f(...)
41end
42
43-- first message sent is received right away
44logf('first message sent', linda.send, linda, 'message', true)
45
46-- second message sent is not received immediatly
47logf('second message sent', linda.send, linda, 'message', true)
48
49-- third message sent triggers receipt of both second and third messages
50logf('third message sent', linda.send, linda, 'message', true)
51
52logf('all done', function() end)
diff --git a/tests/error.lua b/tests/error.lua
new file mode 100644
index 0000000..673bcb5
--- /dev/null
+++ b/tests/error.lua
@@ -0,0 +1,47 @@
1--
2-- Error reporting
3--
4-- Note: this code is supposed to end in errors; not included in 'make test'
5--
6
7require "lanes"
8
9local function lane()
10
11 local subf= function() -- this so that we can see the call stack
12 --error "aa"
13 error({})
14 error(error)
15 end
16 local subf2= function()
17 subf()
18 end
19 subf2()
20end
21
22local function cleanup(err)
23end
24
25local lgen = lanes.gen("*", { --[[finalizer=cleanup]] }, lane)
26
27---
28io.stderr:write( "\n** Error catching **\n" )
29--
30local h= lgen()
31local _,err,stack= h:join() -- wait for the lane (no automatic error propagation)
32
33if err then
34 assert( type(stack)=="table" )
35 io.stderr:write( "Lane error: "..tostring(err).."\n" )
36
37 io.stderr:write( "\t", table.concat(stack,"\n\t"), "\n" );
38end
39
40---
41io.stderr:write( "\n** Error propagation **\n" )
42--
43local h2= lgen()
44local _= h2[0]
45assert(false) -- does NOT get here
46
47--never ends
diff --git a/tests/fibonacci.lua b/tests/fibonacci.lua
new file mode 100644
index 0000000..8867e14
--- /dev/null
+++ b/tests/fibonacci.lua
@@ -0,0 +1,75 @@
1--
2-- FIBONACCI.LUA Copyright (c) 2007-08, Asko Kauppi <akauppi@gmail.com>
3--
4-- Parallel calculation of Fibonacci numbers
5--
6-- A sample of task splitting like Intel TBB library does.
7--
8-- References:
9-- Intel Threading Building Blocks, 'test all'
10-- <http://shareit.intel.com/WikiHome/Articles/111111316>
11--
12
13-- Need to say it's 'local' so it can be an upvalue
14--
15local lanes= require "lanes"
16
17local function WR(str)
18 io.stderr:write( str.."\n" )
19end
20
21-- Threshold for serial calculation (optimal depends on multithreading fixed
22-- cost and is system specific)
23--
24local KNOWN= { [0]=0, 1,1,2,3,5,8,13,21,34,55,89,144 }
25
26--
27-- uint= fib( n_uint )
28--
29local function fib( n )
30 --
31 local sum
32 local floor= assert(math.floor)
33
34 WR( "fib("..n..")" )
35
36 if n <= #KNOWN then
37 sum= KNOWN[n]
38 else
39 -- Splits into two; this task remains waiting for the results
40 --
41 local gen_f= lanes.gen( "io,math,debug", fib )
42
43 local n1=floor(n/2) +1
44 local n2=floor(n/2) -1 + n%2
45
46 WR( "splitting "..n.." -> "..n1.." "..n2 )
47
48 local a= gen_f( n1 )
49 local b= gen_f( n2 )
50
51 -- children running...
52
53 local a2= a[1]^2
54 local b2= b[1]^2
55
56 sum = (n%2==1) and a2+b2 or a2-b2
57 end
58
59 io.stderr:write( "fib("..n..") = "..sum.."\n" )
60
61 return sum
62end
63
64--
65-- Right answers from: <http://sonic.net/~douglasi/fibo.htm>
66--
67local right= { 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269, 2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169, 63245986, 102334155, 165580141, 267914296, 433494437, 701408733, 1134903170, 1836311903, 2971215073, 4807526976, 7778742049, 12586269025, 20365011074, 32951280099, 53316291173, 86267571272, 139583862445, 225851433717, 365435296162, 591286729879, 956722026041, 1548008755920, 2504730781961, 4052739537881, 6557470319842, 10610209857723, 17167680177565, 27777890035288, 44945570212853, 72723460248141, 117669030460994, 190392490709135, 308061521170129, 498454011879264, 806515533049393, 1304969544928657, 2111485077978050, 3416454622906707, 5527939700884757, 8944394323791464, 14472334024676220, 23416728348467684, 37889062373143900, 61305790721611580, 99194853094755490, 160500643816367070, 259695496911122560, 420196140727489660, 679891637638612200, 1100087778366101900, 1779979416004714000, 2880067194370816000, 4660046610375530000, 7540113804746346000, 12200160415121877000, 19740274219868226000, 31940434634990105000, 51680708854858334000, 83621143489848440000, 135301852344706780000, 218922995834555200000
68}
69assert( #right==99 )
70
71local N= 80
72local res= fib(N)
73print( right[N] )
74assert( res==right[N] )
75
diff --git a/tests/fifo.lua b/tests/fifo.lua
new file mode 100644
index 0000000..898b04d
--- /dev/null
+++ b/tests/fifo.lua
@@ -0,0 +1,43 @@
1--
2-- FIFO.LUA
3--
4-- Sample program for Lua Lanes
5--
6
7require "lanes"
8
9local linda= lanes.linda()
10local atomic_inc= lanes.genatomic( linda, "FIFO_n" )
11
12assert( atomic_inc()==1 )
13assert( atomic_inc()==2 )
14
15local function FIFO()
16 local my_channel= "FIFO"..atomic_inc()
17
18 return {
19 -- Giving explicit 'nil' timeout allows numbers to be used as 'my_channel'
20 --
21 send= function(...) linda:send( nil, my_channel, ... ) end,
22 receive= function(timeout) linda:receive( timeout, my_channel ) end
23 }
24end
25
26local A= FIFO()
27local B= FIFO()
28
29print "Sending to A.."
30A:send( 1,2,3,4,5 )
31
32print "Sending to B.."
33B:send( 'a','b','c' )
34
35print "Reading A.."
36print( A:receive( 1.0 ) )
37
38print "Reading B.."
39print( B:receive( 2.0 ) )
40
41-- Note: A and B can be passed between threads, or used as upvalues
42-- by multiple threads (other parts will be copied but the 'linda'
43-- handle is shared userdata and will thus point to the single place)
diff --git a/tests/finalizer.lua b/tests/finalizer.lua
new file mode 100644
index 0000000..c94b36d
--- /dev/null
+++ b/tests/finalizer.lua
@@ -0,0 +1,81 @@
1--
2-- Test resource cleanup
3--
4-- This feature was ... by discussion on the Lua list about exceptions.
5-- The idea is to always run a certain block at exit, whether due to success
6-- or error. Normally, 'pcall' would be used for such but as Lua already
7-- does that, simply giving a 'cleanup=function' parameter is a logical
8-- thing to do. -- AKa 22-Jan-2009
9--
10
11require "lanes"
12
13local FN= "finalizer-test.tmp"
14
15local cleanup
16
17local which= os.time() % 2 -- 0/1
18
19local function lane()
20
21 set_finalizer(cleanup)
22
23 local f,err= io.open(FN,"w")
24 if not f then
25 error( "Could not create "..FN..": "..err )
26 end
27
28 f:write( "Test file that should get removed." )
29
30 io.stderr:write( "File "..FN.." created\n" )
31
32 if which==0 then
33 error("aa") -- exception here; the value needs NOT be a string
34 end
35
36 -- no exception
37end
38
39--
40-- This is called at the end of the lane; whether succesful or not.
41-- Gets the 'error()' parameter as parameter ('nil' if normal return).
42--
43cleanup= function(err)
44
45 -- An error in finalizer will override an error (or success) in the main
46 -- chunk.
47 --
48 --error( "This is important!" )
49
50 if err then
51 io.stderr:write( "Cleanup after error: "..tostring(err).."\n" )
52 else
53 io.stderr:write( "Cleanup after normal return\n" )
54 end
55
56 local _,err2= os.remove(FN)
57 assert(not err2) -- if this fails, it will be shown in the calling script
58 -- as an error from the lane itself
59
60 io.stderr:write( "Removed file "..FN.."\n" )
61end
62
63local lgen = lanes.gen("*", lane)
64
65io.stderr:write "Launching the lane!\n"
66
67local h= lgen()
68
69local _,err,stack= h:join() -- wait for the lane (no automatic error propagation)
70if err then
71 assert(stack)
72 io.stderr:write( "Lane error: "..tostring(err).."\n" )
73 io.stderr:write( "\t", table.concat(stack,"\t\n"), "\n" )
74end
75
76local f= io.open(FN,"r")
77if f then
78 error( "CLEANUP DID NOT WORK: "..FN.." still exists!" )
79end
80
81io.stderr:write "Finished!\n"
diff --git a/tests/hangtest.lua b/tests/hangtest.lua
new file mode 100644
index 0000000..d0bbea4
--- /dev/null
+++ b/tests/hangtest.lua
@@ -0,0 +1,26 @@
1--
2-- Test case for hang on [1]s and :join()s.
3--
4
5require "lanes"
6
7local function ret(b)
8 return b
9end
10local lgen = lanes.gen("*", {}, ret)
11
12for i=1,10000 do
13 local ln = lgen(i)
14
15 print( "getting result for "..i )
16
17 -- Hangs here forever every few hundred runs or so,
18 -- can be illustrated by putting another print() statement
19 -- after, which will never be called.
20 --
21 local result = ln[1];
22
23 assert (result == i);
24end
25
26print "Finished!"
diff --git a/tests/irayo_closure.lua b/tests/irayo_closure.lua
new file mode 100644
index 0000000..faf08fd
--- /dev/null
+++ b/tests/irayo_closure.lua
@@ -0,0 +1,35 @@
1--
2-- Bugs filed by irayo Jul-2008
3--
4--[[
5"Another issue I've noticed is trying to pass a table with a function
6that uses closures in it as a global variable into a new lane. This
7causes a segmentation fault and it appears to be related to the
8luaG_inter_move function near line 835-836 or so in lanes.c, but I
9haven't investigated further.
10e.g. { globals = { data = 1, func = function() useclosurehere() end } }"
11]]
12
13require "lanes"
14
15local function testrun()
16 assert( print )
17 assert( data==1 )
18 assert( type(func)=="function" )
19 func() -- "using the closure"
20 return true
21end
22
23-- Should also work without these lines, but currently doesn't (bug in Lanes,
24-- a function thrown over isn't connected to receiving lane's globals)
25--
26--local print=print
27--local assert=assert
28
29local function useclosurehere()
30 assert( print )
31 print "using the closure"
32end
33
34local lane= lanes.gen( "", { globals = { data=1, func=useclosurehere } }, testrun )()
35print(lane[1])
diff --git a/tests/irayo_recursive.lua b/tests/irayo_recursive.lua
new file mode 100644
index 0000000..82e5a54
--- /dev/null
+++ b/tests/irayo_recursive.lua
@@ -0,0 +1,18 @@
1--
2-- Bugs filed by irayo Jul-2008
3--
4--[[
5This code showed lack of caching 'select', 'type' etc. in 'src/lanes.lua'.
6]]
7local function recurse()
8 print("level "..i);
9 if i > 10 then return "finished" end
10
11 require "lanes"
12
13 local lane = lanes.gen( "*", { globals = { ["i"]= i + 1 } }, recurse ) ()
14 return lane[1]
15end
16
17i = 0;
18recurse()
diff --git a/tests/keeper.lua b/tests/keeper.lua
new file mode 100644
index 0000000..5c8c23a
--- /dev/null
+++ b/tests/keeper.lua
@@ -0,0 +1,47 @@
1--
2-- KEEPER.LUA
3--
4-- Test program for Lua Lanes
5--
6
7require "lanes"
8
9local function keeper(linda)
10 local mt= {
11 __index= function( _, key )
12 return linda:get( key )
13 end,
14 __newindex= function( _, key, val )
15 linda:set( key, val )
16 end
17 }
18 return setmetatable( {}, mt )
19end
20
21--
22local lindaA= lanes.linda()
23local A= keeper( lindaA )
24
25local lindaB= lanes.linda()
26local B= keeper( lindaB )
27
28A.some= 1
29print( A.some )
30assert( A.some==1 )
31
32B.some= "hoo"
33assert( B.some=="hoo" )
34assert( A.some==1 )
35
36function lane()
37 local a= keeper(lindaA)
38 print( a.some )
39 assert( a.some==1 )
40 a.some= 2
41end
42
43local h= lanes.gen( "io", lane )()
44h:join()
45
46print( A.some ) -- 2
47assert( A.some==2 )
diff --git a/tests/launchtest.lua b/tests/launchtest.lua
new file mode 100644
index 0000000..5e3037f
--- /dev/null
+++ b/tests/launchtest.lua
@@ -0,0 +1,78 @@
1--
2-- LAUNCHTEST.LUA Copyright (c) 2007-08, Asko Kauppi <akauppi@gmail.com>
3--
4-- Tests launching speed of N threads
5--
6-- Usage:
7-- [time] lua -lstrict launchtest.lua [threads] [-libs[=io,os,math,...]]
8--
9-- threads: number of threads to launch (like: 2000) :)
10-- libs: combination of "os","io","math","package", ...
11-- just "-libs" for all libraries
12--
13-- Note:
14-- One _can_ reach the system threading level, ie. doing 10000 on
15-- PowerBook G4:
16-- <<
17-- pthread_create( ref, &a, lane_main, data ) failed @ line 316: 35
18-- Command terminated abnormally.
19-- <<
20--
21-- (Lua Lanes _can_ be made tolerable to such congestion cases. Just
22-- currently, it is not. btw, 5000 seems to run okay - system limit
23-- being 2040 simultaneous threads)
24--
25-- To do:
26-- - ...
27--
28
29local N= 1000 -- threads/loops to use
30local M= 1000 -- sieves from 1..M
31local LIBS= nil -- default: load no libraries
32
33local function HELP()
34 io.stderr:write( "Usage: lua launchtest.lua [threads] [-libs[=io,os,math,...]]\n" )
35 exit(1)
36end
37
38local m= require "argtable"
39local argtable= assert(m.argtable)
40
41for k,v in pairs( argtable(...) ) do
42 if k==1 then N= tonumber(v) or HELP()
43 elseif k=="libs" then LIBS= (v==true) and "*" or v
44 else HELP()
45 end
46end
47
48require "lanes"
49
50local g= lanes.gen( LIBS, function(i)
51 --io.stderr:write( i.."\t" )
52 return i
53 end )
54
55local t= {}
56
57for i=1,N do
58 t[i]= g(i)
59end
60
61if false then
62 -- just finish here, without waiting for threads to finish
63 --
64 local st= t[N].status
65 print(st) -- if that is "done", they flew already! :)
66else
67 -- mark that all have been launched, now wait for them to return
68 --
69 io.stderr:write( N.." lanes launched.\n" )
70
71 for i=1,N do
72 local rc= t[i]:join()
73 assert( rc==i )
74 end
75
76 io.stderr:write( N.." lanes finished.\n" )
77end
78
diff --git a/tests/objects.lua b/tests/objects.lua
new file mode 100644
index 0000000..8f56a5f
--- /dev/null
+++ b/tests/objects.lua
@@ -0,0 +1,76 @@
1--
2-- OBJECTS.LUA
3--
4-- Tests that objects (metatables) can be passed between lanes.
5--
6
7require "lanes"
8
9local linda= lanes.linda()
10
11local start_lane= lanes.gen( "io",
12 function( obj1 )
13
14 assert( obj1.v )
15 assert( obj1.print )
16
17 assert( obj1 )
18 local mt1= getmetatable(obj1)
19 assert(mt1)
20
21 local obj2= linda:receive("")
22 assert( obj2 )
23 local mt2= getmetatable(obj2)
24 assert(mt2)
25 assert( mt1==mt2 )
26
27 local v= obj1:print()
28 assert( v=="aaa" )
29
30 v= obj2:print()
31 assert( v=="bbb" )
32
33 return true
34 end
35)
36
37
38local WR= function(str)
39 io.stderr:write( tostring(str).."\n")
40end
41
42
43-- Lanes identifies metatables and copies them only once per each lane.
44--
45-- Having methods in the metatable will make passing objects lighter than
46-- having the methods 'fixed' in the object tables themselves.
47--
48local o_mt= {
49 __index= function( me, key )
50 if key=="print" then
51 return function() WR(me.v); return me.v end
52 end
53 end
54}
55
56local function obj_gen(v)
57 local o= { v=v }
58 setmetatable( o, o_mt )
59 return o
60end
61
62local a= obj_gen("aaa")
63local b= obj_gen("bbb")
64
65assert( a and b )
66
67local mt_a= getmetatable(a)
68local mt_b= getmetatable(b)
69assert( mt_a and mt_a==mt_b )
70
71local h= start_lane(a) -- 1st object as parameter
72
73linda:send( "", b ) -- 2nd object via Linda
74
75assert( h[1]==true ) -- wait for return
76
diff --git a/tests/perftest.lua b/tests/perftest.lua
new file mode 100644
index 0000000..8ce1b3c
--- /dev/null
+++ b/tests/perftest.lua
@@ -0,0 +1,184 @@
1--
2-- PERFTEST.LUA Copyright (c) 2007-08, Asko Kauppi <akauppi@gmail.com>
3--
4-- Performance comparison of multithreaded Lua (= how much ballast does using
5-- Lua Lanes introduce)
6--
7-- Usage:
8-- [time] lua -lstrict perftest.lua [threads] [-plain|-single[=2..n]] [-time] [-prio[=-2..+2[,-2..+2]]]
9--
10-- threads: number of threads to launch (loops in 'plain' mode)
11-- -plain: runs in nonthreaded mode, to get a comparison baseline
12-- -single: runs using just a single CPU core (or 'n' cores if given)
13-- -prio: sets odd numbered threads to higher/lower priority
14--
15-- History:
16-- AKa 20-Jul-08: updated to Lanes 2008
17-- AK 14-Apr-07: works on Win32
18--
19-- To do:
20-- (none?)
21--
22
23-- On MSYS, stderr is buffered. In this test it matters.
24-- Seems, even with this MSYS wants to buffer linewise, needing '\n'
25-- before actual output.
26--
27local MSYS= os.getenv("OSTYPE")=="msys"
28
29
30require "lanes"
31
32local m= require "argtable"
33local argtable= assert( m.argtable )
34
35local N= 1000 -- threads/loops to use
36local M= 1000 -- sieves from 1..M
37local PLAIN= false -- single threaded (true) or using Lanes (false)
38local SINGLE= false -- cores to use (false / 1..n)
39local TIME= false -- use Lua for the timing
40local PRIO_ODD, PRIO_EVEN -- -3..+3
41
42local function HELP()
43 io.stderr:write( "Usage: lua perftest.lua [threads]\n" )
44end
45
46-- nil -> +2
47-- odd_prio[,even_prio]
48--
49local function prio_param(v)
50 if v==true then return 2,-2 end
51
52 local a,b= string.match( v, "^([%+%-]?%d+)%,([%+%-]?%d+)$" )
53 if a then
54 return tonumber(a), tonumber(b)
55 elseif tonumber(v) then
56 return tonumber(v)
57 else
58 error( "Bad priority: "..v )
59 end
60end
61
62for k,v in pairs( argtable(...) ) do
63 if k==1 then N= tonumber(v) or HELP()
64 elseif k=="plain" then PLAIN= true
65 elseif k=="single" then SINGLE= v -- true/number
66 elseif k=="time" then TIME= true
67 elseif k=="prio" then PRIO_ODD, PRIO_EVEN= prio_param(v)
68 else HELP()
69 end
70end
71
72PRIO_ODD= PRIO_ODD or 0
73PRIO_EVEN= PRIO_EVEN or 0
74
75
76-- SAMPLE ADOPTED FROM Lua 5.1.1 test/sieve.lua --
77
78-- the sieve of of Eratosthenes programmed with coroutines
79-- typical usage: lua -e N=1000 sieve.lua | column
80
81-- AK: Wrapped within a surrounding function, so we can pass it to Lanes
82-- Note that coroutines can perfectly fine be used within each Lane. :)
83--
84-- AKa 20-Jul-2008: Now the wrapping to one function is no longer needed;
85-- Lanes 2008 can take the used functions as upvalues.
86--
87local function sieve_lane(N,id)
88
89 if MSYS then
90 io.stderr:setvbuf "no"
91 end
92
93 -- generate all the numbers from 2 to n
94 local function gen (n)
95 return coroutine.wrap(function ()
96 for i=2,n do coroutine.yield(i) end
97 end)
98 end
99
100 -- filter the numbers generated by `g', removing multiples of `p'
101 local function filter (p, g)
102 return coroutine.wrap(function ()
103 while 1 do
104 local n = g()
105 if n == nil then return end
106 if math.mod(n, p) ~= 0 then coroutine.yield(n) end
107 end
108 end)
109 end
110
111 local ret= {} -- returned values: { 2, 3, 5, 7, 11, ... }
112 N=N or 1000 -- from caller
113 local x = gen(N) -- generate primes up to N
114 while 1 do
115 local n = x() -- pick a number until done
116 if n == nil then break end
117 --print(n) -- must be a prime number
118 table.insert( ret, n )
119
120 x = filter(n, x) -- now remove its multiples
121 end
122
123 io.stderr:write(id..(MSYS and "\n" or "\t")) -- mark we're ready
124
125 return ret
126end
127-- ** END OF LANE ** --
128
129
130-- Keep preparation code outside of the performance test
131--
132local f_even= lanes.gen( "base,coroutine,math,table,io", -- "*" = all
133 { priority= PRIO_EVEN }, sieve_lane )
134
135local f_odd= lanes.gen( "base,coroutine,math,table,io", -- "*" = all
136 { priority= PRIO_ODD }, sieve_lane )
137
138io.stderr:write( "*** Counting primes 1.."..M.." "..N.." times ***\n\n" )
139
140local t0= TIME and os.time()
141
142if PLAIN then
143 io.stderr:write( "Plain (no multithreading):\n" )
144
145 for i=1,N do
146 local tmp= sieve_lane(M,i)
147 assert( type(tmp)=="table" and tmp[1]==2 and tmp[168]==997 )
148 end
149else
150 if SINGLE then
151 io.stderr:write( (tonumber(SINGLE) and SINGLE or 1) .. " core(s):\n" )
152 lanes.single(SINGLE) -- limit to N cores (just OS X)
153 else
154 io.stderr:write( "Multi core:\n" )
155 end
156
157 if PRIO_ODD ~= PRIO_EVEN then
158 io.stderr:write( ( PRIO_ODD > PRIO_EVEN and "ODD" or "EVEN" )..
159 " LANES should come first (odd:"..PRIO_ODD..", even:"..PRIO_EVEN..")\n\n" )
160 else
161 io.stderr:write( "EVEN AND ODD lanes should be mingled (both: "..PRIO_ODD..")\n\n" )
162 end
163 local t= {}
164 for i=1,N do
165 t[i]= ((i%2==0) and f_even or f_odd) (M,i)
166 end
167
168 -- Make sure all lanes finished
169 --
170 for i=1,N do
171 local tmp= t[i]:join()
172 assert( type(tmp)=="table" and tmp[1]==2 and tmp[168]==997 )
173 end
174end
175
176io.stderr:write "\n"
177
178if TIME then
179 local t= os.time() - t0
180 io.stderr:write( "*** TIMING: "..t.." seconds ***\n" )
181end
182
183--
184-- end
diff --git a/tests/recursive.lua b/tests/recursive.lua
new file mode 100644
index 0000000..49c03d3
--- /dev/null
+++ b/tests/recursive.lua
@@ -0,0 +1,21 @@
1--
2-- RECURSIVE.LUA
3--
4-- Test program for Lua Lanes
5--
6
7io.stderr:write( "depth:" )
8local function func( depth )
9 io.stderr:write(" " .. depth)
10 if depth > 10 then
11 return "done!"
12 end
13
14 require "lanes"
15 local lane= lanes.gen("*", func)( depth+1 )
16 return lane[1]
17end
18
19local v= func(0)
20assert(v=="done!")
21io.stderr:write("\n")
diff --git a/tests/require.lua b/tests/require.lua
new file mode 100644
index 0000000..2cfe780
--- /dev/null
+++ b/tests/require.lua
@@ -0,0 +1,30 @@
1--
2-- REQUIRE.LUA
3--
4-- Test that 'require' works from sublanes
5--
6require 'lanes'
7
8local function a_lane()
9 -- To require 'math' we still actually need to have it initialized for
10 -- the lane.
11 --
12 require "math"
13 assert( math and math.sqrt )
14 assert( math.sqrt(4)==2 )
15
16 assert( lanes==nil )
17 require "lanes"
18 assert( lanes and lanes.gen )
19
20 local h= lanes.gen( function() return 42 end ) ()
21 local v= h[1]
22
23 return v==42
24end
25
26local gen= lanes.gen( "math,package,string,table", a_lane )
27
28local h= gen()
29local ret= h[1]
30assert( ret==true )
diff --git a/tests/timer.lua b/tests/timer.lua
new file mode 100644
index 0000000..e95f326
--- /dev/null
+++ b/tests/timer.lua
@@ -0,0 +1,93 @@
1--
2-- TIMER.LUA
3--
4-- Sample program for Lua Lanes
5--
6
7-- On MSYS, stderr is buffered. In this test it matters.
8io.stderr:setvbuf "no"
9
10
11require "lanes"
12
13local linda= lanes.linda()
14
15local function PRINT(str)
16 io.stderr:write(str.."\n")
17end
18
19local T1= "1s" -- these keys can be anything...
20local T2= "5s"
21
22local step= {}
23
24lanes.timer( linda, T1, 1.0, 1.0 )
25step[T1]= 1.0
26
27PRINT( "\n*** Timers every second (not synced to wall clock) ***\n" )
28
29local v_first
30local v_last= {} -- { [channel]= num }
31local T2_first_round= true
32
33local caught= {} -- { [T1]= bool, [T2]= bool }
34
35while true do
36 io.stderr:write("waiting...\t")
37 local v,channel= linda:receive( 6.0, T1,T2 )
38 assert( channel==T1 or channel==T2 )
39 caught[channel]= true
40
41 io.stderr:write( ((channel==T1) and "" or "\t\t").. string.format("%.3f",v),"\n" )
42 assert( type(v)=="number" )
43
44 if v_last[channel] then
45 if channel==T2 and T2_first_round then
46 -- do not make measurements, first round is not 5secs due to wall clock adjustment
47 T2_first_round= false
48 else
49 assert( math.abs(v-v_last[channel]- step[channel]) < 0.02 )
50 end
51 end
52
53 if not v_first then
54 v_first= v
55 elseif v-v_first > 3.0 and (not step[T2]) then
56 PRINT( "\n*** Adding timers every 5 second (synced to wall clock) ***\n" )
57
58 -- The first event can be in the past (just cut seconds down to 5s)
59 --
60 local date= os.date("*t")
61 date.sec = date.sec - date.sec%5
62
63 lanes.timer( linda, T2, date, 5.0 )
64 step[T2]= 5.0
65
66 elseif v-v_first > 10 then -- exit condition
67 break
68 end
69 v_last[channel]= v
70end
71
72-- Windows version had a bug where T2 timers were not coming through, at all.
73-- AKa 24-Jan-2009
74--
75assert( caught[T1] )
76assert( caught[T2] )
77
78PRINT( "\n*** Clearing timers ***\n" )
79
80lanes.timer( linda, T1, 0 ) -- reset; no reoccuring ticks
81lanes.timer( linda, T2, 0 )
82
83linda:receive( 0, T1 ) -- clear out; there could be one tick left
84linda:receive( 0, T2 )
85
86assert( linda:get(T1) == nil )
87assert( linda:get(T2) == nil )
88
89PRINT "...making sure no ticks are coming..."
90
91local v= linda:receive( 1.5, T1,T2 ) -- should not get any
92assert(v==nil)
93
diff --git a/tools/bin2c.lua b/tools/bin2c.lua
new file mode 100644
index 0000000..352d18e
--- /dev/null
+++ b/tools/bin2c.lua
@@ -0,0 +1,131 @@
1--
2-- BIN2C.LUA [filename] [-o output.lch]
3--
4-- Convert files to byte arrays for automatic loading with 'lua_dobuffer'.
5--
6-- Based on 'etc/bin2c.c' of Lua 5.0.1 sources by:
7-- Luiz Henrique de Figueiredo (lhf@tecgraf.puc-rio.br)
8--
9-- Changes:
10--
11-- 12-Dec-07/AKa: changed the output to have just the "{ ... }" part; others
12-- (variable name) can be explicitly given before the '#include'
13-- 16-Nov-07/AKa: taken into luaSub
14-- 16-Mar-04/AKa: added 'glua_wrap()' support
15-- xx-Jan-04/AKa: subdirectory names are not included in debug info
16--
17
18local function USAGE()
19 io.stderr:write "lua bin2c.lua [filename] [-o output.lch]"
20 os.exit(-1)
21end
22
23local out_f -- file to output to (stdout if nil)
24
25local function OUT( ... )
26 (out_f or io.stdout): write( ... ); -- ; actually needed by Lua
27 (out_f or io.stdout): write "\n"
28end
29
30local HEAD= "{ "
31local START= ' '
32local FILL= '%3d,'
33local STOP= ""
34local TAIL= "};\n"
35
36--
37local function dump( f )
38 --
39 OUT [[
40/* bin2c.lua generated code -- DO NOT EDIT
41 *
42 * To use from C source:
43 * char my_chunk[]=
44 * #include "my.lch"
45 */]]
46
47 local str= HEAD..'\n'..START
48 local len= 0
49
50 while true do
51 for n=1,20 do
52 local c= f:read(1)
53 if c then
54 str= str..string.format( FILL, string.byte(c) )
55 len= len+1
56 else
57 OUT( str..STOP.. string.format( TAIL, len ) )
58 return -- the end
59 end
60 end
61 OUT(str..STOP)
62 str= START
63 end
64end
65
66--
67local function fdump( fn )
68 --
69 local f= io.open( fn, "rb" ) -- must open as binary
70
71 if not f then
72 error( "bin2c: cannot open "..fn )
73 else
74 dump( f )
75 f:close()
76 end
77end
78
79--
80local function main( argv )
81 --
82 local fn= argv.o
83 if fn then
84 local f,err= io.open( fn, "w" )
85 assert( f, "Unable to write '"..fn.."': "..(err or "") )
86
87 out_f= f
88 end
89
90 if argv[2] then
91 USAGE()
92 elseif argv[1] then
93 fdump( argv[1] )
94 else -- use stdin (no params)
95 if os.getenv("WINDIR") then
96 error "using stdin not allowed on Win32!" -- it wouldn't be binary
97 end
98 dump(io.stdin)
99 end
100
101 if out_f then
102 out_f:close()
103 end
104end
105
106--
107local argv= {}
108local valid_flags= { o=1 } -- lookup: 0=no value, 1=value
109
110-- Need to use while since '-o' advances 'i' by 2
111--
112local args= select('#',...)
113local i=1
114
115while i<=args do
116 local v= select(i,...)
117 local flag= string.match( v, "^%-(.+)" )
118
119 if flag then
120 if not valid_flags[flag] then
121 error( "Unknown flag: -"..flag )
122 end
123 argv[flag]= (i+1<=args) and select(i+1,...) or true
124 i= i+1
125 else
126 table.insert( argv, v ) -- [1..N]
127 end
128 i= i+1
129end
130
131return main(argv)