1
0
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:
tux3 2015-10-10 01:24:04 +02:00
parent 477554ffba
commit 6dc91b156a
3 changed files with 21 additions and 65 deletions

View File

@ -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;

View File

@ -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()

View File

@ -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