gearmulator

Emulation of classic VA synths of the late 90s/2000s that are based on Motorola 56300 family DSPs
Log | Files | Refs | Submodules | README | LICENSE

pmutil.h (5779B)


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