Is it possible to run Lua threads in OS threads safely?

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
30 messages Options
12
Reply | Threaded
Open this post in threaded view
|

Is it possible to run Lua threads in OS threads safely?

James Pascoe
Hi Everybody,

My name is James (Jim) Pascoe and I have been using Lua for around 18 months now. I absolutely love it - Lua's embeddability, speed and instant appeal to users has meant that Lua is becoming very popular in my company (Blu Wireless). Thank you for all your continued hard work.

Apologies if this has been answered before, but I would really appreciate some clarification on lua_newthread(). My understanding is that lua_newthread() provides a distinct execution stack, so you can have independent pieces of Lua code loaded and run by different Lua threads. Also, the reference manual states that Lua threads created by lua_newthread() share the same global environment, which (as I understand it) means that they point to the same global environment in the parent lua_State. However, what I am trying to understand is whether it is possible to run individual Lua threads in separate OS threads safely? And also, is it possible for Lua threads running in separate OS threads to communicate?

As I say, I would really appreciate some clarification on this from the community.                                                                                                                        

Thank you very much and have a good weekend,

Jim
Reply | Threaded
Open this post in threaded view
|

Re: Is it possible to run Lua threads in OS threads safely?

Joseph C. Sible
On Sat, Jun 13, 2020 at 2:05 PM James Pascoe <[hidden email]> wrote:
>
> Also, the reference manual states that Lua threads created by lua_newthread() share the same global environment, which (as I understand it) means that they point to the same global environment in the parent lua_State. However, what I am trying to understand is whether it is possible to run individual Lua threads in separate OS threads safely?

No, that's not possible to safely do with stock Lua. Different OS
threads running Lua at the same time can only safely use completely
separate states, not threads that share a global state.

> And also, is it possible for Lua threads running in separate OS threads to communicate?

On their own, separate Lua states can't communicate at all. You'd have
to use some kind of helper to communicate between them, like Lua Lanes
[1] or luaproc [2].

[1]: https://lualanes.github.io/lanes/
[2]: https://github.com/askyrme/luaproc/

Joseph C. Sible
Reply | Threaded
Open this post in threaded view
|

Re: Is it possible to run Lua threads in OS threads safely?

Oliver Schmidt
On 13.06.20 20:16, Joseph C. Sible wrote:
> On Sat, Jun 13, 2020 at 2:05 PM James Pascoe <[hidden email]> wrote:
>> what I am trying to understand is whether it is possible to run individual Lua threads in separate OS threads safely?
>> No, that's not possible to safely do with stock Lua. Different OS
>> And also, is it possible for Lua threads running in separate OS threads to communicate?
> On their own, separate Lua states can't communicate at all. You'd have
> to use some kind of helper to communicate between them, like Lua Lanes
> [1] or luaproc [2].

you also might be interested to have a look at

- lua-llthreads2 [1] - Low-Level threads for Lua
- mtmsg [2] - Low-level message buffers for inter-thread communication
- mtstates [3] - Pass Lua states to other threads
- mtint [4] - Make threads and coroutines interruptible

These are orthogonal and low level cross platform packages that can be combined
and play well together. IMHO they build a simple and minimal base for building
higher level multi threading constructs in pure Lua.

Best regards,
Oliver

[1]: https://luarocks.org/modules/moteus/lua-llthreads2
[2]: https://luarocks.org/modules/osch/mtmsg
[3]: https://luarocks.org/modules/osch/mtstates
[4]: https://luarocks.org/modules/osch/mtint
Reply | Threaded
Open this post in threaded view
|

Re: Is it possible to run Lua threads in OS threads safely?

Marc Balmer
I am adding https://github.com/arcapos/mqlua/ to the list of possibilities.

> Am 13.06.2020 um 23:24 schrieb Oliver <[hidden email]>:
>
> On 13.06.20 20:16, Joseph C. Sible wrote:
>>> On Sat, Jun 13, 2020 at 2:05 PM James Pascoe <[hidden email]> wrote:
>>> what I am trying to understand is whether it is possible to run individual Lua threads in separate OS threads safely?
>>> No, that's not possible to safely do with stock Lua. Different OS
>>> And also, is it possible for Lua threads running in separate OS threads to communicate?
>> On their own, separate Lua states can't communicate at all. You'd have
>> to use some kind of helper to communicate between them, like Lua Lanes
>> [1] or luaproc [2].
>
> you also might be interested to have a look at
>
> - lua-llthreads2 [1] - Low-Level threads for Lua
> - mtmsg [2] - Low-level message buffers for inter-thread communication
> - mtstates [3] - Pass Lua states to other threads
> - mtint [4] - Make threads and coroutines interruptible
>
> These are orthogonal and low level cross platform packages that can be combined
> and play well together. IMHO they build a simple and minimal base for building
> higher level multi threading constructs in pure Lua.
>
> Best regards,
> Oliver
>
> [1]: https://luarocks.org/modules/moteus/lua-llthreads2
> [2]: https://luarocks.org/modules/osch/mtmsg
> [3]: https://luarocks.org/modules/osch/mtstates
> [4]: https://luarocks.org/modules/osch/mtint
Reply | Threaded
Open this post in threaded view
|

Re: Is it possible to run Lua threads in OS threads safely?

Robert Virding-2
In reply to this post by James Pascoe
A slightly different solution if you want multi-threading would be to use Luerl which runs on top of Erlang/Elixir. It allows you to run multiple Luas each in its own Erlang process, however they cannot share data as the Erlang processes by design don't share. Erlang is of course a widely used system, you are most likely using systems which contain Erlang, and Luerl is also being commercially used.

Whether this is helpful depends, of course, on what your system does.

Robert


On Sat, 13 Jun 2020 at 20:05, James Pascoe <[hidden email]> wrote:
Hi Everybody,

My name is James (Jim) Pascoe and I have been using Lua for around 18 months now. I absolutely love it - Lua's embeddability, speed and instant appeal to users has meant that Lua is becoming very popular in my company (Blu Wireless). Thank you for all your continued hard work.

Apologies if this has been answered before, but I would really appreciate some clarification on lua_newthread(). My understanding is that lua_newthread() provides a distinct execution stack, so you can have independent pieces of Lua code loaded and run by different Lua threads. Also, the reference manual states that Lua threads created by lua_newthread() share the same global environment, which (as I understand it) means that they point to the same global environment in the parent lua_State. However, what I am trying to understand is whether it is possible to run individual Lua threads in separate OS threads safely? And also, is it possible for Lua threads running in separate OS threads to communicate?

As I say, I would really appreciate some clarification on this from the community.                                                                                                                         

Thank you very much and have a good weekend,

Jim
Reply | Threaded
Open this post in threaded view
|

Re: Is it possible to run Lua threads in OS threads safely?

Russell Haley
In reply to this post by Joseph C. Sible


On Sat, Jun 13, 2020 at 11:17 AM Joseph C. Sible <[hidden email]> wrote:
On Sat, Jun 13, 2020 at 2:05 PM James Pascoe <[hidden email]> wrote:
>
> Also, the reference manual states that Lua threads created by lua_newthread() share the same global environment, which (as I understand it) means that they point to the same global environment in the parent lua_State. However, what I am trying to understand is whether it is possible to run individual Lua threads in separate OS threads safely?

No, that's not possible to safely do with stock Lua. Different OS
threads running Lua at the same time can only safely use completely
separate states, not threads that share a global state.

> And also, is it possible for Lua threads running in separate OS threads to communicate?

On their own, separate Lua states can't communicate at all. You'd have
to use some kind of helper to communicate between them, like Lua Lanes
[1] or luaproc [2].

[1]: https://lualanes.github.io/lanes/
[2]: https://github.com/askyrme/luaproc/

If you are working in a unix-like environment, you can also flip the threading model on it's head and use an event loop/engine like cqueues: https://github.com/wahern/cqueues. Cqueues are easy to use because they rely on co-routines and an OS event library (which is transparent to you). There is no callback model so processes are just continuous loops. The documentation describes how it works: http://25thandclement.com/~william/projects/cqueues.html

There is also a web library called lua-http built on cqueues for very high performance web functionality: https://github.com/daurnimator/lua-http

Regards,
Russell


Joseph C. Sible00
Reply | Threaded
Open this post in threaded view
|

Re: Is it possible to run Lua threads in OS threads safely?

Aaron B.
On Sun, 14 Jun 2020 21:28:36 -0700
Russell Haley <[hidden email]> wrote:

> If you are working in a unix-like environment, you can also flip the
> threading model on it's head and use an event loop/engine like cqueues:
> https://github.com/wahern/cqueues. Cqueues are easy to use because they
> rely on co-routines and an OS event library (which is transparent to you).
> There is no callback model so processes are just continuous loops.

cqueues is an excellent framework; however the program will be limited
to only a single CPU core.

...which may or may not matter, depending on the application. But
often when people start talking about "threads" they expect multiple
cores to be utilized.

--
Aaron B. <[hidden email]>
Reply | Threaded
Open this post in threaded view
|

Re: Is it possible to run Lua threads in OS threads safely?

Russell Haley


On Sun, Jun 14, 2020 at 9:47 PM Aaron B. <[hidden email]> wrote:
On Sun, 14 Jun 2020 21:28:36 -0700
Russell Haley <[hidden email]> wrote:

> If you are working in a unix-like environment, you can also flip the
> threading model on it's head and use an event loop/engine like cqueues:
> https://github.com/wahern/cqueues. Cqueues are easy to use because they
> rely on co-routines and an OS event library (which is transparent to you).
> There is no callback model so processes are just continuous loops.

cqueues is an excellent framework; however the program will be limited
to only a single CPU core.

I was unaware of that. Do you know why? (something to do with cpu affinity?)

...which may or may not matter, depending on the application. But
often when people start talking about "threads" they expect multiple
cores to be utilized.

--
Aaron B. <[hidden email]>
Reply | Threaded
Open this post in threaded view
|

Re: Is it possible to run Lua threads in OS threads safely?

Sean Conner
It was thus said that the Great Russell Haley once stated:
> On Sun, Jun 14, 2020 at 9:47 PM Aaron B. <[hidden email]> wrote:
>
> > cqueues is an excellent framework; however the program will be limited
> > to only a single CPU core.
>
> I was unaware of that. Do you know why? (something to do with cpu affinity?)

  I think he means the resulting program is single threaded, not that it's
being restricted to a single CPU.

  -spc
Reply | Threaded
Open this post in threaded view
|

Re: Is it possible to run Lua threads in OS threads safely?

Aaron B.
On Mon, 15 Jun 2020 01:32:22 -0400
Sean Conner <[hidden email]> wrote:

> It was thus said that the Great Russell Haley once stated:
> > On Sun, Jun 14, 2020 at 9:47 PM Aaron B. <[hidden email]> wrote:
> >
> > > cqueues is an excellent framework; however the program will be limited
> > > to only a single CPU core.
> >
> > I was unaware of that. Do you know why? (something to do with cpu affinity?)
>
>   I think he means the resulting program is single threaded, not that it's
> being restricted to a single CPU.
>

You are correct. That's a much better way of saying what I tried to say.

--
Aaron B. <[hidden email]>
Reply | Threaded
Open this post in threaded view
|

Re: Is it possible to run Lua threads in OS threads safely?

Russell Haley
In reply to this post by Sean Conner


On Sun, Jun 14, 2020 at 10:33 PM Sean Conner <[hidden email]> wrote:
It was thus said that the Great Russell Haley once stated:
> On Sun, Jun 14, 2020 at 9:47 PM Aaron B. <[hidden email]> wrote:
>
> > cqueues is an excellent framework; however the program will be limited
> > to only a single CPU core.
>
> I was unaware of that. Do you know why? (something to do with cpu affinity?)

  I think he means the resulting program is single threaded, not that it's
being restricted to a single CPU.

  -spc

Ah, thanks Sean.  That's what I was implying by "flip the threading model on it's head." Instead of having multiple threads each doing one job, you have one thread that can kick off a whole bunch of jobs.
Reply | Threaded
Open this post in threaded view
|

Re: Is it possible to run Lua threads in OS threads safely?

James Pascoe
In reply to this post by James Pascoe
Hi Everybody,

Thank you very much for the amazing responses - this was exactly what I was looking for! I will read the links you suggest in depth and let you know how it goes.

Thank you all again and take care,

Jim
Reply | Threaded
Open this post in threaded view
|

Re: Is it possible to run Lua threads in OS threads safely?

Andrea-2
In reply to this post by Russell Haley
There seem to be a dedicated page on lua-users wiki: http://lua-users.org/wiki/MultiTasking

I have some question highlight below
Does anyone has done some trial to assess pros/cons and performance?

    Andrea 

-- 
Andrea Vitali






Reply | Threaded
Open this post in threaded view
|

Re: Why one would need mtstates/mtmsg?

Oliver Schmidt
Hi,

On 16.06.20 00:23, Andrea wrote:
> I have some question highlight below
>       o *Why one would need mtstates/mtmsg found at  https://github.com/osch?*

The packages mtmsg/mtstates/mtint are only small building blocks to build up
higher level multi threading solutions in Lua scripting language on top of them.

All the other libraries you are mentioning are all-in-one solutions that are
trying to solve several tasks at once in one bigger library/framework.

I started my first multi threading experiments using LuaLanes. However I wanted
to have something more basic. Then I discovered llthreads2 [1] which does only
provide the bare minimum for starting threads, nothing else. Even no inter
thread communication. You could e.g. use ZeroMQ [2] or something else for inter
thread communication with llthreads.

mtmsg [3] provides simple inter thread communication that does not rely on any
thirdparty lib and can be used with arbitrary lua threading libraries. It's main
purpose is to be an as low level as possible companion for llthreads2. But you
can use it in arbitrary scenarions as well: e.g. if you have two bigger parts in
you application that are using different multi threading frameworks/libraries
you can still use mtmsg for inter thread communication between the parts.

mtstates [4] is something I haven't found in other libraries: it is similar to
Rings [5], but allows the created Lua states to be passed to other threads, i.e.
you can invoke a function in the state from different threads, but only from one
at a time. A mtstate object is therefore an object together with a mutex (if you
know Java: this is like having an object with synchronized methods). Again this
library is only an as low level as possible solution that can be used together
with other multi threading solutions to build up higher level constructs.

You could use mtstates to build some sort of simple interthread communication,
but this would be overkill. A typical use case for mtstates is to build up some
sort of framework that is distributing a lot of coroutines on different threads
(for the case that you have a lot of more coroutines than hardware threads). For
this you would put each coroutine into one mtstate object (mtstates object are
lightweight like Lua states). This means you could build something similiar like
cqueues [6] but using more than one system thread.

mtint [7] is also as low level as possible and provides a solution for
interrupting threads. Before writing this, I created a solution for this just
for llthread2 [8] but then I discovered that a simple library could be build
just to interrupt any Lua thread independently from the used multi threading
library.


[1]: https://luarocks.org/modules/moteus/lua-llthreads2
[2]: https://github.com/Neopallium/lua-zmq
[3]: https://luarocks.org/modules/osch/mtmsg
[4]: https://luarocks.org/modules/osch/mtstates
[5]: https://keplerproject.github.io/rings/
[6]: https://github.com/wahern/cqueues
[7]: https://luarocks.org/modules/osch/mtint
[8]: https://github.com/moteus/lua-llthreads2/pull/16
Reply | Threaded
Open this post in threaded view
|

Re: Why one would need mtstates/mtmsg?

Andrea-2

The packages mtmsg/mtstates/mtint are only small building blocks to build up

Thank you for your notes. I really like the concept of small orthogonal building blocks for obvious reasons.

One question: it seems llthreads2 does support Lua 5.3? and is it going to support Lua 5.4?
From the description on GitHub this is not clear. I can read that it does not support Lua 5.0, and then I can guess that it does support 5.1 and 5.2 from the description of some folder.
(the other all-in-one library, LuaLanes, seems to support all Lua versions)

I started my first multi threading experiments using LuaLanes. However I wanted
to have something more basic. Then I discovered llthreads2 [1] which does only

Can you elaborate more on why LuaLanes was not the right choice?
(complexity of APIs, functionality, performance, or other)

mtstates [4] is something I haven't found in other libraries: it is similar to
Rings [5], but allows the created Lua states to be passed to other threads, i.e.

Thank you very much. This is really helpful. Let me recap to see if I understand correctly:
- Rings allow slave states to be created from a master state, basically a way to implement remote procedure calls, and there is the possibility to exchange date between master and slaves. There seem to be no mechanism for thread safety, no mutex. So the slave state can only be used in one thread.
- Mstates allow states to be created, and thread safety is guaranteed by mutex, so they can really be passed to one or more other threads.

Thank you very much for your comments and notes. 

   Andrea

--
Andrea Vitali






Reply | Threaded
Open this post in threaded view
|

Re: Why one would need mtstates/mtmsg?

Oliver Schmidt
On 16.06.20 18:04, Andrea wrote:
> One question: it seems llthreads2 does support Lua 5.3? and is it going to
> support Lua 5.4?
> From the description on GitHub this is not clear. I can read that it does not
> support Lua 5.0, and then I can guess that it does support 5.1 and 5.2 from the
> description of some folder.

I have automated tests that are using llthreads2 without problems under lua 5.1,
5.2 and 5.3. The llthreads2 rockspec file says lua >= 5.1, < 5.4 but I'm
expecting no bigger problems in using it under 5.4. Otherwise I would prepare a
pull request or an issue at the llthreads2 github page. Also all my Lua modules
are still specified as < 5.4 but I'm planing to lift them up to 5.4 in the near
future.

> Can you elaborate more on why LuaLanes was not the right choice?
> (complexity of APIs, functionality, performance, or other)

As I wrote: for me it was not basic enough. The API seems more complex than
needed for my purposes. For example just at the beginning of the documentation
it says a lanes.configure() function has to be called once. This puzzles me,
because it means that you must have control of the application startup (or state
startup?) and cannot use this in e.g. binary plugins containing Lua interpreter
that are in an host application, or a "Lua plugin" inside a host written in Lua
 that did or did not start langes.configure() before. Then reading further down
in the documentation I'm reading about "generator functions. Behind these there
are somewhere the threads. So you have to create a generator function, invoke
this and then you have an object that gives you return values as array members.
A more basic and straight forward approach just would you give a function to
create a thread like in llthreads2 with a start and join function (which in
principle is not much different, the difference here is mainly the wording and
the details). Then I also don't like how automagic upvalues are transferred to
the created thread function. IMHO it would be easier to understand if these
values must be passed directly. Then about Lindas: this seems to me a rather
complex machanism for inter thread communication. As far as I understand, one
could build something similar using Lua scripting code on top of mtmsg & mtstates.

So to summarize up my experience: Lanes seems to be a technical stable and well
tested solution which has a complex API that gives a lot of features. One could
write a wrapper around this to have a simplified view that would make it as easy
as wanted to use sub functionality of Lanes. However, I decided for me it would
be more fun to build up something from basic building blocks and also an
interesting challenge to find out how these basic blocks could look like and I'm
interested and open for discussions if these are the "right" (i.e. as basic and
simple as possible) building blocks or if some functionality is missing or if
another approach could be more basic and simple and result in even smaller
building blocks.

> Thank you very much. This is really helpful. Let me recap to see if I understand
> correctly:
> - Rings allow slave states to be created from a master state, basically a way to
> implement remote procedure calls, and there is the possibility to exchange date

I wouldn't call it "remote" since all Lua states here are in the same process.

> between master and slaves. There seem to be no mechanism for thread safety, no
> mutex. So the slave state can only be used in one thread.

Yes, in Rings there is no mechanism for thread safety, and, also important,
there is no mechanism how to pass a Ring state into another thread.

> - Mstates allow states to be created, and thread safety is guaranteed by mutex,
> so they can really be passed to one or more other threads.

Yes, in mtstates there is a mutex mechanism for thread safety and there is also
a mechanism how to pass a mtstates state to other threads (basically by just
passing the state's integer id somehow to other threads).

Best regards,
Oliver
Reply | Threaded
Open this post in threaded view
|

Re: Is it possible to run Lua threads in OS threads safely?

Norman Ramsey
In reply to this post by Andrea-2
 > There seem to be a dedicated page on lua-users wiki:
 > http://lua-users.org/wiki/MultiTasking
 > ...
 >    - *LuaProc *seems to be the official tool from Lua development Team
 >    (RI), but seems not to be maintained any more
 >    https://github.com/askyrme/luaproc, the original doc is
 >    http://www.inf.puc-rio.br/~roberto/docs/ry08-05.pdf, a more recent doc
 >    https://www.maxwell.vrac.puc-rio.br/30267/30267.PDF (seems to be about a
 >    2018 modified version)
 >       - *Why in the user-lua wiki it is classified as "cooperative" and not
 >       "preemptive"?* (it enables but user-level and OS-level parallelism,
 >       so I guess it is cooperative at the user level and preemptive at the
 >       OS-level.... is this correct?)
 >
 > Does anyone has done some trial to assess pros/cons and performance?

I have been using luaproc for about three years.

Pros:

  - Relatively simple model
  - Excellent speedups (basically 4x speedup on 4-core machine)
  - Mostly reliable
  - Scalar data is relatively easy to pass between threads (as upvalues)

Cons:

  - The concurrency model is a bit limiting, and it took me a while to
    figure out how to use it effectively.  (Details forgotten, alas.)
  - Some code has to be written in strings, so you may not find syntax
    errors as soon or as easily as you might like.
  - To pass table values between threads, I wound up writing my own
    serializer/deserializer.
  - Occasionally a computation locks up on a mysterious way.  Could
    be luaproc or could be the C library we're using to interact with
    the Unix pty interface.

On the whole I'd recommend it.


Norman
Reply | Threaded
Open this post in threaded view
|

Re: Why one would need mtstates/mtmsg?

Andrea-2
In reply to this post by Oliver Schmidt

I have automated tests that are using llthreads2 without problems under lua 5.1,
5.2 and 5.3. The llthreads2 rockspec file says lua >= 5.1, < 5.4 but I'm
expecting no bigger problems in using it under 5.4. Otherwise I would prepare a

thank you very much for confirming this - it is reassuring that the packages are maintained
 
I had a deeper look at the packages and documentation - I have few more questions:

For mtmsg:
- What is the purpose of listeners? It seems a listener is used to listen to more than one buffer but it is not clear why one would do that. If I want to receive messages from multiple tasks/threads I give them the buffer id and they write their messages to that buffer, or not? The example in the documentation seems to indicate that messages are read in order by the listener, even if there are two buffers - so I guess one buffer could really do the job, or not?
- I found a minor spelling error in "This does not affect the underlying buffer, i.e. several buffer referencing objects could operate in different modes acessing the same buffer." by the way I had to re-read it multiple times to understand the meaning of that note. Blocking/non-Blocking is not a property of the buffer, but it defines the behavior of the function that reads the buffer, correct?
- abort: it is not clear to me what this does and why the abort operation can be canceled; what happens when abort is issued?
- references: it is weird that the listener does not keep a reference to the buffers it is listening to, why this is so? if the task/thread that is writing to the buffer ends, then there will be no reference from the writer, and the reader/listener should count as a reference, if not it may happen that the buffers are garbage collected and then what happens to the listener?
- only simple objects can be put into the buffer, no tables right? but one can send a string with the table construction which can then be executed from the receiver, right? this may be worth mentioning in the documentation... 

For mtstates:
- What is the purpose of mtstates.singleton? it seems to behaves like mtstates.newstate when state does not exist, otherwise it behaves like mtstates.state returning an id; but I am not able to fully understand the note on the care to be taken to ensure reference counting is correct; why this does not apply to mtstates.newstate? for both methods, singleton and newstate, the isowner property would be true... can you make an example on when singleton should be used?
- states can be interrupted... therefore the role of mtint is not clear (at least to me - and I am sorry for that) can one contrast the interrupt operation here with the interrupt operation in mtint package? this again may worth be adding to the documentation...

    Andrea

--
Andrea Vitali






Reply | Threaded
Open this post in threaded view
|

Re: Is it possible to run Lua threads in OS threads safely?

Andrea-2
In reply to this post by Norman Ramsey

I have been using luaproc for about three years.

Thank you for sharing your experience. This is very valuable.

I have noticed that you write "mostly reliable" followed by the scary note "occasionally computations locks up on mysterious ways"... do you mean dead-locks?

I also noticed that you mention upvalues as a way to pass data between threads, but based on the documentation one could also use the send/receive methods, right?
(searching the internet, I found that someone mention the fact that if there is a table as an upvalue then newproc fails, and this may not be immediately clear to the developer, is this correct?)

Why do you write that the concurrently model is at the same time "simple" but "it took a while to figure it out how to use it effectively?"...it seems it is not simple after all or maybe it is "too simple" and you bumped into some unintended behavior?

Yes I guess serialization (of tables) is one of the most required functionalities :) It would be useful to have such a method in the table library for that.... I guess it is not there because of the choices one can make (shallow vs deep copies, etc - the mesh of references can be really complex)

Thank you very much for your answer.

    Andrea

--
Andrea Vitali






Reply | Threaded
Open this post in threaded view
|

Re: Why one would need mtstates/mtmsg?

Oliver Schmidt
In reply to this post by Andrea-2
On 17.06.20 23:41, Andrea wrote:
> For *mtmsg*:
> - What is the purpose of listeners?
> so I guess one buffer could really do the job, or not?

Yes you are right, in principle everything can be done with buffer objects
without the need to use listener objects. Your feedback is very welcome and it
inspires me to rethink the architecture of mtmsg.

As far as I remember the initial motivation for the listener was to reduce
overall locking time (every invocation of addmsg/setmsg/nextmsg locks the buffer
object for short time). But actually this is not the case, all buffer objects
connected to the same listener are sharing the listener's mutex (see below for
further thougts about this topic).

The purpose for the listener is to be able to wait at the same time for two kind
of buffer objects: a buffer where messages are appended (using buffer:addmsg())
and one or more buffers where old messages are replaced (using buffer:setmsg()).

Example for buffer:setmsg(): consider an audio thread that sets the current
volume with buffer:setmsg(). The GUI thread reads the current volume from the
buffer and displays a volume meter. Old volume values are not needed in this use
case. If you want to have more than one buffer of this kind (e.g. more than one
volume values distributed in several audio threads) or if you want to mix it
with another buffer that is filled using buffer:addmsg() for messages that
should not be discarded than you could use a listener to listen to all of these
buffers at the same time.

> - I found a minor spelling error in "modes *acessing *the same buffer."

Thanks for reporting, I always have to think again about the writing of
"accessing" ;-)

> understand the meaning of that note. Blocking/non-Blocking is not a property of
> the buffer, but it defines the behavior of the function that reads the buffer,
> correct?

To be more precise: buffer objects are living outside the Lua world. In the Lua
world you have buffer referencing objects. The Blocking/NonBlocking flag is
stored in the buffer referencing object and is evaluated  and considered if you
are invoking a method on the buffer referencing object. Other buffer referencing
objects (which could be in other threads) are having their own blocking flag
even if they are referencing the same underlying buffer.

> - abort: it is not clear to me what this does and why the abort operation can be
> canceled; what happens when abort is issued?

The functions mtmsg.abort() / buffer:abort() / listener:abort() can be used to
interrupt/cancel operations from other threads. These methods are setting an
abortion flag (gloabally / in the buffer / in the listener) which leads to
"mtmsg.error.operation_aborted" errors on most of the methods of the module /
buffer / listener (more details are in the documentation: at every function it
is remarked if this function could raise an operation_aborted error).

For example: if a thread is waiting in buffer:nextmsg() another thread can
invoke buffer:abort() and the call to buffer:nextmsg() ends with the error
"mtmsg.error.operation_aborted". You could of course also send a special message
which then has be interpreted by the to be interrupted thread as abortion. But
if the abortion is only an exception (i.e. is not normal case) then using abort
is easier. So if you detect an error in the main thread and want to cancel the
operation of a worker thread you could invoke abort() on the buffer the thread
is using and then every operation on this buffer raises an operation_aborted
error until you invoke buffer:abort(false). You could also combine this with
"mtint" for interrupting operations of the Lua VM of this thread. Then if the
thread has ended the main thread can call buffer:abort(false) and the buffer can
be used normally again without operation_aborted errors.

BTW: above example also illustrates another use case for listener objects: if
you have several worker threads and each thread is writing to it's own buffer
you can invoke abort() only on the buffer of the thread you want to interrupt.
You then need a listener object if you want to read from all the worker thread
buffers efficiently.

> - references: it is weird that the listener does not keep a reference to the
> buffers it is listening to, why this is so?

I'm not shure what the original reason for this was, perhaps it was the easiest
solution. In fact all buffers that are connected to the same listener are
referencing this listener behind the scene. So the underlying listener object
can only be destructed if the last buffer is destructed.

> to the buffer ends, then there will be no reference from the writer, and the
> reader/listener should count as a reference, if not it may happen that the
> buffers are garbage collected and then what happens to the listener?

The listener just works normally. The unread messages of the destructed buffer
are discarded.

Currently, if you want to receive the last message a thread is writing to an
buffer you have to keep a buffer referencing object until you have realized that
the thread has finished.

I'm going to rethink the current solution since I now have the impression that
this could be improved (but I'm not sure). Again thank you for your feedback!


> - only simple objects can be put into the buffer, no tables right? but one can
> send a string with the table construction which can then be executed from the
> receiver, right? this may be worth mentioning in the documentation...

hmm the documentation cleary says that you can send and receive a string. mtmsg
by intention does not serialize tables. There are several libraries that can
serialize tables.

I'm thinking about extending mtmsg by the following functionality: to reduce
locking time and to be able to efficiently write large messages, "writer" and
"reader" objects could be useful. A writer object then could be used to
incrementally build up a larger message without locking. Then if the message is
complete it could be send with one invocation and very short locking time. The
writer object manages it's own memory and therefore the number of mallocs are
reduced (once the writer has the right memory size, no more mallocs are needed).
The reader object would serve the same purpose for the other direction: you
could read a larger message from a buffer into the reader with short locking
time and then incrementally get all the Lua values out of the reader without
locking.

> For *mtstates*:
> - What is the purpose of mtstates.singleton? it seems to behaves like
> mtstates.newstate when state does not exist, otherwise it behaves like
> mtstates.state returning an id;

No, it's different:

- mtstates.newstate() always returns a state referencing object to a *new* state
even if there is already a state with the same name. So it's valid to have more
than one state with the same name. But then you cannot retrieve the state by
name using mtstates.state(). As the documentation says an "ambiguous_name" error
is raised in these cases.

- mtstates.singleton() returns a state referencing object to a new state if the
state by this name does not exist and returns a state referencing object to an
existing state if the state by this name does exist.

> can you make an example on when singleton should be used?

Hmm normally singletons should be avoided ;-) But if you want to have a
singleton state then you could use mtstates.singleton(). E.g. if you are having
some code in a larger framework or whatsoever where you cannot (or do not want
to) control the setup of everything and your code runs in different threads but
you want to have shared data/state you could use mtstates.singleton() from these
threads to access the shared state without having to create the singleton in the
main thread and without worrying how to initially create this state without race
conditions between the threads.

> but I am not able to fully understand the note
> on the care to be taken to ensure reference counting is correct; why this does
> not apply to mtstates.newstate? for both methods, singleton and newstate, the
> isowner property would be true...

mtstates.newstate() gives a state referencing object that owns the mtstate
object. Another system thread or mtstate object (more precisley: Lua main state)
can only access this mtstate object by using mtstates.state() using the state's
name or id.

mtstates.state() always gives a state referencing object that does not own the
state (i.e. a weak reference).

With this solution reference cycles are not possible. But you have to keep the
state owning reference object until you are sure that you don't need this state
in other threads. Otherwise an invocation of e.g. state:call() in another thread
would lead to an "object_closed" error if all owning reference objects have been
garbage collected.

mtstates.singleton() always gives a state referencing object that does own the
state. With this you can build reference cycles. For example: a singleton state
is constructed by the invocation of mtstates.singleton() with the name "foo".
Then within this singleton state mtstates.singleton() is invoked with the name
"foo". After this the singleton state will have a owning state referencing
object to itself and this is a reference cycle which could only be garbage
collected if the singleton state forgets the state referencing object to itself.
But overall this works and does not lead to an error, it just could lead to a
situation where mtstate objects are not destructed although they are not
reachable from outside. So this also shows that the usage of singletons is
dangerous ;-)

> - states can be interrupted... therefore the role of mtint is not clear (at
> least to me - and I am sorry for that) can one contrast the interrupt operation
> here with the interrupt operation in mtint package?

Ah I forgot this: I implemented mtstate.interrupt before I created mtint. So
there is a small overlap in functionality between both packages... However it
makes the usage easier. If you are using mtint for interruption then you need to
get the interruptible id of the created state. For this the created state has to
invoke mtint.id() to get the interruptible id of itself and then has to pass the
id to the thread that wants to interrupt the state. On the other hand mtint
provides more functionality since it lets you install an interrupt handling
function within the state. So for simple interruption the usage of
mtstates.interrupt() is easier whereas mtint provides more functionality.

Best regards,
Oliver
12