EventQueue includes all features of EventDispatcher and adds event queue features. Note: EventQueue doesn't inherit from EventDispatcher, don't try to cast EventQueue to EventDispatcher.
EventQueue is asynchronous. Events are cached in the queue when `EventQueue::enqueue` is called, and dispatched later when `EventQueue::process` is called.
EventQueue is equivalent to the event system (QEvent) in Qt, or the message processing in Windows API.
Note: the queued events are not copied, moved, assigned, or move assigned, only the listeners are performed with these operations. That's to say, the queued events are not duplicated when an EventQueue is copied or assigned.
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.
The two overloaded functions have similar but slightly difference. How to use them depends on the `ArgumentPassingMode` policy. Please reference the [document of policies](policies.md) for more information.
Note: the arguments life time may be longer than expected. `EventQueue` copies the arguments into internal data structure, after the event is dispatched, the data is cached for next usage, so the arguments won't be destroyed until the data is reused. This is for performance optimization. This is usually not an issue, but if you pass large data in shared pointer, the data may be in the memory for longer time than necessary.
Any new events added to the queue during `process()` are not dispatched during current `process()`.
`process()` is efficient in single thread event processing, it processes all events in the queue in current thread. To process events from multiple threads efficiently, use `processOne()`.
Process one event in the event queue. The first event in the event queue is dispatched once and then removed from the queue.
The function returns true if one event was processed, false if no event was processed.
The listener is called in the thread same as the caller of `processOne`.
Any new events added to the queue during `processOne()` are not dispatched during current `processOne()`.
If there are multiple threads processing events, `processOne()` is more efficient than `process()` because it can split the events processing to different threads. However, if there is only one thread processing events, 'process()' is more efficient.
Note: if `processOne()` is called from multiple threads simultaneously, the events in the event queue are guaranteed dispatched only once.
Process the event queue. Before processing an event, the event is passed to `predictor` and the event will be processed only if `predictor` returns true. If `predictor` returns false, the event will not be processed and be kept in the queue, then `processIf` will continue processing next event in the queue.
`predictor` is a callable object(function, lambda, etc) that takes exactly the same arguments as `EventQueue::enqueue` or have no arguments, and returns a boolean value. eventpp will pass the arguments properly.
1. Process certain events in certain thread. For example, in a GUI application, the UI related events may be only desired to be processed in the main thread. In such case, `predictor` may return true for any UI events, and return false for any non-UI events.
```c++
template <typenamePredictor>
bool processUntil(Predictor && predictor);
```
Process the event queue. Before processing an event, the event is passed to `predictor`. If `predictor` returns true, `processUntil` stops any further processing and returns. If `predictor` returns false, `processUntil` will process the underlying events.
`predictor` is a callable object(function, lambda, etc) that takes exactly the same arguments as `EventQueue::enqueue` or have no arguments, and returns a boolean value. eventpp will pass the arguments properly.
`processUntil` returns true if any event was dispatched, false if no event was dispatched.
`processUntil` has a good use case that limits the process time to simulate "timeout". For example, in a game engine, the event process may be limited to only several milliseconds, the remaining events will be processed in next game loop. In such situation, the `predictor` can return true when time out.
Note: don't write loop as `while(! eventQueue.emptyQueue()) {}`. 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))) ;`.
`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.
`DisableQueueNotify` is useful to improve performance when batching adding events to the queue. For example, in a main loop of a game engine, `DisableQueueNotify` can be created on the start in a frame, then the game adding events to the queue, and the `DisableQueueNotify` is destroyed at the end of a frame and the events are processed.
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.