2022-08-19 15:59:36 +02:00
|
|
|
// Copyright (C) 2016 The Qt Company Ltd.
|
|
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
2014-02-25 16:16:11 +01:00
|
|
|
|
|
|
|
|
#include "stringtable.h"
|
|
|
|
|
|
2014-06-22 11:57:21 -07:00
|
|
|
#include <utils/qtcassert.h>
|
2021-03-03 15:35:34 +01:00
|
|
|
#include <utils/runextensions.h>
|
2014-06-22 11:57:21 -07:00
|
|
|
|
2014-02-25 16:16:11 +01:00
|
|
|
#include <QDebug>
|
2021-03-09 23:01:39 +01:00
|
|
|
#include <QElapsedTimer>
|
2018-02-07 13:18:48 +01:00
|
|
|
#include <QMutex>
|
|
|
|
|
#include <QSet>
|
2014-02-25 16:16:11 +01:00
|
|
|
#include <QThreadPool>
|
2018-02-07 13:18:48 +01:00
|
|
|
#include <QTimer>
|
2014-02-25 16:16:11 +01:00
|
|
|
|
2021-04-28 15:59:18 +02:00
|
|
|
#ifdef WITH_TESTS
|
|
|
|
|
#include <extensionsystem/pluginmanager.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
2021-08-30 10:58:08 +02:00
|
|
|
namespace CppEditor::Internal {
|
2014-02-25 16:16:11 +01:00
|
|
|
|
|
|
|
|
enum {
|
|
|
|
|
GCTimeOut = 10 * 1000 // 10 seconds
|
|
|
|
|
};
|
|
|
|
|
|
2018-02-07 13:18:48 +01:00
|
|
|
enum {
|
|
|
|
|
DebugStringTable = 0
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class StringTablePrivate : public QObject
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
StringTablePrivate();
|
StringTable: Ensure only one GC() thread is running at a time
The possible issue with the current implementation is that
in theory many possible GC() are being executed in parallel.
In this case just one of them is really working and others
are waiting on the locked mutex (first line of the GC()
method). In such a scenario when a call
to StringTablePrivate::insert() is being executed from one
more thread, it may happen that the working GC() thread
is stopped (since m_stopGCRequested.fetchAndStoreAcquire(true)
was executed from insert()) and later the mutex lock may be
granted to the other awaiting GC() thread instead to the
thread which executes insert() method. In this unlikely
scenario the GC() thread won't be canceled and the lock
inside the insert() method may be locked for considerable
amount of time, what is not desired.
The goal of this patch is to resolve the possible issue above
and to simplify the code by eliminating the m_stopGCRequested
variable and make use of QFuture.cancel() / QFuture.isCanceled()
API instead. In addition, since we control now only one
possible thread that executes the GC(), there is no need for
future synchonizer anymore.
GC() function can't be run in parallel in different threads,
as the whole body of GC() is protected with mutex. This means
that whenever a new scheduled call to GC() is being executed,
this new call waits on the mentioned mutex at the beginning of GC().
So, instead of protecting the whole body of GC() with a mutex,
we ensure that the old call to GC() is already finished (if not,
we also cancel the old call) while preparing an asynchronous
call to start a new GC() from inside startGC() method.
Whenever we are calling the insert() method, we still protect the access
to m_strings with a mutex (as insert() is designed to be called
from different threads in parallel). Just after locking the mutex
we are canceling any possible ongoing call to GC(). After canceling
the GC() call, we are sure that no new call to GC() will be executed
until we unlock the mutex, so it's safe now to modify the m_string
data.
Change-Id: If72d0a6f98fb414c6c63117bc9baa667d17e1ffe
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
2021-03-09 18:24:04 +01:00
|
|
|
~StringTablePrivate() override { cancelAndWait(); }
|
2018-02-07 13:18:48 +01:00
|
|
|
|
StringTable: Ensure only one GC() thread is running at a time
The possible issue with the current implementation is that
in theory many possible GC() are being executed in parallel.
In this case just one of them is really working and others
are waiting on the locked mutex (first line of the GC()
method). In such a scenario when a call
to StringTablePrivate::insert() is being executed from one
more thread, it may happen that the working GC() thread
is stopped (since m_stopGCRequested.fetchAndStoreAcquire(true)
was executed from insert()) and later the mutex lock may be
granted to the other awaiting GC() thread instead to the
thread which executes insert() method. In this unlikely
scenario the GC() thread won't be canceled and the lock
inside the insert() method may be locked for considerable
amount of time, what is not desired.
The goal of this patch is to resolve the possible issue above
and to simplify the code by eliminating the m_stopGCRequested
variable and make use of QFuture.cancel() / QFuture.isCanceled()
API instead. In addition, since we control now only one
possible thread that executes the GC(), there is no need for
future synchonizer anymore.
GC() function can't be run in parallel in different threads,
as the whole body of GC() is protected with mutex. This means
that whenever a new scheduled call to GC() is being executed,
this new call waits on the mentioned mutex at the beginning of GC().
So, instead of protecting the whole body of GC() with a mutex,
we ensure that the old call to GC() is already finished (if not,
we also cancel the old call) while preparing an asynchronous
call to start a new GC() from inside startGC() method.
Whenever we are calling the insert() method, we still protect the access
to m_strings with a mutex (as insert() is designed to be called
from different threads in parallel). Just after locking the mutex
we are canceling any possible ongoing call to GC(). After canceling
the GC() call, we are sure that no new call to GC() will be executed
until we unlock the mutex, so it's safe now to modify the m_string
data.
Change-Id: If72d0a6f98fb414c6c63117bc9baa667d17e1ffe
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
2021-03-09 18:24:04 +01:00
|
|
|
void cancelAndWait();
|
2018-02-07 13:18:48 +01:00
|
|
|
QString insert(const QString &string);
|
StringTable: Ensure only one GC() thread is running at a time
The possible issue with the current implementation is that
in theory many possible GC() are being executed in parallel.
In this case just one of them is really working and others
are waiting on the locked mutex (first line of the GC()
method). In such a scenario when a call
to StringTablePrivate::insert() is being executed from one
more thread, it may happen that the working GC() thread
is stopped (since m_stopGCRequested.fetchAndStoreAcquire(true)
was executed from insert()) and later the mutex lock may be
granted to the other awaiting GC() thread instead to the
thread which executes insert() method. In this unlikely
scenario the GC() thread won't be canceled and the lock
inside the insert() method may be locked for considerable
amount of time, what is not desired.
The goal of this patch is to resolve the possible issue above
and to simplify the code by eliminating the m_stopGCRequested
variable and make use of QFuture.cancel() / QFuture.isCanceled()
API instead. In addition, since we control now only one
possible thread that executes the GC(), there is no need for
future synchonizer anymore.
GC() function can't be run in parallel in different threads,
as the whole body of GC() is protected with mutex. This means
that whenever a new scheduled call to GC() is being executed,
this new call waits on the mentioned mutex at the beginning of GC().
So, instead of protecting the whole body of GC() with a mutex,
we ensure that the old call to GC() is already finished (if not,
we also cancel the old call) while preparing an asynchronous
call to start a new GC() from inside startGC() method.
Whenever we are calling the insert() method, we still protect the access
to m_strings with a mutex (as insert() is designed to be called
from different threads in parallel). Just after locking the mutex
we are canceling any possible ongoing call to GC(). After canceling
the GC() call, we are sure that no new call to GC() will be executed
until we unlock the mutex, so it's safe now to modify the m_string
data.
Change-Id: If72d0a6f98fb414c6c63117bc9baa667d17e1ffe
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
2021-03-09 18:24:04 +01:00
|
|
|
void startGC();
|
|
|
|
|
void GC(QFutureInterface<void> &futureInterface);
|
2018-02-07 13:18:48 +01:00
|
|
|
|
StringTable: Ensure only one GC() thread is running at a time
The possible issue with the current implementation is that
in theory many possible GC() are being executed in parallel.
In this case just one of them is really working and others
are waiting on the locked mutex (first line of the GC()
method). In such a scenario when a call
to StringTablePrivate::insert() is being executed from one
more thread, it may happen that the working GC() thread
is stopped (since m_stopGCRequested.fetchAndStoreAcquire(true)
was executed from insert()) and later the mutex lock may be
granted to the other awaiting GC() thread instead to the
thread which executes insert() method. In this unlikely
scenario the GC() thread won't be canceled and the lock
inside the insert() method may be locked for considerable
amount of time, what is not desired.
The goal of this patch is to resolve the possible issue above
and to simplify the code by eliminating the m_stopGCRequested
variable and make use of QFuture.cancel() / QFuture.isCanceled()
API instead. In addition, since we control now only one
possible thread that executes the GC(), there is no need for
future synchonizer anymore.
GC() function can't be run in parallel in different threads,
as the whole body of GC() is protected with mutex. This means
that whenever a new scheduled call to GC() is being executed,
this new call waits on the mentioned mutex at the beginning of GC().
So, instead of protecting the whole body of GC() with a mutex,
we ensure that the old call to GC() is already finished (if not,
we also cancel the old call) while preparing an asynchronous
call to start a new GC() from inside startGC() method.
Whenever we are calling the insert() method, we still protect the access
to m_strings with a mutex (as insert() is designed to be called
from different threads in parallel). Just after locking the mutex
we are canceling any possible ongoing call to GC(). After canceling
the GC() call, we are sure that no new call to GC() will be executed
until we unlock the mutex, so it's safe now to modify the m_string
data.
Change-Id: If72d0a6f98fb414c6c63117bc9baa667d17e1ffe
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
2021-03-09 18:24:04 +01:00
|
|
|
QFuture<void> m_future;
|
|
|
|
|
QMutex m_lock;
|
2018-02-07 13:18:48 +01:00
|
|
|
QSet<QString> m_strings;
|
|
|
|
|
QTimer m_gcCountDown;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static StringTablePrivate *m_instance = nullptr;
|
|
|
|
|
|
|
|
|
|
StringTablePrivate::StringTablePrivate()
|
2014-02-25 16:16:11 +01:00
|
|
|
{
|
|
|
|
|
m_strings.reserve(1000);
|
|
|
|
|
|
2014-08-27 11:50:53 +02:00
|
|
|
m_gcCountDown.setObjectName(QLatin1String("StringTable::m_gcCountDown"));
|
2014-02-25 16:16:11 +01:00
|
|
|
m_gcCountDown.setSingleShot(true);
|
|
|
|
|
m_gcCountDown.setInterval(GCTimeOut);
|
2018-02-07 13:18:48 +01:00
|
|
|
connect(&m_gcCountDown, &QTimer::timeout, this, &StringTablePrivate::startGC);
|
2014-02-25 16:16:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString StringTable::insert(const QString &string)
|
2018-02-07 13:18:48 +01:00
|
|
|
{
|
|
|
|
|
return m_instance->insert(string);
|
|
|
|
|
}
|
|
|
|
|
|
StringTable: Ensure only one GC() thread is running at a time
The possible issue with the current implementation is that
in theory many possible GC() are being executed in parallel.
In this case just one of them is really working and others
are waiting on the locked mutex (first line of the GC()
method). In such a scenario when a call
to StringTablePrivate::insert() is being executed from one
more thread, it may happen that the working GC() thread
is stopped (since m_stopGCRequested.fetchAndStoreAcquire(true)
was executed from insert()) and later the mutex lock may be
granted to the other awaiting GC() thread instead to the
thread which executes insert() method. In this unlikely
scenario the GC() thread won't be canceled and the lock
inside the insert() method may be locked for considerable
amount of time, what is not desired.
The goal of this patch is to resolve the possible issue above
and to simplify the code by eliminating the m_stopGCRequested
variable and make use of QFuture.cancel() / QFuture.isCanceled()
API instead. In addition, since we control now only one
possible thread that executes the GC(), there is no need for
future synchonizer anymore.
GC() function can't be run in parallel in different threads,
as the whole body of GC() is protected with mutex. This means
that whenever a new scheduled call to GC() is being executed,
this new call waits on the mentioned mutex at the beginning of GC().
So, instead of protecting the whole body of GC() with a mutex,
we ensure that the old call to GC() is already finished (if not,
we also cancel the old call) while preparing an asynchronous
call to start a new GC() from inside startGC() method.
Whenever we are calling the insert() method, we still protect the access
to m_strings with a mutex (as insert() is designed to be called
from different threads in parallel). Just after locking the mutex
we are canceling any possible ongoing call to GC(). After canceling
the GC() call, we are sure that no new call to GC() will be executed
until we unlock the mutex, so it's safe now to modify the m_string
data.
Change-Id: If72d0a6f98fb414c6c63117bc9baa667d17e1ffe
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
2021-03-09 18:24:04 +01:00
|
|
|
void StringTablePrivate::cancelAndWait()
|
|
|
|
|
{
|
|
|
|
|
if (!m_future.isRunning())
|
|
|
|
|
return;
|
|
|
|
|
m_future.cancel();
|
|
|
|
|
m_future.waitForFinished();
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-07 13:18:48 +01:00
|
|
|
QString StringTablePrivate::insert(const QString &string)
|
2014-02-25 16:16:11 +01:00
|
|
|
{
|
|
|
|
|
if (string.isEmpty())
|
|
|
|
|
return string;
|
|
|
|
|
|
StringTable: Ensure only one GC() thread is running at a time
The possible issue with the current implementation is that
in theory many possible GC() are being executed in parallel.
In this case just one of them is really working and others
are waiting on the locked mutex (first line of the GC()
method). In such a scenario when a call
to StringTablePrivate::insert() is being executed from one
more thread, it may happen that the working GC() thread
is stopped (since m_stopGCRequested.fetchAndStoreAcquire(true)
was executed from insert()) and later the mutex lock may be
granted to the other awaiting GC() thread instead to the
thread which executes insert() method. In this unlikely
scenario the GC() thread won't be canceled and the lock
inside the insert() method may be locked for considerable
amount of time, what is not desired.
The goal of this patch is to resolve the possible issue above
and to simplify the code by eliminating the m_stopGCRequested
variable and make use of QFuture.cancel() / QFuture.isCanceled()
API instead. In addition, since we control now only one
possible thread that executes the GC(), there is no need for
future synchonizer anymore.
GC() function can't be run in parallel in different threads,
as the whole body of GC() is protected with mutex. This means
that whenever a new scheduled call to GC() is being executed,
this new call waits on the mentioned mutex at the beginning of GC().
So, instead of protecting the whole body of GC() with a mutex,
we ensure that the old call to GC() is already finished (if not,
we also cancel the old call) while preparing an asynchronous
call to start a new GC() from inside startGC() method.
Whenever we are calling the insert() method, we still protect the access
to m_strings with a mutex (as insert() is designed to be called
from different threads in parallel). Just after locking the mutex
we are canceling any possible ongoing call to GC(). After canceling
the GC() call, we are sure that no new call to GC() will be executed
until we unlock the mutex, so it's safe now to modify the m_string
data.
Change-Id: If72d0a6f98fb414c6c63117bc9baa667d17e1ffe
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
2021-03-09 18:24:04 +01:00
|
|
|
QMutexLocker locker(&m_lock);
|
|
|
|
|
// From this point of time any possible new call to startGC() will be held until
|
|
|
|
|
// we finish this function. So we are sure that after canceling the running GC() method now,
|
|
|
|
|
// no new call to GC() will be executed until we finish this function.
|
|
|
|
|
cancelAndWait();
|
|
|
|
|
// A possibly running GC() thread already finished, so it's safe to modify m_strings from
|
|
|
|
|
// now until we unlock the mutex.
|
|
|
|
|
return *m_strings.insert(string);
|
|
|
|
|
}
|
2014-02-25 16:16:11 +01:00
|
|
|
|
StringTable: Ensure only one GC() thread is running at a time
The possible issue with the current implementation is that
in theory many possible GC() are being executed in parallel.
In this case just one of them is really working and others
are waiting on the locked mutex (first line of the GC()
method). In such a scenario when a call
to StringTablePrivate::insert() is being executed from one
more thread, it may happen that the working GC() thread
is stopped (since m_stopGCRequested.fetchAndStoreAcquire(true)
was executed from insert()) and later the mutex lock may be
granted to the other awaiting GC() thread instead to the
thread which executes insert() method. In this unlikely
scenario the GC() thread won't be canceled and the lock
inside the insert() method may be locked for considerable
amount of time, what is not desired.
The goal of this patch is to resolve the possible issue above
and to simplify the code by eliminating the m_stopGCRequested
variable and make use of QFuture.cancel() / QFuture.isCanceled()
API instead. In addition, since we control now only one
possible thread that executes the GC(), there is no need for
future synchonizer anymore.
GC() function can't be run in parallel in different threads,
as the whole body of GC() is protected with mutex. This means
that whenever a new scheduled call to GC() is being executed,
this new call waits on the mentioned mutex at the beginning of GC().
So, instead of protecting the whole body of GC() with a mutex,
we ensure that the old call to GC() is already finished (if not,
we also cancel the old call) while preparing an asynchronous
call to start a new GC() from inside startGC() method.
Whenever we are calling the insert() method, we still protect the access
to m_strings with a mutex (as insert() is designed to be called
from different threads in parallel). Just after locking the mutex
we are canceling any possible ongoing call to GC(). After canceling
the GC() call, we are sure that no new call to GC() will be executed
until we unlock the mutex, so it's safe now to modify the m_string
data.
Change-Id: If72d0a6f98fb414c6c63117bc9baa667d17e1ffe
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
2021-03-09 18:24:04 +01:00
|
|
|
void StringTablePrivate::startGC()
|
|
|
|
|
{
|
2014-02-25 16:16:11 +01:00
|
|
|
QMutexLocker locker(&m_lock);
|
StringTable: Ensure only one GC() thread is running at a time
The possible issue with the current implementation is that
in theory many possible GC() are being executed in parallel.
In this case just one of them is really working and others
are waiting on the locked mutex (first line of the GC()
method). In such a scenario when a call
to StringTablePrivate::insert() is being executed from one
more thread, it may happen that the working GC() thread
is stopped (since m_stopGCRequested.fetchAndStoreAcquire(true)
was executed from insert()) and later the mutex lock may be
granted to the other awaiting GC() thread instead to the
thread which executes insert() method. In this unlikely
scenario the GC() thread won't be canceled and the lock
inside the insert() method may be locked for considerable
amount of time, what is not desired.
The goal of this patch is to resolve the possible issue above
and to simplify the code by eliminating the m_stopGCRequested
variable and make use of QFuture.cancel() / QFuture.isCanceled()
API instead. In addition, since we control now only one
possible thread that executes the GC(), there is no need for
future synchonizer anymore.
GC() function can't be run in parallel in different threads,
as the whole body of GC() is protected with mutex. This means
that whenever a new scheduled call to GC() is being executed,
this new call waits on the mentioned mutex at the beginning of GC().
So, instead of protecting the whole body of GC() with a mutex,
we ensure that the old call to GC() is already finished (if not,
we also cancel the old call) while preparing an asynchronous
call to start a new GC() from inside startGC() method.
Whenever we are calling the insert() method, we still protect the access
to m_strings with a mutex (as insert() is designed to be called
from different threads in parallel). Just after locking the mutex
we are canceling any possible ongoing call to GC(). After canceling
the GC() call, we are sure that no new call to GC() will be executed
until we unlock the mutex, so it's safe now to modify the m_string
data.
Change-Id: If72d0a6f98fb414c6c63117bc9baa667d17e1ffe
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
2021-03-09 18:24:04 +01:00
|
|
|
cancelAndWait();
|
|
|
|
|
m_future = Utils::runAsync(&StringTablePrivate::GC, this);
|
2014-02-25 16:16:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void StringTable::scheduleGC()
|
|
|
|
|
{
|
2020-11-11 16:34:39 +01:00
|
|
|
QMetaObject::invokeMethod(&m_instance->m_gcCountDown, QOverload<>::of(&QTimer::start),
|
|
|
|
|
Qt::QueuedConnection);
|
2014-02-25 16:16:11 +01:00
|
|
|
}
|
|
|
|
|
|
2020-02-04 17:10:52 +01:00
|
|
|
StringTable::StringTable()
|
2014-02-25 16:16:11 +01:00
|
|
|
{
|
2018-02-07 13:18:48 +01:00
|
|
|
m_instance = new StringTablePrivate;
|
2014-02-25 16:16:11 +01:00
|
|
|
}
|
|
|
|
|
|
2020-02-04 17:10:52 +01:00
|
|
|
StringTable::~StringTable()
|
2018-02-07 13:18:48 +01:00
|
|
|
{
|
|
|
|
|
delete m_instance;
|
|
|
|
|
m_instance = nullptr;
|
|
|
|
|
}
|
2014-02-25 16:16:11 +01:00
|
|
|
|
2014-06-22 11:57:21 -07:00
|
|
|
static inline bool isQStringInUse(const QString &string)
|
2014-05-28 15:51:31 -04:00
|
|
|
{
|
2020-09-15 15:33:13 +02:00
|
|
|
auto data_ptr = const_cast<QString&>(string).data_ptr();
|
|
|
|
|
return data_ptr->isShared() || !data_ptr->isMutable() /* QStringLiteral ? */;
|
2014-05-28 15:51:31 -04:00
|
|
|
}
|
|
|
|
|
|
StringTable: Ensure only one GC() thread is running at a time
The possible issue with the current implementation is that
in theory many possible GC() are being executed in parallel.
In this case just one of them is really working and others
are waiting on the locked mutex (first line of the GC()
method). In such a scenario when a call
to StringTablePrivate::insert() is being executed from one
more thread, it may happen that the working GC() thread
is stopped (since m_stopGCRequested.fetchAndStoreAcquire(true)
was executed from insert()) and later the mutex lock may be
granted to the other awaiting GC() thread instead to the
thread which executes insert() method. In this unlikely
scenario the GC() thread won't be canceled and the lock
inside the insert() method may be locked for considerable
amount of time, what is not desired.
The goal of this patch is to resolve the possible issue above
and to simplify the code by eliminating the m_stopGCRequested
variable and make use of QFuture.cancel() / QFuture.isCanceled()
API instead. In addition, since we control now only one
possible thread that executes the GC(), there is no need for
future synchonizer anymore.
GC() function can't be run in parallel in different threads,
as the whole body of GC() is protected with mutex. This means
that whenever a new scheduled call to GC() is being executed,
this new call waits on the mentioned mutex at the beginning of GC().
So, instead of protecting the whole body of GC() with a mutex,
we ensure that the old call to GC() is already finished (if not,
we also cancel the old call) while preparing an asynchronous
call to start a new GC() from inside startGC() method.
Whenever we are calling the insert() method, we still protect the access
to m_strings with a mutex (as insert() is designed to be called
from different threads in parallel). Just after locking the mutex
we are canceling any possible ongoing call to GC(). After canceling
the GC() call, we are sure that no new call to GC() will be executed
until we unlock the mutex, so it's safe now to modify the m_string
data.
Change-Id: If72d0a6f98fb414c6c63117bc9baa667d17e1ffe
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
2021-03-09 18:24:04 +01:00
|
|
|
void StringTablePrivate::GC(QFutureInterface<void> &futureInterface)
|
2014-02-25 16:16:11 +01:00
|
|
|
{
|
2021-04-28 15:59:18 +02:00
|
|
|
#ifdef WITH_TESTS
|
|
|
|
|
if (ExtensionSystem::PluginManager::isScenarioRunning("TestStringTable")) {
|
|
|
|
|
if (ExtensionSystem::PluginManager::finishScenario())
|
2022-10-10 15:59:26 +02:00
|
|
|
QThread::sleep(5);
|
2021-04-28 15:59:18 +02:00
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2014-02-25 16:16:11 +01:00
|
|
|
int initialSize = 0;
|
2021-03-09 23:01:39 +01:00
|
|
|
QElapsedTimer timer;
|
2014-02-25 16:16:11 +01:00
|
|
|
if (DebugStringTable) {
|
|
|
|
|
initialSize = m_strings.size();
|
2021-03-09 23:01:39 +01:00
|
|
|
timer.start();
|
2014-02-25 16:16:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Collect all QStrings which have refcount 1. (One reference in m_strings and nowhere else.)
|
|
|
|
|
for (QSet<QString>::iterator i = m_strings.begin(); i != m_strings.end();) {
|
StringTable: Ensure only one GC() thread is running at a time
The possible issue with the current implementation is that
in theory many possible GC() are being executed in parallel.
In this case just one of them is really working and others
are waiting on the locked mutex (first line of the GC()
method). In such a scenario when a call
to StringTablePrivate::insert() is being executed from one
more thread, it may happen that the working GC() thread
is stopped (since m_stopGCRequested.fetchAndStoreAcquire(true)
was executed from insert()) and later the mutex lock may be
granted to the other awaiting GC() thread instead to the
thread which executes insert() method. In this unlikely
scenario the GC() thread won't be canceled and the lock
inside the insert() method may be locked for considerable
amount of time, what is not desired.
The goal of this patch is to resolve the possible issue above
and to simplify the code by eliminating the m_stopGCRequested
variable and make use of QFuture.cancel() / QFuture.isCanceled()
API instead. In addition, since we control now only one
possible thread that executes the GC(), there is no need for
future synchonizer anymore.
GC() function can't be run in parallel in different threads,
as the whole body of GC() is protected with mutex. This means
that whenever a new scheduled call to GC() is being executed,
this new call waits on the mentioned mutex at the beginning of GC().
So, instead of protecting the whole body of GC() with a mutex,
we ensure that the old call to GC() is already finished (if not,
we also cancel the old call) while preparing an asynchronous
call to start a new GC() from inside startGC() method.
Whenever we are calling the insert() method, we still protect the access
to m_strings with a mutex (as insert() is designed to be called
from different threads in parallel). Just after locking the mutex
we are canceling any possible ongoing call to GC(). After canceling
the GC() call, we are sure that no new call to GC() will be executed
until we unlock the mutex, so it's safe now to modify the m_string
data.
Change-Id: If72d0a6f98fb414c6c63117bc9baa667d17e1ffe
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
2021-03-09 18:24:04 +01:00
|
|
|
if (futureInterface.isCanceled())
|
2014-02-25 16:16:11 +01:00
|
|
|
return;
|
|
|
|
|
|
2014-06-22 11:57:21 -07:00
|
|
|
if (!isQStringInUse(*i))
|
2014-02-25 16:16:11 +01:00
|
|
|
i = m_strings.erase(i);
|
|
|
|
|
else
|
|
|
|
|
++i;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (DebugStringTable) {
|
|
|
|
|
const int currentSize = m_strings.size();
|
|
|
|
|
qDebug() << "StringTable::GC removed" << initialSize - currentSize
|
2021-03-09 23:01:39 +01:00
|
|
|
<< "strings in" << timer.elapsed() << "ms, size is now" << currentSize;
|
2014-02-25 16:16:11 +01:00
|
|
|
}
|
|
|
|
|
}
|
2021-08-30 10:58:08 +02:00
|
|
|
|
|
|
|
|
} // CppEditor::Internal
|