Looking for a lockable RT-security system with one reader with one reader

I am looking for a design without blocking that matches these details:

  • a single writer writes to a structure and reads one reader from this structure (this structure already exists and is safe for reading / writing at the same time)
  • but at some point, the structure must be changed by the author , who then initializes, switches, and writes to the new structure (of the same type, but with new content).
  • and the next time the reader reads, he will switch to this new structure (if the author is multiplied by a new blocking structure, the reader discards these structures, ignoring their data).
  • Structures must be reused, that is, during the write / read / switch operation, allocation / freeing of heap memory for RT purposes is not allowed.

I have currently implemented a ringbuffer containing several instances of these structures; but this implementation suffers from the fact that when the author used all the structures present in the ringbuffer, there is no more room for a change from the structure ... But the rest of the ringbuffer has some data that does not need to be read by the reader, but cannot be reused a writer. As a result, ringbuffer is not suitable for this purpose.

Any design idea (name or pseudo-implementation) without blocking Thank you for considering this problem.

+6
multithreading design lock-free real-time
source share
2 answers

You are on the right track.

Blocking free exchange of fixed messages between threads / processes / processors alt text

Fixed-size ring buffers can be used for seamless communication between threads, processes, or processors if there is one manufacturer and one consumer. Some checks to perform:

The head variable is written only by the manufacturer (as an atomic action after writing)

the tail variable is written only by the consumer (as an atomic action after reading)

Trap: introducing a variable size or fill / empty buffer flag; they are usually written by both the manufacturer and the consumer, and therefore can cause problems.

I usually use ring buffers for this purpose. The most important lesson I learned is that a ring buffer cannot contain more elements. Thus, the variable head and tail are written by the producer and the consumer.

Extension for large / variable sized blocks. To use buffers in a real-time environment, you can use memory pools (often available in optimized form on real-time operating systems) or separate the distribution from use. The latter approaches the question, I think.

extended queue

If you need to exchange large blocks, I suggest using a pool with buffer blocks and reporting pointers to buffers using a queue. So use the third line with buffer pointers. Thus, the distribution can be performed in the application (in the background), and your part in real time has access to a variable amount of memory.

request

while (blockQueue.full != true) { buf = allocate block of memory from heap or buffer pool msg = { .... , buf }; blockQueue.Put(msg) } Producer: pBuf = blockQueue.Get() pQueue.Put() Consumer if (pQueue.Empty == false) { msg=pQueue.Get() // use info in msg, with buf pointer // optionally indicate that buf is no longer used } 
0
source share

Here is one. The key is that there are three buffers, and the reader reserves the buffer from which it reads. The writer writes to one of the other two buffers. Collision risk is minimal. In addition, it is expanding. Just make your elements in arrays one element longer than the number of readers, as well as the number of authors.

 class RingBuffer { RingBuffer():lastFullWrite(0) { //Initialize the elements of dataBeingRead to false for(unsigned int i=0; i<DATA_COUNT; i++) { dataBeingRead[i] = false; } } Data read() { // You may want to check to make sure write has been called once here // to prevent read from grabbing junk data. Else, initialize the elements // of dataArray to something valid. unsigned int indexToRead = lastFullWriteIndex; Data dataCopy; dataBeingRead[indexToRead] = true; dataCopy = dataArray[indexToRead]; dataBeingRead[indexToRead] = false; return dataCopy; } void write( const Data& dataArg ) { unsigned int writeIndex(0); //Search for an unused piece of data. // It O(n), but plenty fast enough for small arrays. while( true == dataBeingRead[writeIndex] && writeIndex < DATA_COUNT ) { writeIndex++; } dataArray[writeIndex] = dataArg; lastFullWrite = &dataArray[writeIndex]; } private: static const unsigned int DATA_COUNT; unsigned int lastFullWrite; Data dataArray[DATA_COUNT]; bool dataBeingRead[DATA_COUNT]; }; 

Note. As it is written here, there are two copies to read your data. If you pass data from a read function through a reference argument, you can reduce this to one copy.

0
source share

All Articles