mirror of
https://github.com/qTox/qTox.git
synced 2024-03-22 14:00:36 +08:00
Use actual mutexes for CameraSource
That homemade spinlock thing was insane, VTune says we were spending 1.5s spinning for no reason when openning a device for example
This commit is contained in:
parent
477554ffba
commit
6dc91b156a
|
@ -395,6 +395,9 @@ void CoreAV::callCallback(ToxAV* toxav, uint32_t friendNum, bool audio, bool vid
|
||||||
|
|
||||||
if (self->calls.contains(friendNum))
|
if (self->calls.contains(friendNum))
|
||||||
{
|
{
|
||||||
|
/// NOTE: Hanging up from a callback is supposed to be UB,
|
||||||
|
/// but since currently the toxav callbacks are fired from the toxcore thread,
|
||||||
|
/// we'll always reach this point through a non-blocking queud connection, so not in the callback.
|
||||||
qWarning() << QString("Rejecting call invite from %1, we're already in that call!").arg(friendNum);
|
qWarning() << QString("Rejecting call invite from %1, we're already in that call!").arg(friendNum);
|
||||||
toxav_call_control(toxav, friendNum, TOXAV_CALL_CONTROL_CANCEL, nullptr);
|
toxav_call_control(toxav, friendNum, TOXAV_CALL_CONTROL_CANCEL, nullptr);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -37,7 +37,6 @@ CameraSource* CameraSource::instance{nullptr};
|
||||||
CameraSource::CameraSource()
|
CameraSource::CameraSource()
|
||||||
: deviceName{"none"}, device{nullptr}, mode(VideoMode{0,0,0}),
|
: deviceName{"none"}, device{nullptr}, mode(VideoMode{0,0,0}),
|
||||||
cctx{nullptr}, cctxOrig{nullptr}, videoStreamIndex{-1},
|
cctx{nullptr}, cctxOrig{nullptr}, videoStreamIndex{-1},
|
||||||
biglock{false}, freelistLock{false},
|
|
||||||
_isOpen{false}, subscriptions{0}
|
_isOpen{false}, subscriptions{0}
|
||||||
{
|
{
|
||||||
subscriptions = 0;
|
subscriptions = 0;
|
||||||
|
@ -73,17 +72,10 @@ void CameraSource::open(const QString deviceName)
|
||||||
|
|
||||||
void CameraSource::open(const QString DeviceName, VideoMode Mode)
|
void CameraSource::open(const QString DeviceName, VideoMode Mode)
|
||||||
{
|
{
|
||||||
{
|
QMutexLocker l{&biglock};
|
||||||
bool expected = false;
|
|
||||||
while (!biglock.compare_exchange_weak(expected, true))
|
|
||||||
expected = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (DeviceName == deviceName && Mode == mode)
|
if (DeviceName == deviceName && Mode == mode)
|
||||||
{
|
|
||||||
biglock = false;
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
if (subscriptions)
|
if (subscriptions)
|
||||||
closeDevice();
|
closeDevice();
|
||||||
|
@ -94,8 +86,6 @@ void CameraSource::open(const QString DeviceName, VideoMode Mode)
|
||||||
|
|
||||||
if (subscriptions && _isOpen)
|
if (subscriptions && _isOpen)
|
||||||
openDevice();
|
openDevice();
|
||||||
|
|
||||||
biglock = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CameraSource::close()
|
void CameraSource::close()
|
||||||
|
@ -110,18 +100,10 @@ bool CameraSource::isOpen()
|
||||||
|
|
||||||
CameraSource::~CameraSource()
|
CameraSource::~CameraSource()
|
||||||
{
|
{
|
||||||
// Fast lock, in case our stream thread is running
|
QMutexLocker l{&biglock};
|
||||||
{
|
|
||||||
bool expected = false;
|
|
||||||
while (!biglock.compare_exchange_weak(expected, true))
|
|
||||||
expected = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_isOpen)
|
if (!_isOpen)
|
||||||
{
|
|
||||||
biglock = false;
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
// Free all remaining VideoFrame
|
// Free all remaining VideoFrame
|
||||||
// Locking must be done precisely this way to avoid races
|
// Locking must be done precisely this way to avoid races
|
||||||
|
@ -144,7 +126,7 @@ CameraSource::~CameraSource()
|
||||||
device = nullptr;
|
device = nullptr;
|
||||||
// Memfence so the stream thread sees a nullptr device
|
// Memfence so the stream thread sees a nullptr device
|
||||||
std::atomic_thread_fence(std::memory_order_release);
|
std::atomic_thread_fence(std::memory_order_release);
|
||||||
biglock=false;
|
l.unlock();
|
||||||
|
|
||||||
// Synchronize with our stream thread
|
// Synchronize with our stream thread
|
||||||
while (streamFuture.isRunning())
|
while (streamFuture.isRunning())
|
||||||
|
@ -153,24 +135,17 @@ CameraSource::~CameraSource()
|
||||||
|
|
||||||
bool CameraSource::subscribe()
|
bool CameraSource::subscribe()
|
||||||
{
|
{
|
||||||
// Fast lock
|
QMutexLocker l{&biglock};
|
||||||
{
|
|
||||||
bool expected = false;
|
|
||||||
while (!biglock.compare_exchange_weak(expected, true))
|
|
||||||
expected = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_isOpen)
|
if (!_isOpen)
|
||||||
{
|
{
|
||||||
++subscriptions;
|
++subscriptions;
|
||||||
biglock = false;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (openDevice())
|
if (openDevice())
|
||||||
{
|
{
|
||||||
++subscriptions;
|
++subscriptions;
|
||||||
biglock = false;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -181,7 +156,6 @@ bool CameraSource::subscribe()
|
||||||
videoStreamIndex = -1;
|
videoStreamIndex = -1;
|
||||||
// Memfence so the stream thread sees a nullptr device
|
// Memfence so the stream thread sees a nullptr device
|
||||||
std::atomic_thread_fence(std::memory_order_release);
|
std::atomic_thread_fence(std::memory_order_release);
|
||||||
biglock = false;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,31 +163,24 @@ bool CameraSource::subscribe()
|
||||||
|
|
||||||
void CameraSource::unsubscribe()
|
void CameraSource::unsubscribe()
|
||||||
{
|
{
|
||||||
// Fast lock
|
QMutexLocker l{&biglock};
|
||||||
{
|
|
||||||
bool expected = false;
|
|
||||||
while (!biglock.compare_exchange_weak(expected, true))
|
|
||||||
expected = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_isOpen)
|
if (!_isOpen)
|
||||||
{
|
{
|
||||||
subscriptions--;
|
--subscriptions;
|
||||||
biglock = false;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!device)
|
if (!device)
|
||||||
{
|
{
|
||||||
qWarning() << "Unsubscribing with zero subscriber";
|
qWarning() << "Unsubscribing with zero subscriber";
|
||||||
biglock = false;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (subscriptions - 1 == 0)
|
if (subscriptions - 1 == 0)
|
||||||
{
|
{
|
||||||
closeDevice();
|
closeDevice();
|
||||||
biglock = false;
|
l.unlock();
|
||||||
|
|
||||||
// Synchronize with our stream thread
|
// Synchronize with our stream thread
|
||||||
while (streamFuture.isRunning())
|
while (streamFuture.isRunning())
|
||||||
|
@ -222,7 +189,6 @@ void CameraSource::unsubscribe()
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
device->close();
|
device->close();
|
||||||
biglock = false;
|
|
||||||
}
|
}
|
||||||
subscriptions--;
|
subscriptions--;
|
||||||
|
|
||||||
|
@ -230,6 +196,8 @@ void CameraSource::unsubscribe()
|
||||||
|
|
||||||
bool CameraSource::openDevice()
|
bool CameraSource::openDevice()
|
||||||
{
|
{
|
||||||
|
qDebug() << "Opening device "<<deviceName;
|
||||||
|
|
||||||
if (device)
|
if (device)
|
||||||
{
|
{
|
||||||
device->open();
|
device->open();
|
||||||
|
@ -301,6 +269,8 @@ bool CameraSource::openDevice()
|
||||||
|
|
||||||
void CameraSource::closeDevice()
|
void CameraSource::closeDevice()
|
||||||
{
|
{
|
||||||
|
qDebug() << "Closing device "<<deviceName;
|
||||||
|
|
||||||
// Free all remaining VideoFrame
|
// Free all remaining VideoFrame
|
||||||
// Locking must be done precisely this way to avoid races
|
// Locking must be done precisely this way to avoid races
|
||||||
for (int i = 0; i < freelist.size(); i++)
|
for (int i = 0; i < freelist.size(); i++)
|
||||||
|
@ -346,18 +316,13 @@ void CameraSource::stream()
|
||||||
if (!frameFinished)
|
if (!frameFinished)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Broadcast a new VideoFrame, it takes ownership of the AVFrame
|
freelistLock.lock();
|
||||||
{
|
|
||||||
bool expected = false;
|
|
||||||
while (!freelistLock.compare_exchange_weak(expected, true))
|
|
||||||
expected = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int freeFreelistSlot = getFreelistSlotLockless();
|
int freeFreelistSlot = getFreelistSlotLockless();
|
||||||
auto frameFreeCb = std::bind(&CameraSource::freelistCallback, this, freeFreelistSlot);
|
auto frameFreeCb = std::bind(&CameraSource::freelistCallback, this, freeFreelistSlot);
|
||||||
std::shared_ptr<VideoFrame> vframe = std::make_shared<VideoFrame>(frame, frameFreeCb);
|
std::shared_ptr<VideoFrame> vframe = std::make_shared<VideoFrame>(frame, frameFreeCb);
|
||||||
freelist.append(vframe);
|
freelist.append(vframe);
|
||||||
freelistLock = false;
|
freelistLock.unlock();
|
||||||
emit frameAvailable(vframe);
|
emit frameAvailable(vframe);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -366,39 +331,28 @@ void CameraSource::stream()
|
||||||
};
|
};
|
||||||
|
|
||||||
forever {
|
forever {
|
||||||
// Fast lock
|
biglock.lock();
|
||||||
{
|
|
||||||
bool expected = false;
|
|
||||||
while (!biglock.compare_exchange_weak(expected, true))
|
|
||||||
expected = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// When a thread makes device null, it releases it, so we acquire here
|
// When a thread makes device null, it releases it, so we acquire here
|
||||||
std::atomic_thread_fence(std::memory_order_acquire);
|
std::atomic_thread_fence(std::memory_order_acquire);
|
||||||
if (!device)
|
if (!device)
|
||||||
{
|
{
|
||||||
biglock = false;
|
biglock.unlock();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
streamLoop();
|
streamLoop();
|
||||||
|
|
||||||
// Give a chance to other functions to pick up the lock if needed
|
// Give a chance to other functions to pick up the lock if needed
|
||||||
biglock = false;
|
biglock.unlock();
|
||||||
QThread::yieldCurrentThread();
|
QThread::yieldCurrentThread();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CameraSource::freelistCallback(int freelistIndex)
|
void CameraSource::freelistCallback(int freelistIndex)
|
||||||
{
|
{
|
||||||
// Fast spinlock
|
QMutexLocker l{&freelistLock};
|
||||||
{
|
|
||||||
bool expected = false;
|
|
||||||
while (!freelistLock.compare_exchange_weak(expected, true))
|
|
||||||
expected = false;
|
|
||||||
}
|
|
||||||
freelist[freelistIndex].reset();
|
freelist[freelistIndex].reset();
|
||||||
freelistLock = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int CameraSource::getFreelistSlotLockless()
|
int CameraSource::getFreelistSlotLockless()
|
||||||
|
|
|
@ -91,8 +91,7 @@ private:
|
||||||
VideoMode mode; ///< What mode we tried to open the device in, all zeros means default mode
|
VideoMode mode; ///< What mode we tried to open the device in, all zeros means default mode
|
||||||
AVCodecContext* cctx, *cctxOrig; ///< Codec context of the camera's selected video stream
|
AVCodecContext* cctx, *cctxOrig; ///< Codec context of the camera's selected video stream
|
||||||
int videoStreamIndex; ///< A camera can have multiple streams, this is the one we're decoding
|
int videoStreamIndex; ///< A camera can have multiple streams, this is the one we're decoding
|
||||||
/// TODO: Replace the spinlocks with QMutexS, which spin and sleep
|
QMutex biglock, freelistLock; ///< True when locked. Faster than mutexes for video decoding.
|
||||||
std::atomic_bool biglock, freelistLock; ///< True when locked. Faster than mutexes for video decoding.
|
|
||||||
std::atomic_bool _isOpen;
|
std::atomic_bool _isOpen;
|
||||||
std::atomic_int subscriptions; ///< Remember how many times we subscribed for RAII
|
std::atomic_int subscriptions; ///< Remember how many times we subscribed for RAII
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user