1
0
mirror of https://github.com/wqking/eventpp.git synced 2024-12-27 16:41:11 +08:00
eventpp/doc/eventqueue.md

132 lines
5.2 KiB
Markdown
Raw Normal View History

2018-05-19 17:42:41 +08:00
# Class EventQueue reference
## Table Of Contents
- [API reference](#apis)
- [Internal data structure](#internal-data-structure)
EventQueue includes all functions of [EventDispatcher](eventdispatcher.md) and adds event queue features. Note: EventQueue doesn't inherit from EventDispatcher, don't try to cast EventQueue to EventDispatcher.
<a name="apis"></a>
## API reference
**Header**
eventpp/eventqueue.h
**Template parameters**
```c++
template <
typename EventGetter,
typename Prototype,
typename Callback = void,
typename ArgumentPassingMode = ArgumentPassingAutoDetect,
typename Threading = MultipleThreading
>
class EventQueue;
```
EventQueue has the exactly same template parameters with EventDispatcher. Please reference [EventDispatcher document](eventdispatcher.md) for details.
**Functions**
```c++
EventQueue() = default;
EventQueue(EventQueue &&) = delete;
EventQueue(const EventQueue &) = delete;
EventQueue & operator = (const EventQueue &) = delete;
```
EventQueue can not be copied, moved, or assigned.
```c++
void enqueue(Args ...args);
template <typename T>
void enqueue(T && first, Args ...args);
```
Put an event into the event queue. The event type is deducted from the arguments of `enqueue`.
All arguments are copied to internal data structure, so the arguments must be copyable.
If an argument is a reference to a base class and a derived object is passed in, only the base object will be stored and the derived object is lost. Usually shared pointer should be used in such situation.
If an argument is a pointer, only the pointer will be stored. The object it points must be available until the event is processed.
The time complexity is O(1).
```c++
void process();
```
Process the event queue. All events in the event queue are dispatched once and then removed from the queue.
The listeners are called in the thread same as the caller of `process`.
Note: if `process()` is called from multiple threads simultaneously, the events in the event queue are guaranteed dispatched only once.
```c++
bool empty() const;
```
Return true if there is no any event in the event queue, false if there are events in the event queue.
Note: in multiple threading environment, the empty state may change immediately after the function returns.
Note: don't write loop as `while(! eventQueue.empty()) ;`. It's dead loop since the compiler will inline the code and the change of empty state is never seen by the loop. The safe approach is `while(eventQueue.waitFor(std::chrono::nanoseconds(0))) ;`.
```c++
void wait() const;
```
`wait` causes the current thread to block until there is new event arrives in the queue.
Note: though `wait` has work around with spurious wakeup internally, the queue is not guaranteed not empty after `wait` returns.
`wait` is useful when a thread processes the event queue. A sampel usage is,
```c++
for(;;) {
eventQueue.wait();
eventQueue.process();
}
```
The code works event if it doesn't `wait`, but doing that will waste CPU power resource.
```c++
template <class Rep, class Period>
bool waitFor(const std::chrono::duration<Rep, Period> & duration) const;
```
Wait for no longer than *duration* time out.
Return true if the queue is not empty, false if the return is caused by time out.
`waitFor` is useful when a event queue processing thread has other condition to check. For example,
```c++
std::atomic<bool> shouldStop(false);
for(;;) {
while(! eventQueue.waitFor(std::chrono::milliseconds(10)) && ! shouldStop.load()) ;
if(shouldStop.load()) {
break;
}
eventQueue.process();
}
```
**Inner class EventQueue::DisableQueueNotify**
`EventQueue::DisableQueueNotify` is a RAII class that temporarily prevents the event queue from waking up any waiting threads. When any `DisableQueueNotify` object exist, calling `enqueue` doesn't wake up any threads that are blocked by `wait`. When the `DisableQueueNotify` object is out of scope, the waking up is resumed. If there are more than one `DisableQueueNotify` objects, the waking up is only resumed after all `DisableQueueNotify` objects are destroyed.
To use `DisableQueueNotify`, construct it with a pointer to event queue.
Sampe code
```c++
using EQ = eventpp::EventQueue<int, void ()>;
EQ queue;
{
EQ::DisableQueueNotify disableNotify(&queue);
// any blocking threads will not be waken up by the below two lines.
queue.enqueue(1);
queue.enqueue(2);
}
// any blocking threads are waken up here immediately.
// any blocking threads will be waken up by below line since there is no DisableQueueNotify.
queue.enqueue(3);
```
<a name="internal-data-structure"></a>
## Internal data structure
EventQueue uses three `std::list` to manage the event queue.
The first busy list holds all nodes with queued events.
The second idle list holds all idle nodes. After an event is dispatched and removed from the queue, instead of freeing the memory, EventQueue moves the unused node to the idle list. This can improve performance and avoid memory fragment.
The third list is a local temporary list used in function `process()`. During processing, the busy list is swapped to the temporary list, all events are dispatched from the temporary list, then the temporary list is returned and appended to the idle list.