QUnicodeTools: fix data race in initialization of libthai symbols

The facilities of qunicodetools.cpp are not limited to the GUI thread,
so initialization must be thread-safe.

The old code wasn't, though, and contained several data races

- non-atomic initialized was read while another thead may write it
- th_brk and th_next_cell were read while another thead may write them

Fix by using Double-Checked Locking. This also prepares the code for
an eventual port to th_brk_find_breaks() (th_brk is deprecated).

The function pointers don't need to be atomic, because all reads from
them are guaranteed to happen-after the writes to them (as long as all
users call init_libthai() and don't proceeed if it returns false; this
could be ensured by returning a struct with the function pointers from
init_libthai() instead of maintaining them as statically-visible
globals, but that's outsize the scope of this patch).

As a drive-by, remove a pointless static_cast<int>(~~int expression~~).

Fixes: QTBUG-105543
Pick-to: 6.4 6.3 6.2
Change-Id: I492acd7e9a257e5c4b91f576e9bc448b6bb96ad1
Reviewed-by: Ievgenii Meshcheriakov <ievgenii.meshcheriakov@qt.io>
Reviewed-by: Lars Knoll <lars.knoll@gmail.com>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
This commit is contained in:
Marc Mutz 2022-08-11 11:15:05 +02:00
parent 50d1a8a377
commit 7d021e3173

View File

@ -3,12 +3,15 @@
#include "qunicodetools_p.h"
#include "qmutex.h"
#include "qunicodetables_p.h"
#include "qvarlengtharray.h"
#if QT_CONFIG(library)
#include "qlibrary.h"
#endif
#include <mutex>
#include <limits.h>
#define FLAG(x) (1 << (x))
@ -1405,11 +1408,15 @@ Q_CONSTINIT static th_next_cell_def th_next_cell = nullptr;
static int init_libthai() {
#if QT_CONFIG(library)
Q_CONSTINIT static bool initialized = false;
if (!initialized && (!th_brk || !th_next_cell)) {
th_brk = reinterpret_cast<th_brk_def>(QLibrary::resolve("thai"_L1, static_cast<int>(LIBTHAI_MAJOR), "th_brk"));
th_next_cell = (th_next_cell_def)QLibrary::resolve("thai"_L1, LIBTHAI_MAJOR, "th_next_cell");
initialized = true;
Q_CONSTINIT static QBasicAtomicInt initialized = Q_BASIC_ATOMIC_INITIALIZER(false);
Q_CONSTINIT static QBasicMutex mutex;
if (!initialized.loadAcquire()) {
const auto locker = std::scoped_lock(mutex);
if (!initialized.loadAcquire()) {
th_brk = reinterpret_cast<th_brk_def>(QLibrary::resolve("thai"_L1, LIBTHAI_MAJOR, "th_brk"));
th_next_cell = (th_next_cell_def)QLibrary::resolve("thai"_L1, LIBTHAI_MAJOR, "th_next_cell");
initialized.storeRelease(true);
}
}
if (th_brk && th_next_cell)
return 1;