#include <evo/event.h>
template<class T = Event>
class evo::EventQueue< T >
Lock-free event processing queue.
- Many producers add events while one or more consumers process (invoke) them
- This supports a "single" or "multi" consumer model, each with different advantages:
- Single: A single consumer thread calls process() repeatedly to process events, while other threads call add() to add events to the queue
- This is useful for integrating with another event processing loop, which will have it's own way to wait for events
- For other cases a "wait mechanism" is likely needed when process() returns false (queue is empty), to avoid a spin loop
- Multi: Multiple consumers allow parallel event processing and must call process_multi() with a common mutex – this doesn't affect the producers (mutex is only for consumers)
- This is useful with async events to offload CPU-heavy or synchronous-I/O work to another thread, freeing up the async event-loop to handle other events
- For CPU-heavy threads this generally shouldn't have more threads then the number of CPUs on the system
- For synchronous-I/O more threads are likely needed since they spend time blocking (waiting) – this should be tuned for a given system
- Each event:
- is a callback object inheriting Event, which is popped from the queue and invoked (called) by the consumer thread
- is assigned a unique sequence number – this can handle 1 million events per second for over 500,000 years before maxing out
uint64
sequence numbers – results are undefined if the sequence number overflows
- While the queue is full, producers will spin wait (with 1 nanosecond sleep) – this should be avoided
- Caution: When events are processed on the same thread, an event must not call add() on the same queue that invoked (called) the event, this will deadlock if the queue is full
- Caution: Graceful shutdown with empty queue expected – incomplete events can leak memory or have undefined behavior
- Template Parameters
-
T | Event type to use – must be Event or inherit from it
- Concrete types have slightly better performace (inlining, no vtable looups)
|
- Example
bool operator()() {
return true;
}
};
int main() {
return 0;
}
◆ EventT
◆ Size
◆ EventQueue()
Constructor.
- Parameters
-
size | Queue size, rounded up to nearest power of 2 |
◆ ~EventQueue()
Destructor.
- Queue should be empty, otherwise incomplete events can leak memory or have undefined behavior
◆ add()
void add |
( |
T * |
event, |
|
|
ulongl |
spinwait_ns = 1 |
|
) |
| |
|
inline |
Add an event to queue.
- This takes ownership of the event pointer, and will free it once the event is completed (via C++ delete operator)
- The event is only freed if it returns true, otherwise it's assumed that ownership was transferred elsewhere
- This blocks while queue is full (spin-wait with 1 nanosecond sleep) – a full queue should be avoided
- If this is a multi-consumer queue using process_multiwait(), call notify_multiwait() after this
- Caution: When events are processed on the same thread, do not call from the same queue that invoked (called) the event, this will deadlock if the queue is full
- Parameters
-
event | Event pointer to add and take ownership of |
spinwait_ns | Spin-wait sleep time in nanoseconds (usually default is preferred) – used to sleep each loop while spin waiting |
◆ notify_multiwait()
void notify_multiwait |
( |
U & |
condmutex | ) |
|
|
inline |
Notify an item has been added with multiple consumer threads.
- Template Parameters
-
U | Conditio/Mutex type, inferred from argument |
- Parameters
-
condmutex | Condition/mutex to use |
◆ process()
Process queued events and return.
- This pops and invokes (calls) all queued events
- Popped events that return true are freed, otherwise they are detached (owned elsewhere)
- Caution: Only 1 thread may call this at a time, otherwise results are undefined
- Returns
- Whether any events were processed
◆ process_multi()
bool process_multi |
( |
U & |
mutex | ) |
|
|
inline |
Process queued events and return, allowing multiple consumer threads.
- This locks the mutex while extracting the next event, and unlocks it while invoking (calling) the event (giving the queue to the next consumer)
- Caution: Do not mix with process() on the same instance
- Template Parameters
-
- Parameters
-
mutex | Mutex or Condition object to lock while extracting next event from queue |
- Returns
- Whether any events were processed
◆ process_multiwait()
void process_multiwait |
( |
U & |
condmutex, |
|
|
AtomicInt & |
stopflag, |
|
|
ulong |
waitms = 1 |
|
) |
| |
|
inline |
Process queued events until stopflag is set, allowing multiple consumer threads, and waiting with condmutex while idle.
- This locks the mutex while extracting the next event, and unlocks it while invoking (calling) the event (giving the queue to the next consumer)
- While queue is empty this waits on
condmutex
using a timeout of waitms
- Call notify_multiwait() after adding an event to try to wake up a consumer
- Caution: Do not mix with process() or process_multi() on the same instance
- Template Parameters
-
U | Condition/Mutex type, inferred from argument |
- Parameters
-
condmutex | Condition/mutex object to lock while extracting next event from queue |
stopflag | Flag to stop processing when set to non-zero |
waitms | Max wait time in milliseconds, 0 for none (spin-wait, not recommended) |
◆ DEFAULT_SIZE
const Size DEFAULT_SIZE = 256 |
|
static |
The documentation for this class was generated from the following file: