[BUG] LuaSocket is not thread safe

classic Classic list List threaded Threaded
16 messages Options
Reply | Threaded
Open this post in threaded view
|

[BUG] LuaSocket is not thread safe

Viacheslav Usov
With this grand opening title, I would like to attract attention to the following problem in mime.c:.

Function luaopen_mime_core that is called when the mime module is required, calls function b64setup, which initialises a static (global) array b64unbase. It does this as follows:

static void b64setup(UC *unbase)
{
    int i;
    for (i = 0; i <= 255; i++) unbase[i] = (UC) 255;
    for (i = 0; i < 64; i++) unbase[b64base[i]] = (UC) i;
    unbase['='] = 0;
}

Observe that the first loop fills the entire array with byte value 255, then certain elements within the array are overwritten with some other values. Those other values are expected in mime.unb64.

In an application that hosts multiple Lua states this function will be called multiple times if code in multiple states requires mime. If the states run in concurrent threads, these calls may execute concurrently, too, and then can also run concurrently with the code that calls mime.unb64, which may see the incorrect byte value 255 set up by the first loop. There is no simple way to reproduce this except by using a debugger to freeze and thaw threads in the right places.

There are multiple ways to fix that, but perhaps it is best to initialise those arrays statically, because running the init code multiple times does not make much sense.

Cheers,
V.
Reply | Threaded
Open this post in threaded view
|

Re: [BUG] LuaSocket is not thread safe

Eric Westbrook
Thank you!  I've added this as LuaSocket issue #274[0].

On 2019-03-09 04:53, Viacheslav Usov wrote:
> There are multiple ways to fix that, but perhaps it is best to
> initialise those arrays statically, because running the init code
> multiple times does not make much sense.

I agree.

We're currently pushing hard toward a fresh release of LuaSocket, so
this information is very timely and well received, and should definitely
be addressed.  All help and contributions are always very welcome,
including, especially right now, any that help clean and fix outstanding
issues in preparation for a release.

Best regards,
Eric Westbrook
https://github.com/diegonehab/luasocket

[0] https://github.com/diegonehab/luasocket/issues/274

Reply | Threaded
Open this post in threaded view
|

Re: [BUG] LuaSocket is not thread safe

Eric Westbrook
This condition has been fixed in the LuaSocket master branch and the
associated issue has been closed.

Thanks again for the report!

Best regards,
Eric Westbrook
https://github.com/diegonehab/luasocket

On 2019-03-09 13:55, Eric Westbrook wrote:
> LuaSocket issue #274[0].
> [0] https://github.com/diegonehab/luasocket/issues/274

Reply | Threaded
Open this post in threaded view
|

Re: [BUG] LuaSocket is not thread safe

Philippe Verdy
In reply to this post by Viacheslav Usov
I do agree, such a small arrayshould be a simple constant static declaration, not a function (and no need to use a boolean static tracker to see if it's initialized), or should be called only from the static initializer "b64setup" of the library (once for all threads) which should not run multiple times (such library initializer should have an exclusive mutex, which should be part of the "require" feature when it loads modules from concurrent threads).

However Lua does not natively uses truly preemptive concurrent threads (Posix like) and its core source just uses cooperative threads that never run concurrently (only when they yield), so this bug is not observed with the base implementation where all Lua threads (states) are running in the same thread (other threads are not used directly by Lua code but only by internal implemention for OS integration of I/O or socket/networking, notably listeners that then distribute the work loads and queue/serialize them for execution in the main thread where cooperative Lua threads are running).

Because of this, most Lua code is not thread-safe and it would require extensive work to make it so and allow the standard implementation being fully aware of preemptive concurrent threads as there's still no standard API for using mutexes, critical sections or thread-local storage, even if this API would be a NO-OP for the basic cooperative implementation.

For now I've not seen any Lua implementation working successfully to run Lua states in concurrent threads. If this exists, there is necearrily numerous patches everywhere in the source base to add many declarations and specific code to control concurrent accesses and serialize them when needed (with mutexes, critical sections and some variables in thread local storage when we want to limit the performance cost of mutexes and serialization). As well, making Lua using fully concurrent threads can expose it to severe security problems (e.g. Spectre/Meltdown issues) and modifications in the compiler chain (e.g. using "Retpoline" workarounds): this is also a complex issue in other scripting languages (notably Javascript and Java) and simpler to manage if concurrency is done only with full processes (because this problem should be fixed at OS-level), for example when using pools of worker processes to run multiple Lua instances on application servers: concurrent threads would be performing better than concurrent processes. But the basic cooperative threads of Lua are much easier to secure.

So your problem is actually: how do we control when b64setup() can run, and more generally how can "require" work concurrently and safely in a true multithreading environment to initialize the loaded modules? May be we don't need to patch this code and this should be better handled at a higher level in Lua's core implementation of "require"...

Le sam. 9 mars 2019 à 13:21, Viacheslav Usov <[hidden email]> a écrit :
With this grand opening title, I would like to attract attention to the following problem in mime.c:.

Function luaopen_mime_core that is called when the mime module is required, calls function b64setup, which initialises a static (global) array b64unbase. It does this as follows:

static void b64setup(UC *unbase)
{
    int i;
    for (i = 0; i <= 255; i++) unbase[i] = (UC) 255;
    for (i = 0; i < 64; i++) unbase[b64base[i]] = (UC) i;
    unbase['='] = 0;
}

Observe that the first loop fills the entire array with byte value 255, then certain elements within the array are overwritten with some other values. Those other values are expected in mime.unb64.

In an application that hosts multiple Lua states this function will be called multiple times if code in multiple states requires mime. If the states run in concurrent threads, these calls may execute concurrently, too, and then can also run concurrently with the code that calls mime.unb64, which may see the incorrect byte value 255 set up by the first loop. There is no simple way to reproduce this except by using a debugger to freeze and thaw threads in the right places.

There are multiple ways to fix that, but perhaps it is best to initialise those arrays statically, because running the init code multiple times does not make much sense.

Cheers,
V.
Reply | Threaded
Open this post in threaded view
|

Re: [BUG] LuaSocket is not thread safe

Viacheslav Usov
On Sun, Mar 10, 2019 at 10:38 AM Philippe Verdy <[hidden email]> wrote:

> For now I've not seen any Lua implementation working successfully to run Lua states in concurrent threads.

Mr. Verdy is obviously inexperienced in these matters.

Lua can be used very successfully in multi-threaded applications.

Modulo bugs in some libraries.

Cheers,
V.

Reply | Threaded
Open this post in threaded view
|

Re: [BUG] LuaSocket is not thread safe

Tim Hill
In reply to this post by Philippe Verdy


> On Mar 10, 2019, at 1:37 AM, Philippe Verdy <[hidden email]> wrote:
>
>
> For now I've not seen any Lua implementation working successfully to run Lua states in concurrent threads. If this exists, there is necearrily numerous patches everywhere in the source base to add many declarations and specific code to control concurrent accesses and serialize them when needed (with mutexes, critical sections and some variables in thread local storage when we want to limit the performance cost of mutexes and serialization). As well, making Lua using fully concurrent threads can expose it to severe security problems (e.g. Spectre/Meltdown issues) and modifications in the compiler chain (e.g. using "Retpoline" workarounds): this is also a complex issue in other scripting languages (notably Javascript and Java) and simpler to manage if concurrency is done only with full processes (because this problem should be fixed at OS-level), for example when using pools of worker processes to run multiple Lua instances on application servers: concurrent threads would be performing better than concurrent processes. But the basic cooperative threads of Lua are much easier to secure.
>

I think you misunderstand the OP. Lus itself is not multi-threaded, but IS thread-safe. That is, you can create multiple Lua states using lua_newstate() and run these in independent, concurrent thread with perfect safety WITHOUT any special patching or locking, since these two Lua states will not share ANY state at all. The OP was noting that, should you do such a thing, the library in question may fail since it is NOT thread safe. This is quite different from a SINGLE Lua VM running multiple threads concurrently.

—TIm



Reply | Threaded
Open this post in threaded view
|

Re: [BUG] LuaSocket is not thread safe

Philippe Verdy
Le lun. 11 mars 2019 à 03:46, Tim Hill <[hidden email]> a écrit :
I think you misunderstand the OP. Lus itself is not multi-threaded, but IS thread-safe. That is, you can create multiple Lua states using lua_newstate() and run these in independent, concurrent thread with perfect safety WITHOUT any special patching or locking, since these two Lua states will not share ANY state at all.

And your "ANY" word is wrong here: the state alone is not enough given that the Lua VM itself runs as a thread and uses libraries that are not thread-safe if they use C/C++ static variables instead of dynamic memory, or stack, or thread local storage.

But the cooperative model of "states" in Lua (that the doc also names "threads"... very confusively) has its own security limits: the cooperative Lua threads do not have any resource limits: they are very sensitive to DoS attacks given that there's no preemption for their allowed time slot or memory: a single Lua thread can steal indefinitely all resources that other Lua threads are waiting. Great, concurrent Lua threads cannot inspect what other Lua threads are doing because there's no preemption, but then privacy has a cost: DoS sensitivity, and also lack of interactivity and lot of unpredictable and unbounded delays for response times: remote clients of a Lua-based server will experiment very frequent timeouts, they can start doing things then be suspended for minutes or hours because of any other Lua thread that is just computing something intensively without ever yield()'ing (or making blocking I/O calls, such thing including an implicit internal yield).
 
The OP was noting that, should you do such a thing, the library in question may fail since it is NOT thread safe. This is quite different from a SINGLE Lua VM running multiple threads concurrently.

But then you run concurrent Lua VMs (in as many OS-level threads), not concurrent Lua threads (that run in a single OS-level thread), but you suppose that these separate VMs are completely independant (they are not: they also share critical resources, notably the OS-level memory allocator). The Lua VM does not really offer a good protection, and in such situation the multiple Lua VMs should better run as concurrent processes (e.g. in Fast-CGI for Apache servers, with a set of worker processes, like in traditional PHP servers: this makes the service much more resistant to DoS attacks and allows faster restart if ever one of these VMs is crashing or exhaust its limits and must be aborted, including for security reasons).

If we want true multithreading in Lua, the Lua states (those exposed in the Lua language, not those in LuaC!) should also be runnable concurrently without even having to yield: a thread could be started with a time slot limit (by default infinite like today) that the VM would preempt and it's natural in that case that these Lua states (not LuaC states) are implementede on top of preemptable POSIX-like threads, managed at OS level.

The LuaC state jsut represents the first main OS-level thread that serializes the cooperative threads, but cooperative threads would run in another OS-level thread. I/O and networking integration would also run in another OS-level thread. Any one of these Lua threads (not LuaC=OS thread) could then be interrupted. service listeners, and blocking I/O would use aynchrnous completion or timeout events and preemption when these events occur.

Of course not all environments where Lua is integrated have capabilities to run Posix-like threads. But it's really unfortunate that Lua cannot make use of real threads (except by instantiating multiple separate VMs) to make use of today's multicore CPUs or multi-CPU systems (notably for app servers!). Having to instaciate multiple separate VMs also does not allow easy cooperation and interaction between them, and is also problematic in terms of resource allocation and management (notably for memory: large applications cannot share memory for their common code, Lua applications have to be compiled multiple times, by each VM).

Basically Lua just works today just like Windows 3.x in the 1990's before OS/2 and then NT were created: Windows 3.x worked but as long as there were not thousands "threads" as of today but only one or two dozens (above that level, we could see the UI freeze almost constantly, by the DoS-like effect of absence of preemption). The same happens in Lua: there's absolutely no balancing of resource usage and no way to control it and pace those that are using the CPU too much. The cooperative model is still very fragile (and we remember what it was in Windows 3.x when interminable "hangs" required users to CTRL+ALT+DEL for rebooting abruptly...

There's still no virtalization at all and the term "VM" is abused when we speak about Lua if it is just a "virtual machine" but without virtual memory and virtual CPU.

The cooperative model is only good if all Lua threads are part of the same application developed and managed by the same source/authour, but not for handling services with user-written scripts (see how Lua is used in Wikimedia: it has no other choice than fixing a timeout limit and run each request in a separate VM, itself running in a separate worker process, and then kill it it the timeslot is exhausted, it works with FastCGI as long as we can a large pool of worker threads ready to service a request and an external monitor that can kill the offenders; but it's not a good solution for many servers because killing worker processes means that we also need additional processes to perform cleanup and unlock/release the temporary resources; and it requires a large amount of resources: many CPUs or cores, much more memory, and large farms of servers: this solution does not scale very well, it is too costly). But if we can do real multi-threading, we can better use the resources, avoid starvation of concurrent works, avoid most timeouts for clients, and manage the workload of servers with reasonnable response time and with more modest resources available to service many more requests.


Reply | Threaded
Open this post in threaded view
|

Re: [BUG] LuaSocket is not thread safe

Diego Nehab-4
For what it's worth, this has already been fixed on LuaSocket's github repository. If you have anything else to report, please open an issue there.

On Mon, Mar 11, 2019 at 1:17 AM Philippe Verdy <[hidden email]> wrote:
Le lun. 11 mars 2019 à 03:46, Tim Hill <[hidden email]> a écrit :
I think you misunderstand the OP. Lus itself is not multi-threaded, but IS thread-safe. That is, you can create multiple Lua states using lua_newstate() and run these in independent, concurrent thread with perfect safety WITHOUT any special patching or locking, since these two Lua states will not share ANY state at all.

And your "ANY" word is wrong here: the state alone is not enough given that the Lua VM itself runs as a thread and uses libraries that are not thread-safe if they use C/C++ static variables instead of dynamic memory, or stack, or thread local storage.

But the cooperative model of "states" in Lua (that the doc also names "threads"... very confusively) has its own security limits: the cooperative Lua threads do not have any resource limits: they are very sensitive to DoS attacks given that there's no preemption for their allowed time slot or memory: a single Lua thread can steal indefinitely all resources that other Lua threads are waiting. Great, concurrent Lua threads cannot inspect what other Lua threads are doing because there's no preemption, but then privacy has a cost: DoS sensitivity, and also lack of interactivity and lot of unpredictable and unbounded delays for response times: remote clients of a Lua-based server will experiment very frequent timeouts, they can start doing things then be suspended for minutes or hours because of any other Lua thread that is just computing something intensively without ever yield()'ing (or making blocking I/O calls, such thing including an implicit internal yield).
 
The OP was noting that, should you do such a thing, the library in question may fail since it is NOT thread safe. This is quite different from a SINGLE Lua VM running multiple threads concurrently.

But then you run concurrent Lua VMs (in as many OS-level threads), not concurrent Lua threads (that run in a single OS-level thread), but you suppose that these separate VMs are completely independant (they are not: they also share critical resources, notably the OS-level memory allocator). The Lua VM does not really offer a good protection, and in such situation the multiple Lua VMs should better run as concurrent processes (e.g. in Fast-CGI for Apache servers, with a set of worker processes, like in traditional PHP servers: this makes the service much more resistant to DoS attacks and allows faster restart if ever one of these VMs is crashing or exhaust its limits and must be aborted, including for security reasons).

If we want true multithreading in Lua, the Lua states (those exposed in the Lua language, not those in LuaC!) should also be runnable concurrently without even having to yield: a thread could be started with a time slot limit (by default infinite like today) that the VM would preempt and it's natural in that case that these Lua states (not LuaC states) are implementede on top of preemptable POSIX-like threads, managed at OS level.

The LuaC state jsut represents the first main OS-level thread that serializes the cooperative threads, but cooperative threads would run in another OS-level thread. I/O and networking integration would also run in another OS-level thread. Any one of these Lua threads (not LuaC=OS thread) could then be interrupted. service listeners, and blocking I/O would use aynchrnous completion or timeout events and preemption when these events occur.

Of course not all environments where Lua is integrated have capabilities to run Posix-like threads. But it's really unfortunate that Lua cannot make use of real threads (except by instantiating multiple separate VMs) to make use of today's multicore CPUs or multi-CPU systems (notably for app servers!). Having to instaciate multiple separate VMs also does not allow easy cooperation and interaction between them, and is also problematic in terms of resource allocation and management (notably for memory: large applications cannot share memory for their common code, Lua applications have to be compiled multiple times, by each VM).

Basically Lua just works today just like Windows 3.x in the 1990's before OS/2 and then NT were created: Windows 3.x worked but as long as there were not thousands "threads" as of today but only one or two dozens (above that level, we could see the UI freeze almost constantly, by the DoS-like effect of absence of preemption). The same happens in Lua: there's absolutely no balancing of resource usage and no way to control it and pace those that are using the CPU too much. The cooperative model is still very fragile (and we remember what it was in Windows 3.x when interminable "hangs" required users to CTRL+ALT+DEL for rebooting abruptly...

There's still no virtalization at all and the term "VM" is abused when we speak about Lua if it is just a "virtual machine" but without virtual memory and virtual CPU.

The cooperative model is only good if all Lua threads are part of the same application developed and managed by the same source/authour, but not for handling services with user-written scripts (see how Lua is used in Wikimedia: it has no other choice than fixing a timeout limit and run each request in a separate VM, itself running in a separate worker process, and then kill it it the timeslot is exhausted, it works with FastCGI as long as we can a large pool of worker threads ready to service a request and an external monitor that can kill the offenders; but it's not a good solution for many servers because killing worker processes means that we also need additional processes to perform cleanup and unlock/release the temporary resources; and it requires a large amount of resources: many CPUs or cores, much more memory, and large farms of servers: this solution does not scale very well, it is too costly). But if we can do real multi-threading, we can better use the resources, avoid starvation of concurrent works, avoid most timeouts for clients, and manage the workload of servers with reasonnable response time and with more modest resources available to service many more requests.


Reply | Threaded
Open this post in threaded view
|

Re: [BUG] LuaSocket is not thread safe

Coda Highland
In reply to this post by Tim Hill


On Sun, Mar 10, 2019 at 10:36 PM Tim Hill <[hidden email]> wrote:


> On Mar 10, 2019, at 1:37 AM, Philippe Verdy <[hidden email]> wrote:
>
>
> For now I've not seen any Lua implementation working successfully to run Lua states in concurrent threads. If this exists, there is necearrily numerous patches everywhere in the source base to add many declarations and specific code to control concurrent accesses and serialize them when needed (with mutexes, critical sections and some variables in thread local storage when we want to limit the performance cost of mutexes and serialization). As well, making Lua using fully concurrent threads can expose it to severe security problems (e.g. Spectre/Meltdown issues) and modifications in the compiler chain (e.g. using "Retpoline" workarounds): this is also a complex issue in other scripting languages (notably Javascript and Java) and simpler to manage if concurrency is done only with full processes (because this problem should be fixed at OS-level), for example when using pools of worker processes to run multiple Lua instances on application servers: concurrent threads would be performing better than concurrent processes. But the basic cooperative threads of Lua are much easier to secure.
>

I think you misunderstand the OP. Lus itself is not multi-threaded, but IS thread-safe. That is, you can create multiple Lua states using lua_newstate() and run these in independent, concurrent thread with perfect safety WITHOUT any special patching or locking, since these two Lua states will not share ANY state at all. The OP was noting that, should you do such a thing, the library in question may fail since it is NOT thread safe. This is quite different from a SINGLE Lua VM running multiple threads concurrently.

—TIm


Not sharing state means that Lua is reentrant. And it is reentrant, modulo bugs like the one under discussion. Thread-safety is a stronger guarantee that says that the code will avoid stepping on its own toes if two threads access the SAME state at the same time.

Full thread safety is probably not a worthwhile design goal. Marshaling communication between independent states (e.g. Lanes) is a lot easier to achieve with a lot less overhead for the singlethreaded case.

/s/ Adam
Reply | Threaded
Open this post in threaded view
|

Re: [BUG] LuaSocket is not thread safe

Viacheslav Usov
In reply to this post by Diego Nehab-4
On Mon, Mar 11, 2019 at 6:05 AM Diego Nehab <[hidden email]> wrote:

> For what it's worth, this has already been fixed on LuaSocket's github repository. If you have anything else to report, please open an issue there.

Thank you for your prompt response!

I would like to ask you to consider one other thing. The README in the repo has said for six years that the version is 3.0-rc1. While we can of course use git commit hashes to target particular versions, not seeing the version change makes it difficult to judge whether any given state of the repo is stable or transient.

Cheers,
V.


Reply | Threaded
Open this post in threaded view
|

Re: [BUG] LuaSocket is not thread safe

Viacheslav Usov
In reply to this post by Coda Highland
On Mon, Mar 11, 2019 at 5:46 AM Coda Highland <[hidden email]> wrote:

> Not sharing state means that Lua is reentrant. And it is reentrant, modulo bugs like the one under discussion. Thread-safety is a stronger guarantee that says that the code will avoid stepping on its own toes if two threads access the SAME state at the same time.

Some people believe otherwise. Wikipedia's page on reentracy has example that demonstrate that these two terms are not in a weaker/stronger relationship.

In my opinion, when we say that something is thread-safe, reentrant or not, this tends to be ambiguous unless we specify fully the expectations and the actual behaviour, as I hope I did in my original message.

Cheers,
V.
Reply | Threaded
Open this post in threaded view
|

Re: [BUG] LuaSocket is not thread safe

Francisco Olarte
In reply to this post by Philippe Verdy
On Mon, Mar 11, 2019 at 5:16 AM Philippe Verdy <[hidden email]> wrote:

> If we want true multithreading in Lua, the Lua states (those exposed in the Lua language, not those in LuaC!) should also be runnable concurrently without even having to yield: a thread could be started with a time slot limit (by default infinite like today) that the VM would preempt and it's natural in that case that these Lua states (not LuaC states) are implementede on top of preemptable POSIX-like threads, managed at OS level.

Are you aware that coroutines solve some problems, and that threads
are a very poor substitute for coroutines? Why do you want to
sacrifice them to get threads? You can (easily) simulate coroutines
using threads or processes ( or machines ), but they are not a good
substitute.

Also, are you aware that enabling thread support on a language makes
every piece of code pay a price, even the non-threaded ones?

Francisco Olarte.

Reply | Threaded
Open this post in threaded view
|

Re: [BUG] LuaSocket is not thread safe

Philippe Verdy
I do not want a "replacement", just the possibility to convert coroutines as plain threads. I do not contest the interest of coroutines, basically for implement the producer/consumer model within the same app, but not for handling services between different users or diffeernt security contexts.

Le lun. 11 mars 2019 à 12:09, Francisco Olarte <[hidden email]> a écrit :
On Mon, Mar 11, 2019 at 5:16 AM Philippe Verdy <[hidden email]> wrote:

> If we want true multithreading in Lua, the Lua states (those exposed in the Lua language, not those in LuaC!) should also be runnable concurrently without even having to yield: a thread could be started with a time slot limit (by default infinite like today) that the VM would preempt and it's natural in that case that these Lua states (not LuaC states) are implementede on top of preemptable POSIX-like threads, managed at OS level.

Are you aware that coroutines solve some problems, and that threads
are a very poor substitute for coroutines? Why do you want to
sacrifice them to get threads? You can (easily) simulate coroutines
using threads or processes ( or machines ), but they are not a good
substitute.

Also, are you aware that enabling thread support on a language makes
every piece of code pay a price, even the non-threaded ones?

Francisco Olarte.
Reply | Threaded
Open this post in threaded view
|

Re: [BUG] LuaSocket is not thread safe

Francisco Olarte
Hello Philippe:

Did I do something which deserves this top-posted reply?I seem t
orecall it wasn't your style. Anyway.

On Mon, Mar 11, 2019 at 1:26 PM Philippe Verdy <[hidden email]> wrote:
> I do not want a "replacement",

My fault, it seem you wanted that.

> just the possibility to convert coroutines as plain threads.

This eliminates one / several of the advantages of coroutines, unless
you do it in a very restricted way. Normally what you do is having a
process composed of threads composed of coroutines.But a coroutine in
a thread is a different beast.

> I do not contest the interest of coroutines, basically for implement the producer/consumer model within the same app,

mmm, this, specifically, is quite well served by threads. I mean,
everytime I've done it I got just a sync point, a queue of consumable
items, or two, a queue of consumed items to refill. Threads work fine
there and are advantageous if you wan to customize fan in/fan out.

> but not for handling services between different users or diffeernt security contexts.
or for multicore mandelbrot set calculations. But specifically, in
Lua, given how cheap states are, you can specifically solve that
problem easily, and probably better, using several interpreters. After
all, running several "security contexts" in a single memory space ( at
the lua level ), is complicated ( in fact, not from a performance but
from a security point of view, coroutines, which do not have races,
may be better ).

Francisco Olarte.

Reply | Threaded
Open this post in threaded view
|

Re: [BUG] LuaSocket is not thread safe

Coda Highland
In reply to this post by Viacheslav Usov


On Mon, Mar 11, 2019 at 2:56 AM Viacheslav Usov <[hidden email]> wrote:
On Mon, Mar 11, 2019 at 5:46 AM Coda Highland <[hidden email]> wrote:

> Not sharing state means that Lua is reentrant. And it is reentrant, modulo bugs like the one under discussion. Thread-safety is a stronger guarantee that says that the code will avoid stepping on its own toes if two threads access the SAME state at the same time.

Some people believe otherwise. Wikipedia's page on reentracy has example that demonstrate that these two terms are not in a weaker/stronger relationship.

In my opinion, when we say that something is thread-safe, reentrant or not, this tends to be ambiguous unless we specify fully the expectations and the actual behaviour, as I hope I did in my original message.

Cheers,
V.

It's not my fault that practical computer programming doesn't use the vocabulary the same way that theoretical computer science does. Based purely on the formal definitions, all thread-safe code is necessarily reentrant -- if it weren't, it wouldn't be thread-safe.

The example of code that is thread-safe but not reentrant on Wikipedia is wrong. It's true that the code in the example is not reentrant, but I argue that it is also not actually thread-safe. Threads don't just refer to high-level operating system threads. They refer to any sort of multitasking, whether preemptive, cooperative, or true multiprocessing. Just because a system interrupt occurs in the same OS context doesn't mean it isn't concurrent execution; it's preemptive multitasking with some fairly specific requirements.

That said, because the vocabulary is used differently between those groups, you're right that it can be ambiguous, and you did indeed do a good job of specifying the expectations.

/s/ Adam
Reply | Threaded
Open this post in threaded view
|

Re: [BUG] LuaSocket is not thread safe

Philippe Verdy
An interesting reading:  "N4024: Distinguishing coroutines and fibers" (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4024.pdf).

Lua currently only has coroutines, but implementing fibers can be made on top of coroutines (you just need a single coroutine acting as the scheduler).
Sill both are using a "cooperative" strategy (both require working tasks to "yield").
But fibers are also implemented in OSes (e.g. Windows, which provides its scheduler that can be tuned to work like coroutines: the scheduler has no other choice than transfering the control from one coroutine back to its blocking caller coroutine which used "resume").

Still there's no warranty that any coroutine or fiber will ever bring back their control of execution: a single coroutine or fiber can still freeze completely all others (including the scheduler's coroutine managing the "fibers"). That's were both are still not usable as we want to serve multiple clients.

The alternative using processes is too costly (takes too much resources, and context switching is really much slower because of preemption at any time), but there's little difference in terms of context-switching performance between threads and processes. But processes allow stricter separation of memory use and allow managing quotas: no one process will consume all resources needed by other processes, which remains possible with threads unless the threads' scheduler (working in a separate process) sets and manages limits between threads in the same process.

Neither threads or processes can be implemented with fibers or coroutines but threads can be emulated by processes (using resources sharing mecanisms).

And for servers (notably application servers that manage many users with distinct ressource quotas, on memory, or IO, or networking, or time) and for applications that should benefit parallelism, running evering in a single thread (of a single process) is bad (does not scale well with multicore CPUs or multiple CPUs, and does not allow easy dynamic transfert of execution from one host to another in a computing grid): fibers and coroutines do not offer what is needed to warranty a good response time: we do need threads as the minimum, i.e. preemption on time-slots, without reaching a point where workers will "yield". As well multithreading (or multiprocessing if one wants stricter isolation) is needed for tracing and debugging: Lua is not easy to trace or debug, it requires a modification inside the Lua VM (running in its own thread) to implement the debugger, in order to force the control to a debugger thread (itself not living in Lua space, so debugging by users themselves is not really working, and what the debugger does cannot be natively implemented in user space and written in Lua itself). This has a consequence: Lua programs are much more difficult to debug, and hard to tune, and almost impossible to scale up (and it runs poorly on multicore CPUs or in multiCPU symetric systems and does not suite well for CPU intensive programs, and true parallelism becomes almost impossible except for very small fragments or by using a custom library that will delegate some work to a dedicated CPU or GPU and wait/block for completion, or will need to delegate the work to a remote host via network APIs). As well networking is still not very good, and both coroutines and fibers are extremely fragile against DoS attacks (this also makes Lua not suitable for many app servers, notably for the web).

Even though I do not ask support for fibers (OS fibers could be used by Lua implementations to implement Lua coroutines), there's a very good use case at least for threads (non multithreading-capable systems can still use processes if they are multiprocess-capable), and parallelism should be less limtied than just small fragments of codes (such as "vector" processing with specific instruction sets for CPUs and GPUs).

Many programs now use parallelism extensively and would not even run without them (e.g. most games): they need at least threads. As well web servers need at least threads. But I do not see why we cannot create a Lua "coroutine" with a parameter allowing it to be preempted (with a timeout event) and so allowing Lua to create a new thread (or process if therere are memory mappings to perform data exchanges between processes). As well, true async I/O and networking listeners will need it. And a warrantied response time is also needed for works with strict time requirements (e.g. multimedia rendering: users don't want to see their video freeze, and don't want audio to suddenly block and with frequent silences): Lua alone is not sufficient as long it remains self-containged in a single thread (with absoluutely no isolation at all between workers).







Le lun. 11 mars 2019 à 18:14, Coda Highland <[hidden email]> a écrit :


On Mon, Mar 11, 2019 at 2:56 AM Viacheslav Usov <[hidden email]> wrote:
On Mon, Mar 11, 2019 at 5:46 AM Coda Highland <[hidden email]> wrote:

> Not sharing state means that Lua is reentrant. And it is reentrant, modulo bugs like the one under discussion. Thread-safety is a stronger guarantee that says that the code will avoid stepping on its own toes if two threads access the SAME state at the same time.

Some people believe otherwise. Wikipedia's page on reentracy has example that demonstrate that these two terms are not in a weaker/stronger relationship.

In my opinion, when we say that something is thread-safe, reentrant or not, this tends to be ambiguous unless we specify fully the expectations and the actual behaviour, as I hope I did in my original message.

Cheers,
V.

It's not my fault that practical computer programming doesn't use the vocabulary the same way that theoretical computer science does. Based purely on the formal definitions, all thread-safe code is necessarily reentrant -- if it weren't, it wouldn't be thread-safe.

The example of code that is thread-safe but not reentrant on Wikipedia is wrong. It's true that the code in the example is not reentrant, but I argue that it is also not actually thread-safe. Threads don't just refer to high-level operating system threads. They refer to any sort of multitasking, whether preemptive, cooperative, or true multiprocessing. Just because a system interrupt occurs in the same OS context doesn't mean it isn't concurrent execution; it's preemptive multitasking with some fairly specific requirements.

That said, because the vocabulary is used differently between those groups, you're right that it can be ambiguous, and you did indeed do a good job of specifying the expectations.

/s/ Adam