40dabbff0e769a13e47e145aa6449394d59802ec
[ardour.git] / libs / backends / wavesaudio / portmidi / pmutil.h
1 /* pmutil.h -- some helpful utilities for building midi \r
2                applications that use PortMidi \r
3  */\r
4 \r
5 #ifdef __cplusplus\r
6 extern "C" {\r
7 #endif /* __cplusplus */\r
8 \r
9 typedef void PmQueue;\r
10 \r
11 /*\r
12     A single-reader, single-writer queue is created by\r
13     Pm_QueueCreate(), which takes the number of messages and\r
14     the message size as parameters. The queue only accepts\r
15     fixed sized messages. Returns NULL if memory cannot be allocated.\r
16 \r
17     This queue implementation uses the "light pipe" algorithm which\r
18     operates correctly even with multi-processors and out-of-order\r
19     memory writes. (see Alexander Dokumentov, "Lock-free Interprocess\r
20     Communication," Dr. Dobbs Portal, http://www.ddj.com/, \r
21     articleID=189401457, June 15, 2006. This algorithm requires\r
22     that messages be translated to a form where no words contain\r
23     zeros. Each word becomes its own "data valid" tag. Because of\r
24     this translation, we cannot return a pointer to data still in \r
25     the queue when the "peek" method is called. Instead, a buffer \r
26     is preallocated so that data can be copied there. Pm_QueuePeek() \r
27     dequeues a message into this buffer and returns a pointer to \r
28     it. A subsequent Pm_Dequeue() will copy from this buffer.\r
29 \r
30     This implementation does not try to keep reader/writer data in\r
31     separate cache lines or prevent thrashing on cache lines. \r
32     However, this algorithm differs by doing inserts/removals in\r
33     units of messages rather than units of machine words. Some\r
34     performance improvement might be obtained by not clearing data\r
35     immediately after a read, but instead by waiting for the end\r
36     of the cache line, especially if messages are smaller than\r
37     cache lines. See the Dokumentov article for explanation.\r
38 \r
39     The algorithm is extended to handle "overflow" reporting. To report\r
40     an overflow, the sender writes the current tail position to a field.\r
41     The receiver must acknowlege receipt by zeroing the field. The sender\r
42     will not send more until the field is zeroed.\r
43     \r
44     Pm_QueueDestroy() destroys the queue and frees its storage.\r
45  */\r
46 \r
47 PMEXPORT PmQueue *Pm_QueueCreate(long num_msgs, int32_t bytes_per_msg);\r
48 PMEXPORT PmError Pm_QueueDestroy(PmQueue *queue);\r
49 \r
50 /* \r
51     Pm_Dequeue() removes one item from the queue, copying it into msg.\r
52     Returns 1 if successful, and 0 if the queue is empty.\r
53     Returns pmBufferOverflow if what would have been the next thing\r
54     in the queue was dropped due to overflow. (So when overflow occurs,\r
55     the receiver can receive a queue full of messages before getting the\r
56     overflow report. This protocol ensures that the reader will be \r
57     notified when data is lost due to overflow.\r
58  */\r
59 PMEXPORT PmError Pm_Dequeue(PmQueue *queue, void *msg);\r
60 \r
61 \r
62 /*\r
63     Pm_Enqueue() inserts one item into the queue, copying it from msg.\r
64     Returns pmNoError if successful and pmBufferOverflow if the queue was \r
65     already full. If pmBufferOverflow is returned, the overflow flag is set.\r
66  */\r
67 PMEXPORT PmError Pm_Enqueue(PmQueue *queue, void *msg);\r
68 \r
69 \r
70 /*\r
71     Pm_QueueFull() returns non-zero if the queue is full\r
72     Pm_QueueEmpty() returns non-zero if the queue is empty\r
73 \r
74     Either condition may change immediately because a parallel\r
75     enqueue or dequeue operation could be in progress. Furthermore,\r
76     Pm_QueueEmpty() is optimistic: it may say false, when due to \r
77     out-of-order writes, the full message has not arrived. Therefore,\r
78     Pm_Dequeue() could still return 0 after Pm_QueueEmpty() returns\r
79     false. On the other hand, Pm_QueueFull() is pessimistic: if it\r
80     returns false, then Pm_Enqueue() is guaranteed to succeed. \r
81 \r
82     Error conditions: Pm_QueueFull() returns pmBadPtr if queue is NULL.\r
83     Pm_QueueEmpty() returns FALSE if queue is NULL.\r
84  */\r
85 PMEXPORT int Pm_QueueFull(PmQueue *queue);\r
86 PMEXPORT int Pm_QueueEmpty(PmQueue *queue);\r
87 \r
88 \r
89 /*\r
90     Pm_QueuePeek() returns a pointer to the item at the head of the queue,\r
91     or NULL if the queue is empty. The item is not removed from the queue.\r
92     Pm_QueuePeek() will not indicate when an overflow occurs. If you want\r
93     to get and check pmBufferOverflow messages, use the return value of\r
94     Pm_QueuePeek() *only* as an indication that you should call \r
95     Pm_Dequeue(). At the point where a direct call to Pm_Dequeue() would\r
96     return pmBufferOverflow, Pm_QueuePeek() will return NULL but internally\r
97     clear the pmBufferOverflow flag, enabling Pm_Enqueue() to resume\r
98     enqueuing messages. A subsequent call to Pm_QueuePeek()\r
99     will return a pointer to the first message *after* the overflow. \r
100     Using this as an indication to call Pm_Dequeue(), the first call\r
101     to Pm_Dequeue() will return pmBufferOverflow. The second call will\r
102     return success, copying the same message pointed to by the previous\r
103     Pm_QueuePeek().\r
104 \r
105     When to use Pm_QueuePeek(): (1) when you need to look at the message\r
106     data to decide who should be called to receive it. (2) when you need\r
107     to know a message is ready but cannot accept the message.\r
108 \r
109     Note that Pm_QueuePeek() is not a fast check, so if possible, you \r
110     might as well just call Pm_Dequeue() and accept the data if it is there.\r
111  */\r
112 PMEXPORT void *Pm_QueuePeek(PmQueue *queue);\r
113 \r
114 /*\r
115     Pm_SetOverflow() allows the writer (enqueuer) to signal an overflow\r
116     condition to the reader (dequeuer). E.g. when transfering data from \r
117     the OS to an application, if the OS indicates a buffer overrun,\r
118     Pm_SetOverflow() can be used to insure that the reader receives a\r
119     pmBufferOverflow result from Pm_Dequeue(). Returns pmBadPtr if queue\r
120     is NULL, returns pmBufferOverflow if buffer is already in an overflow\r
121     state, returns pmNoError if successfully set overflow state.\r
122  */\r
123 PMEXPORT PmError Pm_SetOverflow(PmQueue *queue);\r
124 \r
125 #ifdef __cplusplus\r
126 }\r
127 #endif /* __cplusplus */\r