libcamera: signal: Make slots list private

The slots list is touched from most of the Signal template functions. In
order to prepare for thread-safety, move handling of the list to a small
number of non-template functions in the SignalBase class.

This incidently fixes a bug in signal disconnection handling where the
signal wasn't removed from the object's signals list, as pointed out by
the signals unit test.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>
Reviewed-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
This commit is contained in:
Laurent Pinchart 2020-01-19 04:29:33 +02:00
parent b6d93f9772
commit 667f53b522
4 changed files with 81 additions and 54 deletions

View file

@ -48,9 +48,7 @@ protected:
virtual void message(Message *msg); virtual void message(Message *msg);
private: private:
template<typename... Args> friend class SignalBase;
friend class Signal;
friend class BoundMethodBase;
friend class Thread; friend class Thread;
void notifyThreadMove(); void notifyThreadMove();

View file

@ -7,6 +7,7 @@
#ifndef __LIBCAMERA_SIGNAL_H__ #ifndef __LIBCAMERA_SIGNAL_H__
#define __LIBCAMERA_SIGNAL_H__ #define __LIBCAMERA_SIGNAL_H__
#include <functional>
#include <list> #include <list>
#include <type_traits> #include <type_traits>
#include <vector> #include <vector>
@ -19,23 +20,18 @@ namespace libcamera {
class SignalBase class SignalBase
{ {
public: public:
template<typename T> void disconnect(Object *object);
void disconnect(T *obj)
{
for (auto iter = slots_.begin(); iter != slots_.end(); ) {
BoundMethodBase *slot = *iter;
if (slot->match(obj)) {
iter = slots_.erase(iter);
delete slot;
} else {
++iter;
}
}
}
protected: protected:
friend class Object; using SlotList = std::list<BoundMethodBase *>;
std::list<BoundMethodBase *> slots_;
void connect(BoundMethodBase *slot);
void disconnect(std::function<bool(SlotList::iterator &)> match);
SlotList slots();
private:
SlotList slots_;
}; };
template<typename... Args> template<typename... Args>
@ -45,12 +41,7 @@ public:
Signal() {} Signal() {}
~Signal() ~Signal()
{ {
for (BoundMethodBase *slot : slots_) { disconnect();
Object *object = slot->object();
if (object)
object->disconnect(this);
delete slot;
}
} }
#ifndef __DOXYGEN__ #ifndef __DOXYGEN__
@ -59,8 +50,7 @@ public:
ConnectionType type = ConnectionTypeAuto) ConnectionType type = ConnectionTypeAuto)
{ {
Object *object = static_cast<Object *>(obj); Object *object = static_cast<Object *>(obj);
object->connect(this); SignalBase::connect(new BoundMethodMember<T, void, Args...>(obj, object, func, type));
slots_.push_back(new BoundMethodMember<T, void, Args...>(obj, object, func, type));
} }
template<typename T, typename R, typename std::enable_if<!std::is_base_of<Object, T>::value>::type * = nullptr> template<typename T, typename R, typename std::enable_if<!std::is_base_of<Object, T>::value>::type * = nullptr>
@ -69,63 +59,62 @@ public:
#endif #endif
void connect(T *obj, R (T::*func)(Args...)) void connect(T *obj, R (T::*func)(Args...))
{ {
slots_.push_back(new BoundMethodMember<T, R, Args...>(obj, nullptr, func)); SignalBase::connect(new BoundMethodMember<T, R, Args...>(obj, nullptr, func));
} }
template<typename R> template<typename R>
void connect(R (*func)(Args...)) void connect(R (*func)(Args...))
{ {
slots_.push_back(new BoundMethodStatic<R, Args...>(func)); SignalBase::connect(new BoundMethodStatic<R, Args...>(func));
} }
void disconnect() void disconnect()
{ {
for (BoundMethodBase *slot : slots_) SignalBase::disconnect([](SlotList::iterator &iter) {
delete slot; return true;
slots_.clear(); });
} }
template<typename T> template<typename T>
void disconnect(T *obj) void disconnect(T *obj)
{ {
SignalBase::disconnect(obj); SignalBase::disconnect([obj](SlotList::iterator &iter) {
return (*iter)->match(obj);
});
} }
template<typename T, typename R> template<typename T, typename R>
void disconnect(T *obj, R (T::*func)(Args...)) void disconnect(T *obj, R (T::*func)(Args...))
{ {
for (auto iter = slots_.begin(); iter != slots_.end(); ) { SignalBase::disconnect([obj, func](SlotList::iterator &iter) {
BoundMethodArgs<R, Args...> *slot = BoundMethodArgs<R, Args...> *slot =
static_cast<BoundMethodArgs<R, Args...> *>(*iter); static_cast<BoundMethodArgs<R, Args...> *>(*iter);
if (!slot->match(obj))
return false;
/* /*
* If the object matches the slot, the slot is * If the object matches the slot, the slot is
* guaranteed to be a member slot, so we can safely * guaranteed to be a member slot, so we can safely
* cast it to BoundMethodMember<T, Args...> to match * cast it to BoundMethodMember<T, Args...> to match
* func. * func.
*/ */
if (slot->match(obj) && return static_cast<BoundMethodMember<T, R, Args...> *>(slot)->match(func);
static_cast<BoundMethodMember<T, R, Args...> *>(slot)->match(func)) { });
iter = slots_.erase(iter);
delete slot;
} else {
++iter;
}
}
} }
template<typename R> template<typename R>
void disconnect(R (*func)(Args...)) void disconnect(R (*func)(Args...))
{ {
for (auto iter = slots_.begin(); iter != slots_.end(); ) { SignalBase::disconnect([func](SlotList::iterator &iter) {
BoundMethodArgs<R, Args...> *slot = *iter; BoundMethodArgs<R, Args...> *slot =
if (slot->match(nullptr) && static_cast<BoundMethodArgs<R, Args...> *>(*iter);
static_cast<BoundMethodStatic<R, Args...> *>(slot)->match(func)) {
iter = slots_.erase(iter); if (!slot->match(nullptr))
delete slot; return false;
} else {
++iter; return static_cast<BoundMethodStatic<R, Args...> *>(slot)->match(func);
} });
}
} }
void emit(Args... args) void emit(Args... args)
@ -134,8 +123,7 @@ public:
* Make a copy of the slots list as the slot could call the * Make a copy of the slots list as the slot could call the
* disconnect operation, invalidating the iterator. * disconnect operation, invalidating the iterator.
*/ */
std::vector<BoundMethodBase *> slots{ slots_.begin(), slots_.end() }; for (BoundMethodBase *slot : slots())
for (BoundMethodBase *slot : slots)
static_cast<BoundMethodArgs<void, Args...> *>(slot)->activate(args...); static_cast<BoundMethodArgs<void, Args...> *>(slot)->activate(args...);
} }
}; };

View file

@ -76,7 +76,12 @@ Object::Object(Object *parent)
*/ */
Object::~Object() Object::~Object()
{ {
for (SignalBase *signal : signals_) /*
* Move signals to a private list to avoid concurrent iteration and
* deletion of items from Signal::disconnect().
*/
std::list<SignalBase *> signals(std::move(signals_));
for (SignalBase *signal : signals)
signal->disconnect(this); signal->disconnect(this);
if (pendingMessages_) if (pendingMessages_)

View file

@ -14,6 +14,42 @@
namespace libcamera { namespace libcamera {
void SignalBase::connect(BoundMethodBase *slot)
{
Object *object = slot->object();
if (object)
object->connect(this);
slots_.push_back(slot);
}
void SignalBase::disconnect(Object *object)
{
disconnect([object](SlotList::iterator &iter) {
return (*iter)->match(object);
});
}
void SignalBase::disconnect(std::function<bool(SlotList::iterator &)> match)
{
for (auto iter = slots_.begin(); iter != slots_.end(); ) {
if (match(iter)) {
Object *object = (*iter)->object();
if (object)
object->disconnect(this);
delete *iter;
iter = slots_.erase(iter);
} else {
++iter;
}
}
}
SignalBase::SlotList SignalBase::slots()
{
return slots_;
}
/** /**
* \class Signal * \class Signal
* \brief Generic signal and slot communication mechanism * \brief Generic signal and slot communication mechanism