mirror of
https://github.com/qTox/qTox.git
synced 2024-03-22 14:00:36 +08:00
refactor: properly lock against Core thread
We we're calling toxav_* functions without synchronizing to any of the Tox threads. Additionally remove the call timeout, it creates timers from different threads, which causes errors.
This commit is contained in:
parent
29f659c659
commit
98cfe9838f
|
@ -628,7 +628,7 @@ ToxCorePtr Core::makeToxCore(const QByteArray& savedata, const ICoreSettings* co
|
||||||
|
|
||||||
// toxcore is successfully created, create toxav
|
// toxcore is successfully created, create toxav
|
||||||
// TODO(sudden6): don't create CoreAv here, Core should be usable without CoreAV
|
// TODO(sudden6): don't create CoreAv here, Core should be usable without CoreAV
|
||||||
core->av = CoreAV::makeCoreAV(core->tox.get());
|
core->av = CoreAV::makeCoreAV(core->tox.get(), core->coreLoopLock);
|
||||||
if (!core->av) {
|
if (!core->av) {
|
||||||
qCritical() << "Toxav failed to start";
|
qCritical() << "Toxav failed to start";
|
||||||
if (err) {
|
if (err) {
|
||||||
|
|
|
@ -68,11 +68,12 @@
|
||||||
* deadlock.
|
* deadlock.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
CoreAV::CoreAV(std::unique_ptr<ToxAV, ToxAVDeleter> toxav)
|
CoreAV::CoreAV(std::unique_ptr<ToxAV, ToxAVDeleter> toxav, QMutex& toxCoreLock)
|
||||||
: audio{nullptr}
|
: audio{nullptr}
|
||||||
, toxav{std::move(toxav)}
|
, toxav{std::move(toxav)}
|
||||||
, coreavThread{new QThread{this}}
|
, coreavThread{new QThread{this}}
|
||||||
, iterateTimer{new QTimer{this}}
|
, iterateTimer{new QTimer{this}}
|
||||||
|
, coreLock{toxCoreLock}
|
||||||
{
|
{
|
||||||
assert(coreavThread);
|
assert(coreavThread);
|
||||||
assert(iterateTimer);
|
assert(iterateTimer);
|
||||||
|
@ -104,7 +105,7 @@ void CoreAV::connectCallbacks(ToxAV& toxav)
|
||||||
* @param core pointer to the Tox instance
|
* @param core pointer to the Tox instance
|
||||||
* @return CoreAV instance on success, {} on failure
|
* @return CoreAV instance on success, {} on failure
|
||||||
*/
|
*/
|
||||||
CoreAV::CoreAVPtr CoreAV::makeCoreAV(Tox* core)
|
CoreAV::CoreAVPtr CoreAV::makeCoreAV(Tox* core, QMutex &toxCoreLock)
|
||||||
{
|
{
|
||||||
TOXAV_ERR_NEW err;
|
TOXAV_ERR_NEW err;
|
||||||
std::unique_ptr<ToxAV, ToxAVDeleter> toxav{toxav_new(core, &err)};
|
std::unique_ptr<ToxAV, ToxAVDeleter> toxav{toxav_new(core, &err)};
|
||||||
|
@ -124,7 +125,7 @@ CoreAV::CoreAVPtr CoreAV::makeCoreAV(Tox* core)
|
||||||
|
|
||||||
assert(toxav != nullptr);
|
assert(toxav != nullptr);
|
||||||
|
|
||||||
return CoreAVPtr{new CoreAV{std::move(toxav)}};
|
return CoreAVPtr{new CoreAV{std::move(toxav), toxCoreLock}};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -235,9 +236,7 @@ bool CoreAV::isCallVideoEnabled(const Friend* f) const
|
||||||
bool CoreAV::answerCall(uint32_t friendNum, bool video)
|
bool CoreAV::answerCall(uint32_t friendNum, bool video)
|
||||||
{
|
{
|
||||||
QMutexLocker locker{&callsLock};
|
QMutexLocker locker{&callsLock};
|
||||||
|
QMutexLocker coreLocker{&coreLock};
|
||||||
// This callback should come from the Core thread
|
|
||||||
assert(QThread::currentThread() != coreavThread.get());
|
|
||||||
|
|
||||||
qDebug() << QString("Answering call %1").arg(friendNum);
|
qDebug() << QString("Answering call %1").arg(friendNum);
|
||||||
auto it = calls.find(friendNum);
|
auto it = calls.find(friendNum);
|
||||||
|
@ -260,9 +259,7 @@ bool CoreAV::answerCall(uint32_t friendNum, bool video)
|
||||||
bool CoreAV::startCall(uint32_t friendNum, bool video)
|
bool CoreAV::startCall(uint32_t friendNum, bool video)
|
||||||
{
|
{
|
||||||
QMutexLocker locker{&callsLock};
|
QMutexLocker locker{&callsLock};
|
||||||
|
QMutexLocker coreLocker{&coreLock};
|
||||||
// This callback should come from the Core thread
|
|
||||||
assert(QThread::currentThread() != coreavThread.get());
|
|
||||||
|
|
||||||
qDebug() << QString("Starting call with %1").arg(friendNum);
|
qDebug() << QString("Starting call with %1").arg(friendNum);
|
||||||
auto it = calls.find(friendNum);
|
auto it = calls.find(friendNum);
|
||||||
|
@ -280,17 +277,14 @@ bool CoreAV::startCall(uint32_t friendNum, bool video)
|
||||||
assert(audio != nullptr);
|
assert(audio != nullptr);
|
||||||
ToxFriendCallPtr call = ToxFriendCallPtr(new ToxFriendCall(friendNum, video, *this, *audio));
|
ToxFriendCallPtr call = ToxFriendCallPtr(new ToxFriendCall(friendNum, video, *this, *audio));
|
||||||
assert(call != nullptr);
|
assert(call != nullptr);
|
||||||
auto ret = calls.emplace(friendNum, std::move(call));
|
calls.emplace(friendNum, std::move(call));
|
||||||
ret.first->second->startTimeout(friendNum);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CoreAV::cancelCall(uint32_t friendNum)
|
bool CoreAV::cancelCall(uint32_t friendNum)
|
||||||
{
|
{
|
||||||
QMutexLocker locker{&callsLock};
|
QMutexLocker locker{&callsLock};
|
||||||
|
QMutexLocker coreLocker{&coreLock};
|
||||||
// This callback should come from the Core thread
|
|
||||||
assert(QThread::currentThread() != coreavThread.get());
|
|
||||||
|
|
||||||
qDebug() << QString("Cancelling call with %1").arg(friendNum);
|
qDebug() << QString("Cancelling call with %1").arg(friendNum);
|
||||||
if (!toxav_call_control(toxav.get(), friendNum, TOXAV_CALL_CONTROL_CANCEL, nullptr)) {
|
if (!toxav_call_control(toxav.get(), friendNum, TOXAV_CALL_CONTROL_CANCEL, nullptr)) {
|
||||||
|
@ -380,6 +374,7 @@ void CoreAV::sendCallVideo(uint32_t callId, std::shared_ptr<VideoFrame> vframe)
|
||||||
|
|
||||||
if (call.getNullVideoBitrate()) {
|
if (call.getNullVideoBitrate()) {
|
||||||
qDebug() << "Restarting video stream to friend" << callId;
|
qDebug() << "Restarting video stream to friend" << callId;
|
||||||
|
QMutexLocker coreLocker{&coreLock};
|
||||||
toxav_video_set_bit_rate(toxav.get(), callId, VIDEO_DEFAULT_BITRATE, nullptr);
|
toxav_video_set_bit_rate(toxav.get(), callId, VIDEO_DEFAULT_BITRATE, nullptr);
|
||||||
call.setNullVideoBitrate(false);
|
call.setNullVideoBitrate(false);
|
||||||
}
|
}
|
||||||
|
@ -756,7 +751,6 @@ void CoreAV::stateCallback(ToxAV* toxav, uint32_t friendNum, uint32_t state, voi
|
||||||
} else {
|
} else {
|
||||||
// If our state was null, we started the call and were still ringing
|
// If our state was null, we started the call and were still ringing
|
||||||
if (!call.getState() && state) {
|
if (!call.getState() && state) {
|
||||||
call.stopTimeout();
|
|
||||||
call.setActive(true);
|
call.setActive(true);
|
||||||
emit self->avStart(friendNum, call.getVideoEnabled());
|
emit self->avStart(friendNum, call.getVideoEnabled());
|
||||||
} else if ((call.getState() & TOXAV_FRIEND_CALL_STATE_SENDING_V)
|
} else if ((call.getState() & TOXAV_FRIEND_CALL_STATE_SENDING_V)
|
||||||
|
|
|
@ -46,7 +46,7 @@ class CoreAV : public QObject
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using CoreAVPtr = std::unique_ptr<CoreAV>;
|
using CoreAVPtr = std::unique_ptr<CoreAV>;
|
||||||
static CoreAVPtr makeCoreAV(Tox* core);
|
static CoreAVPtr makeCoreAV(Tox* core, QMutex& coreLock);
|
||||||
|
|
||||||
void setAudio(IAudioControl& newAudio);
|
void setAudio(IAudioControl& newAudio);
|
||||||
IAudioControl* getAudio();
|
IAudioControl* getAudio();
|
||||||
|
@ -112,7 +112,7 @@ private:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit CoreAV(std::unique_ptr<ToxAV, ToxAVDeleter> tox);
|
explicit CoreAV(std::unique_ptr<ToxAV, ToxAVDeleter> tox, QMutex &toxCoreLock);
|
||||||
void connectCallbacks(ToxAV& toxav);
|
void connectCallbacks(ToxAV& toxav);
|
||||||
|
|
||||||
void process();
|
void process();
|
||||||
|
@ -149,6 +149,13 @@ private:
|
||||||
|
|
||||||
// protect 'calls' and 'groupCalls' from being modified by ToxAV and Tox threads
|
// protect 'calls' and 'groupCalls' from being modified by ToxAV and Tox threads
|
||||||
mutable QMutex callsLock{QMutex::Recursive};
|
mutable QMutex callsLock{QMutex::Recursive};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief needed to synchronize with the Core thread, some toxav_* functions
|
||||||
|
* must not execute at the same time as tox_iterate()
|
||||||
|
* @note This must be a recursive mutex as we're going to lock it in callbacks
|
||||||
|
*/
|
||||||
|
QMutex& coreLock;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // COREAV_H
|
#endif // COREAV_H
|
||||||
|
|
|
@ -190,29 +190,6 @@ void ToxFriendCall::onAudioSinkInvalidated()
|
||||||
sink = std::move(newSink);
|
sink = std::move(newSink);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ToxFriendCall::startTimeout(uint32_t callId)
|
|
||||||
{
|
|
||||||
if (!timeoutTimer) {
|
|
||||||
timeoutTimer = std::unique_ptr<QTimer>{new QTimer{}};
|
|
||||||
// We might move, so we need copies of members. CoreAV won't move while we're alive
|
|
||||||
CoreAV* avCopy = av;
|
|
||||||
QObject::connect(timeoutTimer.get(), &QTimer::timeout,
|
|
||||||
[avCopy, callId]() { avCopy->timeoutCall(callId); });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!timeoutTimer->isActive())
|
|
||||||
timeoutTimer->start(CALL_TIMEOUT);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ToxFriendCall::stopTimeout()
|
|
||||||
{
|
|
||||||
if (!timeoutTimer)
|
|
||||||
return;
|
|
||||||
|
|
||||||
timeoutTimer->stop();
|
|
||||||
timeoutTimer.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
TOXAV_FRIEND_CALL_STATE ToxFriendCall::getState() const
|
TOXAV_FRIEND_CALL_STATE ToxFriendCall::getState() const
|
||||||
{
|
{
|
||||||
return state;
|
return state;
|
||||||
|
|
|
@ -95,17 +95,11 @@ public:
|
||||||
ToxFriendCall& operator=(ToxFriendCall&& other) = delete;
|
ToxFriendCall& operator=(ToxFriendCall&& other) = delete;
|
||||||
~ToxFriendCall();
|
~ToxFriendCall();
|
||||||
|
|
||||||
void startTimeout(uint32_t callId);
|
|
||||||
void stopTimeout();
|
|
||||||
|
|
||||||
TOXAV_FRIEND_CALL_STATE getState() const;
|
TOXAV_FRIEND_CALL_STATE getState() const;
|
||||||
void setState(const TOXAV_FRIEND_CALL_STATE& value);
|
void setState(const TOXAV_FRIEND_CALL_STATE& value);
|
||||||
|
|
||||||
void playAudioBuffer(const int16_t* data, int samples, unsigned channels, int sampleRate) const;
|
void playAudioBuffer(const int16_t* data, int samples, unsigned channels, int sampleRate) const;
|
||||||
|
|
||||||
protected:
|
|
||||||
std::unique_ptr<QTimer> timeoutTimer;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QMetaObject::Connection audioSinkInvalid;
|
QMetaObject::Connection audioSinkInvalid;
|
||||||
void onAudioSourceInvalidated();
|
void onAudioSourceInvalidated();
|
||||||
|
@ -113,7 +107,6 @@ private:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TOXAV_FRIEND_CALL_STATE state{TOXAV_FRIEND_CALL_STATE_NONE};
|
TOXAV_FRIEND_CALL_STATE state{TOXAV_FRIEND_CALL_STATE_NONE};
|
||||||
static constexpr int CALL_TIMEOUT = 45000;
|
|
||||||
std::unique_ptr<IAudioSink> sink = nullptr;
|
std::unique_ptr<IAudioSink> sink = nullptr;
|
||||||
uint32_t friendId;
|
uint32_t friendId;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue
Block a user