On 10/24/22 05:40, LIU Hao via Gcc-patches wrote:
在 2022/10/21 20:34, i.nix...@autistici.org 写道:
got it...
anyway it seems logical to me the way I proposed :)
Below is a message forwarded from mingw-w64-public, elaborating the
necessity of a new thread model.
As there are objections from other mingw-w64 developers, I am putting
those patches against mingw-w64-crt on hold for now. Despite that, all
threading facilities - mutexes, condition variables, once flags, etc.
- are still fully functional within the mcf thread model.
In addition, I will keep maintaining my personal builds (from GCC 12
release branch) with these patches at https://gcc-mcf.lhmouse.com/.
-------- Forwarded Message --------
在 2022/10/23 18:06, Jacek Caban 写道:
>
> Please, let's not do that. It's possible to fix existing
implementations, we don't need to make
> things more complicated than they are.
>
Okay okay, I think I have to compose a thorough list of problems that
we are facing at the moment, and had better have a permalink to the
mailing list archive that I can reference elsewhere. I have been tired
of repeating the same grounds of arguments again and again:
1. In a DLL, destructors of static objects and callbacks that are
registered
with `atexit()`, are executed by `LdrShutdownProcess()`, after all
the other
thread have been terminated `ZwTerminateProcessO(NULL, status)`.
This means
that, if another thread has been terminated while holding a mutex,
the mutex
can never get unlocked. If a destructor attempts to lock the same
mutex,
deadlocks will occur. Destructors of executables do not suffer
from this
issue, because they are executed before `RtlExitUserProcess()`.
Standard behavior: Static destructors and exit callbacks should be
executed
while other threads are running. If another thread attempts to
access a
destroyed object, the behavior is undefined; the user is
responsible to
prevent this from happening, by joining or suspending it.
2. Following 1, in a DLL, static destructors and exit callbacks are still
invoked when `_Exit()` or `quick_exit()` is called.
Standard behavior: `_Exit()` should not perform any cleanup; not
even open
files are flushed. `quick_exit()` shall invoke all quick-exit
callbacks in
reverse order, then call `_Exit()`.
3. There is a use-after-free bug [1] about thread-local destructors. I
suspect
this is caused by emutls, because GCC uses `__cxa_thread_atexit()` to
register thread-local destructors, which could interleave with
`emutls_destroy()`.
Standard behavior: This is not allowed to happen. mcfgthread
solves this
issue by running thread-local destructors and thread-specific key
destructors as two separate passes [3].
[1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80816
[2]
https://github.com/gcc-mirror/gcc/blob/f84e4fb44aa26b71fbc64e0532fd24d96e5caa3f/libgcc/emutls.c#L96
[3]
https://github.com/lhmouse/mcfgthread/blob/63e034d375caf585e2921cd3455f1048feb2172d/src/xglobals.c#L249
4. In the win32 thread model, thread-specific key destructors are
called at
process exit [4], after static destructors.
Standard behavior: They shall be called only when a thread exits,
and the
associated thread-specific values are not a null pointer. They
shall not be
called when a program terminates; instead, users are responsible for
deallocating such resources before calling `exit()`. This
requirement is
missing in POSIX, but formally specified by ISO/IEC 9899:2017, as
the 4th
paragraph in '7.26.6.1 The tss_create function'.
[4]
https://github.com/mingw-w64/mingw-w64/blob/d0a034a04d312434b842c4869a8a900568d8db98/mingw-w64-crt/crt/tlsthrd.c#L134
Those 4 points describes problems that you solve in the new threading
model, but there is no reason they can't be fixed for existing threading
models. In fact, ideally they would be fixed for all threading models.
Except now we need to worry about one more threading model, meaning that
future bugs will be even harder to fix.
5. Wait operations, of timed mutexes and condition variables, should take
absolute time points as `struct timespec`.
Standard behavior: Both POSIX and ISO C specifies them as such,
while all
Windows APIs take relative durations as a 32-bit integer of
milliseconds,
which can also easily get overflown.
This also may be supported in existing threading models. Overflow is
trivial to fix by waiting in a loop. (There are other reasons why OS
support for absolute timeout is slightly better, but the price of this
design decision makes it questionable. I plan to elaborate more on that
on mingw ML, but I need to find time to do a bit of research first).
Jacek