Click here to Skip to main content
15,886,199 members
Articles / Programming Languages / C++
Article

Circular Buffer Made Easy

Rate me:
Please Sign up or sign in to vote.
2.41/5 (9 votes)
6 Nov 20064 min read 68.7K   895   17   14
A circular buffer with thread safe read/write operations.

Introduction

A circular buffer is a fixed amount of memory area that can be used in a circular fashion. That means data can be stored from the beginning of the buffer to the end and then repeat the process. The process is same for the retrieval operation. Here, the reading and writing operations are supposed to be done from different threads. In this case, there arises two conditions. One is to check whether there is enough space in the buffer to perform a write operation. If not, the condition is called a buffer overrun. The second is to check whether there is enough data to be read. If not, the condition is called a buffer underrun.

This implementation of a circular buffer handles both buffer underruns and buffer overruns. Data can be written to the buffer from one thread and the same can be read from another thread. Two indices are used to track the position of both read and write positions inside the buffer. Following are the assumptions made in this implementation of a circular buffer.

  1. The size of the circular buffer is fixed.
  2. The circular buffer is supposed to handle a known amount of data, however large it may be. This means that the reading thread will read all the data that is written by the writing thread, and the writing thread will continue writing till the known amount of data is reached. Otherwise, either the read or the write function will infinitely wait for the data or space respectively.

Before a read or write operation is done, it will check for the availability of data or space using these indices. After a read/write operation, these indices are updated according to the amount of data that is read or written. The following code shows a read operation.

BOOL CCircularBuffer::ReadBuffer(PBYTE pBuff, LONG lSize)
{
 LONG lDist;
 /*wait for enough data*/
 for(;;)
 {
  if(GetBufferArea() >= lSize)
   break;
  Sleep(10);
 }
 m_Lock.Lock();
 if( ( m_ReadPtr + lSize) > CIRCULAR_BUFFER_SIZE )  
 {
  lDist  =  CIRCULAR_BUFFER_SIZE - m_ReadPtr;
  CopyMemory(pBuff, (m_pBuffer + m_ReadPtr), lDist);
  CopyMemory(pBuff + lDist, m_pBuffer, lSize - lDist);
  m_ReadPtr = lSize - lDist;
 }
 else
 {
  CopyMemory(pBuff, (m_pBuffer + m_ReadPtr), lSize);
  m_ReadPtr += lSize;
 }
 m_Lock.Unlock();
 return TRUE;
}

A lock is used for getting a mutually exclusive access to the read and write indices. The function GetBufferArea()will calculate the amount of data available in the buffer. If sufficient data is not available, it will wait till it is available. If anyone can suggest a more efficient method, they are most welcome. In the same way, the write method uses a function named GetBufferSpace()which will return the amount of space available in the buffer.

The function InitBuffer() should be called before the buffer can be used. This function allocates a fixed amount of memory for the circular buffer.

Case Study

CD burning

Here, the requirement is to burn a set of files to a CD. In order to burn a CD, an ISO9660 image is to be created. This image is then written to a blank CD. What is done is, the image is created and is written to a circular buffer from one thread, and a CD burning thread will read the date from the circular buffer and writes to a blank CD. The ISO image is built using a two pass method. After the first pass, the size of the image is known and hence the writing thread is aware of the size of the image so that the thread can keep reading and burning till the known size is reached.

In this case, the size of the circular buffer is 100 MB. First, the image building thread is started and the main thread will wait for some time, and then the writing thread is started. This is because once the burning is started, there should be an uninterrupted flow of data, otherwise some CD-RW which doesn’t have Buffer-Underrun-Protection feature will fail to burn the entire image before it is completed.

The use of the above described circular buffer is found very effective in the above case.

Video stream player

There is a given amount of MPEG data available. The MPEG data is not a file, rather it is a stream. The circular buffer has been used here in an efficient way. The MPEG stream is played using Microsoft DirectShow. Here, the data is read from the source and is written to the circular buffer from one thread. Another thread will read the data from the buffer and play the stream.

The downloadable source files contain two header files. One is the Lock.h which is the declaration of the class CLock, which is used for getting an exclusive access to some of the shared variables. Include the header file named CircularBuffer.h into your project, and declare an object of the class CCircularBuffer and use that object as described above.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
United States United States
Shooting for the Moon.

Comments and Discussions

 
GeneralMy vote of 3 Pin
SYAMKUMAR S8-Oct-13 22:01
professionalSYAMKUMAR S8-Oct-13 22:01 
Generalbig mistake... (everybody see this article and collect it to use. ) Pin
_Kay_21-Apr-09 20:56
_Kay_21-Apr-09 20:56 
GeneralNeed Help SOS Pin
Jawad Arshed20-Jun-08 0:49
Jawad Arshed20-Jun-08 0:49 
GeneralRe: Need Help SOS Pin
Nisamudheen6-Sep-08 20:18
Nisamudheen6-Sep-08 20:18 
GeneralOdd loop Pin
Tomillo29-Nov-07 22:14
Tomillo29-Nov-07 22:14 
GeneralRe: Odd loop Pin
stevetrace24-Apr-09 3:42
stevetrace24-Apr-09 3:42 
GeneralAn Erro Pin
danieltidus13-Nov-07 1:36
danieltidus13-Nov-07 1:36 
hi, i believe that you didn't consider the case when de both pointer are to point to the same position on buffer. The functions getBufferSpace and getBufferArea return wrong values in some cases.

I resolve the problem using this:

http://en.wikipedia.org/wiki/Circular_buffer

section: "Full / Empty Buffer Distinction"

Regards!

Daniel Faustino (vulgo Piancó)

GeneralRe: An Erro Pin
Nisamudheen25-Nov-07 19:54
Nisamudheen25-Nov-07 19:54 
Questionvideo stream player Pin
kamiyaqub21-Aug-07 22:57
kamiyaqub21-Aug-07 22:57 
QuestionCase Study CD burning Pin
bbronja7-Dec-06 9:11
bbronja7-Dec-06 9:11 
AnswerRe: Case Study CD burning Pin
Nisamudheen11-Dec-06 22:01
Nisamudheen11-Dec-06 22:01 
GeneralRe: Case Study CD burning Pin
bbronja12-Dec-06 16:53
bbronja12-Dec-06 16:53 
GeneralRe: Case Study CD burning Pin
Nisamudheen13-Dec-06 21:26
Nisamudheen13-Dec-06 21:26 
GeneralNo sample files or link is broken! Pin
bcraun6-Nov-06 2:59
bcraun6-Nov-06 2:59 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.