mailing list of musl libc
 help / color / mirror / code / Atom feed
From: Markus Wichmann <nullplan@gmx.net>
To: musl@lists.openwall.com
Subject: Re: [musl] Suggestion for thread safety
Date: Mon, 21 Feb 2022 18:42:23 +0100	[thread overview]
Message-ID: <20220221174223.GA2079@voyager> (raw)
In-Reply-To: <CAOZ3c1rUN24Arx5iy+Zm-4gYajfBthQsgL9q-iiNQpAo1aZL8w@mail.gmail.com>

On Mon, Feb 21, 2022 at 11:36:06AM +0000, Lee Shallis wrote:
> First I'll start with the code snippets I've copied from my own code:
>
> [...]
>
> #define LockErrors( ud ) LockStdErr( ud ); LockSysErr( ud )
> #define FreeErrors( ud ) FreeSysErr( ud ); FreeStdErr( ud )
>

Style problem: Put a "do {} while (0)" around these, or else you can
have unintended effects. The code "if (cond) LockErrors(foo);" will
always execute "LockSysErr()". And if you put in complementary code at
the bottom of the function, it will always execute "FreeStdErr()",
leading to undefined behavior, and good luck finding it.

> [...]
>         dint err;
>         LOCK_ERRORS
>         (
>             errno = 0;
>             *data = *data ? realloc( *data, size ) : malloc( size );
>             err = *data ? 0 : errno;
>         );

Why lock anything here? errno is thread local, and the allocation
functions must be thread-safe. There is no need to lock anything here.

> SHARED_EXP void LockSiData( LOCK **shared, LOCK *ptr )
> {
>     while ( *shared != ptr )
>     {
>         if ( !(*shared) )
>             *shared = ptr;
>         pauseCB( ptr );
>     }
> }
>

And now I have to ask what the possible point of this is. Your call to
an external function manages to make you avoid the need for a volatile
keyword (pauseCB might access global variables, and shared might be
pointing at one, so the compiler doesn't know the state of *shared after
the return of pauseCB), however, there is probably also some kind of
barrier missing. And in practice this is going to burn up a CPU core
with completely unnecessary work.

After you have established to your satisfaction that the lock is taken,
it would be best to suspend the current thread for exactly as long as
the lock holder takes to reach the complementary unlock function. You
can use futexes for that. Better yet, you can just use a pthread_mutex_t
for that, which does all that you need internally.

Besides, if "shared" points to a shared variable (as the name suggests),
then this constitutes a data race (multiple threads read, some threads
write) and is therefore undefined behavior. It also has a race condition
beyond that (if one thread is suspended after seeing *shared is NULL,
but before setting *shared, another can pass the same check, set
*shared, and exit the loop. A function like pthread_yield() doesn't have
to do anything useful against that).

It looks like you actually want some kind of compare-and-swap function
here, but again, you can also just skip it in favor of a
pthread_mutex_t.

> [...]
>
> Take what you will of the above, the Allot function was included just
> to give you an idea of how the locks are made, as for pauseCB that
> should be re-mapped to a variant that calls something like
> pthread_yield(), it's what I tested with, anyways the general idea is
> that the shared lock defaults to NULL when not locked, when you want
> to lock it you 1st wait for it to be NULL then as soon as it's empty
> you fill it with your own pointer then wait for other threads who
> detected the same situation to fill there pointers into the shared
> pointer (assuming any other threads were trying to lock the pointer),
> when the wait is over you check again if the pointer is not matching
> your own, if not then you failed to lock and should wait longer, using
> pointers lends itself well to threading as the pointer being used to
> take the shared pointer will normally be unique to the thread (because
> rarely will anyone try to use the same pointer in multiple threads for
> this process that has a clear example in it's declaration), using
> const char * for the LOCK typedef means a thread can give more
> information about itself or the pointer it locked with if it so
> chooses.
>

Structure your thoughts into sentences, and your sentences into
paragraphs, please. The above is one long run-on sentence, that starts
with a point about the Allot function and ends in a justification for
the type, and in between has visited many other topics. You are not
James Joyce, and you are not writing the Ulysses.

Anyway, what I'm still trying to find is the point to all of this.
Mutual exclusion is a solved problem, and is in general hard to solve
while maintaining some semblance of performance and remaining safe.
People with more knowledge than me have poured a lot of thought into
libraries that do this, so why not use them? Especially since they are
included in the threading implementation already.

> [GRIP is a fine-grained lock]

Another solved problem. You can use counting semaphores to solve the
same problem in a simpler way, at least if I understood correctly.
Alternatively, you can use an interesting system of mutexes and
conditions, but that typically doesn't end up looking very pretty.

Ciao,
Markus

  reply	other threads:[~2022-02-21 17:42 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-02-21 11:36 Lee Shallis
2022-02-21 17:42 ` Markus Wichmann [this message]
2022-02-23  0:30   ` Lee Shallis
2022-02-23 18:57     ` Markus Wichmann
2022-02-23 20:06       ` Rich Felker
2022-02-26  9:56       ` Lee Shallis
2022-02-26 11:38         ` Joakim Sindholt
2022-02-27 23:32           ` Lee Shallis
2022-02-28  0:15             ` Rich Felker
2022-02-28  8:48             ` Joakim Sindholt
2022-02-28 14:43               ` Lee Shallis
2022-02-28 15:19                 ` Rich Felker
2022-02-28 15:50                 ` Joakim Sindholt
2022-02-28 16:07                   ` Lee Shallis
2022-03-02  1:44                     ` Lee Shallis
2022-02-23  1:19 ` Rich Felker

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20220221174223.GA2079@voyager \
    --to=nullplan@gmx.net \
    --cc=musl@lists.openwall.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/musl/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).