QSharedPointer thread safety?
-
Let's say I have this code
struct QuestDetails { QString adventurerId; QMap<QString,QVariant> questParams; SomeCustomType custom; } //running on main thread class GameManager { signals: ... void QuestStarted(const QSharedPointer<QuestDetails>& questDetails); private: void dosomething() { QMap<QString,QVariant> newQuestParams; newQuestParams["target"] = "Deckard"; newQuestParams["deadline"] = QDateTime::currentDateTime().addDays(1); auto questDetails = QSharedPointer<QuestDetails>(new QuestDetails); questDetails->adventurerId = "Cloud"; questDetails->questParams = newQuestParams; questDetails->custom = SomeCustomType("ABC", 123, "moredata"); emit QuestStarted(questDetails); } }; //running on thread #2 class Adventurer { public slots: void onQuestStarted(const QSharedPointer<QuestDetails>& questDetails) { qDebug() << questDetails->questParams; } }; //running on thread #3 class EvilSpy { void onQuestStarted(const QSharedPointer<QuestDetails>& questDetails) { QMap<QString,QVariant> modifiedMap = questDetails->questParams; modifiedMap["target"] = "Deckard's wife"; modifiedMap["extra-key"] = 123; qDebug() << "my modified map:" << modifiedMap; } };
Question 1: looking at just GameManager and Adventurer, is there anything wrong with this? The doc says "in general, a given QSharedPointer or QWeakPointer object cannot be accessed by multiple threads at the same time without synchronization." I don't want to use mutexes, I just want the QuestDetails to be deleted safely when all slots have finished handling it and reference counter drops to 0. Let's say Adventurer thread is doing a long calculation at the time the main thread is emitting QuestStarted. Is there a risk of the QSharedPointer counter dropping to 0 right after emitting and deleting the QuestDetails by the time the Adventurer thread is available and begins processing that signal?
assuming question 1 is safe...then Question 2: is there anything wrong with what the EvilSpy class is doing? From what I've read about implicit containers, the questParams QMap will be fully copied during the non-const call "modifiedMap["extra-key"] = 123". The original is never modified. According to the doc "implicit shared classes can safely be copied across threads, like any other value classes", so this is fine, right?
Keeping in mind, I have no intention of having my slots running on multiple threads modify the objects given as the signal argument. They are always accessed read-only. Is this enough to ensure thread safety?
-
The ref counting of a shared pointer is thread-safe so there can't be a double delete. Therefore what you're doing is correct as long as you don't modify your data after the emit.
-
@thierryhenry14 Signals/slots across threads use copies of parameters, even if you specify the parameters as references. So, it should be safe, as a copy of your shared pointer will be created as soon as you emit the signal.