gecko/xpcom/threads/nsEventQueue.h
Nathan Froyd d2eb391d4e Bug 1195767 - part 2 - create an nsEventQueueBase templated over the monitor type; r=gerald
Clients of nsEventQueue don't always need fully reentrant monitors.
Let's account for that by having a base class templated on the monitor
type.  This change also opens up the possibility of having the monitor
for the event queue not owned by the event queue itself, but by the
client class, which makes a lot more sense than the current design.
2015-08-28 13:26:17 -04:00

155 lines
4.5 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsEventQueue_h__
#define nsEventQueue_h__
#include <stdlib.h>
#include "mozilla/Monitor.h"
#include "mozilla/ReentrantMonitor.h"
#include "nsIRunnable.h"
#include "nsCOMPtr.h"
#include "mozilla/AlreadyAddRefed.h"
template<typename MonitorType>
struct MonitorAutoEnterChooser;
template<>
struct MonitorAutoEnterChooser<mozilla::Monitor>
{
typedef mozilla::MonitorAutoLock Type;
};
template<>
struct MonitorAutoEnterChooser<mozilla::ReentrantMonitor>
{
typedef mozilla::ReentrantMonitorAutoEnter Type;
};
// A threadsafe FIFO event queue...
template<typename MonitorType>
class nsEventQueueBase
{
public:
typedef MonitorType Monitor;
typedef typename MonitorAutoEnterChooser<Monitor>::Type MonitorAutoEnterType;
nsEventQueueBase();
~nsEventQueueBase();
// This method adds a new event to the pending event queue. The queue holds
// a strong reference to the event after this method returns. This method
// cannot fail.
void PutEvent(already_AddRefed<nsIRunnable>&& aEvent,
MonitorAutoEnterType& aProofOfLock);
// This method gets an event from the event queue. If mayWait is true, then
// the method will block the calling thread until an event is available. If
// the event is null, then the method returns immediately indicating whether
// or not an event is pending. When the resulting event is non-null, the
// caller is responsible for releasing the event object. This method does
// not alter the reference count of the resulting event.
bool GetEvent(bool aMayWait, nsIRunnable** aEvent,
MonitorAutoEnterType& aProofOfLock);
// This method returns the next pending event or null.
bool GetPendingEvent(nsIRunnable** aRunnable,
MonitorAutoEnterType& aProofOfLock)
{
return GetEvent(false, aRunnable, aProofOfLock);
}
size_t Count(MonitorAutoEnterType& aProofOfLock);
private:
bool IsEmpty()
{
return !mHead || (mHead == mTail && mOffsetHead == mOffsetTail);
}
enum
{
EVENTS_PER_PAGE = 255
};
// Page objects are linked together to form a simple deque.
struct Page
{
struct Page* mNext;
nsIRunnable* mEvents[EVENTS_PER_PAGE];
};
static_assert((sizeof(Page) & (sizeof(Page) - 1)) == 0,
"sizeof(Page) should be a power of two to avoid heap slop.");
static Page* NewPage()
{
return static_cast<Page*>(moz_xcalloc(1, sizeof(Page)));
}
static void FreePage(Page* aPage)
{
free(aPage);
}
Page* mHead;
Page* mTail;
uint16_t mOffsetHead; // offset into mHead where next item is removed
uint16_t mOffsetTail; // offset into mTail where next item is added
};
class nsEventQueue : protected nsEventQueueBase<mozilla::ReentrantMonitor>
{
private:
typedef nsEventQueueBase<mozilla::ReentrantMonitor> Base;
// Can't use typedefs or type alias templates here to name the base type.
friend class nsEventQueueBase<mozilla::ReentrantMonitor>;
typedef Base::Monitor MonitorType;
typedef Base::MonitorAutoEnterType MonitorAutoEnterType;
MonitorType mMonitor;
public:
nsEventQueue();
// This method adds a new event to the pending event queue. The queue holds
// a strong reference to the event after this method returns. This method
// cannot fail.
void PutEvent(nsIRunnable* aEvent);
void PutEvent(already_AddRefed<nsIRunnable>&& aEvent);
// This method gets an event from the event queue. If mayWait is true, then
// the method will block the calling thread until an event is available. If
// the event is null, then the method returns immediately indicating whether
// or not an event is pending. When the resulting event is non-null, the
// caller is responsible for releasing the event object. This method does
// not alter the reference count of the resulting event.
bool GetEvent(bool aMayWait, nsIRunnable** aEvent);
// This method returns true if there is a pending event.
bool HasPendingEvent()
{
return GetEvent(false, nullptr);
}
// This method returns the next pending event or null.
bool GetPendingEvent(nsIRunnable** aRunnable)
{
return GetEvent(false, aRunnable);
}
size_t Count();
MonitorType& GetReentrantMonitor()
{
return mMonitor;
}
};
#endif // nsEventQueue_h__