blob: bf40b4824a39a0c54ab498dae81540d87d149499 [file] [log] [blame]
/* Copyright (c) 2015 Google Inc.
* Barret Rhoden <brho@cs.berkeley.edu>
* See LICENSE for details.
*
* Coalescing Event Queue: encapuslates the essence of epoll/kqueue in shared
* memory: a dense array of sticky status bits.
*
* Like all event mailboxes, CEQs are multi-producer, multi-consumer, with the
* producer not trusting the consumer.
*
* The producer sends ceq_events with a certain ID (the ev_type). Events of a
* particular ID are coalesced in one spot, such that N events can occur and the
* consumer only receives them as one event. These are the "sticky status
* bits". For example, setting flags in an int coalesce (you can set the same
* flag repeatedly, and it's still set), and an atomic counter coalesces small
* increments into a larger value. The nature of the operation (atomic_or,
* atomic_add) depends on a field in the CEQ. There is also a data blob field,
* last-write-wins. There aren't a lot of guarantees associated with it, but
* it's still useful for some apps.
*
* Internally, a CEQ maintains an array of ceq_event structs for every possible
* ID, so the maximum ID should be known at ceq creation time. These structs
* coalesce the events. To keep the consumer from needing to scan the array for
* activity, there is a separate ring buffer that contains the indices of
* ceq_events with activity. This is the "dense array."
*
* The ring buffer is actually an optimization. If anything goes wrong, we can
* tell the consumer to scan the entire array. Likewise, spurious entries in
* the ring are safe; the consumer just does an extra check.
*
* In general, every time we have an event, we make sure there's a pointer in
* the ring. That's the purposed of 'idx_posted' - whether or not we think our
* index is posted in the ring. */
#pragma once
#include <ros/bits/event.h>
#include <ros/atomic.h>
#include <ros/ring_buffer.h>
#define CEQ_OR 1
#define CEQ_ADD 2
struct ceq_event {
atomic_t coalesce; /* ev_arg2 */
uint64_t blob_data; /* ev_arg3 */
bool idx_posted; /* syncing with cons */
uint64_t user_data;
};
/* The events array and the ring buffer are provided by the consumer.
*
* Ring values are -1 for "unconsumed" and an index into *events otherwise.
*
* Similar to BCQs, the ring buffer must be a power of two and is managed with
* three index variables:
*
* prod_idx: the next slot to be produced
* cons_pvt_idx: the next slot a consumer can claim
* cons_pub_idx: the last slot (farthest left / oldest) that hasn't been
* consumed/made ready to be produced by the producer (it is
* what the consumer produces).
*
* The ring is has no new entries that need consumed when prod == pvt. The
* number of entries filled is prod - pub. The number of available entries
* (nr_empty) is the size - (prod - pub). */
struct ceq {
struct ceq_event *events; /* consumer pointer */
unsigned int nr_events;
unsigned int last_recovered;
atomic_t max_event_ever;
int32_t *ring; /* consumer pointer */
uint32_t ring_sz; /* size (power of 2) */
uint8_t operation; /* e.g. CEQ_OR */
bool ring_overflowed;
bool overflow_recovery;
atomic_t prod_idx; /* next slot to fill */
atomic_t cons_pub_idx; /* consumed so far */
atomic_t cons_pvt_idx; /* next slot to get */
uint32_t u_lock[2]; /* user space lock */
};