diff options
| author | Benoit Germain <bnt.germain@gmail.com> | 2012-06-27 14:07:55 +0200 |
|---|---|---|
| committer | Benoit Germain <bnt.germain@gmail.com> | 2012-06-27 14:07:55 +0200 |
| commit | 6234c2113a2b52ddc9fa900e7848f2cd19bd9394 (patch) | |
| tree | deb14fd69b53b863b825c954b50a58667e1bc19c | |
| parent | ccd465a1c7c7257e85eb04fcc6767b38e794dbbe (diff) | |
| download | lanes-3.1.4.tar.gz lanes-3.1.4.tar.bz2 lanes-3.1.4.zip | |
* when a transfered function is not found in source, guess its name to help the user find out what's wrongv3.1.4
* new function lanes.nameof()
| -rw-r--r-- | CHANGES | 4 | ||||
| -rw-r--r-- | docs/index.html | 111 | ||||
| -rw-r--r-- | src/lanes.c | 1 | ||||
| -rw-r--r-- | src/lanes.lua | 1 | ||||
| -rw-r--r-- | src/tools.c | 139 | ||||
| -rw-r--r-- | src/tools.h | 2 |
6 files changed, 146 insertions, 112 deletions
| @@ -1,6 +1,10 @@ | |||
| 1 | 1 | ||
| 2 | CHANGES: | 2 | CHANGES: |
| 3 | 3 | ||
| 4 | CHANGE 40: BGe 26-Jun-2012 | ||
| 5 | * when a transfered function is not found in source, guess its name to help the user find out what's wrong | ||
| 6 | * new function lanes.nameof() | ||
| 7 | |||
| 4 | CHANGE 39: BGe 23-Jun-2012 | 8 | CHANGE 39: BGe 23-Jun-2012 |
| 5 | * lanes.timer() accepts a first_secs=nil to stop a timer | 9 | * lanes.timer() accepts a first_secs=nil to stop a timer |
| 6 | * timer lane catches errors and prints them | 10 | * timer lane catches errors and prints them |
diff --git a/docs/index.html b/docs/index.html index 52ba852..2d1c78e 100644 --- a/docs/index.html +++ b/docs/index.html | |||
| @@ -56,7 +56,7 @@ | |||
| 56 | 56 | ||
| 57 | <p><br/><font size="-1"><i>Copyright © 2007-12 Asko Kauppi, Benoit Germain. All rights reserved.</i> | 57 | <p><br/><font size="-1"><i>Copyright © 2007-12 Asko Kauppi, Benoit Germain. All rights reserved.</i> |
| 58 | <br>Lua Lanes is published under the same <A HREF="http://en.wikipedia.org/wiki/MIT_License">MIT license</A> as Lua 5.1. | 58 | <br>Lua Lanes is published under the same <A HREF="http://en.wikipedia.org/wiki/MIT_License">MIT license</A> as Lua 5.1. |
| 59 | </p><p>This document was revised on 23-Jun-12, and applies to version 3.1.4 | 59 | </p><p>This document was revised on 26-Jun-12, and applies to version 3.1.4 |
| 60 | </font></p> | 60 | </font></p> |
| 61 | 61 | ||
| 62 | </center> | 62 | </center> |
| @@ -360,6 +360,17 @@ also in the new lanes. | |||
| 360 | <p>Each lane also gets a function <tt>set_debug_threadname()</tt> that it can use anytime to do as the name says. | 360 | <p>Each lane also gets a function <tt>set_debug_threadname()</tt> that it can use anytime to do as the name says. |
| 361 | Supported debuggers are Microsoft Visual Studio (for the C side) and Decoda (for the Lua side). | 361 | Supported debuggers are Microsoft Visual Studio (for the C side) and Decoda (for the Lua side). |
| 362 | </p> | 362 | </p> |
| 363 | <p> | ||
| 364 | If a lane body pulls a C function imported by a module required before Lanes itself (thus not through a hooked <tt>require</tt>), the lane generator creation will raise an error. | ||
| 365 | The function name it shows is a path where it was found by scanning _G. As a utility, the name guessing functionality is exposed as such: | ||
| 366 | <table border="1" bgcolor="#E0E0FF" cellpadding="10"> | ||
| 367 | <tr> | ||
| 368 | <td> | ||
| 369 | <code> | ||
| 370 | "type", "name" = lanes.nameof( o) | ||
| 371 | </code> | ||
| 372 | </table> | ||
| 373 | </p> | ||
| 363 | 374 | ||
| 364 | <h3>Free running lanes</h3> | 375 | <h3>Free running lanes</h3> |
| 365 | 376 | ||
| @@ -1089,103 +1100,7 @@ its actual value. | |||
| 1089 | <h2 id="changes">Change log</h2> | 1100 | <h2 id="changes">Change log</h2> |
| 1090 | 1101 | ||
| 1091 | <p> | 1102 | <p> |
| 1092 | Apr-2012 | 1103 | See CHANGES. |
| 1093 | <ul> | ||
| 1094 | <li>improved LuaJIT2 compatibility by handling "*" library set through luaL_openlibs().</li> | ||
| 1095 | </ul> | ||
| 1096 | |||
| 1097 | |||
| 1098 | Feb-2012 | ||
| 1099 | <ul> | ||
| 1100 | <li>Added support for an on_state_create callback invoked on a pristine Lua state created by Lanes.</li> | ||
| 1101 | <li>This required a change in the <tt>lanes.configure()</tt> signature, hence the minor version bump.</li> | ||
| 1102 | </ul> | ||
| 1103 | |||
| 1104 | |||
| 1105 | Nov-2011 | ||
| 1106 | <ul> | ||
| 1107 | <li>process exit change: close everything at GC when main state closes, not when atexit() handlers are processed</li> | ||
| 1108 | <li>Lua 5.2-style module:</li> | ||
| 1109 | <ul> | ||
| 1110 | <li>module() is no longer used to implement lanes.lua</li> | ||
| 1111 | <li>a global "lanes" variable is no longer created when the module is required</li> | ||
| 1112 | <li>the Lanes module table is returned instead</li> | ||
| 1113 | </ul> | ||
| 1114 | <li>Lanes must be initialized before used:</li> | ||
| 1115 | <ul> | ||
| 1116 | <li>the first occurence of 'require "lanes"' produces a minimal interface that only contains a configure() function</li> | ||
| 1117 | <li>the remainder of the interface is made available once this function is called</li> | ||
| 1118 | <li>subsequent calls to configure() do nothing</li> | ||
| 1119 | <li>configure() controls the number of keeper states and the startup of timers</li> | ||
| 1120 | </ul> | ||
| 1121 | <li>* LuaJIT 2 compatibility</li> | ||
| 1122 | <ul> | ||
| 1123 | <li>non-Lua functions are no longer copied by creating a C closure from a C pointer, but through 2-way lookup tables</li> | ||
| 1124 | <li>this means that if a lane function body pulls non-Lua functions, the lane generator description must contain the list of libraries and modules that exports them</li> | ||
| 1125 | <li>introduces a change in configuration .globals management: contents are copied *after* std libs are loaded</li> | ||
| 1126 | <li>new .required configuration entry to list modules that must be require()'ed before lane body is transferred</li> | ||
| 1127 | </ul> | ||
| 1128 | <li>lane:cancel() wakes up waiting lindas like what is done at lane shutdown</li> | ||
| 1129 | <li>removed packagepath and packagecpath options, replaced by a package table, whose fields path, cpath, loaders, preload are transfered</li> | ||
| 1130 | </ul> | ||
| 1131 | |||
| 1132 | Mar-2011 (not yet versioned) | ||
| 1133 | <ul> | ||
| 1134 | <li>linda honors __tostring and __concat</li> | ||
| 1135 | <li>new accessor linda:count(), to get info about data stored inside a linda.</li> | ||
| 1136 | <li>new lanes options packagepath and packagecpath, in case one needs to set them differently than the default.</li> | ||
| 1137 | </ul> | ||
| 1138 | |||
| 1139 | Mar-2011 (2.1.0) | ||
| 1140 | <ul> | ||
| 1141 | <li>fixed potential crash at application shutdown when calling lua_close() on a killed thread's VM.</li> | ||
| 1142 | <li>exposed cancel_test() in the lanes to enable manual testing for cancellation requests.</li> | ||
| 1143 | <li>removed kludgy {globals={threadName}} support, replaced with a new function set_debug_threadname().</li> | ||
| 1144 | </ul> | ||
| 1145 | Feb-2011 (2.0.11): | ||
| 1146 | <ul> | ||
| 1147 | <li>Fixed bug where reference to Linda object was dropped for a short time (crashing if GC was run during that time).</li> | ||
| 1148 | <li>Changed the atexit code to trip the timer thread's write signal.</li> | ||
| 1149 | <li>Changed lanes.c to export functions as a module rather than writing them directly to the globals table.</li> | ||
| 1150 | </ul> | ||
| 1151 | |||
| 1152 | Jan-2011 (2.0.10): | ||
| 1153 | <ul> | ||
| 1154 | <li>linda_send was waiting on the wrong signal</li> | ||
| 1155 | <li>buildfix when using i586-mingw32msvc-gcc cross compiler</li> | ||
| 1156 | <li>lanes_h:cancel() returns a boolean as it should</li> | ||
| 1157 | <li>timers could get blocked sometimes because they were initialized with negative values</li> | ||
| 1158 | <li>prepare_timeout could generate an illegal setting</li> | ||
| 1159 | </ul> | ||
| 1160 | |||
| 1161 | Dec-2010 (2.0.9): | ||
| 1162 | <ul> | ||
| 1163 | <li>Fixed 'memory leak' in some situations where a free running lane is collected before application shutdown</li> | ||
| 1164 | <li>Fix LuaJIT2 incompatibility (no 'tostring' hijack anymore)</li> | ||
| 1165 | <li>Added support to generate a lane from a string</li> | ||
| 1166 | </ul> | ||
| 1167 | |||
| 1168 | Aug-2010 (2.0.6): | ||
| 1169 | <ul> | ||
| 1170 | <li>Fixed some memory leaks</li> | ||
| 1171 | <li>Fixed error in passing parameters to finalizers</li> | ||
| 1172 | <li>Fixed missing argument propagation in lane:cancel</li> | ||
| 1173 | <li>Added thread name debugging in VS</li> | ||
| 1174 | </ul> | ||
| 1175 | |||
| 1176 | Jan-2009 (2.0.3): | ||
| 1177 | <ul> | ||
| 1178 | <li>Added 'finalizer' to lane options. (TBD: not implemented yet!) | ||
| 1179 | </li> | ||
| 1180 | <li>Added call stack to errors coming from a lane. | ||
| 1181 | </li> | ||
| 1182 | </ul> | ||
| 1183 | |||
| 1184 | Jul-2008 (2.0): | ||
| 1185 | <ul> | ||
| 1186 | <li>Too many changes to list (you'll need to re-read this manual) | ||
| 1187 | </li> | ||
| 1188 | </ul> | ||
| 1189 | </p> | 1104 | </p> |
| 1190 | 1105 | ||
| 1191 | <!-- footnotes +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> | 1106 | <!-- footnotes +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> |
diff --git a/src/lanes.c b/src/lanes.c index eebe06a..3e906c0 100644 --- a/src/lanes.c +++ b/src/lanes.c | |||
| @@ -2303,6 +2303,7 @@ static const struct luaL_reg lanes_functions [] = { | |||
| 2303 | {"linda", LG_linda}, | 2303 | {"linda", LG_linda}, |
| 2304 | {"now_secs", LG_now_secs}, | 2304 | {"now_secs", LG_now_secs}, |
| 2305 | {"wakeup_conv", LG_wakeup_conv}, | 2305 | {"wakeup_conv", LG_wakeup_conv}, |
| 2306 | {"nameof", luaG_nameof}, | ||
| 2306 | {"_single", LG__single}, | 2307 | {"_single", LG__single}, |
| 2307 | {NULL, NULL} | 2308 | {NULL, NULL} |
| 2308 | }; | 2309 | }; |
diff --git a/src/lanes.lua b/src/lanes.lua index 5aeeaf4..d1082af 100644 --- a/src/lanes.lua +++ b/src/lanes.lua | |||
| @@ -564,6 +564,7 @@ end | |||
| 564 | lanes.gen = gen | 564 | lanes.gen = gen |
| 565 | lanes.linda = mm.linda | 565 | lanes.linda = mm.linda |
| 566 | lanes.cancel_error = mm.cancel_error | 566 | lanes.cancel_error = mm.cancel_error |
| 567 | lanes.nameof = mm.nameof | ||
| 567 | lanes.timer = timer | 568 | lanes.timer = timer |
| 568 | lanes.genlock = genlock | 569 | lanes.genlock = genlock |
| 569 | lanes.genatomic = genatomic | 570 | lanes.genatomic = genatomic |
diff --git a/src/tools.c b/src/tools.c index ee2a1ca..65c70bf 100644 --- a/src/tools.c +++ b/src/tools.c | |||
| @@ -1200,38 +1200,149 @@ static void push_cached_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, ui | |||
| 1200 | } | 1200 | } |
| 1201 | 1201 | ||
| 1202 | /* | 1202 | /* |
| 1203 | * Return some name helping to identify an object | ||
| 1204 | */ | ||
| 1205 | int discover_object_name_recur( lua_State* L, int _shortest, int _length) | ||
| 1206 | { | ||
| 1207 | int const what = 1; // o "r" {c} {fqn} ... {?} | ||
| 1208 | int const result = 2; | ||
| 1209 | int const cache = 3; | ||
| 1210 | int const fqn = 4; | ||
| 1211 | // no need to scan this table if the name we will discover is longer than one we already know | ||
| 1212 | if( _shortest <= _length + 1) | ||
| 1213 | { | ||
| 1214 | return _shortest; | ||
| 1215 | } | ||
| 1216 | STACK_GROW( L, 3); | ||
| 1217 | STACK_CHECK(L) | ||
| 1218 | // stack top contains the table to search in | ||
| 1219 | lua_pushvalue( L, -1); // o "r" {c} {fqn} ... {?} {?} | ||
| 1220 | lua_rawget( L, cache); // o "r" {c} {fqn} ... {?} nil/1 | ||
| 1221 | // if table is already visited, we are done | ||
| 1222 | if( !lua_isnil( L, -1)) | ||
| 1223 | { | ||
| 1224 | lua_pop( L, 1); // o "r" {c} {fqn} ... {?} | ||
| 1225 | return _shortest; | ||
| 1226 | } | ||
| 1227 | // examined table is not in the cache, add it now | ||
| 1228 | lua_pop( L, 1); // o "r" {c} {fqn} ... {?} | ||
| 1229 | lua_pushvalue( L, -1); // o "r" {c} {fqn} ... {?} {?} | ||
| 1230 | lua_pushinteger( L, 1); // o "r" {c} {fqn} ... {?} {?} 1 | ||
| 1231 | lua_rawset( L, cache); // o "r" {c} {fqn} ... {?} | ||
| 1232 | // scan table contents | ||
| 1233 | lua_pushnil( L); // o "r" {c} {fqn} ... {?} nil | ||
| 1234 | while( lua_next( L, -2)) // o "r" {c} {fqn} ... {?} k v | ||
| 1235 | { | ||
| 1236 | //char const *const key = lua_tostring( L, -2); // only for debugging (BEWARE, IT MAY CHANGE THE VALUE IF IT IS CONVERTIBLE, AND WRECK THE LOOP ITERATION PROCESS!) | ||
| 1237 | // append key name to fqn stack | ||
| 1238 | ++ _length; | ||
| 1239 | lua_pushvalue( L, -2); // o "r" {c} {fqn} ... {?} k v k | ||
| 1240 | lua_rawseti( L, fqn, _length); // o "r" {c} {fqn} ... {?} k v | ||
| 1241 | if( lua_rawequal( L, -1, what)) // is it what we are looking for? | ||
| 1242 | { | ||
| 1243 | // update shortest name | ||
| 1244 | if( _length < _shortest) | ||
| 1245 | { | ||
| 1246 | _shortest = _length; | ||
| 1247 | luaG_pushFQN( L, fqn, _length); // o "r" {c} {fqn} ... {?} k v "fqn" | ||
| 1248 | lua_replace( L, result); // o "r" {c} {fqn} ... {?} k v | ||
| 1249 | } | ||
| 1250 | // no need to search further at this level | ||
| 1251 | lua_pop( L, 2); // o "r" {c} {fqn} ... {?} | ||
| 1252 | break; | ||
| 1253 | } | ||
| 1254 | else if( lua_istable( L, -1)) | ||
| 1255 | { | ||
| 1256 | _shortest = discover_object_name_recur( L, _shortest, _length); | ||
| 1257 | } | ||
| 1258 | // make ready for next iteration | ||
| 1259 | lua_pop( L, 1); // o "r" {c} {fqn} ... {?} k | ||
| 1260 | // remove name from fqn stack | ||
| 1261 | lua_pushnil( L); // o "r" {c} {fqn} ... {?} k nil | ||
| 1262 | lua_rawseti( L, fqn, _length); // o "r" {c} {fqn} ... {?} k | ||
| 1263 | -- _length; | ||
| 1264 | } // o "r" {c} {fqn} ... {?} | ||
| 1265 | // remove the visited table from the cache, in case a shorter path to the searched object exists | ||
| 1266 | lua_pushvalue( L, -1); // o "r" {c} {fqn} ... {?} {?} | ||
| 1267 | lua_pushnil( L); // o "r" {c} {fqn} ... {?} {?} nil | ||
| 1268 | lua_rawset( L, cache); // o "r" {c} {fqn} ... {?} | ||
| 1269 | STACK_END( L, 0) | ||
| 1270 | return _shortest; | ||
| 1271 | } | ||
| 1272 | |||
| 1273 | /* | ||
| 1274 | * "type", "name" = lanes.nameof( o) | ||
| 1275 | */ | ||
| 1276 | int luaG_nameof( lua_State* L) | ||
| 1277 | { | ||
| 1278 | int what = lua_gettop( L); | ||
| 1279 | if( what > 1) | ||
| 1280 | { | ||
| 1281 | luaL_argerror( L, what, "too many arguments."); | ||
| 1282 | } | ||
| 1283 | |||
| 1284 | // numbers, strings, booleans and nil can't be identified | ||
| 1285 | if( lua_type( L, 1) < LUA_TTABLE) | ||
| 1286 | { | ||
| 1287 | lua_pushstring( L, luaL_typename( L, 1)); // o "type" | ||
| 1288 | lua_insert( L, -2); // "type" o | ||
| 1289 | return 2; | ||
| 1290 | } | ||
| 1291 | STACK_GROW( L, 4); | ||
| 1292 | // this slot will contain the shortest name we found when we are done | ||
| 1293 | lua_pushnil( L); // o nil | ||
| 1294 | // push a cache that will contain all already visited tables | ||
| 1295 | lua_newtable( L); // o nil {c} | ||
| 1296 | // push a table whose contents are strings that, when concatenated, produce unique name | ||
| 1297 | lua_newtable( L); // o nil {c} {fqn} | ||
| 1298 | // this is where we start the search | ||
| 1299 | lua_pushvalue( L, LUA_GLOBALSINDEX); // o nil {c} {fqn} _G | ||
| 1300 | (void) discover_object_name_recur( L, 6666, 0); | ||
| 1301 | lua_pop( L, 3); // o "result" | ||
| 1302 | lua_pushstring( L, luaL_typename( L, 1)); // o "result" "type" | ||
| 1303 | lua_replace( L, -3); // "type" "result" | ||
| 1304 | return 2; | ||
| 1305 | } | ||
| 1306 | |||
| 1307 | /* | ||
| 1203 | * Push a looked-up native/LuaJIT function. | 1308 | * Push a looked-up native/LuaJIT function. |
| 1204 | */ | 1309 | */ |
| 1205 | static void lookup_native_func( lua_State *L2, lua_State *L, uint_t i) | 1310 | static void lookup_native_func( lua_State *L2, lua_State *L, uint_t i) |
| 1206 | { | 1311 | { |
| 1207 | char const *fqn; | 1312 | char const *fqn; // L // L2 |
| 1208 | size_t len; | 1313 | size_t len; |
| 1209 | _ASSERT_L( L, lua_isfunction( L, i)); | 1314 | _ASSERT_L( L, lua_isfunction( L, i)); // ... f ... |
| 1210 | STACK_CHECK( L) | 1315 | STACK_CHECK( L) |
| 1211 | STACK_CHECK( L2) | ||
| 1212 | // fetch the name from the source state's lookup table | 1316 | // fetch the name from the source state's lookup table |
| 1213 | lua_getfield( L, LUA_REGISTRYINDEX, LOOKUP_KEY); // {} | 1317 | lua_getfield( L, LUA_REGISTRYINDEX, LOOKUP_KEY); // ... f ... {} |
| 1214 | _ASSERT_L( L, lua_istable( L, -1)); | 1318 | _ASSERT_L( L, lua_istable( L, -1)); |
| 1215 | lua_pushvalue( L, i); // {} f | 1319 | lua_pushvalue( L, i); // ... f ... {} f |
| 1216 | lua_rawget( L, -2); // {} "f.q.n" | 1320 | lua_rawget( L, -2); // ... f ... {} "f.q.n" |
| 1217 | fqn = lua_tolstring( L, -1, &len); | 1321 | fqn = lua_tolstring( L, -1, &len); |
| 1322 | // popping doesn't invalidate the pointer since this is an interned string gotten from the lookup database | ||
| 1323 | lua_pop( L, 2); // ... f ... | ||
| 1218 | if( !fqn) | 1324 | if( !fqn) |
| 1219 | { | 1325 | { |
| 1220 | luaL_error( L, "function not found in origin transfer database."); | 1326 | lua_pushvalue( L, i); // ... f ... f |
| 1327 | // try to discover the name of the function we want to send | ||
| 1328 | luaG_nameof( L); // ... f ... "type" "name" | ||
| 1329 | (void) luaL_error( L, "%s %s not found in origin transfer database.", lua_tostring( L, -2), lua_tostring( L, -1)); | ||
| 1330 | return; | ||
| 1221 | } | 1331 | } |
| 1332 | STACK_END( L, 0) | ||
| 1222 | // push the equivalent function in the destination's stack, retrieved from the lookup table | 1333 | // push the equivalent function in the destination's stack, retrieved from the lookup table |
| 1223 | lua_getfield( L2, LUA_REGISTRYINDEX, LOOKUP_KEY); // {} | 1334 | STACK_CHECK( L2) |
| 1335 | lua_getfield( L2, LUA_REGISTRYINDEX, LOOKUP_KEY); // {} | ||
| 1224 | _ASSERT_L( L2, lua_istable( L2, -1)); | 1336 | _ASSERT_L( L2, lua_istable( L2, -1)); |
| 1225 | lua_pushlstring( L2, fqn, len); // {} "f.q.n" | 1337 | lua_pushlstring( L2, fqn, len); // {} "f.q.n" |
| 1226 | lua_pop( L, 2); // | 1338 | lua_rawget( L2, -2); // {} f |
| 1227 | lua_rawget( L2, -2); // {} f | ||
| 1228 | if( !lua_isfunction( L2, -1)) | 1339 | if( !lua_isfunction( L2, -1)) |
| 1229 | { | 1340 | { |
| 1230 | luaL_error( L, "function %s not found in destination transfer database.", fqn); | 1341 | (void) luaL_error( L, "function %s not found in destination transfer database.", fqn); |
| 1342 | return; | ||
| 1231 | } | 1343 | } |
| 1232 | lua_remove( L2, -2); // f | 1344 | lua_remove( L2, -2); // f |
| 1233 | STACK_END( L2, 1) | 1345 | STACK_END( L2, 1) |
| 1234 | STACK_END( L, 0) | ||
| 1235 | } | 1346 | } |
| 1236 | 1347 | ||
| 1237 | #define LOG_FUNC_INFO 0 | 1348 | #define LOG_FUNC_INFO 0 |
diff --git a/src/tools.h b/src/tools.h index 67f9874..b8dc362 100644 --- a/src/tools.h +++ b/src/tools.h | |||
| @@ -65,6 +65,8 @@ void luaG_push_proxy( lua_State *L, luaG_IdFunction idfunc, DEEP_PRELUDE *deep_u | |||
| 65 | int luaG_inter_copy( lua_State *L, lua_State *L2, uint_t n); | 65 | int luaG_inter_copy( lua_State *L, lua_State *L2, uint_t n); |
| 66 | int luaG_inter_move( lua_State *L, lua_State *L2, uint_t n); | 66 | int luaG_inter_move( lua_State *L, lua_State *L2, uint_t n); |
| 67 | 67 | ||
| 68 | int luaG_nameof( lua_State* L); | ||
| 69 | |||
| 68 | // Lock for reference counter inc/dec locks (to be initialized by outside code) | 70 | // Lock for reference counter inc/dec locks (to be initialized by outside code) |
| 69 | // | 71 | // |
| 70 | extern MUTEX_T deep_lock; | 72 | extern MUTEX_T deep_lock; |
