aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES8
-rw-r--r--Makefile14
-rw-r--r--docs/index.html14
-rw-r--r--setup-vc.cmd15
-rw-r--r--src/keeper.c36
-rw-r--r--src/keeper.h1
-rw-r--r--src/keeper.lua225
-rw-r--r--src/lanes.c159
8 files changed, 310 insertions, 162 deletions
diff --git a/CHANGES b/CHANGES
index 31ca342..f4ff88a 100644
--- a/CHANGES
+++ b/CHANGES
@@ -3,6 +3,14 @@ CHANGES:
3 3
4CHANGE X: 4CHANGE X:
5 5
6CHANGE 31 BGe 17-Apr-2011
7* linda uses a fast FIFO implementation to speed up data exchanges
8* new linda:count() method
9* new linda batched data read mode
10* proper key type check in all linda methods
11* fix setup-vc.cmd to support Visual Studio 2010 and Windows 7 64 bits
12* bugfix: release keeper state mutex at desinit
13
6CHANGE 30 BGe 30-Mar-2011 14CHANGE 30 BGe 30-Mar-2011
7* linda honors __tostring and __concat 15* linda honors __tostring and __concat
8* new accessor linda:keys(), to retrieve the list of keys with pending data inside a linda 16* new accessor linda:keys(), to retrieve the list of keys with pending data inside a linda
diff --git a/Makefile b/Makefile
index ddd8675..23bec03 100644
--- a/Makefile
+++ b/Makefile
@@ -85,11 +85,12 @@ test:
85 $(MAKE) recursive 85 $(MAKE) recursive
86 $(MAKE) func_is_string 86 $(MAKE) func_is_string
87 $(MAKE) atexit 87 $(MAKE) atexit
88 $(MAKE) linda_perf
88 89
89basic: tests/basic.lua $(_TARGET_SO) 90basic: tests/basic.lua $(_TARGET_SO)
90 $(_PREFIX) $(LUA) $< 91 $(_PREFIX) $(LUA) $<
91 92
92# 93#
93# This tries to show out a bug which happens in lane cleanup (multicore CPU's only) 94# This tries to show out a bug which happens in lane cleanup (multicore CPU's only)
94# 95#
95REP_ARGS=-llanes -e "print'say aaa'; for i=1,10 do print(i) end" 96REP_ARGS=-llanes -e "print'say aaa'; for i=1,10 do print(i) end"
@@ -155,6 +156,9 @@ appendud: tests/appendud.lua $(_TARGET_SO)
155func_is_string: tests/func_is_string.lua $(_TARGET_SO) 156func_is_string: tests/func_is_string.lua $(_TARGET_SO)
156 $(_PREFIX) $(LUA) $< 157 $(_PREFIX) $(LUA) $<
157 158
159linda_perf: tests/linda_perf.lua $(_TARGET_SO)
160 $(_PREFIX) $(LUA) $<
161
158atexit: tests/atexit.lua $(_TARGET_SO) 162atexit: tests/atexit.lua $(_TARGET_SO)
159 $(_PREFIX) $(LUA) $< 163 $(_PREFIX) $(LUA) $<
160 164
@@ -190,7 +194,7 @@ LUA_LIBDIR=$(DESTDIR)/lib/lua/5.1
190LUA_SHAREDIR=$(DESTDIR)/share/lua/5.1 194LUA_SHAREDIR=$(DESTDIR)/share/lua/5.1
191 195
192# 196#
193# AKa 17-Oct: changed to use 'install -m 644' and 'cp -p' 197# AKa 17-Oct: changed to use 'install -m 644' and 'cp -p'
194# 198#
195install: $(_TARGET_SO) src/lanes.lua 199install: $(_TARGET_SO) src/lanes.lua
196 mkdir -p $(LUA_LIBDIR) $(LUA_SHAREDIR) 200 mkdir -p $(LUA_LIBDIR) $(LUA_SHAREDIR)
@@ -211,7 +215,7 @@ tar tgz:
211ifeq "$(VERSION)" "" 215ifeq "$(VERSION)" ""
212 echo "Usage: make tar VERSION=x.x"; false 216 echo "Usage: make tar VERSION=x.x"; false
213else 217else
214 $(MAKE) clean 218 $(MAKE) clean
215 -rm -rf $(MODULE)-$(VERSION) 219 -rm -rf $(MODULE)-$(VERSION)
216 mkdir $(MODULE)-$(VERSION) 220 mkdir $(MODULE)-$(VERSION)
217 tar c * --exclude=.svn --exclude=.DS_Store --exclude="_*" \ 221 tar c * --exclude=.svn --exclude=.DS_Store --exclude="_*" \
@@ -223,8 +227,8 @@ else
223 rm -rf $(MODULE)-$(VERSION) 227 rm -rf $(MODULE)-$(VERSION)
224 md5sum $(MODULE)-$(VERSION).tgz 228 md5sum $(MODULE)-$(VERSION).tgz
225endif 229endif
226 230
227 231
228#--- Undocumented --- 232#--- Undocumented ---
229# 233#
230 234
diff --git a/docs/index.html b/docs/index.html
index 7d61d96..3c4fe59 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -483,7 +483,7 @@ level locking is required; each Linda operation is atomic.
483<p>Characteristics of the Lanes implementation of Lindas are: 483<p>Characteristics of the Lanes implementation of Lindas are:
484 484
485<ul> 485<ul>
486 <li>keys can be of number, string or boolean type 486 <li>keys can be of boolean, number, string or light userdata type
487 </li> 487 </li>
488 <li>values can be any type supported by inter-state copying (same limits 488 <li>values can be any type supported by inter-state copying (same limits
489 as for function parameters and upvalues) 489 as for function parameters and upvalues)
@@ -560,17 +560,23 @@ They can be used for making shared tables of storage among the lanes.
560Writing to a slot overwrites existing value, and clears any possible queued 560Writing to a slot overwrites existing value, and clears any possible queued
561entries. Table access and <tt>send</tt>/<tt>receive</tt> can be used together; 561entries. Table access and <tt>send</tt>/<tt>receive</tt> can be used together;
562reading a slot essentially peeks the next outcoming value of a queue. 562reading a slot essentially peeks the next outcoming value of a queue.
563</p><p> 563</p>
564 <p>
564 565
565 <table border="1" bgcolor="#E0E0FF" cellpadding="10"> 566 <table border="1" bgcolor="#E0E0FF" cellpadding="10">
566 <tr> 567 <tr>
567 <td> 568 <td>
568 <code>[val]= linda_h:keys()</code> 569 <code>[val]= linda_h:count( [key[,...]])</code>
569 </table> 570 </table>
570 571
571 </p> 572 </p>
572 <p> 573 <p>
573 A way to examine the slots in which a linda contains some pending data to be read. 574 Returns some information about the contents of the linda.
575 <br/>If no key is specified, and the linda is empty, returns nothing.
576 <br/>If no key is specified, and the linda is not empty, returns a table of key/count pairs that counts the number of items in each
577 of the exiting keys of the linda. This count can be 0 if the key has been used but is empty.
578 <br/>If a single key is specified, returns the number of pending items, or nothing if the key is unknown.
579 <br/>If more than one key is specified, return a table of key/count pairs for the known keys.
574 </p> 580 </p>
575 581
576<!-- 582<!--
diff --git a/setup-vc.cmd b/setup-vc.cmd
index 247459c..e623495 100644
--- a/setup-vc.cmd
+++ b/setup-vc.cmd
@@ -30,10 +30,19 @@ goto FOUND_VC
30 30
31:TRY_VC9 31:TRY_VC9
32set VSINSTALLDIR=%ProgramFiles%\Microsoft Visual Studio 9.0 32set VSINSTALLDIR=%ProgramFiles%\Microsoft Visual Studio 9.0
33if not exist "%VSINSTALLDIR%\VC\vcvarsall.bat" goto ERR_NOVC 33if exist "%VSINSTALLDIR%\VC\vcvarsall.bat" goto WARN_VC
34set VSINSTALLDIR=%ProgramFiles(x86)%\Microsoft Visual Studio 9.0
35if exist "%VSINSTALLDIR%\VC\vcvarsall.bat" goto WARN_VC
34 36
37:TRY_VC10
38set VSINSTALLDIR=%ProgramFiles%\Microsoft Visual Studio 10.0
39if exist "%VSINSTALLDIR%\VC\vcvarsall.bat" goto WARN_VC
40set VSINSTALLDIR=%ProgramFiles(x86)%\Microsoft Visual Studio 10.0
41if exist "%VSINSTALLDIR%\VC\vcvarsall.bat" goto WARN_VC
42
43:WARN_VC
35echo. 44echo.
36echo *** Warning: Visual C++ 2008 in use *** 45echo *** Warning: Visual C++ 2008/2010 in use ***
37echo. 46echo.
38echo Using VC++2005 is recommended for runtime compatibility issues 47echo Using VC++2005 is recommended for runtime compatibility issues
39echo (LuaBinaries and LfW use it; if you compile everything from 48echo (LuaBinaries and LfW use it; if you compile everything from
@@ -70,7 +79,7 @@ goto EXIT
70REM --- 79REM ---
71:ERR_NOVC 80:ERR_NOVC
72echo. 81echo.
73echo ** ERROR: Visual C++ 2005/08 Express - not detected 82echo ** ERROR: Visual C++ 2005/08/10 Express - not detected
74echo You can set the environment variables separately, and run 'make-vc.cmd' 83echo You can set the environment variables separately, and run 'make-vc.cmd'
75echo or download the compiler from: 84echo or download the compiler from:
76echo http://msdn.microsoft.com/vstudio/express/downloads/ 85echo http://msdn.microsoft.com/vstudio/express/downloads/
diff --git a/src/keeper.c b/src/keeper.c
index 01e8880..5b355cb 100644
--- a/src/keeper.c
+++ b/src/keeper.c
@@ -103,15 +103,10 @@ char const *init_keepers( int const _nbKeepers)
103 luaG_openlibs( L, "io,table,package" ); // 'io' for debugging messages, package because we need to require modules exporting idfuncs 103 luaG_openlibs( L, "io,table,package" ); // 'io' for debugging messages, package because we need to require modules exporting idfuncs
104 serialize_require( L); 104 serialize_require( L);
105 105
106 /* We could use an empty table in 'keeper.lua' as the sentinel, but maybe
107 * checking for a lightuserdata is faster. (any unique value will do -> take the address of some global of ours)
108 */
109 lua_pushlightuserdata( L, &GNbKeepers);
110 lua_setglobal( L, "nil_sentinel");
111 106
112 // Read in the preloaded chunk (and run it) 107 // Read in the preloaded chunk (and run it)
113 // 108 //
114 if (luaL_loadbuffer( L, keeper_chunk, sizeof(keeper_chunk), "=lanes_keeper" )) 109 if (luaL_loadbuffer( L, keeper_chunk, sizeof(keeper_chunk), "@keeper.lua"))
115 return "luaL_loadbuffer() failed"; // LUA_ERRMEM 110 return "luaL_loadbuffer() failed"; // LUA_ERRMEM
116 111
117 if (lua_pcall( L, 0 /*args*/, 0 /*results*/, 0 /*errfunc*/ )) 112 if (lua_pcall( L, 0 /*args*/, 0 /*results*/, 0 /*errfunc*/ ))
@@ -152,6 +147,34 @@ void keeper_release( struct s_Keeper *K)
152 MUTEX_UNLOCK( &K->lock_); 147 MUTEX_UNLOCK( &K->lock_);
153} 148}
154 149
150void keeper_toggle_nil_sentinels( lua_State *L, int _val_i, int _nil_to_sentinel)
151{
152 int i, n = lua_gettop( L);
153 /* We could use an empty table in 'keeper.lua' as the sentinel, but maybe
154 * checking for a lightuserdata is faster. (any unique value will do -> take the address of some global of ours)
155 */
156 void *nil_sentinel = &GNbKeepers;
157 for( i = _val_i; i <= n; ++ i)
158 {
159 if( _nil_to_sentinel)
160 {
161 if( lua_isnil( L, i))
162 {
163 lua_pushlightuserdata( L, nil_sentinel);
164 lua_replace( L, i);
165 }
166 }
167 else
168 {
169 if( lua_touserdata( L, i) == nil_sentinel)
170 {
171 lua_pushnil( L);
172 lua_replace( L, i);
173 }
174 }
175 }
176}
177
155/* 178/*
156* Call a function ('func_name') in the keeper state, and pass on the returned 179* Call a function ('func_name') in the keeper state, and pass on the returned
157* values to 'L'. 180* values to 'L'.
@@ -197,6 +220,7 @@ void close_keepers(void)
197 lua_close( GKeepers[i].L); 220 lua_close( GKeepers[i].L);
198 GKeepers[i].L = 0; 221 GKeepers[i].L = 0;
199 //assert( GKeepers[i].count == 0); 222 //assert( GKeepers[i].count == 0);
223 MUTEX_FREE( &GKeepers[i].lock_);
200 } 224 }
201 if( GKeepers) free( GKeepers); 225 if( GKeepers) free( GKeepers);
202 GKeepers = NULL; 226 GKeepers = NULL;
diff --git a/src/keeper.h b/src/keeper.h
index e959c7c..27a0f68 100644
--- a/src/keeper.h
+++ b/src/keeper.h
@@ -11,6 +11,7 @@ struct s_Keeper
11const char *init_keepers( int const _nbKeepers); 11const char *init_keepers( int const _nbKeepers);
12struct s_Keeper *keeper_acquire( const void *ptr); 12struct s_Keeper *keeper_acquire( const void *ptr);
13void keeper_release( struct s_Keeper *K); 13void keeper_release( struct s_Keeper *K);
14void keeper_toggle_nil_sentinels( lua_State *L, int _val_i, int _nil_to_sentinel);
14int keeper_call( lua_State *K, char const *func_name, lua_State *L, void *linda, uint_t starting_index); 15int keeper_call( lua_State *K, char const *func_name, lua_State *L, void *linda, uint_t starting_index);
15void close_keepers(void); 16void close_keepers(void);
16 17
diff --git a/src/keeper.lua b/src/keeper.lua
index 4bb181a..828932e 100644
--- a/src/keeper.lua
+++ b/src/keeper.lua
@@ -34,30 +34,27 @@ THE SOFTWARE.
34=============================================================================== 34===============================================================================
35]]-- 35]]--
36 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) 37-- We only need to have base and table libraries (and io for debugging)
42-- 38--
43local table_concat = assert( table.concat) 39local table_concat = assert( table.concat)
44local table_insert = assert( table.insert) 40local table_insert = assert( table.insert)
45local table_remove = assert( table.remove) 41local table_remove = assert( table.remove)
42local select, unpack = assert( select), assert( unpack)
46 43
47--[[ 44--[[
48local function WR(...) 45local function WR(...)
49 if io then 46 if io then
50 io.stderr:write( table_concat({...},'\t').."\n" ) 47 io.stderr:write( table_concat({...},'\t').."\n" )
51 end 48 end
52end 49end
53 50
54local function DEBUG(title,ud,key) 51local function DEBUG(title,ud,key)
55 assert( title and ud and key ) 52 assert( title and ud and key )
56 53
57 local data,incoming,_= tables(ud) 54 local data,_= tables(ud)
58 55
59 local s= tostring(data[key]) 56 local s= tostring(data[key])
60 for _,v in ipairs( incoming[key] or {} ) do 57 for _,v in ipairs( data[key] or {} ) do
61 s= s..", "..tostring(v) 58 s= s..", "..tostring(v)
62 end 59 end
63 WR( "*** "..title.." ("..tostring(key).."): ", s ) 60 WR( "*** "..title.." ("..tostring(key).."): ", s )
@@ -65,34 +62,64 @@ end
65--]] 62--]]
66 63
67----- 64-----
68-- Actual data store 65-- FIFO for a key
69--
70-- { [linda_deep_ud]= { key= val [, ...] }
71-- ...
72-- }
73-- 66--
74local _data= {} 67
68local fifo_new = function()
69 return { first = 1, count = 0}
70end
71
72local fifo_push = function( fifo, ...)
73 local first, count, added = fifo.first, fifo.count, select( '#', ...)
74 local start = first + count - 1
75 for i = 1, added do
76 fifo[start + i] = select( i, ...)
77 end
78 fifo.count = count + added
79end
80
81local fifo_peek = function( fifo, count)
82 if count <= fifo.count then
83 local first = fifo.first
84 local last = first + count - 1
85 return unpack( fifo, first, last)
86 end
87end
88
89local fifo_pop = function( fifo, count)
90 if count > fifo.count then error("list is too short") end
91 local first = fifo.first
92 local last = first + count - 1
93 local out = { unpack( fifo, first, last)}
94 for i = first, last do
95 fifo[i] = nil
96 end
97 fifo.first = first + count
98 fifo.count = fifo.count - count
99 return unpack( out)
100end
101
75 102
76----- 103-----
77-- Entries queued for use when the existing 'data[ud][key]' entry is consumed. 104-- Actual data store
78-- 105--
79-- { [linda_deep_ud]= { key= { val [, ... } [, ...] } 106-- { [linda_deep_ud]= { key= { val [, ... ] } [, ...] }
80-- ... 107-- ...
81-- } 108-- }
82-- 109--
83local _incoming= {} 110local _data= {}
84 111
85----- 112-----
86-- Length limits (if any) for queues 113-- Length limits (if any) for queues
87-- 114--
88-- 0: don't queue values at all; ':send()' waits if the slot is not vacant 115-- 0: don't queue values at all; ':send()' waits if the slot is not vacant
89-- N: allow N values to be queued (slot itself + N-1); wait if full 116-- N: allow N values to be queued (slot itself + N-1); wait if full
90-- nil: no limits, '_incoming' may grow endlessly 117-- nil: no limits, '_data' may grow endlessly
91-- 118--
92local _limits= {} 119local _limits= {}
93 120
94----- 121-----
95-- data_tbl, incoming_tbl, limits_tbl = tables( linda_deep_ud ) 122-- data_tbl, limits_tbl = tables( linda_deep_ud )
96-- 123--
97-- Gives appropriate tables for a certain Linda (creates them if needed) 124-- Gives appropriate tables for a certain Linda (creates them if needed)
98-- 125--
@@ -101,14 +128,13 @@ local function tables( ud )
101 -- 128 --
102 if not _data[ud] then 129 if not _data[ud] then
103 _data[ud]= {} 130 _data[ud]= {}
104 _incoming[ud]= {}
105 _limits[ud]= {} 131 _limits[ud]= {}
106 end 132 end
107 return _data[ud], _incoming[ud], _limits[ud] 133 return _data[ud], _limits[ud]
108end 134end
109 135
110----- 136-----
111-- bool= send( linda_deep_ud, key, ... ) 137-- bool= send( linda_deep_ud, key, ...)
112-- 138--
113-- Send new data (1..N) to 'key' slot. This send is atomic; all the values 139-- Send new data (1..N) to 'key' slot. This send is atomic; all the values
114-- end up one after each other (this is why having possibility for sending 140-- end up one after each other (this is why having possibility for sending
@@ -119,42 +145,28 @@ end
119-- Returns: 'true' if all the values were placed 145-- Returns: 'true' if all the values were placed
120-- 'false' if sending would exceed the queue limit (wait & retry) 146-- 'false' if sending would exceed the queue limit (wait & retry)
121-- 147--
122function send( ud, key, ... ) 148function send( ud, key, ...)
123 149
124 local data,incoming,limits= tables(ud) 150 local data, limits = tables( ud)
125 151
126 local n= select('#',...) 152 local n = select( '#', ...)
127 if n==0 then return true end -- nothing to send 153 if n == 0 then return true end -- nothing to send
128 154
129 -- Initialize queue for all keys that have been used with ':send()' 155 -- Initialize queue for all keys that have been used with ':send()'
130 -- 156 --
131 if incoming[key]==nil then 157 if data[key] == nil then
132 incoming[key]= {} 158 data[key] = fifo_new()
133 end 159 end
160 local fifo = data[key]
134 161
135 local len= data[key] and 1+#incoming[key] or 0 162 local len = fifo.count
136 local m= limits[key] 163 local m = limits[key]
137 164
138 if m and len+n > m then 165 if m and len+n > m then
139 return false -- would exceed the limit; try again later 166 return false -- would exceed the limit; try again later
140 end 167 end
141 168
142 for i=1,n do 169 fifo_push( fifo, ...)
143 local val= select(i,...)
144
145 -- 'nil' in the data replaced by sentinel
146 if val==nil then
147 val= nil_sentinel
148 end
149
150 if len==0 then
151 data[key]= val
152 len= 1
153 else
154 incoming[key][len]= val
155 len= len+1
156 end
157 end
158 return true 170 return true
159end 171end
160 172
@@ -165,94 +177,125 @@ end
165-- Read any of the given keys, consuming the data found. Keys are read in 177-- Read any of the given keys, consuming the data found. Keys are read in
166-- order. 178-- order.
167-- 179--
168function receive( ud, ... ) 180function receive( ud, ...)
169 181
170 local data,incoming,_= tables(ud) 182 local data, _ = tables( ud)
171 183
172 for i=1,select('#',...) do 184 for i = 1, select( '#', ...) do
173 local key= select(i,...) 185 local key = select( i, ...)
174 local val= data[key] 186 local fifo = data[key]
175 187 if fifo and fifo.count > 0 then
176 if val~=nil then 188 local val = fifo_pop( fifo, 1)
177 if incoming[key] and incoming[key][1]~=nil then 189 if val ~= nil then
178 -- pop [1] from 'incoming[key]' into the actual slot 190 return val, key
179 data[key]= table_remove( incoming[key], 1 )
180 else
181 data[key]= nil -- empty the slot
182 end
183 if val==nil_sentinel then
184 val= nil
185 end 191 end
186 return val, key
187 end 192 end
188 end 193 end
189 --return nil 194end
195
196
197-----
198-- [val1, ... valCOUNT]= receive_batched( linda_deep_ud, batch_sentinel, key , COUNT)
199--
200-- Read any of the given keys, consuming the data found. Keys are read in
201-- order.
202--
203receive_batched = function( ud, batch_sentinel, key, count)
204 if count > 0 then
205 local data, _ = tables( ud)
206 local fifo = data[key]
207 if fifo and fifo.count >= count then
208 return fifo_pop( fifo, count)
209 end
210 end
190end 211end
191 212
192 213
193----- 214-----
194-- = limit( linda_deep_ud, key, uint ) 215-- = limit( linda_deep_ud, key, uint )
195-- 216--
196function limit( ud, key, n ) 217function limit( ud, key, n)
197 218
198 local _,_,limits= tables(ud) 219 local _, limits = tables( ud)
199 220
200 limits[key]= n 221 limits[key] = n
201end 222end
202 223
203 224
204----- 225-----
205-- void= set( linda_deep_ud, key, [val] ) 226-- void= set( linda_deep_ud, key, [val] )
206-- 227--
207function set( ud, key, val ) 228function set( ud, key, val)
208 229
209 local data,incoming,_= tables(ud) 230 local data, _ = tables( ud)
210 231
211 -- Setting a key to 'nil' really clears it; only queing uses sentinels. 232 -- Setting a key to 'nil' really clears it; only queing uses sentinels.
212 -- 233 --
213 data[key]= val 234 if val ~= nil then
214 incoming[key]= nil 235 local fifo = fifo_new()
236 fifo_push( fifo, val)
237 data[key] = fifo
238 else
239 data[key] = nil
240 end
215end 241end
216 242
217 243
218----- 244-----
219-- [val]= get( linda_deep_ud, key ) 245-- [val]= get( linda_deep_ud, key )
220-- 246--
221function get( ud, key ) 247function get( ud, key)
222 248 local data, _ = tables( ud)
223 local data,_,_= tables(ud) 249 local fifo = data[key]
224 250 return fifo and fifo_peek( fifo, 1)
225 local val= data[key]
226 if val==nil_sentinel then
227 val= nil
228 end
229 return val
230end 251end
231 252
232 253
233----- 254-----
234-- [val]= keys( linda_deep_ud) 255-- [val]= count( linda_deep_ud, ...)
235-- 256--
236function keys( ud) 257-- 3 modes of operation
237 258-- linda:count() -> returns a table of key/count pairs
238 local data,_,_= tables(ud) 259-- linda:count(key) returns the number of items waiting in the key
239 260-- linda:count(key,...) -> returns a table telling, for each key, the number of items
240 local out = {} 261function count( ud, ...)
241 for key, v in pairs( data) do 262 local data, _ = tables( ud)
242 table_insert( out, key) 263 local n = select( '#', ...)
264 if n == 0 then
265 local out
266 for key, _ in pairs( data) do
267 local fifo = data[key]
268 local count = fifo and fifo.count or 0
269 out = out or {}
270 out[key] = count
271 found = true
272 end
273 return out
274 elseif n == 1 then
275 local key = ...
276 local fifo = data[key]
277 return fifo and fifo.count or nil
278 else -- more than 1 key
279 local out
280 for i = 1, n do
281 local key = select( i, ...)
282 local fifo = data[key]
283 local count = fifo and fifo.count or nil
284 out = out or {}
285 out[key] = count
286 end
287 return out
243 end 288 end
244 return (#out > 0) and out or nil
245end 289end
246 290
247 291
248----- 292-----
249-- void= clear( linda_deep_ud ) 293-- void= clear( linda_deep_ud)
250-- 294--
251-- Clear the data structures used for a Linda (at its destructor) 295-- Clear the data structures used for a Linda (at its destructor)
252-- 296--
253function clear( ud ) 297function clear( ud)
254 298
255 _data[ud]= nil 299 _data[ud]= nil
256 _incoming[ud]= nil
257 _limits[ud]= nil 300 _limits[ud]= nil
258end 301end
diff --git a/src/lanes.c b/src/lanes.c
index e06d367..e514e6b 100644
--- a/src/lanes.c
+++ b/src/lanes.c
@@ -51,7 +51,7 @@
51 * ... 51 * ...
52 */ 52 */
53 53
54const char *VERSION= "2.1.0"; 54const char *VERSION= "2.2.0";
55 55
56/* 56/*
57=============================================================================== 57===============================================================================
@@ -249,6 +249,20 @@ static void linda_id( lua_State*, char const * const which);
249#define lua_toLinda(L,n) ((struct s_Linda *)luaG_todeep( L, linda_id, n )) 249#define lua_toLinda(L,n) ((struct s_Linda *)luaG_todeep( L, linda_id, n ))
250 250
251 251
252static void check_key_types( lua_State *L, int _start, int _end)
253{
254 int i;
255 for( i = _start; i <= _end; ++ i)
256 {
257 int t = lua_type( L, i);
258 if( t == LUA_TBOOLEAN || t == LUA_TNUMBER || t == LUA_TSTRING || t == LUA_TLIGHTUSERDATA)
259 {
260 continue;
261 }
262 luaL_error( L, "argument #%d: invalid key type (not a boolean, string, number or light userdata)", i);
263 }
264}
265
252/* 266/*
253* bool= linda_send( linda_ud, [timeout_secs=-1,] key_num|str|bool|lightuserdata, ... ) 267* bool= linda_send( linda_ud, [timeout_secs=-1,] key_num|str|bool|lightuserdata, ... )
254* 268*
@@ -271,15 +285,18 @@ LUAG_FUNC( linda_send)
271 if( lua_isnumber(L, 2)) 285 if( lua_isnumber(L, 2))
272 { 286 {
273 timeout= SIGNAL_TIMEOUT_PREPARE( lua_tonumber(L,2) ); 287 timeout= SIGNAL_TIMEOUT_PREPARE( lua_tonumber(L,2) );
274 key_i++; 288 ++ key_i;
275 } 289 }
276 else if( lua_isnil( L, 2)) 290 else if( lua_isnil( L, 2)) // alternate explicit "no timeout" by passing nil before the key
277 { 291 {
278 key_i++; 292 ++ key_i;
279 } 293 }
280 294
281 if( lua_isnil( L, key_i)) 295 // make sure the keys are of a valid type
282 luaL_error( L, "nil key" ); 296 check_key_types( L, key_i, key_i);
297
298 // convert nils to some special non-nil sentinel in sent values
299 keeper_toggle_nil_sentinels( L, key_i + 1, 1);
283 300
284 STACK_GROW(L, 1); 301 STACK_GROW(L, 1);
285 { 302 {
@@ -315,11 +332,11 @@ LUAG_FUNC( linda_send)
315 332
316 cancel = cancel_test( L); // testing here causes no delays 333 cancel = cancel_test( L); // testing here causes no delays
317 if (cancel) 334 if (cancel)
335 {
318 break; 336 break;
337 }
319 338
320 // Bugfix by Benoit Germain Dec-2009: change status of lane to "waiting" 339 // change status of lane to "waiting"
321 //
322#if 1
323 { 340 {
324 struct s_lane *s; 341 struct s_lane *s;
325 enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings 342 enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings
@@ -338,6 +355,7 @@ LUAG_FUNC( linda_send)
338 ASSERT_L( s->waiting_on == NULL); 355 ASSERT_L( s->waiting_on == NULL);
339 s->waiting_on = &linda->read_happened; 356 s->waiting_on = &linda->read_happened;
340 } 357 }
358 // could not send because no room: wait until some data was read before trying again, or until timeout is reached
341 if( !SIGNAL_WAIT( &linda->read_happened, &K->lock_, timeout)) 359 if( !SIGNAL_WAIT( &linda->read_happened, &K->lock_, timeout))
342 { 360 {
343 if( s) 361 if( s)
@@ -353,12 +371,6 @@ LUAG_FUNC( linda_send)
353 s->status = prev_status; 371 s->status = prev_status;
354 } 372 }
355 } 373 }
356#else
357 // K lock will be released for the duration of wait and re-acquired
358 //
359 if( !SIGNAL_WAIT( &linda->read_happened, &K->lock_, timeout))
360 break; // timeout
361#endif
362 } 374 }
363 STACK_END( KL, 0) 375 STACK_END( KL, 0)
364 keeper_release( K); 376 keeper_release( K);
@@ -379,18 +391,23 @@ LUAG_FUNC( linda_send)
379 391
380 392
381/* 393/*
382* [val, key]= linda_receive( linda_ud, [timeout_secs_num=-1], key_num|str|bool|lightuserdata [, ...] ) 394 * 2 modes of operation
383* 395 * [val, key]= linda_receive( linda_ud, [timeout_secs_num=-1], key_num|str|bool|lightuserdata [, ...] )
384* Receive a value from Linda, consuming it. 396 * Consumes a single value from the Linda, in any key.
385* 397 * Returns: received value (which is consumed from the slot), and the key which had it
386* Returns: value received (which is consumed from the slot) 398
387* key which had it 399 * [val1, ... valCOUNT]= linda_receive( linda_ud, [timeout_secs_num=-1], linda.batched, key_num|str|bool|lightuserdata, COUNT)
388*/ 400 * Consumes COUNT values from the linda, from a single key.
401 * returns the COUNT consumed values, or nil if there weren't enough values to consume
402 *
403 */
404#define BATCH_SENTINEL "270e6c9d-280f-4983-8fee-a7ecdda01475"
389LUAG_FUNC( linda_receive) 405LUAG_FUNC( linda_receive)
390{ 406{
391 struct s_Linda *linda = lua_toLinda( L, 1); 407 struct s_Linda *linda = lua_toLinda( L, 1);
392 int pushed; 408 int pushed, expected_pushed;
393 bool_t cancel = FALSE; 409 bool_t cancel = FALSE;
410 char *keeper_receive;
394 411
395 time_d timeout = -1.0; 412 time_d timeout = -1.0;
396 uint_t key_i = 2; 413 uint_t key_i = 2;
@@ -400,26 +417,44 @@ LUAG_FUNC( linda_receive)
400 if( lua_isnumber( L, 2)) 417 if( lua_isnumber( L, 2))
401 { 418 {
402 timeout = SIGNAL_TIMEOUT_PREPARE( lua_tonumber( L, 2)); 419 timeout = SIGNAL_TIMEOUT_PREPARE( lua_tonumber( L, 2));
403 key_i++; 420 ++ key_i;
404 } 421 }
405 else if( lua_isnil( L, 2)) 422 else if( lua_isnil( L, 2)) // alternate explicit "no timeout" by passing nil before the key
406 { 423 {
407 key_i++; 424 ++ key_i;
408 } 425 }
409 426
427 // make sure the keys are of a valid type
428 check_key_types( L, key_i, lua_gettop( L));
429
430 // are we in batched mode?
431 lua_pushliteral( L, BATCH_SENTINEL);
432 if( lua_equal( L, key_i, -1))
433 {
434 keeper_receive = "receive_batched";
435 expected_pushed = (int)luaL_checkinteger( L, key_i + 2);
436 }
437 else
438 {
439 keeper_receive = "receive";
440 expected_pushed = 2;
441 }
442 lua_pop( L, 1);
443
410 { 444 {
411 struct s_Keeper *K = keeper_acquire( linda); 445 struct s_Keeper *K = keeper_acquire( linda);
412 for( ;;) 446 for( ;;)
413 { 447 {
414 pushed = keeper_call( K->L, "receive", L, linda, key_i); 448 pushed = keeper_call( K->L, keeper_receive, L, linda, key_i);
415 if( pushed < 0) 449 if( pushed < 0)
416 { 450 {
417 break; 451 break;
418 } 452 }
419 if( pushed > 0) 453 if( pushed > 0)
420 { 454 {
421 ASSERT_L( pushed == 2); 455 ASSERT_L( pushed == expected_pushed);
422 456 // replace sentinels with real nils
457 keeper_toggle_nil_sentinels( L, lua_gettop( L) - pushed, 0);
423 // To be done from within the 'K' locking area 458 // To be done from within the 'K' locking area
424 // 459 //
425 SIGNAL_ALL( &linda->read_happened); 460 SIGNAL_ALL( &linda->read_happened);
@@ -438,9 +473,7 @@ LUAG_FUNC( linda_receive)
438 break; 473 break;
439 } 474 }
440 475
441 // Bugfix by Benoit Germain Dec-2009: change status of lane to "waiting" 476 // change status of lane to "waiting"
442 //
443#if 1
444 { 477 {
445 struct s_lane *s; 478 struct s_lane *s;
446 enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings 479 enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings
@@ -459,6 +492,7 @@ LUAG_FUNC( linda_receive)
459 ASSERT_L( s->waiting_on == NULL); 492 ASSERT_L( s->waiting_on == NULL);
460 s->waiting_on = &linda->write_happened; 493 s->waiting_on = &linda->write_happened;
461 } 494 }
495 // not enough data to read: wakeup when data was sent, or when timeout is reached
462 if( !SIGNAL_WAIT( &linda->write_happened, &K->lock_, timeout)) 496 if( !SIGNAL_WAIT( &linda->write_happened, &K->lock_, timeout))
463 { 497 {
464 if( s) 498 if( s)
@@ -474,12 +508,6 @@ LUAG_FUNC( linda_receive)
474 s->status = prev_status; 508 s->status = prev_status;
475 } 509 }
476 } 510 }
477#else
478 // Release the K lock for the duration of wait, and re-acquire
479 //
480 if( !SIGNAL_WAIT( &linda->write_happened, &K->lock_, timeout))
481 break;
482#endif
483 } 511 }
484 keeper_release( K); 512 keeper_release( K);
485 } 513 }
@@ -501,6 +529,7 @@ LUAG_FUNC( linda_receive)
501* = linda_set( linda_ud, key_num|str|bool|lightuserdata [,value] ) 529* = linda_set( linda_ud, key_num|str|bool|lightuserdata [,value] )
502* 530*
503* Set a value to Linda. 531* Set a value to Linda.
532* TODO: what do we do if we set to non-nil and limit is 0?
504* 533*
505* Existing slot value is replaced, and possible queue entries removed. 534* Existing slot value is replaced, and possible queue entries removed.
506*/ 535*/
@@ -510,9 +539,14 @@ LUAG_FUNC( linda_set)
510 bool_t has_value = !lua_isnil( L, 3); 539 bool_t has_value = !lua_isnil( L, 3);
511 luaL_argcheck( L, linda, 1, "expected a linda object!"); 540 luaL_argcheck( L, linda, 1, "expected a linda object!");
512 541
542 // make sure the key is of a valid type
543 check_key_types( L, 2, 2);
544
513 { 545 {
546 int pushed;
514 struct s_Keeper *K = keeper_acquire( linda); 547 struct s_Keeper *K = keeper_acquire( linda);
515 int pushed = keeper_call( K->L, "set", L, linda, 2); 548 // no nil->sentinel toggling, we really clear the linda contents for the given key with a set()
549 pushed = keeper_call( K->L, "set", L, linda, 2);
516 if( pushed >= 0) // no error? 550 if( pushed >= 0) // no error?
517 { 551 {
518 ASSERT_L( pushed == 0); 552 ASSERT_L( pushed == 0);
@@ -537,28 +571,28 @@ LUAG_FUNC( linda_set)
537 571
538 572
539/* 573/*
540* [val]= linda_keys( linda_ud) 574 * [val] = linda_count( linda_ud, [key [, ...]])
541* 575 *
542* Get the list of keys with pending data in the linda 576 * Get a count of the pending elements in the specified keys
543*/ 577 */
544LUAG_FUNC( linda_keys) 578LUAG_FUNC( linda_count)
545{ 579{
546 struct s_Linda *linda= lua_toLinda( L, 1); 580 struct s_Linda *linda= lua_toLinda( L, 1);
547 int pushed; 581 int pushed;
582
548 luaL_argcheck( L, linda, 1, "expected a linda object!"); 583 luaL_argcheck( L, linda, 1, "expected a linda object!");
584 // make sure the keys are of a valid type
585 check_key_types( L, 2, lua_gettop( L));
549 586
550 { 587 {
551 struct s_Keeper *K = keeper_acquire( linda); 588 struct s_Keeper *K = keeper_acquire( linda);
552 pushed = keeper_call( K->L, "keys", L, linda, 2); 589 pushed = keeper_call( K->L, "count", L, linda, 2);
553 ASSERT_L( pushed==0 || pushed==1 ); 590 keeper_release( K);
554 keeper_release(K);
555 // must trigger error after keeper state has been released
556 if( pushed < 0) 591 if( pushed < 0)
557 { 592 {
558 luaL_error( L, "tried to copy unsupported types"); 593 luaL_error( L, "tried to count an invalid key");
559 } 594 }
560 } 595 }
561
562 return pushed; 596 return pushed;
563} 597}
564 598
@@ -567,17 +601,25 @@ LUAG_FUNC( linda_keys)
567* [val]= linda_get( linda_ud, key_num|str|bool|lightuserdata ) 601* [val]= linda_get( linda_ud, key_num|str|bool|lightuserdata )
568* 602*
569* Get a value from Linda. 603* Get a value from Linda.
604* TODO: add support to get multiple values?
570*/ 605*/
571LUAG_FUNC( linda_get) 606LUAG_FUNC( linda_get)
572{ 607{
573 struct s_Linda *linda= lua_toLinda( L, 1); 608 struct s_Linda *linda= lua_toLinda( L, 1);
574 int pushed; 609 int pushed;
610
575 luaL_argcheck( L, linda, 1, "expected a linda object!"); 611 luaL_argcheck( L, linda, 1, "expected a linda object!");
612 // make sure the key is of a valid type
613 check_key_types( L, 2, 2);
576 614
577 { 615 {
578 struct s_Keeper *K = keeper_acquire( linda); 616 struct s_Keeper *K = keeper_acquire( linda);
579 pushed = keeper_call( K->L, "get", L, linda, 2); 617 pushed = keeper_call( K->L, "get", L, linda, 2);
580 ASSERT_L( pushed==0 || pushed==1 ); 618 ASSERT_L( pushed==0 || pushed==1 );
619 if( pushed > 0)
620 {
621 keeper_toggle_nil_sentinels( L, lua_gettop( L) - pushed, 0);
622 }
581 keeper_release(K); 623 keeper_release(K);
582 // must trigger error after keeper state has been released 624 // must trigger error after keeper state has been released
583 if( pushed < 0) 625 if( pushed < 0)
@@ -598,7 +640,10 @@ LUAG_FUNC( linda_get)
598LUAG_FUNC( linda_limit) 640LUAG_FUNC( linda_limit)
599{ 641{
600 struct s_Linda *linda= lua_toLinda( L, 1 ); 642 struct s_Linda *linda= lua_toLinda( L, 1 );
643
601 luaL_argcheck( L, linda, 1, "expected a linda object!"); 644 luaL_argcheck( L, linda, 1, "expected a linda object!");
645 // make sure the key is of a valid type
646 check_key_types( L, 2, 2);
602 647
603 { 648 {
604 struct s_Keeper *K = keeper_acquire( linda); 649 struct s_Keeper *K = keeper_acquire( linda);
@@ -775,21 +820,24 @@ static void linda_id( lua_State *L, char const * const which)
775 lua_pushcfunction( L, LG_linda_receive ); 820 lua_pushcfunction( L, LG_linda_receive );
776 lua_setfield( L, -2, "receive" ); 821 lua_setfield( L, -2, "receive" );
777 822
778 lua_pushcfunction( L, LG_linda_keys );
779 lua_setfield( L, -2, "keys" );
780
781 lua_pushcfunction( L, LG_linda_limit ); 823 lua_pushcfunction( L, LG_linda_limit );
782 lua_setfield( L, -2, "limit" ); 824 lua_setfield( L, -2, "limit" );
783 825
784 lua_pushcfunction( L, LG_linda_set ); 826 lua_pushcfunction( L, LG_linda_set );
785 lua_setfield( L, -2, "set" ); 827 lua_setfield( L, -2, "set" );
786 828
829 lua_pushcfunction( L, LG_linda_count );
830 lua_setfield( L, -2, "count" );
831
787 lua_pushcfunction( L, LG_linda_get ); 832 lua_pushcfunction( L, LG_linda_get );
788 lua_setfield( L, -2, "get" ); 833 lua_setfield( L, -2, "get" );
789 834
790 lua_pushcfunction( L, LG_linda_deep ); 835 lua_pushcfunction( L, LG_linda_deep );
791 lua_setfield( L, -2, "deep" ); 836 lua_setfield( L, -2, "deep" );
792 837
838 lua_pushliteral( L, BATCH_SENTINEL);
839 lua_setfield(L, -2, "batched");
840
793 STACK_END(L,1) 841 STACK_END(L,1)
794 } 842 }
795 else if( strcmp( which, "module") == 0) 843 else if( strcmp( which, "module") == 0)
@@ -803,6 +851,11 @@ static void linda_id( lua_State *L, char const * const which)
803 } 851 }
804} 852}
805 853
854/*
855 * ud = lanes.linda()
856 *
857 * returns a linda object
858 */
806LUAG_FUNC( linda) 859LUAG_FUNC( linda)
807{ 860{
808 return luaG_deep_userdata( L, linda_id); 861 return luaG_deep_userdata( L, linda_id);