diff options
-rw-r--r-- | CHANGES | 8 | ||||
-rw-r--r-- | Makefile | 14 | ||||
-rw-r--r-- | docs/index.html | 14 | ||||
-rw-r--r-- | setup-vc.cmd | 15 | ||||
-rw-r--r-- | src/keeper.c | 36 | ||||
-rw-r--r-- | src/keeper.h | 1 | ||||
-rw-r--r-- | src/keeper.lua | 225 | ||||
-rw-r--r-- | src/lanes.c | 159 |
8 files changed, 310 insertions, 162 deletions
@@ -3,6 +3,14 @@ CHANGES: | |||
3 | 3 | ||
4 | CHANGE X: | 4 | CHANGE X: |
5 | 5 | ||
6 | CHANGE 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 | |||
6 | CHANGE 30 BGe 30-Mar-2011 | 14 | CHANGE 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 |
@@ -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 | ||
89 | basic: tests/basic.lua $(_TARGET_SO) | 90 | basic: 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 | # |
95 | REP_ARGS=-llanes -e "print'say aaa'; for i=1,10 do print(i) end" | 96 | REP_ARGS=-llanes -e "print'say aaa'; for i=1,10 do print(i) end" |
@@ -155,6 +156,9 @@ appendud: tests/appendud.lua $(_TARGET_SO) | |||
155 | func_is_string: tests/func_is_string.lua $(_TARGET_SO) | 156 | func_is_string: tests/func_is_string.lua $(_TARGET_SO) |
156 | $(_PREFIX) $(LUA) $< | 157 | $(_PREFIX) $(LUA) $< |
157 | 158 | ||
159 | linda_perf: tests/linda_perf.lua $(_TARGET_SO) | ||
160 | $(_PREFIX) $(LUA) $< | ||
161 | |||
158 | atexit: tests/atexit.lua $(_TARGET_SO) | 162 | atexit: tests/atexit.lua $(_TARGET_SO) |
159 | $(_PREFIX) $(LUA) $< | 163 | $(_PREFIX) $(LUA) $< |
160 | 164 | ||
@@ -190,7 +194,7 @@ LUA_LIBDIR=$(DESTDIR)/lib/lua/5.1 | |||
190 | LUA_SHAREDIR=$(DESTDIR)/share/lua/5.1 | 194 | LUA_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 | # |
195 | install: $(_TARGET_SO) src/lanes.lua | 199 | install: $(_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: | |||
211 | ifeq "$(VERSION)" "" | 215 | ifeq "$(VERSION)" "" |
212 | echo "Usage: make tar VERSION=x.x"; false | 216 | echo "Usage: make tar VERSION=x.x"; false |
213 | else | 217 | else |
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 |
225 | endif | 229 | endif |
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. | |||
560 | Writing to a slot overwrites existing value, and clears any possible queued | 560 | Writing to a slot overwrites existing value, and clears any possible queued |
561 | entries. Table access and <tt>send</tt>/<tt>receive</tt> can be used together; | 561 | entries. Table access and <tt>send</tt>/<tt>receive</tt> can be used together; |
562 | reading a slot essentially peeks the next outcoming value of a queue. | 562 | reading 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 |
32 | set VSINSTALLDIR=%ProgramFiles%\Microsoft Visual Studio 9.0 | 32 | set VSINSTALLDIR=%ProgramFiles%\Microsoft Visual Studio 9.0 |
33 | if not exist "%VSINSTALLDIR%\VC\vcvarsall.bat" goto ERR_NOVC | 33 | if exist "%VSINSTALLDIR%\VC\vcvarsall.bat" goto WARN_VC |
34 | set VSINSTALLDIR=%ProgramFiles(x86)%\Microsoft Visual Studio 9.0 | ||
35 | if exist "%VSINSTALLDIR%\VC\vcvarsall.bat" goto WARN_VC | ||
34 | 36 | ||
37 | :TRY_VC10 | ||
38 | set VSINSTALLDIR=%ProgramFiles%\Microsoft Visual Studio 10.0 | ||
39 | if exist "%VSINSTALLDIR%\VC\vcvarsall.bat" goto WARN_VC | ||
40 | set VSINSTALLDIR=%ProgramFiles(x86)%\Microsoft Visual Studio 10.0 | ||
41 | if exist "%VSINSTALLDIR%\VC\vcvarsall.bat" goto WARN_VC | ||
42 | |||
43 | :WARN_VC | ||
35 | echo. | 44 | echo. |
36 | echo *** Warning: Visual C++ 2008 in use *** | 45 | echo *** Warning: Visual C++ 2008/2010 in use *** |
37 | echo. | 46 | echo. |
38 | echo Using VC++2005 is recommended for runtime compatibility issues | 47 | echo Using VC++2005 is recommended for runtime compatibility issues |
39 | echo (LuaBinaries and LfW use it; if you compile everything from | 48 | echo (LuaBinaries and LfW use it; if you compile everything from |
@@ -70,7 +79,7 @@ goto EXIT | |||
70 | REM --- | 79 | REM --- |
71 | :ERR_NOVC | 80 | :ERR_NOVC |
72 | echo. | 81 | echo. |
73 | echo ** ERROR: Visual C++ 2005/08 Express - not detected | 82 | echo ** ERROR: Visual C++ 2005/08/10 Express - not detected |
74 | echo You can set the environment variables separately, and run 'make-vc.cmd' | 83 | echo You can set the environment variables separately, and run 'make-vc.cmd' |
75 | echo or download the compiler from: | 84 | echo or download the compiler from: |
76 | echo http://msdn.microsoft.com/vstudio/express/downloads/ | 85 | echo 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 | ||
150 | void 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 | |||
11 | const char *init_keepers( int const _nbKeepers); | 11 | const char *init_keepers( int const _nbKeepers); |
12 | struct s_Keeper *keeper_acquire( const void *ptr); | 12 | struct s_Keeper *keeper_acquire( const void *ptr); |
13 | void keeper_release( struct s_Keeper *K); | 13 | void keeper_release( struct s_Keeper *K); |
14 | void keeper_toggle_nil_sentinels( lua_State *L, int _val_i, int _nil_to_sentinel); | ||
14 | int keeper_call( lua_State *K, char const *func_name, lua_State *L, void *linda, uint_t starting_index); | 15 | int keeper_call( lua_State *K, char const *func_name, lua_State *L, void *linda, uint_t starting_index); |
15 | void close_keepers(void); | 16 | void 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 | -- | ||
39 | assert( 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 | -- |
43 | local table_concat = assert( table.concat) | 39 | local table_concat = assert( table.concat) |
44 | local table_insert = assert( table.insert) | 40 | local table_insert = assert( table.insert) |
45 | local table_remove = assert( table.remove) | 41 | local table_remove = assert( table.remove) |
42 | local select, unpack = assert( select), assert( unpack) | ||
46 | 43 | ||
47 | --[[ | 44 | --[[ |
48 | local function WR(...) | 45 | local 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 |
52 | end | 49 | end |
53 | 50 | ||
54 | local function DEBUG(title,ud,key) | 51 | local 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 | -- |
74 | local _data= {} | 67 | |
68 | local fifo_new = function() | ||
69 | return { first = 1, count = 0} | ||
70 | end | ||
71 | |||
72 | local 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 | ||
79 | end | ||
80 | |||
81 | local 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 | ||
87 | end | ||
88 | |||
89 | local 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) | ||
100 | end | ||
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 | -- |
83 | local _incoming= {} | 110 | local _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 | -- |
92 | local _limits= {} | 119 | local _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] |
108 | end | 134 | end |
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 | -- |
122 | function send( ud, key, ... ) | 148 | function 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 |
159 | end | 171 | end |
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 | -- |
168 | function receive( ud, ... ) | 180 | function 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 | 194 | end |
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 | -- | ||
203 | receive_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 | ||
190 | end | 211 | end |
191 | 212 | ||
192 | 213 | ||
193 | ----- | 214 | ----- |
194 | -- = limit( linda_deep_ud, key, uint ) | 215 | -- = limit( linda_deep_ud, key, uint ) |
195 | -- | 216 | -- |
196 | function limit( ud, key, n ) | 217 | function 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 |
201 | end | 222 | end |
202 | 223 | ||
203 | 224 | ||
204 | ----- | 225 | ----- |
205 | -- void= set( linda_deep_ud, key, [val] ) | 226 | -- void= set( linda_deep_ud, key, [val] ) |
206 | -- | 227 | -- |
207 | function set( ud, key, val ) | 228 | function 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 | ||
215 | end | 241 | end |
216 | 242 | ||
217 | 243 | ||
218 | ----- | 244 | ----- |
219 | -- [val]= get( linda_deep_ud, key ) | 245 | -- [val]= get( linda_deep_ud, key ) |
220 | -- | 246 | -- |
221 | function get( ud, key ) | 247 | function 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 | ||
230 | end | 251 | end |
231 | 252 | ||
232 | 253 | ||
233 | ----- | 254 | ----- |
234 | -- [val]= keys( linda_deep_ud) | 255 | -- [val]= count( linda_deep_ud, ...) |
235 | -- | 256 | -- |
236 | function 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 = {} | 261 | function 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 | ||
245 | end | 289 | end |
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 | -- |
253 | function clear( ud ) | 297 | function clear( ud) |
254 | 298 | ||
255 | _data[ud]= nil | 299 | _data[ud]= nil |
256 | _incoming[ud]= nil | ||
257 | _limits[ud]= nil | 300 | _limits[ud]= nil |
258 | end | 301 | end |
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 | ||
54 | const char *VERSION= "2.1.0"; | 54 | const 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 | ||
252 | static 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" | ||
389 | LUAG_FUNC( linda_receive) | 405 | LUAG_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 | */ |
544 | LUAG_FUNC( linda_keys) | 578 | LUAG_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 | */ |
571 | LUAG_FUNC( linda_get) | 606 | LUAG_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) | |||
598 | LUAG_FUNC( linda_limit) | 640 | LUAG_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 | */ | ||
806 | LUAG_FUNC( linda) | 859 | LUAG_FUNC( linda) |
807 | { | 860 | { |
808 | return luaG_deep_userdata( L, linda_id); | 861 | return luaG_deep_userdata( L, linda_id); |