Skip to content

Commit 792f5a7

Browse files
committed
mtstates.state(): optionally create named state if it does not exist
1 parent 9686147 commit 792f5a7

File tree

4 files changed

+135
-79
lines changed

4 files changed

+135
-79
lines changed

README.md

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ assert(thread:join())
163163

164164
### Module Functions
165165

166-
* **`mtstates.newstate([name,][libs,]setup[,...)`**
166+
* <a id="newstate">**`mtstates.newstate([name,][libs,]setup[,...)`**</a>
167167

168168
Creates a new state. The given setup function is executed in the new state. The
169169
setup function must return a state callback function which can be called
@@ -188,24 +188,45 @@ assert(thread:join())
188188
*mtstates.error.state_result*
189189

190190
* **`mtstates.state(id|name)`**
191+
* **`mtstates.state(name,[libs,]setup[,...)`**
191192

192-
Creates a lua object for referencing an existing state. The state must
193-
be referenced by its *id* or *name*. Referencing the state by *id* is
194-
much faster than referencing by *name* if the number of states
195-
increases.
196-
197-
* *id* - integer, the unique state id that can be obtained by
198-
*state:id()*.
199-
200-
* *name* - string, the optional name that was given when the
201-
state was created with *mtstates.newstate()*. To
202-
find a state by name the name must be unique for
203-
the whole process.
204-
205-
Possible errors: *mtstates.error.ambiguous_name*,
206-
*mtstates.error.unknown_object*
207-
193+
* **First form:**
194+
195+
Creates a lua object for referencing an existing state. The state must
196+
be referenced by its *id* or *name*. Referencing the state by *id* is
197+
much faster than referencing by *name* if the number of states
198+
increases.
199+
200+
* *id* - integer, the unique state id that can be obtained by
201+
*state:id()*.
202+
203+
* *name* - string, the optional name that was given when the
204+
state was created with *mtstates.newstate()*. To
205+
find a state by name the name must be unique for
206+
the whole process.
207+
208+
Possible errors: *mtstates.error.ambiguous_name*,
209+
*mtstates.error.unknown_object*
210+
208211

212+
* **Second form:**
213+
214+
Creates a lua object for referencing an existing state by name. If the
215+
state referenced by the name does not exist, a new state is created using
216+
the supplied parameters. This invocation can be used to atomically construct a
217+
globally named state securely from different threads.
218+
219+
* *name* - mandatory string, name for finding an existing state. If the state is
220+
not found the following parameters are used to create a new state
221+
with the given name.
222+
223+
* *libs*, *setup*, *...* - same parameters as in [*mtstates.newstate()*](#newstate).
224+
225+
Possible errors: *mtstates.error.ambiguous_name*,
226+
*mtstates.error.invoking_state*,
227+
*mtstates.error.state_result*
228+
229+
209230
* **`mtstates.type(arg)`**
210231

211232
Returns the type of *arg* as string. Same as *type(arg)* for builtin types.

src/state.c

Lines changed: 77 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -254,12 +254,19 @@ static const luaL_Reg mtstates_stdlibs[] =
254254
{ NULL, NULL}
255255
};
256256

257-
static int Mtstates_newState2(lua_State* L);
258257

258+
static int Mtstates_newState1(lua_State* L, bool isNewState);
259259
static int Mtstates_newState(lua_State* L)
260+
{
261+
return Mtstates_newState1(L, true);
262+
}
263+
264+
static int Mtstates_newState2(lua_State* L);
265+
static int Mtstates_newState1(lua_State* L, bool isNewState)
260266
{
261267
NewStateVars vars; memset(&vars, 0, sizeof(NewStateVars));
262268
NewStateVars* this = &vars;
269+
this->isNewState = isNewState;
263270

264271
int nargs = lua_gettop(L);
265272

@@ -301,14 +308,71 @@ static int Mtstates_newState3(lua_State* L);
301308

302309
static int Mtstates_newState2(lua_State* L)
303310
{
304-
int arg = 1;
311+
NewStateVars* this = (NewStateVars*)lua_touserdata(L, 1);
305312

306-
NewStateVars* this = (NewStateVars*)lua_touserdata(L, arg++);
307-
308-
int firstArg = arg;
309-
int lastArg = lua_gettop(L);
310-
int nargs = lastArg - firstArg + 1;
313+
const int firstArg = 2;
314+
const int lastArg = lua_gettop(L);
315+
const int nargs = lastArg - firstArg + 1;
316+
317+
if (!this->isNewState)
318+
{
319+
int arg = firstArg;
320+
321+
bool hasArg = false;
322+
const char* stateName = NULL;
323+
size_t stateNameLength = 0;
324+
lua_Integer stateId = 0;
325+
326+
if (arg <= lastArg) {
327+
if (lua_type(L, arg) == LUA_TSTRING) {
328+
stateName = lua_tolstring(L, arg++, &stateNameLength);
329+
hasArg = true;
330+
}
331+
else if (lua_type(L, arg) == LUA_TNUMBER && lua_isinteger(L, arg)) {
332+
stateId = lua_tointeger(L, arg++);
333+
hasArg = true;
334+
}
335+
}
336+
if (!hasArg) {
337+
this->errorArg = arg;
338+
return luaL_error(L, "state name or id expected");
339+
}
340+
341+
/* Lock */
342+
343+
async_mutex_lock(mtstates_global_lock); this->globalLocked = true;
344+
345+
MtState* state = NULL;
346+
if (stateName != NULL) {
347+
bool unique;
348+
state = findStateWithName(stateName, stateNameLength, &unique);
349+
if (!state && arg > lastArg) {
350+
return mtstates_ERROR_UNKNOWN_OBJECT_state_name(L, stateName, stateNameLength);
351+
} else if (state && !unique) {
352+
return mtstates_ERROR_AMBIGUOUS_NAME_state_name(L, stateName, stateNameLength);
353+
}
354+
} else {
355+
state = findStateWithId(stateId);
356+
if (!state) {
357+
return mtstates_ERROR_UNKNOWN_OBJECT_state_id(L, stateId);
358+
}
359+
}
360+
if (state) {
361+
StateUserData* userData = lua_newuserdata(L, sizeof(StateUserData));
362+
memset(userData, 0, sizeof(StateUserData));
363+
pushStateMeta(L); /* -> udata, meta */
364+
lua_setmetatable(L, -2); /* -> udata */
365+
366+
userData->state = state;
367+
atomic_inc(&state->used);
311368

369+
this->nrslts = 1;
370+
return this->nrslts;
371+
}
372+
// else we have a stateName and more args -> create new state
373+
}
374+
375+
int arg = firstArg;
312376
const char* stateName = NULL;
313377
size_t stateNameLength = 0;
314378
if (arg <= lastArg && nargs >= 2) {
@@ -373,7 +437,10 @@ static int Mtstates_newState2(lua_State* L)
373437
pushStateMeta(L); /* -> udata, meta */
374438
lua_setmetatable(L, -2); /* -> udata */
375439

376-
async_mutex_lock(mtstates_global_lock); this->globalLocked = true;
440+
if (!this->globalLocked) {
441+
async_mutex_lock(mtstates_global_lock);
442+
this->globalLocked = true;
443+
}
377444

378445
/* ------------------------------------------------------------------------------------ */
379446

@@ -524,62 +591,10 @@ static int Mtstates_newState3(lua_State* L2)
524591
}
525592

526593

594+
static int Mtstates_newState1(lua_State* L, bool isNewState);
527595
static int Mtstates_state(lua_State* L)
528596
{
529-
int arg = 1;
530-
531-
bool hasArg = false;
532-
const char* stateName = NULL;
533-
size_t stateNameLength = 0;
534-
lua_Integer stateId = 0;
535-
536-
if (lua_gettop(L) >= arg) {
537-
if (lua_type(L, arg) == LUA_TSTRING) {
538-
stateName = lua_tolstring(L, arg++, &stateNameLength);
539-
hasArg = true;
540-
}
541-
else if (lua_type(L, arg) == LUA_TNUMBER && lua_isinteger(L, arg)) {
542-
stateId = lua_tointeger(L, arg++);
543-
hasArg = true;
544-
}
545-
}
546-
if (!hasArg) {
547-
return luaL_argerror(L, arg, "state name or id expected");
548-
}
549-
550-
StateUserData* userData = lua_newuserdata(L, sizeof(StateUserData));
551-
memset(userData, 0, sizeof(StateUserData));
552-
pushStateMeta(L); /* -> udata, meta */
553-
lua_setmetatable(L, -2); /* -> udata */
554-
555-
/* Lock */
556-
557-
async_mutex_lock(mtstates_global_lock);
558-
559-
MtState* state;
560-
if (stateName != NULL) {
561-
bool unique;
562-
state = findStateWithName(stateName, stateNameLength, &unique);
563-
if (!state) {
564-
async_mutex_unlock(mtstates_global_lock);
565-
return mtstates_ERROR_UNKNOWN_OBJECT_state_name(L, stateName, stateNameLength);
566-
} else if (!unique) {
567-
async_mutex_unlock(mtstates_global_lock);
568-
return mtstates_ERROR_AMBIGUOUS_NAME_state_name(L, stateName, stateNameLength);
569-
}
570-
} else {
571-
state = findStateWithId(stateId);
572-
if (!state) {
573-
async_mutex_unlock(mtstates_global_lock);
574-
return mtstates_ERROR_UNKNOWN_OBJECT_state_id(L, stateId);
575-
}
576-
}
577-
578-
userData->state = state;
579-
atomic_inc(&state->used);
580-
581-
async_mutex_unlock(mtstates_global_lock);
582-
return 1;
597+
return Mtstates_newState1(L, false);
583598
}
584599

585600

src/state_intern.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ typedef struct {
2222

2323
typedef struct
2424
{
25+
bool isNewState;
26+
2527
bool globalLocked;
2628
bool stateLocked;
2729

tests/test01.lua

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,4 +500,22 @@ do
500500
assert(ok and rslt1 == 103 and rslt2 == 100)
501501
end
502502
PRINT("==================================================================================")
503+
do
504+
local stateSetup = function()
505+
return function(x)
506+
return 100 + x
507+
end
508+
end
509+
local _, err = pcall(function()
510+
mtstates.state("foo")
511+
end)
512+
assert(err:match(mtstates.error.unknown_object))
513+
local s1 = mtstates.state("foo", stateSetup)
514+
local s2 = mtstates.state("foo", stateSetup)
515+
assert(s1:id() == s2:id())
516+
assert(s2:call(3) == 103)
517+
local s3 = mtstates.state("foo")
518+
assert(s1:id() == s3:id())
519+
end
520+
PRINT("==================================================================================")
503521
print("Done.")

0 commit comments

Comments
 (0)