@@ -141,6 +141,7 @@ assert(thread:join())
141141 * [ Module Functions] ( #module-functions )
142142 * mtstates.newstate()
143143 * mtstates.state()
144+ * mtstates.singleton()
144145 * mtstates.id()
145146 * mtstates.type()
146147 * [ State Methods] ( #state-methods )
@@ -153,7 +154,6 @@ assert(thread:join())
153154 * [ Errors] ( #errors )
154155 * mtstates.error.ambiguous_name
155156 * mtstates.error.concurrent_access
156- * mtstates.error.cycle_detected
157157 * mtstates.error.interrupted
158158 * mtstates.error.invoking_state
159159 * mtstates.error.object_closed
@@ -186,53 +186,66 @@ assert(thread:join())
186186 are given as arguments to the setup function. Arguments can be
187187 simple data types (string, number, boolean, nil, light user data).
188188
189+ This function returns a state referencing lua object with * state: isowner () == true* .
190+
189191 Possible errors: * mtstates.error.invoking_state* ,
190192 * mtstates.error.state_result*
191193
192194* ** ` mtstates.state(id|name) ` **
193- * ** ` mtstates.state(name,[libs,]setup[,...) ` **
194195
195- * ** First form:**
196-
197- Creates a lua object for referencing an existing state. The state must
198- be referenced by its * id* or * name* . Referencing the state by * id* is
199- much faster than referencing by * name* if the number of states
200- increases.
196+ Creates a lua object for referencing an existing state. The state must
197+ be referenced by its * id* or * name* . Referencing the state by * id* is
198+ much faster than referencing by * name* if the number of states
199+ increases.
200+
201+ * * id* - integer, the unique state id that can be obtained by
202+ * state: id ()* .
203+
204+ * * name* - string, the optional name that was given when the
205+ state was created with * mtstates.newstate()* . To
206+ find a state by name the name must be unique for
207+ the whole process.
208+
209+ This function returns a state referencing lua object with * state: isowner () == false* .
210+
211+ Possible errors: * mtstates.error.ambiguous_name* ,
212+ * mtstates.error.unknown_object*
213+
214+
215+ * ** ` mtstates.singleton(name,[libs,]setup[,...) ` **
216+
217+ Creates a lua object for referencing an existing state by name. If the
218+ state referenced by the name does not exist, a new state is created using
219+ the supplied parameters. This invocation can be used to atomically construct
220+ a globally named state securely from different threads.
221+
222+ If * mtstates.singleton()* is called with the same * name* parameter from
223+ concurrently running threads the second invocation waits until the first
224+ invocation is finished or fails.
201225
202- * * id* - integer, the unique state id that can be obtained by
203- * state: id ()* .
204226
205- * * name* - string, the optional name that was given when the
206- state was created with * mtstates.newstate()* . To
207- find a state by name the name must be unique for
208- the whole process.
227+ * * name* - mandatory string, name for finding an existing state. If the state is
228+ not found the following parameters are used to create a new state
229+ with the given name.
209230
210- Possible errors: * mtstates.error.ambiguous_name* ,
211- * mtstates.error.unknown_object*
231+ * * libs* , * setup* , * ...* - same parameters as in [ * mtstates.newstate()* ] ( #newstate ) .
212232
233+ This function returns a state referencing lua object with * state: isowner () == true* .
213234
214- * ** Second form:**
215-
216- Creates a lua object for referencing an existing state by name. If the
217- state referenced by the name does not exist, a new state is created using
218- the supplied parameters. This invocation can be used to atomically construct a
219- globally named state securely from different threads.
220-
221- * * name* - mandatory string, name for finding an existing state. If the state is
222- not found the following parameters are used to create a new state
223- with the given name.
224-
225- * * libs* , * setup* , * ...* - same parameters as in [ * mtstates.newstate()* ] ( #newstate ) .
235+ This function should only by used for very special use cases: Because references
236+ to the same * mtstates* 's state from different lua states are reference counted
237+ special care has to be taken that no reference cycle is constructed using
238+ * mtstates.singleton()* .
226239
227- Possible errors: * mtstates.error.ambiguous_name* ,
228- * mtstates.error.invoking_state* ,
229- * mtstates.error.state_result*
240+ Possible errors: * mtstates.error.ambiguous_name* ,
241+ * mtstates.error.invoking_state* ,
242+ * mtstates.error.state_result*
230243
231244* ** ` mtstates.id() ` **
232245
233246 Gives the state id of the currently running state invoking this function.
234247 Returns ` nil ` if the current state was not constructed via
235- * mtstates.newstate()* or the second form of * mtstates.state ()* .
248+ * mtstates.newstate()* or * mtstates.singleton ()* .
236249
237250
238251* ** ` mtstates.type(arg) ` **
@@ -279,8 +292,7 @@ assert(thread:join())
279292 Returns the results of the state callback function. Results can be simple data types
280293 (string, number, boolean, nil, light user data).
281294
282- Possible errors: * mtstates.error.cycle_detected*
283- * mtstates.error.interrupted* ,
295+ Possible errors: * mtstates.error.interrupted* ,
284296 * mtstates.error.invoking_state* ,
285297 * mtstates.error.object_closed* ,
286298 * mtstates.error.state_result*
@@ -322,11 +334,29 @@ assert(thread:join())
322334 at every operation again, if * false* the state is no
323335 longer interrupted.
324336
337+
338+ * ** ` state:isowner() ` **
339+
340+ Returns ` true ` if the state referencing lua object owns the referenced
341+ state. If the last owning lua object is garbage collected the underlying
342+ state is closed.
343+
344+ State referencing objects constructed via * mtstates.newstate()* or
345+ * mtstates.singleton()* are owning the state.
346+
347+ State referencing objects constructed via * mtstates.state()* are
348+ not owning the state.
349+
350+
325351* ** ` state:close() ` **
326352
327353 Closes the underlying state and frees the memory. Every operation from any
328- referencing object raises a * mtstates.error.object_closed* . A closed state
329- cannot be reactivated.
354+ referencing object raises a * mtstates.error.object_closed* .
355+
356+ A closed state cannot be found via its name or id using the function
357+ * mtstates.state()* .
358+
359+ A closed state cannot be reactivated.
330360
331361 Possible errors: * mtstates.error.concurrent_access*
332362
@@ -363,13 +393,6 @@ assert(thread:join())
363393 Raised if * state: close ()* is called while the state is processing a call
364394 on a parallel running thread.
365395
366- * ** ` mtstates.error.cycle_detected ` **
367-
368- Raised if * state: call ()* of a state is called while it is called by the same
369- thread. This can only occur if one state gets somehow a reference to itself.
370- It is strictly advised not to build cycle of states because this can lead
371- to memory leaks.
372-
373396* ** ` mtstates.error.interrupted ` **
374397
375398 The state was interrupted by invoking the method * state: interrupt ()* .
@@ -407,45 +430,31 @@ assert(thread:join())
407430 cannot be created because the object cannot be found by
408431 the given id or name.
409432
410- All mtstates objects are subject to garbage collection and therefore a reference to a
411- created object is needed to keep it alive, i.e. if you want to pass an object
412- to another thread via name or id, a reference to this object should be kept in the
413- thread that created the object, until the receiving thread signaled that a reference
414- to the object has been constructed in the receiving thread, example:
433+ All mtstates objects are subject to garbage collection and therefore a owning
434+ reference to a created object is needed to keep it alive, example:
415435
416436 ``` lua
417- local llthreads = require (" llthreads2.ex" )
418- local mtmsg = require (" mtmsg" )
419437 local mtstates = require (" mtstates" )
420- local threadIn = mtmsg .newbuffer ()
421- local threadOut = mtmsg .newbuffer ()
422- local state = mtstates .newstate (" return function() end" )
423- local stateId = state :id ()
424- local thread = llthreads .new (function (inId , outId , stateId )
425- local mtmsg = require (" mtmsg" )
426- local mtstates = require (" mtstates" )
427- local threadIn = mtmsg .buffer (inId )
428- local threadOut = mtmsg .buffer (outId )
429- local state = mtstates .state (stateId )
430- assert (state :id () == stateId )
431- threadOut :addmsg (" started" )
432- assert (threadIn :nextmsg () == " exit" )
433- threadOut :addmsg (" finished" )
434- end ,
435- threadIn :id (),
436- threadOut :id (),
437- stateId )
438- -- state = nil -- not now!
439- -- collectgarbage()
440- thread :start ()
441- assert (threadOut :nextmsg () == " started" )
442- state = nil -- now it's safe
438+ local s1 = mtstates .newstate (" return function() return 3 end" )
439+ local id = s1 :id ()
440+ local s2 = mtstates .state (id )
441+ local s3 = mtstates .state (id )
442+ assert (s1 :isowner () == true )
443+ assert (s2 :isowner () == false )
444+ assert (s3 :isowner () == false )
445+ s2 = nil
443446 collectgarbage ()
444- threadIn : addmsg ( " exit " )
445- assert (threadOut : nextmsg () == " finished " )
446- assert ( thread : join ())
447+ assert ( s3 : call () == 3 )
448+ assert (mtstates . state ( id ): id () == id )
449+ s1 = nil
447450 collectgarbage ()
448- local _ , err = pcall (function () mtstates .state (stateId ) end )
451+ local _ , err = pcall (function ()
452+ s3 :call ()
453+ end )
454+ assert (err :match (mtstates .error .object_closed ))
455+ local _ , err = pcall (function ()
456+ mtstates .state (id )
457+ end )
449458 assert (err :match (mtstates .error .unknown_object ))
450459 ```
451460
0 commit comments