CPPMyth
Library to interoperate with MythTV server
Loading...
Searching...
No Matches
ringbuffer.cpp
1/*
2 * Copyright (C) 2022 Jean-Luc Barriere
3 *
4 * This Program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2, or (at your option)
7 * any later version.
8 *
9 * This Program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; see the file COPYING. If not, write to
16 * the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston,
17 * MA 02110-1301 USA
18 * http://www.gnu.org/copyleft/gpl.html
19 *
20 */
21
22#include "ringbuffer.h"
23
24#include "os/threads/mutex.h"
25
26using namespace NSROOT;
27
28RingBufferPacket::RingBufferPacket(int _capacity)
29: id(0)
30, size(0)
31, data(new char [_capacity])
32, capacity(_capacity)
33{
34}
35
36RingBufferPacket::~RingBufferPacket()
37{
38 if (data)
39 delete [] data;
40}
41
42RingBuffer::RingBuffer(int capacity)
43: m_ringlock(new OS::Mutex())
44, m_poollock(new OS::Mutex())
45, m_capacity(capacity)
46, m_count(0)
47, m_unread(0)
48, m_buffer()
49, m_read(nullptr)
50, m_write(nullptr)
51, m_pool()
52{
53 assert(capacity > 0);
54 m_buffer.resize(capacity);
55 init();
56}
57
58RingBuffer::~RingBuffer()
59{
60 m_ringlock->lock();
61 for (std::vector<Chunk*>::iterator it = m_buffer.begin(); it != m_buffer.end(); ++it)
62 delete *it;
63 m_ringlock->unlock();
64 m_poollock->lock();
65 while (!m_pool.empty())
66 {
67 delete m_pool.front();
68 m_pool.pop_front();
69 }
70 m_poollock->unlock();
71 delete m_poollock;
72 delete m_ringlock;
73}
74
76{
77 Chunk * previous = nullptr;
78 for (std::vector<Chunk*>::iterator it = m_buffer.begin(); it != m_buffer.end(); ++it)
79 {
80 *it = new Chunk();
81 if (previous)
82 previous->next = *it;
83 previous = *it;
84 }
85 if (previous)
86 previous->next = *(m_buffer.begin());
87 m_write = *(m_buffer.begin());
89}
90
92{
93 return m_capacity;
94}
95
97{
98 OS::LockGuard g(*m_ringlock);
99 if (!m_unread)
100 return 0;
101 // fix overwriting: move to the next available
102 while(m_read->packet == nullptr)
103 m_read = m_read->next;
104 return m_read->packet->size;
105}
106
108{
109 OS::LockGuard g(*m_ringlock);
110 return m_unread;
111}
112
114{
115 OS::LockGuard g(*m_ringlock);
116 return (m_unread && m_read == m_write);
117}
118
120{
121 OS::LockGuard g(*m_ringlock);
122 // reset of unread implies the reset of packet size
123 // so clean all chunks in the buffer
124 for (std::vector<Chunk*>::iterator it = m_buffer.begin(); it != m_buffer.end(); ++it)
125 {
126 if ((*it)->packet)
127 freePacket((*it)->packet);
128 (*it)->packet = nullptr;
129 }
130 m_count = m_unread = 0;
131 m_read = m_write;
132}
133
134int RingBuffer::write(const char * data, int len)
135{
136 if (len > 0)
137 {
138 RingBufferPacket * _packet = needPacket(len);
139 _packet->size = len;
140 memcpy(_packet->data, data, len);
141 {
142 OS::LockGuard g(*m_ringlock);
143 if (m_write->packet)
144 {
145 // overwriting a packet implies to update unread because the data will be destroyed,
146 // and no longer available for reading.
147 m_unread -= m_write->packet->size;
148 freePacket(m_write->packet);
149 }
150 m_write->packet = _packet;
151 m_write->packet->id = ++m_count;
152 m_write = m_write->next;
153 m_unread += _packet->size;
154 }
155 }
156 return len;
157}
158
160{
161 RingBufferPacket * _packet = needPacket(len);
162 _packet->size = 0;
163 return _packet;
164}
165
167{
168 if (packet)
169 {
170 OS::LockGuard g(*m_ringlock);
171 if (m_write->packet)
172 {
173 // overwriting a packet implies to update unread because the data will be destroyed,
174 // and no longer available for reading.
175 m_unread -= m_write->packet->size;
176 freePacket(m_write->packet);
177 }
178 m_write->packet = packet;
179 m_write->packet->id = ++m_count;
180 m_write = m_write->next;
181 m_unread += packet->size;
182 }
183}
184
186{
187 RingBufferPacket * p = nullptr;
188 {
189 OS::LockGuard g(*m_ringlock);
190 if (m_unread)
191 {
192 // fix overwriting: move to the next available
193 while (m_read->packet == nullptr)
194 m_read = m_read->next;
195 p = m_read->packet;
196 m_read->packet = nullptr;
197 m_read = m_read->next;
198 m_unread -= p->size;
199 }
200 }
201 return p;
202}
203
205{
206 m_poollock->lock();
207 m_pool.push_back(p);
208 m_poollock->unlock();
209}
210
211RingBufferPacket * RingBuffer::needPacket(int size)
212{
213 RingBufferPacket * p = nullptr;
214 m_poollock->lock();
215 if (!m_pool.empty())
216 {
217 p = m_pool.front();
218 m_pool.pop_front();
219 m_poollock->unlock();
220 if (p->capacity >= size)
221 {
222 p->id = 0;
223 return p;
224 }
225 //DBG(DBG_DEBUG, "%s: freed packet from buffer (%d)\n", __FUNCTION__, p->capacity);
226 delete p;
227 }
228 else
229 {
230 m_poollock->unlock();
231 }
232 p = new RingBufferPacket(size);
233 //DBG(DBG_DEBUG, "%s: allocated packet to buffer (%d)\n", __FUNCTION__, p->capacity);
234 return p;
235}
bool full() const
int capacity() const
RingBufferPacket * read()
unsigned m_count
buffer size
Definition ringbuffer.h:141
Chunk * m_write
chunk to read
Definition ringbuffer.h:154
unsigned m_unread
total count of processed chunk
Definition ringbuffer.h:142
void freePacket(RingBufferPacket *p)
unsigned bytesUnread() const
RingBufferPacket * newPacket(int len)
void init()
chunk to write
int write(const char *data, int len)
Chunk * m_read
buffer of chunk
Definition ringbuffer.h:153
void writePacket(RingBufferPacket *packet)
total size of unread data in the buffer
Definition ringbuffer.h:145