This section will provide some annotated examples of ring buffers in action. We will build a pair of simple ring buffer programs.
The first called consumer will create a ring buffer, attach to it as a consumer and just accept all the data that is being written to the ring buffer. consumer will not exit unless you interrupt it. Data will be read in 1Mbyte chunks.
The second program called producer will attach to a ring buffer and write several data buffers to the ring. The data buffers will consist of counting patterns and will be 1Mbyte long.
Used together, these programs can (and have been), used to measure raw ring buffer performance. Throughout, we will do essentially no error checking for the sake of brevity and clarity. Production quality software, should check for errors at every step and consideration must be given to graceful error recovery or at least error reportage.
Before a ring buffer can be used, it must be created. When creating a ring buffer, the creator can specify the size of the ring buffer (which determines the size of the largest message that can be sent to the ring), as well as the maximum number of consumers that can connect to the ring.
The consumer program will create the ring buffer. It will be run first and sit there expecting buffers from the producer, which will be run later. In this way, the consumer will not miss any data that might have been written prior to its startup.
Let's look at the consumer code:
Example 29-4. Sample ring buffer consumer
#include <string> #include <CRingBuffer.h> using namespace std; int main(int argc, char**argv) { string ringname("timing"); CRingBuffer::create(ringname); CRingBuffer ring(ringname, CRingBuffer::consumer); ring.setPollInterval(1); char buffer[1024*1024]; while(1) { ring.get(buffer, sizeof(buffer), sizeof(buffer)); } }
As you can see, this is a relatively short and simple program.
CRingBuffer
class which provides the ring buffer application
programming interface
string
class, which lives in the
std
namespace, we won't have to specify it as
std::string
create
static
member function. This function creates the ring
buffer and formats its header. If the ring buffer
is already created, it is still formatted.
In general, the ring buffer should not be formatted
in the middle of a running application.
Additional optional parameters to the
create
method allow you to specify the size of the data region
in the ring buffer as well as the maximum number of
consumers that can connect to the ring.
CRingBuffer
object. There are two types of
CRingBuffer
objects;
producer and
consumer
objects.
As their names imply, producer objects
insert data into the ring buffer, while
consumers remove data from the ring buffer.
There can only be one producer at a time.
consumer
creates a consumer ring buffer.
The polling interval determines the latency and the cost in compute cycles to wait for data. The lower the poll interval, the lower the latency. the lower the poll interval, the more compute intensive waiting is.
setPollInterval
sets the poll interval. In this case we set the
poll interval to 1 millisecond.
get
member function gets data from the ring buffer
blocking if necessary. The two sizes are
the size of the buffer (maximum number of bytes
that can be read), and the minimum number of
bytes that must be available for transfer to this
consumer prior to satisfying the
get
.
If necessary, the
get
method will block until the minimum required
bytes of data are available.
Our ring buffer producer program will assume that the ring buffer has already been created. It will attach to the ring buffer as a producer, and send 10,000 messages, where each message is 1024*1024 bytes (1 Megabyte).
Example 29-5. A sample Ring Buffer producer program
#include <string> #include <CRingBuffer.h> using namespace std; int main(int argc, char** argv) { string ringname("timing"); CRingBuffer ring(ringname, CRingBuffer::producer); ring.setPollInterval(1); char buffer[1024*1024]; for (int i =0; i < sizeof(buffer); i++) { buffer[i] = i; } for (int i =0; i < 10000; i++) { ring.put(buffer, sizeof(buffer)); } }
CRingBuffer
class that serves as the API to
ring buffers.
Note that the producer and consumer programs must agree on a message format. In our simple examples, the messages have been fixed length 1Mbytes messages. A more general protocol might include a message size at the front of the message.