QMessageLogger: initialize QT_FATAL_xxx cooperatively between threads

The use of function-local static implied there was a guard variable to
perform a thread-safe initialization (a critical section). We don't need
that: we can just use a plain QBasicAtomicInt and cooperatively
initialize, in parallel. This way, the QMessageLogger code does not need
to check the status of the guard variable before every access to the
atomic, which would be an atomic access before the atomic access.

Change-Id: Iedfbe8e28ca5168dea43fffddf205d122c97f71c
Reviewed-by: Marc Mutz <marc.mutz@qt.io>
This commit is contained in:
Thiago Macieira 2025-01-14 18:01:56 -08:00
parent c8ecd0eb22
commit 009131e556

View File

@ -149,30 +149,54 @@ static int checked_var_value(const char *varname)
return (ok && value >= 0) ? value : 1; return (ok && value >= 0) ? value : 1;
} }
static bool is_fatal_count_down(QAtomicInt &n) static bool isFatalCountDown(const char *varname, QBasicAtomicInt &n)
{ {
// it's fatal if the current value is exactly 1, static const int Uninitialized = -1;
// otherwise decrement if it's non-zero static const int NeverFatal = 0;
static const int ImmediatelyFatal = 1;
int v = n.loadRelaxed(); int v = n.loadRelaxed();
while (v > 1 && !n.testAndSetRelaxed(v, v - 1, v)) if (v == Uninitialized) {
qYieldCpu(); // first, initialize from the environment
return v == 1; // we exited the loop, so either v == 0 or CAS succeeded to set n from v to v-1 const int env = checked_var_value(varname);
if (env == NeverFatal) {
// not fatal, now or in the future, so use a fast path
n.storeRelaxed(NeverFatal);
return false;
} else if (env == ImmediatelyFatal) {
return true;
} else if (n.testAndSetRelaxed(Uninitialized, env - 1, v)) {
return false; // not yet fatal
} else {
// some other thread initialized before we did
}
} }
while (v > ImmediatelyFatal && !n.testAndSetRelaxed(v, v - 1, v))
qYieldCpu();
// We exited the loop, so either v already was ImmediatelyFatal or we
// succeeded to set n from v to v-1.
return v == ImmediatelyFatal;
}
Q_CONSTINIT static QBasicAtomicInt fatalCriticalsCount = { -1 };
Q_CONSTINIT static QBasicAtomicInt fatalWarningsCount = { -1 };
static bool isFatal(QtMsgType msgType) static bool isFatal(QtMsgType msgType)
{ {
if (msgType == QtFatalMsg) switch (msgType){
return true; case QtFatalMsg:
return true; // always fatal
if (msgType == QtCriticalMsg) { case QtCriticalMsg:
static QAtomicInt fatalCriticals = checked_var_value("QT_FATAL_CRITICALS"); return isFatalCountDown("QT_FATAL_CRITICALS", fatalCriticalsCount);
return is_fatal_count_down(fatalCriticals);
}
if (msgType == QtWarningMsg || msgType == QtCriticalMsg) { case QtWarningMsg:
static QAtomicInt fatalWarnings = checked_var_value("QT_FATAL_WARNINGS"); return isFatalCountDown("QT_FATAL_WARNINGS", fatalWarningsCount);
return is_fatal_count_down(fatalWarnings);
case QtDebugMsg:
case QtInfoMsg:
break; // never fatal
} }
return false; return false;