PortMidi
Cross-platform MIDI IO library
|
Typedefs | |
typedef void | PmQueue |
The queue representation is opaque. More... | |
Functions | |
PMEXPORT PmQueue * | Pm_QueueCreate (long num_msgs, int32_t bytes_per_msg) |
create a single-reader, single-writer queue. More... | |
PMEXPORT PmError | Pm_QueueDestroy (PmQueue *queue) |
destroy a queue and free its storage. More... | |
PMEXPORT PmError | Pm_Dequeue (PmQueue *queue, void *msg) |
remove one message from the queue, copying it into msg . More... | |
PMEXPORT PmError | Pm_Enqueue (PmQueue *queue, void *msg) |
insert one message into the queue, copying it from msg . More... | |
PMEXPORT int | Pm_QueueFull (PmQueue *queue) |
test if the queue is full. More... | |
PMEXPORT int | Pm_QueueEmpty (PmQueue *queue) |
test if the queue is empty. More... | |
PMEXPORT void * | Pm_QueuePeek (PmQueue *queue) |
get a pointer to the item at the head of the queue. More... | |
PMEXPORT PmError | Pm_SetOverflow (PmQueue *queue) |
allows the writer (enqueuer) to signal an overflow condition to the reader (dequeuer). More... | |
typedef void PmQueue |
The queue representation is opaque.
Declare a queue as PmQueue *
remove one message from the queue, copying it into msg
.
queue | a queue created by Pm_QueueCreate(). |
msg | address to which the message, if any, is copied. |
insert one message into the queue, copying it from msg
.
queue | a queue created by Pm_QueueCreate(). |
msg | address of the message to be enqueued. |
PMEXPORT PmQueue * Pm_QueueCreate | ( | long | num_msgs, |
int32_t | bytes_per_msg | ||
) |
create a single-reader, single-writer queue.
num_msgs | the number of messages the queue can hold |
the | fixed message size |
The queue only accepts fixed sized messages.
This queue implementation uses the "light pipe" algorithm which operates correctly even with multi-processors and out-of-order memory writes. (see Alexander Dokumentov, "Lock-free Interprocess Communication," Dr. Dobbs Portal, http://www.ddj.com/, articleID=189401457, June 15, 2006. This algorithm requires that messages be translated to a form where no words contain zeros. Each word becomes its own "data valid" tag. Because of this translation, we cannot return a pointer to data still in the queue when the "peek" method is called. Instead, a buffer is preallocated so that data can be copied there. Pm_QueuePeek() dequeues a message into this buffer and returns a pointer to it. A subsequent Pm_Dequeue() will copy from this buffer.
This implementation does not try to keep reader/writer data in separate cache lines or prevent thrashing on cache lines. However, this algorithm differs by doing inserts/removals in units of messages rather than units of machine words. Some performance improvement might be obtained by not clearing data immediately after a read, but instead by waiting for the end of the cache line, especially if messages are smaller than cache lines. See the Dokumentov article for explanation.
The algorithm is extended to handle "overflow" reporting. To report an overflow, the sender writes the current tail position to a field. The receiver must acknowlege receipt by zeroing the field. The sender will not send more until the field is zeroed.
destroy a queue and free its storage.
queue | a queue created by Pm_QueueCreate(). |
Uses #pm_free().
PMEXPORT int Pm_QueueEmpty | ( | PmQueue * | queue | ) |
test if the queue is empty.
queue | a queue created by Pm_QueueCreate(). |
The empty condition may change immediately because a parallel enqueue operation could be in progress. Furthermore, the result is optimistic: it may say false, when due to out-of-order writes, the full message has not arrived. Therefore, Pm_Dequeue() could still return 0 after Pm_QueueEmpty() returns false.
PMEXPORT int Pm_QueueFull | ( | PmQueue * | queue | ) |
test if the queue is full.
queue | a queue created by Pm_QueueCreate(). |
queue
is NULL.The full condition may change immediately because a parallel dequeue operation could be in progress. The result is pessimistic: if it returns false (zero) to the single writer, then Pm_Enqueue() is guaranteed to succeed.
PMEXPORT void * Pm_QueuePeek | ( | PmQueue * | queue | ) |
get a pointer to the item at the head of the queue.
queue | a queue created by Pm_QueueCreate(). |
The message is not removed from the queue. Pm_QueuePeek() will not indicate when an overflow occurs. If you want to get and check #pmBufferOverflow messages, use the return value of Pm_QueuePeek() only as an indication that you should call Pm_Dequeue(). At the point where a direct call to Pm_Dequeue() would return #pmBufferOverflow, Pm_QueuePeek() will return NULL, but internally clear the #pmBufferOverflow flag, enabling Pm_Enqueue() to resume enqueuing messages. A subsequent call to Pm_QueuePeek() will return a pointer to the first message after the overflow. Using this as an indication to call Pm_Dequeue(), the first call to Pm_Dequeue() will return #pmBufferOverflow. The second call will return success, copying the same message pointed to by the previous Pm_QueuePeek().
When to use Pm_QueuePeek(): (1) when you need to look at the message data to decide who should be called to receive it. (2) when you need to know a message is ready but cannot accept the message.
Note that Pm_QueuePeek() is not a fast check, so if possible, you might as well just call Pm_Dequeue() and accept the data if it is there.
allows the writer (enqueuer) to signal an overflow condition to the reader (dequeuer).
queue | a queue created by Pm_QueueCreate(). |
E.g., when transfering data from the OS to an application, if the OS indicates a buffer overrun, Pm_SetOverflow() can be used to insure that the reader receives a #pmBufferOverflow result from Pm_Dequeue().