зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1219984. Part 2 - add support for multiple arguments. r=kinetik.
This commit is contained in:
Родитель
eb17eba41a
Коммит
ef41bf18de
|
@ -10,6 +10,7 @@
|
|||
#include "mozilla/AbstractThread.h"
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "mozilla/Tuple.h"
|
||||
#include "mozilla/TypeTraits.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
|
@ -130,19 +131,25 @@ class ListenerHelper {
|
|||
// Define our custom runnable to minimize copy of the event data.
|
||||
// NS_NewRunnableFunction will result in 2 copies of the event data.
|
||||
// One is captured by the lambda and the other is the copy of the lambda.
|
||||
template <typename T>
|
||||
template <typename... Ts>
|
||||
class R : public nsRunnable {
|
||||
typedef typename RemoveCV<typename RemoveReference<T>::Type>::Type ArgType;
|
||||
public:
|
||||
template <typename U>
|
||||
R(RevocableToken* aToken, const Function& aFunction, U&& aEvent)
|
||||
: mToken(aToken), mFunction(aFunction), mEvent(Forward<U>(aEvent)) {}
|
||||
template <typename... Us>
|
||||
R(RevocableToken* aToken, const Function& aFunction, Us&&... aEvents)
|
||||
: mToken(aToken)
|
||||
, mFunction(aFunction)
|
||||
, mEvents(Forward<Us>(aEvents)...) {}
|
||||
|
||||
template <typename... Vs, size_t... Is>
|
||||
void Invoke(Tuple<Vs...>& aEvents, IndexSequence<Is...>) {
|
||||
// Enable move whenever possible since mEvent won't be used anymore.
|
||||
mFunction(Move(Get<Is>(aEvents))...);
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() override {
|
||||
// Don't call the listener if it is disconnected.
|
||||
if (!mToken->IsRevoked()) {
|
||||
// Enable move whenever possible since mEvent won't be used anymore.
|
||||
mFunction(Move(mEvent));
|
||||
Invoke(mEvents, typename IndexSequenceFor<Ts...>::Type());
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -150,25 +157,29 @@ class ListenerHelper {
|
|||
private:
|
||||
RefPtr<RevocableToken> mToken;
|
||||
Function mFunction;
|
||||
ArgType mEvent;
|
||||
|
||||
template <typename T>
|
||||
using ArgType = typename RemoveCV<typename RemoveReference<T>::Type>::Type;
|
||||
Tuple<ArgType<Ts>...> mEvents;
|
||||
};
|
||||
|
||||
public:
|
||||
ListenerHelper(RevocableToken* aToken, Target* aTarget, const Function& aFunc)
|
||||
: mToken(aToken), mTarget(aTarget), mFunction(aFunc) {}
|
||||
|
||||
// |F| takes one argument.
|
||||
template <typename F, typename T>
|
||||
// |F| takes one or more arguments.
|
||||
template <typename F, typename... Ts>
|
||||
typename EnableIf<TakeArgs<F>::value, void>::Type
|
||||
Dispatch(const F& aFunc, T&& aEvent) {
|
||||
nsCOMPtr<nsIRunnable> r = new R<T>(mToken, aFunc, Forward<T>(aEvent));
|
||||
DispatchHelper(const F& aFunc, Ts&&... aEvents) {
|
||||
nsCOMPtr<nsIRunnable> r =
|
||||
new R<Ts...>(mToken, aFunc, Forward<Ts>(aEvents)...);
|
||||
EventTarget<Target>::Dispatch(mTarget.get(), r.forget());
|
||||
}
|
||||
|
||||
// |F| takes no arguments. Don't bother passing aEvent.
|
||||
template <typename F, typename T>
|
||||
template <typename F, typename... Ts>
|
||||
typename EnableIf<!TakeArgs<F>::value, void>::Type
|
||||
Dispatch(const F& aFunc, T&&) {
|
||||
DispatchHelper(const F& aFunc, Ts&&...) {
|
||||
const RefPtr<RevocableToken>& token = mToken;
|
||||
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([=] () {
|
||||
// Don't call the listener if it is disconnected.
|
||||
|
@ -179,9 +190,9 @@ public:
|
|||
EventTarget<Target>::Dispatch(mTarget.get(), r.forget());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Dispatch(T&& aEvent) {
|
||||
Dispatch(mFunction, Forward<T>(aEvent));
|
||||
template <typename... Ts>
|
||||
void Dispatch(Ts&&... aEvents) {
|
||||
DispatchHelper(mFunction, Forward<Ts>(aEvents)...);
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -219,44 +230,44 @@ private:
|
|||
* Since virtual methods can not be templated, this class is specialized
|
||||
* to provide different Dispatch() overloads depending on EventPassMode.
|
||||
*/
|
||||
template <typename ArgType, EventPassMode Mode = EventPassMode::Copy>
|
||||
template <EventPassMode Mode, typename... As>
|
||||
class Listener : public ListenerBase {
|
||||
public:
|
||||
virtual ~Listener() {}
|
||||
virtual void Dispatch(const ArgType& aEvent) = 0;
|
||||
virtual void Dispatch(const As&... aEvents) = 0;
|
||||
};
|
||||
|
||||
template <typename ArgType>
|
||||
class Listener<ArgType, EventPassMode::Move> : public ListenerBase {
|
||||
template <typename... As>
|
||||
class Listener<EventPassMode::Move, As...> : public ListenerBase {
|
||||
public:
|
||||
virtual ~Listener() {}
|
||||
virtual void Dispatch(ArgType&& aEvent) = 0;
|
||||
virtual void Dispatch(As&&... aEvents) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Store the registered target thread and function so it knows where and to
|
||||
* whom to send the event data.
|
||||
*/
|
||||
template <typename Target, typename Function, typename ArgType, EventPassMode>
|
||||
class ListenerImpl : public Listener<ArgType, EventPassMode::Copy> {
|
||||
template <typename Target, typename Function, EventPassMode, typename... As>
|
||||
class ListenerImpl : public Listener<EventPassMode::Copy, As...> {
|
||||
public:
|
||||
ListenerImpl(Target* aTarget, const Function& aFunction)
|
||||
: mHelper(ListenerBase::Token(), aTarget, aFunction) {}
|
||||
void Dispatch(const ArgType& aEvent) override {
|
||||
mHelper.Dispatch(aEvent);
|
||||
void Dispatch(const As&... aEvents) override {
|
||||
mHelper.Dispatch(aEvents...);
|
||||
}
|
||||
private:
|
||||
ListenerHelper<Target, Function> mHelper;
|
||||
};
|
||||
|
||||
template <typename Target, typename Function, typename ArgType>
|
||||
class ListenerImpl<Target, Function, ArgType, EventPassMode::Move>
|
||||
: public Listener<ArgType, EventPassMode::Move> {
|
||||
template <typename Target, typename Function, typename... As>
|
||||
class ListenerImpl<Target, Function, EventPassMode::Move, As...>
|
||||
: public Listener<EventPassMode::Move, As...> {
|
||||
public:
|
||||
ListenerImpl(Target* aTarget, const Function& aFunction)
|
||||
: mHelper(ListenerBase::Token(), aTarget, aFunction) {}
|
||||
void Dispatch(ArgType&& aEvent) override {
|
||||
mHelper.Dispatch(Move(aEvent));
|
||||
void Dispatch(As&&... aEvents) override {
|
||||
mHelper.Dispatch(Move(aEvents)...);
|
||||
}
|
||||
private:
|
||||
ListenerHelper<Target, Function> mHelper;
|
||||
|
@ -278,9 +289,23 @@ struct PassModePicker {
|
|||
EventPassMode::Copy : EventPassMode::Move;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return true if any type is a reference type.
|
||||
*/
|
||||
template <typename Head, typename... Tails>
|
||||
struct IsAnyReference {
|
||||
static const bool value = IsReference<Head>::value ||
|
||||
IsAnyReference<Tails...>::value;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct IsAnyReference<T> {
|
||||
static const bool value = IsReference<T>::value;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename T, ListenerMode> class MediaEventSource;
|
||||
template <ListenerMode, typename... Ts> class MediaEventSourceImpl;
|
||||
|
||||
/**
|
||||
* Not thread-safe since this is not meant to be shared and therefore only
|
||||
|
@ -289,8 +314,8 @@ template <typename T, ListenerMode> class MediaEventSource;
|
|||
* listener from an event source.
|
||||
*/
|
||||
class MediaEventListener {
|
||||
template <typename T, ListenerMode>
|
||||
friend class MediaEventSource;
|
||||
template <ListenerMode, typename... Ts>
|
||||
friend class MediaEventSourceImpl;
|
||||
|
||||
public:
|
||||
MediaEventListener() {}
|
||||
|
@ -329,16 +354,22 @@ private:
|
|||
/**
|
||||
* A generic and thread-safe class to implement the observer pattern.
|
||||
*/
|
||||
template <typename EventType, ListenerMode Mode = ListenerMode::NonExclusive>
|
||||
class MediaEventSource {
|
||||
static_assert(!IsReference<EventType>::value, "Ref-type not supported!");
|
||||
typedef typename detail::EventTypeTraits<EventType>::ArgType ArgType;
|
||||
static const detail::EventPassMode PassMode
|
||||
= detail::PassModePicker<Mode>::Value;
|
||||
typedef detail::Listener<ArgType, PassMode> Listener;
|
||||
template <ListenerMode Mode, typename... Es>
|
||||
class MediaEventSourceImpl {
|
||||
static_assert(!detail::IsAnyReference<Es...>::value,
|
||||
"Ref-type not supported!");
|
||||
|
||||
template <typename T>
|
||||
using ArgType = typename detail::EventTypeTraits<T>::ArgType;
|
||||
|
||||
static const detail::EventPassMode PassMode =
|
||||
detail::PassModePicker<Mode>::Value;
|
||||
|
||||
typedef detail::Listener<PassMode, ArgType<Es>...> Listener;
|
||||
|
||||
template<typename Target, typename Func>
|
||||
using ListenerImpl = detail::ListenerImpl<Target, Func, ArgType, PassMode>;
|
||||
using ListenerImpl =
|
||||
detail::ListenerImpl<Target, Func, PassMode, ArgType<Es>...>;
|
||||
|
||||
template <typename Method>
|
||||
using TakeArgs = detail::TakeArgs<Method>;
|
||||
|
@ -353,13 +384,13 @@ class MediaEventSource {
|
|||
return MediaEventListener((*l)->Token());
|
||||
}
|
||||
|
||||
// |Method| takes one argument.
|
||||
// |Method| takes one or more arguments.
|
||||
template <typename Target, typename This, typename Method>
|
||||
typename EnableIf<TakeArgs<Method>::value, MediaEventListener>::Type
|
||||
ConnectInternal(Target* aTarget, This* aThis, Method aMethod) {
|
||||
detail::RawPtr<This> thiz(aThis);
|
||||
auto f = [=] (ArgType&& aEvent) {
|
||||
(thiz.get()->*aMethod)(Move(aEvent));
|
||||
auto f = [=] (ArgType<Es>&&... aEvents) {
|
||||
(thiz.get()->*aMethod)(Move(aEvents)...);
|
||||
};
|
||||
return ConnectInternal(aTarget, f);
|
||||
}
|
||||
|
@ -420,10 +451,10 @@ public:
|
|||
}
|
||||
|
||||
protected:
|
||||
MediaEventSource() : mMutex("MediaEventSource::mMutex") {}
|
||||
MediaEventSourceImpl() : mMutex("MediaEventSourceImpl::mMutex") {}
|
||||
|
||||
template <typename T>
|
||||
void NotifyInternal(T&& aEvent) {
|
||||
template <typename... Ts>
|
||||
void NotifyInternal(Ts&&... aEvents) {
|
||||
MutexAutoLock lock(mMutex);
|
||||
for (int32_t i = mListeners.Length() - 1; i >= 0; --i) {
|
||||
auto&& l = mListeners[i];
|
||||
|
@ -433,7 +464,7 @@ protected:
|
|||
mListeners.RemoveElementAt(i);
|
||||
continue;
|
||||
}
|
||||
l->Dispatch(Forward<T>(aEvent));
|
||||
l->Dispatch(Forward<Ts>(aEvents)...);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -442,17 +473,25 @@ private:
|
|||
nsTArray<UniquePtr<Listener>> mListeners;
|
||||
};
|
||||
|
||||
template <typename... Es>
|
||||
using MediaEventSource =
|
||||
MediaEventSourceImpl<ListenerMode::NonExclusive, Es...>;
|
||||
|
||||
template <typename... Es>
|
||||
using MediaEventSourceExc =
|
||||
MediaEventSourceImpl<ListenerMode::Exclusive, Es...>;
|
||||
|
||||
/**
|
||||
* A class to separate the interface of event subject (MediaEventSource)
|
||||
* and event publisher. Mostly used as a member variable to publish events
|
||||
* to the listeners.
|
||||
*/
|
||||
template <typename EventType, ListenerMode Mode = ListenerMode::NonExclusive>
|
||||
class MediaEventProducer : public MediaEventSource<EventType, Mode> {
|
||||
template <typename... Es>
|
||||
class MediaEventProducer : public MediaEventSource<Es...> {
|
||||
public:
|
||||
template <typename T>
|
||||
void Notify(T&& aEvent) {
|
||||
this->NotifyInternal(Forward<T>(aEvent));
|
||||
template <typename... Ts>
|
||||
void Notify(Ts&&... aEvents) {
|
||||
this->NotifyInternal(Forward<Ts>(aEvents)...);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -468,6 +507,18 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A producer with Exclusive mode.
|
||||
*/
|
||||
template <typename... Es>
|
||||
class MediaEventProducerExc : public MediaEventSourceExc<Es...> {
|
||||
public:
|
||||
template <typename... Ts>
|
||||
void Notify(Ts&&... aEvents) {
|
||||
this->NotifyInternal(Forward<Ts>(aEvents)...);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif //MediaEventSource_h_
|
||||
|
|
|
@ -19,10 +19,8 @@
|
|||
namespace mozilla {
|
||||
|
||||
class TimedMetadata;
|
||||
typedef MediaEventProducer<TimedMetadata, ListenerMode::Exclusive>
|
||||
TimedMetadataEventProducer;
|
||||
typedef MediaEventSource<TimedMetadata, ListenerMode::Exclusive>
|
||||
TimedMetadataEventSource;
|
||||
typedef MediaEventProducerExc<TimedMetadata> TimedMetadataEventProducer;
|
||||
typedef MediaEventSourceExc<TimedMetadata> TimedMetadataEventSource;
|
||||
|
||||
// A struct that contains the metadata of a media, and the time at which those
|
||||
// metadata should start to be reported.
|
||||
|
|
|
@ -304,7 +304,7 @@ TEST(MediaEventSource, MoveOnly)
|
|||
RefPtr<TaskQueue> queue = new TaskQueue(
|
||||
GetMediaThreadPool(MediaThreadType::PLAYBACK));
|
||||
|
||||
MediaEventProducer<UniquePtr<int>, ListenerMode::Exclusive> source;
|
||||
MediaEventProducerExc<UniquePtr<int>> source;
|
||||
|
||||
auto func = [] (UniquePtr<int>&& aEvent) {
|
||||
EXPECT_EQ(*aEvent, 20);
|
||||
|
|
Загрузка…
Ссылка в новой задаче