aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormoteus <mimir@newmail.ru>2013-12-26 12:00:41 +0400
committermoteus <mimir@newmail.ru>2013-12-26 12:00:41 +0400
commitfebf9da1af8ba4cf0f8cc64b6af2adb0dcf9b354 (patch)
tree735ef634f8ebeab2101aa21897fd4e4e2f826f32
parent46ed59584e5407c49a02f1ea6bede6487259a92e (diff)
downloadlua-llthreads2-febf9da1af8ba4cf0f8cc64b6af2adb0dcf9b354.tar.gz
lua-llthreads2-febf9da1af8ba4cf0f8cc64b6af2adb0dcf9b354.tar.bz2
lua-llthreads2-febf9da1af8ba4cf0f8cc64b6af2adb0dcf9b354.zip
First commit.
-rw-r--r--.gitignore13
-rw-r--r--COPYRIGHT.llthreads19
-rw-r--r--README.md9
-rw-r--r--msvc/llthreads.sln20
-rw-r--r--msvc/llthreads.vcproj208
-rw-r--r--src/l52util.c117
-rw-r--r--src/l52util.h46
-rw-r--r--src/llthread.c669
-rw-r--r--test/test_llthreads.lua80
-rw-r--r--test/test_table_copy.lua134
-rw-r--r--test/test_threads.lua74
11 files changed, 1388 insertions, 1 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..0c19188
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,13 @@
1*.d
2*.spec
3*.obj
4*.dll
5*.o
6*.lib
7*.exp
8*.suo
9*.ncb
10*.user
11*.pdb
12msvc/Debug/
13msvc/Release/
diff --git a/COPYRIGHT.llthreads b/COPYRIGHT.llthreads
new file mode 100644
index 0000000..4826006
--- /dev/null
+++ b/COPYRIGHT.llthreads
@@ -0,0 +1,19 @@
1Copyright (c) 2011 by Robert G. Jakabosky <bobby@sharedrealm.com>
2
3Permission is hereby granted, free of charge, to any person obtaining a copy
4of this software and associated documentation files (the "Software"), to deal
5in the Software without restriction, including without limitation the rights
6to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7copies of the Software, and to permit persons to whom the Software is
8furnished to do so, subject to the following conditions:
9
10The above copyright notice and this permission notice shall be included in
11all copies or substantial portions of the Software.
12
13THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19THE SOFTWARE.
diff --git a/README.md b/README.md
index 072348b..2547cbc 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,11 @@
1lua-llthreads2 1lua-llthreads2
2============== 2==============
3 3
4[llthreads](https://github.com/Neopallium/lua-llthreads) rewrited without [LuaNativeObjects](https://github.com/Neopallium/LuaNativeObjects) 4This is full dropin replacement for [llthreads](https://github.com/Neopallium/lua-llthreads) library.
5
6##Incompatibility list with origin llthreads library
7* does not support ffi interface (use Lua C API for LuaJIT)
8* returns nil instead of false on error
9* start method returns self instead of true on success
10* does not open all standart libraries (set LLTHREAD_REGISTER_STD_LIBRARY to on this feature)
11* register loaders for llthreads library itself
diff --git a/msvc/llthreads.sln b/msvc/llthreads.sln
new file mode 100644
index 0000000..1277174
--- /dev/null
+++ b/msvc/llthreads.sln
@@ -0,0 +1,20 @@
1
2Microsoft Visual Studio Solution File, Format Version 10.00
3# Visual Studio 2008
4Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "llthreads", "llthreads.vcproj", "{60F3B657-C1F7-47F7-B159-3EEEA6B1220D}"
5EndProject
6Global
7 GlobalSection(SolutionConfigurationPlatforms) = preSolution
8 Debug|Win32 = Debug|Win32
9 Release|Win32 = Release|Win32
10 EndGlobalSection
11 GlobalSection(ProjectConfigurationPlatforms) = postSolution
12 {60F3B657-C1F7-47F7-B159-3EEEA6B1220D}.Debug|Win32.ActiveCfg = Debug|Win32
13 {60F3B657-C1F7-47F7-B159-3EEEA6B1220D}.Debug|Win32.Build.0 = Debug|Win32
14 {60F3B657-C1F7-47F7-B159-3EEEA6B1220D}.Release|Win32.ActiveCfg = Release|Win32
15 {60F3B657-C1F7-47F7-B159-3EEEA6B1220D}.Release|Win32.Build.0 = Release|Win32
16 EndGlobalSection
17 GlobalSection(SolutionProperties) = preSolution
18 HideSolutionNode = FALSE
19 EndGlobalSection
20EndGlobal
diff --git a/msvc/llthreads.vcproj b/msvc/llthreads.vcproj
new file mode 100644
index 0000000..7b2a0e2
--- /dev/null
+++ b/msvc/llthreads.vcproj
@@ -0,0 +1,208 @@
1<?xml version="1.0" encoding="windows-1251"?>
2<VisualStudioProject
3 ProjectType="Visual C++"
4 Version="9,00"
5 Name="llthreads"
6 ProjectGUID="{60F3B657-C1F7-47F7-B159-3EEEA6B1220D}"
7 RootNamespace="llthreads"
8 Keyword="Win32Proj"
9 TargetFrameworkVersion="196613"
10 >
11 <Platforms>
12 <Platform
13 Name="Win32"
14 />
15 </Platforms>
16 <ToolFiles>
17 </ToolFiles>
18 <Configurations>
19 <Configuration
20 Name="Debug|Win32"
21 OutputDirectory="$(SolutionDir)$(ConfigurationName)"
22 IntermediateDirectory="$(ConfigurationName)"
23 ConfigurationType="2"
24 CharacterSet="1"
25 >
26 <Tool
27 Name="VCPreBuildEventTool"
28 />
29 <Tool
30 Name="VCCustomBuildTool"
31 />
32 <Tool
33 Name="VCXMLDataGeneratorTool"
34 />
35 <Tool
36 Name="VCWebServiceProxyGeneratorTool"
37 />
38 <Tool
39 Name="VCMIDLTool"
40 />
41 <Tool
42 Name="VCCLCompilerTool"
43 Optimization="0"
44 AdditionalIncludeDirectories="$(LUA_DIR)\include"
45 PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;LLTHREADS_EXPORTS"
46 MinimalRebuild="true"
47 BasicRuntimeChecks="3"
48 RuntimeLibrary="3"
49 UsePrecompiledHeader="0"
50 WarningLevel="3"
51 DebugInformationFormat="4"
52 />
53 <Tool
54 Name="VCManagedResourceCompilerTool"
55 />
56 <Tool
57 Name="VCResourceCompilerTool"
58 />
59 <Tool
60 Name="VCPreLinkEventTool"
61 />
62 <Tool
63 Name="VCLinkerTool"
64 AdditionalOptions="/EXPORT:luaopen_llthreads"
65 AdditionalDependencies="lua5.1.lib"
66 LinkIncremental="2"
67 AdditionalLibraryDirectories="$(LUA_DIR)\lib"
68 GenerateDebugInformation="true"
69 SubSystem="2"
70 TargetMachine="1"
71 />
72 <Tool
73 Name="VCALinkTool"
74 />
75 <Tool
76 Name="VCManifestTool"
77 />
78 <Tool
79 Name="VCXDCMakeTool"
80 />
81 <Tool
82 Name="VCBscMakeTool"
83 />
84 <Tool
85 Name="VCFxCopTool"
86 />
87 <Tool
88 Name="VCAppVerifierTool"
89 />
90 <Tool
91 Name="VCPostBuildEventTool"
92 />
93 </Configuration>
94 <Configuration
95 Name="Release|Win32"
96 OutputDirectory="$(SolutionDir)$(ConfigurationName)"
97 IntermediateDirectory="$(ConfigurationName)"
98 ConfigurationType="2"
99 CharacterSet="1"
100 WholeProgramOptimization="1"
101 >
102 <Tool
103 Name="VCPreBuildEventTool"
104 />
105 <Tool
106 Name="VCCustomBuildTool"
107 />
108 <Tool
109 Name="VCXMLDataGeneratorTool"
110 />
111 <Tool
112 Name="VCWebServiceProxyGeneratorTool"
113 />
114 <Tool
115 Name="VCMIDLTool"
116 />
117 <Tool
118 Name="VCCLCompilerTool"
119 Optimization="2"
120 EnableIntrinsicFunctions="true"
121 AdditionalIncludeDirectories="$(LUA_DIR)\include"
122 PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;LLTHREADS_EXPORTS"
123 RuntimeLibrary="2"
124 EnableFunctionLevelLinking="true"
125 UsePrecompiledHeader="0"
126 WarningLevel="3"
127 DebugInformationFormat="3"
128 />
129 <Tool
130 Name="VCManagedResourceCompilerTool"
131 />
132 <Tool
133 Name="VCResourceCompilerTool"
134 />
135 <Tool
136 Name="VCPreLinkEventTool"
137 />
138 <Tool
139 Name="VCLinkerTool"
140 AdditionalDependencies="lua5.1.lib"
141 LinkIncremental="1"
142 AdditionalLibraryDirectories="$(LUA_DIR)\lib"
143 GenerateDebugInformation="true"
144 SubSystem="2"
145 OptimizeReferences="2"
146 EnableCOMDATFolding="2"
147 TargetMachine="1"
148 />
149 <Tool
150 Name="VCALinkTool"
151 />
152 <Tool
153 Name="VCManifestTool"
154 />
155 <Tool
156 Name="VCXDCMakeTool"
157 />
158 <Tool
159 Name="VCBscMakeTool"
160 />
161 <Tool
162 Name="VCFxCopTool"
163 />
164 <Tool
165 Name="VCAppVerifierTool"
166 />
167 <Tool
168 Name="VCPostBuildEventTool"
169 />
170 </Configuration>
171 </Configurations>
172 <References>
173 </References>
174 <Files>
175 <Filter
176 Name="Source Files"
177 Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
178 UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
179 >
180 </Filter>
181 <Filter
182 Name="Header Files"
183 Filter="h;hpp;hxx;hm;inl;inc;xsd"
184 UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
185 >
186 <File
187 RelativePath="..\src\l52util.h"
188 >
189 </File>
190 </Filter>
191 <Filter
192 Name="Resource Files"
193 Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
194 UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
195 >
196 <File
197 RelativePath="..\src\l52util.c"
198 >
199 </File>
200 <File
201 RelativePath="..\src\llthread.c"
202 >
203 </File>
204 </Filter>
205 </Files>
206 <Globals>
207 </Globals>
208</VisualStudioProject>
diff --git a/src/l52util.c b/src/l52util.c
new file mode 100644
index 0000000..592c1d1
--- /dev/null
+++ b/src/l52util.c
@@ -0,0 +1,117 @@
1#include "l52util.h"
2
3#include <memory.h>
4#include <assert.h>
5
6#if LUA_VERSION_NUM >= 502
7
8int luaL_typerror (lua_State *L, int narg, const char *tname) {
9 const char *msg = lua_pushfstring(L, "%s expected, got %s", tname,
10 luaL_typename(L, narg));
11 return luaL_argerror(L, narg, msg);
12}
13
14void luaL_register (lua_State *L, const char *libname, const luaL_Reg *l){
15 if(libname) lua_newtable(L);
16 luaL_setfuncs(L, l, 0);
17}
18
19#else
20
21void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup){
22 luaL_checkstack(L, nup, "too many upvalues");
23 for (; l->name != NULL; l++) { /* fill the table with given functions */
24 int i;
25 for (i = 0; i < nup; i++) /* copy upvalues to the top */
26 lua_pushvalue(L, -nup);
27 lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */
28 lua_setfield(L, -(nup + 2), l->name);
29 }
30 lua_pop(L, nup); /* remove upvalues */
31}
32
33void lua_rawgetp(lua_State *L, int index, const void *p){
34 index = lua_absindex(L, index);
35 lua_pushlightuserdata(L, (void *)p);
36 lua_rawget(L, index);
37}
38
39void lua_rawsetp (lua_State *L, int index, const void *p){
40 index = lua_absindex(L, index);
41 lua_pushlightuserdata(L, (void *)p);
42 lua_insert(L, -2);
43 lua_rawset(L, index);
44}
45
46#endif
47
48int lutil_newmetatablep (lua_State *L, const void *p) {
49 lua_rawgetp(L, LUA_REGISTRYINDEX, p);
50 if (!lua_isnil(L, -1)) /* name already in use? */
51 return 0; /* leave previous value on top, but return 0 */
52 lua_pop(L, 1);
53
54 lua_newtable(L); /* create metatable */
55 lua_pushvalue(L, -1); /* duplicate metatable to set*/
56 lua_rawsetp(L, LUA_REGISTRYINDEX, p);
57
58 return 1;
59}
60
61void lutil_getmetatablep (lua_State *L, const void *p) {
62 lua_rawgetp(L, LUA_REGISTRYINDEX, p);
63}
64
65void lutil_setmetatablep (lua_State *L, const void *p) {
66 lutil_getmetatablep(L, p);
67 assert(lua_istable(L,-1));
68 lua_setmetatable (L, -2);
69}
70
71int lutil_isudatap (lua_State *L, int ud, const void *p) {
72 if (lua_isuserdata(L, ud)){
73 if (lua_getmetatable(L, ud)) { /* does it have a metatable? */
74 int res;
75 lutil_getmetatablep(L,p); /* get correct metatable */
76 res = lua_rawequal(L, -1, -2); /* does it have the correct mt? */
77 lua_pop(L, 2); /* remove both metatables */
78 return res;
79 }
80 }
81 return 0;
82}
83
84void *lutil_checkudatap (lua_State *L, int ud, const void *p) {
85 void *up = lua_touserdata(L, ud);
86 if (up != NULL) { /* value is a userdata? */
87 if (lua_getmetatable(L, ud)) { /* does it have a metatable? */
88 lutil_getmetatablep(L,p); /* get correct metatable */
89 if (lua_rawequal(L, -1, -2)) { /* does it have the correct mt? */
90 lua_pop(L, 2); /* remove both metatables */
91 return up;
92 }
93 }
94 }
95 luaL_typerror(L, ud, p); /* else error */
96 return NULL; /* to avoid warnings */
97}
98
99int lutil_createmetap (lua_State *L, const void *p, const luaL_Reg *methods, int nup) {
100 if (!lutil_newmetatablep(L, p))
101 return 0;
102
103 lua_insert(L, -1 - nup); /* move mt prior upvalues */
104 luaL_setfuncs (L, methods, nup); /* define methods */
105 lua_pushliteral (L, "__index"); /* define metamethods */
106 lua_pushvalue (L, -2);
107 lua_settable (L, -3);
108
109 return 1;
110}
111
112void *lutil_newudatap_impl(lua_State *L, size_t size, const void *p){
113 void *obj = lua_newuserdata (L, size);
114 memset(obj, 0, size);
115 lutil_setmetatablep(L, p);
116 return obj;
117}
diff --git a/src/l52util.h b/src/l52util.h
new file mode 100644
index 0000000..f4f0497
--- /dev/null
+++ b/src/l52util.h
@@ -0,0 +1,46 @@
1#ifndef _LZUTILS_H_9B43D914_9652_4E22_9A43_8073502BF3F4_
2#define _LZUTILS_H_9B43D914_9652_4E22_9A43_8073502BF3F4_
3
4#include "lua.h"
5#include "lauxlib.h"
6
7#if LUA_VERSION_NUM >= 502 // lua 5.2
8
9// lua_rawgetp
10// lua_rawsetp
11// luaL_setfuncs
12// lua_absindex
13
14
15#define lua_objlen lua_rawlen
16
17int luaL_typerror (lua_State *L, int narg, const char *tname);
18
19void luaL_register (lua_State *L, const char *libname, const luaL_Reg *l);
20
21#else // lua 5.1
22
23// functions form lua 5.2
24
25# define lua_absindex(L, i) (((i)>0)?(i):((i)<=LUA_REGISTRYINDEX?(i):(lua_gettop(L)+(i)+1)))
26# define lua_rawlen lua_objlen
27
28void lua_rawgetp (lua_State *L, int index, const void *p);
29void lua_rawsetp (lua_State *L, int index, const void *p);
30void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup);
31
32#endif
33
34int lutil_newmetatablep (lua_State *L, const void *p);
35void lutil_getmetatablep (lua_State *L, const void *p);
36void lutil_setmetatablep (lua_State *L, const void *p);
37
38#define lutil_newudatap(L, TTYPE, TNAME) (TTYPE *)lutil_newudatap_impl(L, sizeof(TTYPE), TNAME)
39int lutil_isudatap (lua_State *L, int ud, const void *p);
40void *lutil_checkudatap (lua_State *L, int ud, const void *p);
41int lutil_createmetap (lua_State *L, const void *p, const luaL_Reg *methods, int nup);
42
43void *lutil_newudatap_impl (lua_State *L, size_t size, const void *p);
44
45#endif
46
diff --git a/src/llthread.c b/src/llthread.c
new file mode 100644
index 0000000..a11ad18
--- /dev/null
+++ b/src/llthread.c
@@ -0,0 +1,669 @@
1#if !defined(_WIN32) && !defined(USE_PTHREAD)
2# define USE_PTHREAD
3#endif
4
5#ifndef USE_PTHREAD
6# include <windows.h>
7# include <stdio.h>
8# include <process.h>
9#else
10# include <pthread.h>
11# include <stdio.h>
12#endif
13
14/*export*/
15#ifdef _WIN32
16# define LLTHREADS_EXPORT_API __declspec(dllexport)
17#else
18# define LLTHREADS_EXPORT_API LUALIB_API
19#endif
20
21/* wrap strerror_s(). */
22#ifdef _WIN32
23# ifdef __GNUC__
24# ifndef strerror_r
25# define strerror_r(errno, buf, buflen) do { \
26 strncpy((buf), strerror(errno), (buflen)-1); \
27 (buf)[(buflen)-1] = '\0'; \
28 } while(0)
29# endif
30# else
31# ifndef strerror_r
32# define strerror_r(errno, buf, buflen) strerror_s((buf), (buflen), (errno))
33# endif
34# endif
35#endif
36
37#ifndef USE_PTHREAD
38# define OS_THREAD_RETURT unsigned int __stdcall
39# define INVALID_THREAD INVALID_HANDLE_VALUE
40# define INFINITE_JOIN_TIMEOUT INFINITE
41typedef DWORD join_timeout_t;
42typedef HANDLE os_thread_t;
43#else
44# define OS_THREAD_RETURT void *
45# define INVALID_THREAD 0
46# define INFINITE_JOIN_TIMEOUT -1
47typedef int join_timeout_t;
48typedef pthread_t os_thread_t;
49#endif
50
51#include "l52util.h"
52#include <lualib.h>
53
54LLTHREADS_EXPORT_API int luaopen_llthreads(lua_State *L);
55
56//{ traceback
57
58#define ERROR_LEN 1024
59
60/******************************************************************************
61* traceback() function from Lua 5.1/5.2 source.
62* Copyright (C) 1994-2008 Lua.org, PUC-Rio. All rights reserved.
63*
64* Permission is hereby granted, free of charge, to any person obtaining
65* a copy of this software and associated documentation files (the
66* "Software"), to deal in the Software without restriction, including
67* without limitation the rights to use, copy, modify, merge, publish,
68* distribute, sublicense, and/or sell copies of the Software, and to
69* permit persons to whom the Software is furnished to do so, subject to
70* the following conditions:
71*
72* The above copyright notice and this permission notice shall be
73* included in all copies or substantial portions of the Software.
74*
75* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
76* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
77* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
78* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
79* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
80* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
81* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
82******************************************************************************/
83#if !defined(LUA_VERSION_NUM) || (LUA_VERSION_NUM == 501)
84/* from Lua 5.1 */
85static int traceback (lua_State *L) {
86 if (!lua_isstring(L, 1)) /* 'message' not a string? */
87 return 1; /* keep it intact */
88 lua_getglobal(L, "debug");
89 if (!lua_istable(L, -1)) {
90 lua_pop(L, 1);
91 return 1;
92 }
93 lua_getfield(L, -1, "traceback");
94 if (!lua_isfunction(L, -1)) {
95 lua_pop(L, 2);
96 return 1;
97 }
98 lua_pushvalue(L, 1); /* pass error message */
99 lua_pushinteger(L, 2); /* skip this function and traceback */
100 lua_call(L, 2, 1); /* call debug.traceback */
101 return 1;
102}
103#else
104/* from Lua 5.2 */
105static int traceback (lua_State *L) {
106 const char *msg = lua_tostring(L, 1);
107 if (msg)
108 luaL_traceback(L, L, msg, 1);
109 else if (!lua_isnoneornil(L, 1)) { /* is there an error object? */
110 if (!luaL_callmeta(L, 1, "__tostring")) /* try its 'tostring' metamethod */
111 lua_pushliteral(L, "(no error message)");
112 }
113 return 1;
114}
115#endif
116
117//}
118
119//{ copy values
120
121/* maximum recursive depth of table copies. */
122#define MAX_COPY_DEPTH 30
123
124typedef struct {
125 lua_State *from_L;
126 lua_State *to_L;
127 int has_cache;
128 int cache_idx;
129 int is_arg;
130} llthread_copy_state;
131
132static int llthread_copy_table_from_cache(llthread_copy_state *state, int idx) {
133 void *ptr;
134
135 /* convert table to pointer for lookup in cache. */
136 ptr = (void *)lua_topointer(state->from_L, idx);
137 if(ptr == NULL) return 0; /* can't convert to pointer. */
138
139 /* check if we need to create the cache. */
140 if(!state->has_cache) {
141 lua_newtable(state->to_L);
142 lua_replace(state->to_L, state->cache_idx);
143 state->has_cache = 1;
144 }
145
146 lua_pushlightuserdata(state->to_L, ptr);
147 lua_rawget(state->to_L, state->cache_idx);
148 if(lua_isnil(state->to_L, -1)) {
149 /* not in cache. */
150 lua_pop(state->to_L, 1);
151 /* create new table and add to cache. */
152 lua_newtable(state->to_L);
153 lua_pushlightuserdata(state->to_L, ptr);
154 lua_pushvalue(state->to_L, -2);
155 lua_rawset(state->to_L, state->cache_idx);
156 return 0;
157 }
158 /* found table in cache. */
159 return 1;
160}
161
162static int llthread_copy_value(llthread_copy_state *state, int depth, int idx) {
163 const char *str;
164 size_t str_len;
165 int kv_pos;
166
167 /* Maximum recursive depth */
168 if(++depth > MAX_COPY_DEPTH) {
169 return luaL_error(state->from_L, "Hit maximum copy depth (%d > %d).", depth, MAX_COPY_DEPTH);
170 }
171
172 /* only support string/number/boolean/nil/table/lightuserdata. */
173 switch(lua_type(state->from_L, idx)) {
174 case LUA_TNIL:
175 lua_pushnil(state->to_L);
176 break;
177 case LUA_TNUMBER:
178 lua_pushnumber(state->to_L, lua_tonumber(state->from_L, idx));
179 break;
180 case LUA_TBOOLEAN:
181 lua_pushboolean(state->to_L, lua_toboolean(state->from_L, idx));
182 break;
183 case LUA_TSTRING:
184 str = lua_tolstring(state->from_L, idx, &(str_len));
185 lua_pushlstring(state->to_L, str, str_len);
186 break;
187 case LUA_TLIGHTUSERDATA:
188 lua_pushlightuserdata(state->to_L, lua_touserdata(state->from_L, idx));
189 break;
190 case LUA_TTABLE:
191 /* make sure there is room on the new state for 3 values (table,key,value) */
192 if(!lua_checkstack(state->to_L, 3)) {
193 return luaL_error(state->from_L, "To stack overflow!");
194 }
195 /* make room on from stack for key/value pairs. */
196 luaL_checkstack(state->from_L, 2, "From stack overflow!");
197
198 /* check cache for table. */
199 if(llthread_copy_table_from_cache(state, idx)) {
200 /* found in cache don't need to copy table. */
201 break;
202 }
203 lua_pushnil(state->from_L);
204 while (lua_next(state->from_L, idx) != 0) {
205 /* key is at (top - 1), value at (top), but we need to normalize these
206 * to positive indices */
207 kv_pos = lua_gettop(state->from_L);
208 /* copy key */
209 llthread_copy_value(state, depth, kv_pos - 1);
210 /* copy value */
211 llthread_copy_value(state, depth, kv_pos);
212 /* Copied key and value are now at -2 and -1 in state->to_L. */
213 lua_settable(state->to_L, -3);
214 /* Pop value for next iteration */
215 lua_pop(state->from_L, 1);
216 }
217 break;
218 case LUA_TFUNCTION:
219 case LUA_TUSERDATA:
220 case LUA_TTHREAD:
221 default:
222 if (state->is_arg) {
223 return luaL_argerror(state->from_L, idx, "function/userdata/thread types un-supported.");
224 } else {
225 /* convert un-supported types to an error string. */
226 lua_pushfstring(state->to_L, "Un-supported value: %s: %p",
227 lua_typename(state->from_L, lua_type(state->from_L, idx)), lua_topointer(state->from_L, idx));
228 }
229 }
230
231 return 1;
232}
233
234static int llthread_copy_values(lua_State *from_L, lua_State *to_L, int idx, int top, int is_arg) {
235 llthread_copy_state state;
236 int nvalues = 0;
237 int n;
238
239 nvalues = (top - idx) + 1;
240 /* make sure there is room on the new state for the values. */
241 if(!lua_checkstack(to_L, nvalues + 1)) {
242 return luaL_error(from_L, "To stack overflow!");
243 }
244
245 /* setup copy state. */
246 state.from_L = from_L;
247 state.to_L = to_L;
248 state.is_arg = is_arg;
249 state.has_cache = 0; /* don't create cache table unless it is needed. */
250 lua_pushnil(to_L);
251 state.cache_idx = lua_gettop(to_L);
252
253 nvalues = 0;
254 for(n = idx; n <= top; n++) {
255 llthread_copy_value(&state, 0, n);
256 ++nvalues;
257 }
258
259 /* remove cache table. */
260 lua_remove(to_L, state.cache_idx);
261
262 return nvalues;
263}
264
265//}
266
267static int fail(lua_State *L, const char *msg){
268 lua_pushnil(L);
269 lua_pushstring(L, msg);
270 return 2;
271}
272
273#define flags_t unsigned char
274
275#define TSTATE_NONE (flags_t)0
276#define TSTATE_STARTED (flags_t)1<<0
277#define TSTATE_DETACHED (flags_t)1<<1
278#define TSTATE_JOINED (flags_t)1<<2
279
280/*At leas one flag*/
281#define FLAG_IS_SET(O, F) (O->flags & (flags_t)(F))
282/*All flags*/
283#define FLAGS_IS_SET(O, F) ((F) == FLAG_IS_SET(O, F))
284#define FLAG_SET(O, F) O->flags |= (flags_t)(F)
285#define FLAG_UNSET(O, F) O->flags &= ~((flags_t)(F))
286
287#define ALLOC_STRUCT(S) (S*)calloc(1, sizeof(S))
288#define FREE_STRUCT(O) free(O)
289
290typedef struct llthread_child_t {
291 lua_State *L;
292 int status;
293 flags_t flags;
294} llthread_child_t;
295
296typedef struct llthread_t {
297 llthread_child_t *child;
298 os_thread_t thread;
299 flags_t flags;
300} llthread_t;
301
302//{ llthread_child
303
304static void open_thread_libs(lua_State *L){
305#ifdef LLTHREAD_REGISTER_STD_LIBRARY
306# define L_REGLIB(L, name) lua_pushcfunction(L, luaopen_##name); lua_setfield(L, -2, #name)
307#else
308# define L_REGLIB(L, name) lua_cpcall(L, luaopen_##name, 0)
309#endif
310
311 int top = lua_gettop(L);
312 lua_cpcall(L, luaopen_base, 0);
313 lua_cpcall(L, luaopen_package, 0);
314 lua_settop(L, top);
315
316 /* get package.preload */
317 lua_getglobal(L, "package"); lua_getfield(L, -1, "preload"); lua_remove(L, -2);
318
319 L_REGLIB(L, io );
320 L_REGLIB(L, os );
321 L_REGLIB(L, math );
322 L_REGLIB(L, table );
323 L_REGLIB(L, debug );
324 L_REGLIB(L, string );
325 L_REGLIB(L, llthreads );
326
327 lua_settop(L, top);
328#undef L_REGLIB
329}
330
331static llthread_child_t *llthread_child_new() {
332 llthread_child_t *this = ALLOC_STRUCT(llthread_child_t);
333 if(!this) return NULL;
334
335 memset(this, 0, sizeof(llthread_child_t));
336
337 /* create new lua_State for the thread. */
338 /* open standard libraries. */
339 /* push traceback function as first value on stack. */
340 this->L = luaL_newstate();
341 open_thread_libs(this->L);
342 lua_pushcfunction(this->L, traceback);
343
344 return this;
345}
346
347static void llthread_child_destroy(llthread_child_t *this) {
348 lua_close(this->L);
349 FREE_STRUCT(this);
350}
351
352static OS_THREAD_RETURT llthread_child_thread_run(void *arg) {
353 llthread_child_t *this = (llthread_child_t *)arg;
354 lua_State *L = this->L;
355 int nargs = lua_gettop(L) - 2;
356
357 this->status = lua_pcall(L, nargs, LUA_MULTRET, 1);
358
359 /* alwasy print errors here, helps with debugging bad code. */
360 if(this->status != 0) {
361 const char *err_msg = lua_tostring(L, -1);
362 fprintf(stderr, "Error from thread: %s\n", err_msg);
363 fflush(stderr);
364 }
365
366 /* if thread is detached, then destroy the child state. */
367 if(FLAG_IS_SET(this, TSTATE_DETACHED)) {
368 /* thread is detached, so it must clean-up the child state. */
369 llthread_child_destroy(this);
370 this = NULL;
371 }
372
373#ifndef USE_PTHREAD
374 if(this) {
375 /* attached thread, don't close thread handle. */
376 _endthreadex(0);
377 } else {
378 /* detached thread, close thread handle. */
379 _endthread();
380 }
381 return 0;
382#else
383 return this;
384#endif
385}
386
387//}
388
389//{ llthread
390
391static llthread_t *llthread_new() {
392 llthread_t *this = ALLOC_STRUCT(llthread_t);
393 if(!this) return NULL;
394
395 this->flags = TSTATE_NONE;
396 this->thread = INVALID_THREAD;
397 this->child = llthread_child_new();
398 if(!this->child){
399 FREE_STRUCT(this);
400 return NULL;
401 }
402
403 return this;
404}
405
406static void llthread_cleanup_child(llthread_t *this) {
407 if(this->child) {
408 llthread_child_destroy(this->child);
409 this->child = NULL;
410 }
411}
412
413static void llthread_destroy(llthread_t *this) {
414 /* We still own the child thread object iff the thread was not started or
415 * we have joined the thread.
416 */
417 if(FLAG_IS_SET(this, TSTATE_JOINED)||(this->flags == TSTATE_NONE)) {
418 llthread_cleanup_child(this);
419 }
420 FREE_STRUCT(this);
421}
422
423static int llthread_push_args(lua_State *L, llthread_child_t *child, int idx, int top) {
424 return llthread_copy_values(L, child->L, idx, top, 1 /* is_arg */);
425}
426
427static int llthread_push_results(lua_State *L, llthread_child_t *child, int idx, int top) {
428 return llthread_copy_values(child->L, L, idx, top, 0 /* is_arg */);
429}
430
431static int llthread_start(llthread_t *this, int start_detached) {
432 llthread_child_t *child = this->child;
433 int rc = 0;
434
435 if(start_detached){
436 FLAG_SET(child, TSTATE_DETACHED);
437 }
438
439#ifndef USE_PTHREAD
440 this->thread = (HANDLE)_beginthreadex(NULL, 0, llthread_child_thread_run, child, 0, NULL);
441#else
442 rc = pthread_create(&(this->thread), NULL, llthread_child_thread_run, child);
443 if(rc == 0){
444 this->thread = INVALID_THREAD
445 }
446#endif
447
448 if(this->thread != INVALID_THREAD) {
449 FLAG_SET(this, TSTATE_STARTED);
450 if(start_detached) {
451 FLAG_SET(this, TSTATE_DETACHED);
452 this->child = NULL;
453#ifdef USE_PTHREAD
454 rc = pthread_detach(this->thread);
455#endif
456 }
457 }
458
459 return rc;
460}
461
462static int llthread_join(llthread_t *this, join_timeout_t timeout) {
463#ifndef USE_PTHREAD
464 DWORD ret = 0;
465 if(INVALID_THREAD == this->thread) return 0;
466 ret = WaitForSingleObject( this->thread, timeout );
467 if( ret == WAIT_OBJECT_0){ /* Destroy the thread object. */
468 CloseHandle( this->thread );
469 this->thread = INVALID_THREAD;
470 FLAG_SET(this, TSTATE_JOINED);
471 return 0;
472 }
473 else if( ret == WAIT_TIMEOUT ){
474 return 1;
475 }
476 return 2;
477#else
478 /* then join the thread. */
479 int rc = pthread_join(this->thread, NULL);
480 if(rc == 0) {
481 FLAG_SET(this, TSTATE_JOINED);
482 }
483 return rc;
484#endif
485}
486
487static llthread_t *llthread_create(lua_State *L, const char *code, size_t code_len) {
488 llthread_t *this = llthread_new();
489 llthread_child_t *child = this->child;
490
491 /* load Lua code into child state. */
492 int rc = luaL_loadbuffer(child->L, code, code_len, code);
493 if(rc != 0) {
494 /* copy error message to parent state. */
495 size_t len; const char *str = lua_tolstring(child->L, -1, &len);
496 if(str != NULL) {
497 lua_pushlstring(L, str, len);
498 } else {
499 /* non-string error message. */
500 lua_pushfstring(L, "luaL_loadbuffer() failed to load Lua code: rc=%d", rc);
501 }
502 llthread_destroy(this);
503 lua_error(L);
504 return NULL;
505 }
506
507 /* copy extra args from main state to child state. */
508 /* Push all args after the Lua code. */
509 llthread_push_args(L, child, 3, lua_gettop(L));
510
511 return this;
512}
513
514//}
515
516//{ Lua interface to llthread
517
518#define LLTHREAD_T_NAME "LLThread"
519static const char *LLTHREAD_T = LLTHREAD_T_NAME;
520
521static llthread_t *l_llthread_at (lua_State *L, int i) {
522 llthread_t **this = (llthread_t **)lutil_checkudatap (L, i, LLTHREAD_T);
523 luaL_argcheck (L, this != NULL, i, "thread expected");
524 luaL_argcheck (L, *this != NULL, i, "thread expected");
525 // luaL_argcheck (L, !(counter->flags & FLAG_DESTROYED), 1, "PDH Counter is destroyed");
526 return *this;
527}
528
529static int l_llthread_delete(lua_State *L) {
530 llthread_t **pthis = (llthread_t **)lutil_checkudatap (L, 1, LLTHREAD_T);
531 llthread_t *this;
532 luaL_argcheck (L, pthis != NULL, 1, "thread expected");
533 this = *pthis;
534
535 /*already exists*/
536 if(this == NULL) return 0;
537
538 /* if the thread has been started and has not been detached/joined. */
539 if( FLAG_IS_SET(this, TSTATE_STARTED) &&
540 !FLAG_IS_SET(this, (TSTATE_DETACHED|TSTATE_JOINED))
541 ){
542 /* then join the thread. */
543 llthread_child_t *child = this->child;
544 llthread_join(this, INFINITE_JOIN_TIMEOUT);
545 if(child && child->status != 0) {
546 const char *err_msg = lua_tostring(child->L, -1);
547 fprintf(stderr, "Error from non-joined thread: %s\n", err_msg);
548 fflush(stderr);
549 }
550 }
551
552 llthread_destroy(this);
553 *pthis = NULL;
554
555 return 0;
556}
557
558static int l_llthread_start(lua_State *L) {
559 llthread_t *this = l_llthread_at(L, 1);
560 int start_detached = lua_toboolean(L, 2);
561 int rc;
562
563 if(this->flags != TSTATE_NONE) {
564 return fail(L, "Thread already started.");
565 }
566
567 rc = llthread_start(this, start_detached);
568 if(rc != 0) {
569 char buf[ERROR_LEN];
570 strerror_r(errno, buf, ERROR_LEN);
571 return fail(L, buf);
572 }
573
574 lua_settop(L, 1); // return this
575 return 1;
576}
577
578static int l_llthread_join(lua_State *L) {
579 llthread_t *this = l_llthread_at(L, 1);
580 llthread_child_t *child = this->child;
581 int rc;
582
583 if(!FLAG_IS_SET(this, TSTATE_STARTED )) {
584 return fail(L, "Can't join a thread that hasn't be started.");
585 }
586 if( FLAG_IS_SET(this, TSTATE_DETACHED)) {
587 return fail(L, "Can't join a thread that has been detached.");
588 }
589 if( FLAG_IS_SET(this, TSTATE_JOINED )) {
590 return fail(L, "Can't join a thread that has already been joined.");
591 }
592
593 /* join the thread. */
594 rc = llthread_join(this, INFINITE_JOIN_TIMEOUT);
595
596 /* Push all results after the Lua code. */
597 if(child && FLAG_IS_SET(this, TSTATE_JOINED)) {
598 int top;
599 if(child->status != 0) {
600 const char *err_msg = lua_tostring(child->L, -1);
601 lua_pushboolean(L, 0);
602 lua_pushfstring(L, "Error from child thread: %s", err_msg);
603 top = 2;
604 } else {
605 lua_pushboolean(L, 1);
606 top = lua_gettop(child->L);
607 /* return results to parent thread. */
608 llthread_push_results(L, child, 2, top);
609 }
610 llthread_cleanup_child(this);
611 return top;
612 }
613
614#ifndef USE_PTHREAD
615 if( rc == 1 ){
616 lua_pushboolean(L, 0);
617 lua_pushstring(L, "timeout");
618 return 2;
619 }
620#endif
621
622 {
623 char buf[ERROR_LEN];
624 strerror_r(errno, buf, ERROR_LEN);
625
626 llthread_cleanup_child(this);
627
628 lua_pushboolean(L, 0);
629 lua_pushstring(L, buf);
630 return 2;
631 }
632
633}
634
635static int l_llthread_new(lua_State *L) {
636 size_t lua_code_len; const char *lua_code = luaL_checklstring(L, 1, &lua_code_len);
637 llthread_t **this = lutil_newudatap(L, llthread_t*, LLTHREAD_T);
638 lua_insert(L, 2); /*move self prior args*/
639 *this = llthread_create(L, lua_code, lua_code_len);
640
641 lua_settop(L, 2);
642 return 1;
643}
644
645static const struct luaL_Reg l_llthread_meth[] = {
646 {"start", l_llthread_start },
647 {"join", l_llthread_join },
648 {"__gc", l_llthread_delete },
649
650 {NULL, NULL}
651};
652
653//}
654
655static const struct luaL_Reg l_llthreads_lib[] = {
656 {"new", l_llthread_new },
657
658 {NULL, NULL}
659};
660
661LLTHREADS_EXPORT_API int luaopen_llthreads(lua_State *L) {
662 int top = lua_gettop(L);
663 lutil_createmetap(L, LLTHREAD_T, l_llthread_meth, 0);
664 lua_settop(L, top);
665
666 lua_newtable(L);
667 luaL_setfuncs(L, l_llthreads_lib, 0);
668 return 1;
669}
diff --git a/test/test_llthreads.lua b/test/test_llthreads.lua
new file mode 100644
index 0000000..3474b9b
--- /dev/null
+++ b/test/test_llthreads.lua
@@ -0,0 +1,80 @@
1-- Copyright (c) 2011 by Robert G. Jakabosky <bobby@sharedrealm.com>
2--
3-- Permission is hereby granted, free of charge, to any person obtaining a copy
4-- of this software and associated documentation files (the "Software"), to deal
5-- in the Software without restriction, including without limitation the rights
6-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7-- copies of the Software, and to permit persons to whom the Software is
8-- furnished to do so, subject to the following conditions:
9--
10-- The above copyright notice and this permission notice shall be included in
11-- all copies or substantial portions of the Software.
12--
13-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19-- THE SOFTWARE.
20
21local llthreads = require"llthreads"
22
23local sleep
24local status, socket = pcall(require,"socket")
25if status then
26 sleep = function(secs)
27 return socket.sleep(secs)
28 end
29end
30
31if not sleep then
32 local status, ztimer = pcall(require, "lzmq.timer")
33 if status then
34 sleep = function(secs)
35 ztimer.sleep(secs * 1000)
36 end
37 end
38end
39
40if not sleep then
41 sleep = function(secs)
42 os.execute("sleep " .. tonumber(secs))
43 end
44end
45
46local function detached_thread(...)
47 local thread = llthreads.new([[ print("print_detached_thread:", ...) ]], ...)
48 -- start detached thread
49 assert(thread:start(true))
50 return thread
51end
52
53local function print_thread(...)
54 local thread = llthreads.new([[ print("print_thread:", ...); ]], ...)
55 -- start joinable thread
56 assert(thread:start())
57 return thread
58end
59
60local function pass_through_thread(...)
61 local thread = llthreads.new([[ return "pass_thread:", ... ]], ...)
62 -- start joinable thread
63 assert(thread:start())
64 return thread
65end
66
67local thread1 = detached_thread("number:", 1234, "nil:", nil, "bool:", true)
68
69sleep(1)
70
71local thread2 = print_thread("number:", 1234, "nil:", nil, "bool:", true)
72print("thread2:join: results # = ", select('#', thread2:join()))
73
74sleep(1)
75
76local thread3 = pass_through_thread("number:", 1234, "nil:", nil, "bool:", true)
77print("thread3:join:", thread3:join())
78
79sleep(1)
80
diff --git a/test/test_table_copy.lua b/test/test_table_copy.lua
new file mode 100644
index 0000000..0408ad3
--- /dev/null
+++ b/test/test_table_copy.lua
@@ -0,0 +1,134 @@
1-- Copyright (c) 2011 by Robert G. Jakabosky <bobby@sharedrealm.com>
2--
3-- Permission is hereby granted, free of charge, to any person obtaining a copy
4-- of this software and associated documentation files (the "Software"), to deal
5-- in the Software without restriction, including without limitation the rights
6-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7-- copies of the Software, and to permit persons to whom the Software is
8-- furnished to do so, subject to the following conditions:
9--
10-- The above copyright notice and this permission notice shall be included in
11-- all copies or substantial portions of the Software.
12--
13-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19-- THE SOFTWARE.
20
21local llthreads = require"llthreads"
22
23local sleep
24local status, socket = pcall(require,"socket")
25if status then
26 sleep = function(secs)
27 return socket.sleep(secs)
28 end
29else
30 sleep = function(secs)
31 os.execute("sleep " .. tonumber(secs))
32 end
33end
34
35local dump_code = [==[
36local function dump_recur(seen, obj, depth)
37 local out
38 local t = type(obj)
39 -- if not a table just convert to string.
40 if t ~= "table" then
41 if t == "string" then
42 return '"' .. obj .. '"'
43 end
44 return tostring(obj)
45 end
46 -- check if this table has been seen already.
47 if seen[obj] then
48 return "Already dumped " .. tostring(obj)
49 end
50 seen[obj] = true
51 -- restrict max depth.
52 if depth >= 10 then
53 return "{... max depth reached ...}"
54 end
55 depth = depth + 1
56 -- output table key/value pairs
57 local tabs = string.rep(" ",depth)
58 local out = "{\n"
59 for k,v in pairs(obj) do
60 if type(k) ~= "number" then
61 out = out .. tabs .. '[' .. dump_recur(seen, k, depth) .. '] = ' ..
62 dump_recur(seen, v, depth) .. ',\n'
63 else
64 out = out .. tabs .. '[' .. k .. '] = ' .. dump_recur(seen, v, depth) .. ',\n'
65 end
66 end
67 return out .. tabs:sub(1,-3) .. "}"
68end
69
70local obj = ...
71local seen = {}
72return dump_recur(seen, obj, 0)
73]==]
74
75local dump = (loadstring or load)(dump_code)
76
77local child_code = [==[
78local dump = (loadstring or load)[[
79]==] .. dump_code .. [==[
80]]
81local args = ...
82
83print("Child thread args:", dump(args))
84
85-- return all values.
86return ...
87]==]
88
89local function test_thread_value_copying(...)
90 local args = {...}
91 print("Main thread args:", dump(args))
92 local thread = llthreads.new(child_code, args)
93 -- start joinable thread
94 assert(thread:start())
95
96 local status, results = thread:join()
97 print("Main thread results:", dump(results))
98end
99
100-- create some tables.
101local a1 = { "a1" }
102local a2 = { "a2" }
103local a3 = { "a3" }
104local a4 = { "a4" }
105local b1 = { a1, a2, a3, a4 }
106local b2 = { a1=a1, a2=a2, a3=a3, a4=a4 }
107
108--
109-- no loops
110--
111test_thread_value_copying(b1, b2)
112
113local top = {}
114-- self reference.
115top.top = top
116top[top] = top
117-- nested reference.
118top.sub1 = { sub2 = { sub3 = { top } } }
119
120--
121-- loops
122--
123test_thread_value_copying(top)
124
125--
126-- Test max depth
127--
128local outer = {}
129for n=1,100 do
130 outer = {outer}
131end
132local status, err = pcall(test_thread_value_copying,outer)
133assert(not status, "Assertion failed: max depth test failed.")
134
diff --git a/test/test_threads.lua b/test/test_threads.lua
new file mode 100644
index 0000000..467526e
--- /dev/null
+++ b/test/test_threads.lua
@@ -0,0 +1,74 @@
1-- Copyright (c) 2011 by Ross Anderson <ross_j_anderson@yahoo.com>
2--
3-- Permission is hereby granted, free of charge, to any person obtaining a copy
4-- of this software and associated documentation files (the "Software"), to deal
5-- in the Software without restriction, including without limitation the rights
6-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7-- copies of the Software, and to permit persons to whom the Software is
8-- furnished to do so, subject to the following conditions:
9--
10-- The above copyright notice and this permission notice shall be included in
11-- all copies or substantial portions of the Software.
12--
13-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19-- THE SOFTWARE.
20
21-- Sub-thread processing example in Lua using llthreads - 1,000 quick sub-thread execution
22
23-- luajit sub_threads.lua
24
25local llthreads = require"llthreads"
26
27local num_threads = tonumber(arg[1] or 1000)
28
29-- level 0 string literal enclosure [[ ]] of child execution code
30local thread_code = [[
31 local lua_init = os.getenv("lua_init")
32 if lua_init and #lua_init > 0 then
33 if lua_init:sub(1,1) == '@' then
34 dofile(lua_init:sub(2))
35 else
36 assert((loadstring or load)(lua_init))()
37 end
38 end
39
40 local num_threads = ...
41 print("CHILD: received from ROOT params:", ...)
42 local llthreads = require"llthreads" -- need to re-declare this under this scope
43 local t = {} -- thread storage table
44
45 -- create a new child sub-thread execution code - it requires level 1 literal string [=[ ]=] enclosures, level 2 would be [==[ ]==]
46 local executed_child_code = [=[
47 return "Hello from child sub-thread, new input params:", ...
48 ]=]
49
50 -- create 1000 sub-threads - which creates an incremental 30% / 20% utilization spike on the two AMD cpu cores
51 print("CHILD: Create sub threads:", num_threads)
52 for i=1,num_threads do
53 -- create child sub-thread with code to execute and the input parmeters
54 local thread = llthreads.new(executed_child_code , "number:", 1000 + i, "nil:", nil, "bool:", true)
55 assert(thread:start()) -- start new child sub-thread
56 table.insert(t, thread) -- append the thread at the end of the thread table
57 end
58
59 -- wait (block) for all child sub-threads to complete before returning to ROOT
60 while true do
61 -- always wait on the first element, since order is not important
62 print("CHILD: sub-thread returned: ", t[1]:join())
63 table.remove(t,1) -- always remove the first element
64 if (#t == 0) then break end
65 end
66 return ... -- return the parents' input params back to the root
67]]
68
69-- create child thread.
70local thread = llthreads.new(thread_code, num_threads, "number:", 1000, "nil:", nil, "bool:", true)
71-- start joinable child thread.
72assert(thread:start())
73-- wait for all child and child sub-threads to finish
74print("ROOT: child returned: ", thread:join())