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

Reply via email to