Skip to content

Commit 89870a8

Browse files
committed
Made RingBuf methods thread safe
1 parent e64fc36 commit 89870a8

4 files changed

Lines changed: 70 additions & 28 deletions

File tree

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# ArduinoRingBuffer
22

3-
This is a simple ring buffer library for the Arduino. It is written in vanilla C, and can easily be modified to work with other platforms simply be removing the `#include "Arduino.h" `. It can buffer any fixed size object (ints, floats, structs, etc...).
3+
This is a simple ring buffer library for the Arduino. It is written in vanilla C, and can easily be modified to work with other platforms. It can buffer any fixed size object (ints, floats, structs, etc...).
44

55
## Project History
66
I needed a way to buffer sensor events for a group engineering IOT project that I was working on at Cornell. We needed to record changes in IR trip wires that happened in ms timeframes, and tight loop polling was not working. We needed interrupts and a buffering library. I couldn't find any suitable Arduino Libraries that could buffer any sized object, so I wrote my own.
@@ -62,7 +62,7 @@ Append an element to the buffer, where object is a pointer to object you wish to
6262
void *peek(RingBuf *self, unsigned int num);
6363
```
6464

65-
Peek at the num'th element in the buffer. Returns a void pointer to the location of the num'th element. If num is out of bounds or the num'th element is empty, a NULL pointer is returned. Cast the result of this call into a pointer of whatever type you are storing in the buffer. Note that this gives you direct memory access to the location of the num'th element in the buffer, allowing you to directly edit elements in the buffer.
65+
Peek at the num'th element in the buffer. Returns a void pointer to the location of the num'th element. If num is out of bounds or the num'th element is empty, a NULL pointer is returned. Cast the result of this call into a pointer of whatever type you are storing in the buffer. Note that this gives you direct memory access to the location of the num'th element in the buffer, allowing you to directly edit elements in the buffer. Note that while all of RingBuf's public methods are thread safe (including this one), directly using the pointer returned from this method is not thread safe. To use this returned pointer safely, disable interrupts first with `noInterrupts()`, do whatever you need to do with the pointer, then you can reenable interrupts by calling `interrupts()`.
6666

6767
### pull()
6868

@@ -72,6 +72,7 @@ void *pull(RingBuf *self, void *object);
7272

7373
Pull the first element out of the buffer. The first element is copied into the location pointed to by object. Returns a NULL pointer if the buffer is empty, otherwise returns object.
7474

75+
7576
### numElements()
7677
```
7778
unsigned int numElements(RingBuf *self);

RingBuf.c

Lines changed: 65 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*/
77
#include "RingBuf.h"
88
#include <string.h>
9+
#include <util/atomic.h>
910

1011
/////// Constructor //////////
1112
RingBuf *RingBuf_new(int size, int len)
@@ -83,56 +84,97 @@ int RingBufIncrStart(RingBuf *self)
8384
// Add an object struct to RingBuf
8485
int RingBufAdd(RingBuf *self, void *object)
8586
{
86-
int index = self->next_end_index(self);
87-
if (index < 0) return -1;
88-
memcpy(self->buf + index*self->size, object, self->size);
89-
//if not empty incriment end index
90-
if (!self->isEmpty(self)) self->incr_end_index(self);
91-
self->elements++;
92-
return index;
87+
int index;
88+
// Perform all atomic opertaions
89+
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
90+
{
91+
index = self->next_end_index(self);
92+
//if not full
93+
if (index >= 0)
94+
{
95+
if (!self->isEmpty(self)) self->incr_end_index(self);
96+
self->elements++;
97+
memcpy(self->buf + index*self->size, object, self->size);
98+
}
99+
}
93100

101+
return index;
94102
}
95103

96104
// Return pointer to num element, return null on empty or num out of bounds
97105
void *RingBufPeek(RingBuf *self, unsigned int num)
98106
{
99-
//empty or out of bounds
100-
if (self->isEmpty(self) || num > self->elements - 1) return NULL;
101-
102-
return &self->buf[((self->start + num)%self->len)*self->size];
107+
void *ret;
108+
// Perform all atomic opertaions
109+
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
110+
{
111+
//empty or out of bounds
112+
if (self->isEmpty(self) || num > self->elements - 1) ret = NULL;
113+
else ret = &self->buf[((self->start + num)%self->len)*self->size];
114+
}
115+
return ret;
103116
}
104117

105118
// Returns and removes first buffer element
106119
void *RingBufPull(RingBuf *self, void *object)
107120
{
108-
if (self->isEmpty(self)) return NULL;
109-
// Copy Object
110-
memcpy(object, self->buf+self->start*self->size, self->size);
111-
//Don't mess things up to much, even if called on an empty buffer
112-
if (!self->isEmpty(self))
121+
void *ret;
122+
// Perform all atomic opertaions
123+
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
113124
{
114-
self->elements--;
115-
// don't incriment if removing last element
116-
if (!self->isEmpty(self)) self->incr_start_index(self);
125+
if (self->isEmpty(self)) ret = NULL;
126+
// Else copy Object
127+
else
128+
{
129+
memcpy(object, self->buf+self->start*self->size, self->size);
130+
self->elements--;
131+
// don't incriment start if removing last element
132+
if (!self->isEmpty(self)) self->incr_start_index(self);
133+
ret = object;
134+
}
117135
}
118136

119-
return object;
137+
return ret;
120138
}
121139

122140
// Returns number of elemnts in buffer
123141
unsigned int RingBufNumElements(RingBuf *self)
124142
{
125-
return self->elements;
143+
unsigned int elements;
144+
145+
// Perform all atomic opertaions
146+
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
147+
{
148+
elements = self->elements;
149+
}
150+
151+
return elements;
126152
}
127153

128154
// Returns true if buffer is full
129155
bool RingBufIsFull(RingBuf *self)
130156
{
131-
return self->elements == self->len;
157+
bool ret;
158+
159+
// Perform all atomic opertaions
160+
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
161+
{
162+
ret = self->elements == self->len;
163+
}
164+
165+
return ret;
132166
}
133167

134168
// Returns true if buffer is empty
135169
bool RingBufIsEmpty(RingBuf *self)
136170
{
137-
return !self->elements;
171+
bool ret;
172+
173+
// Perform all atomic opertaions
174+
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
175+
{
176+
ret = !self->elements;
177+
}
178+
179+
return ret;
138180
}

examples/RingBufInterruptsExample/RingBufInterruptsExample.ino

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ void loop() {
4949
// Print the number of elements
5050
Serial.print("There are: ");
5151
Serial.print(buf->numElements(buf));
52-
Serial.println(" elements in the ring buffer");
52+
Serial.println(" element(s) in the ring buffer");
5353

5454
// Check if empty
5555
if (buf->isEmpty(buf))
@@ -115,4 +115,3 @@ void print_buf_contents()
115115
Serial.println("______Done dumping contents_______");
116116

117117
}
118-

library.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@ sentence=A library for buffering items into a ring (circular) buffer
66
paragraph=This library is perfect for capturing pin states, timestamps, etc.. during an ISR. Then in void loop(), the buffer can be asynchronously processed whenever your program has free time.
77
category=Data Storage
88
url=https://github.com/wizard97/ArduinoRingBuffer
9-
architectures=*
9+
architectures=avr

0 commit comments

Comments
 (0)