new stacktrace function in libpbd3; variable size GUI request thread queues
[ardour.git] / libs / pbd3 / pbd / ringbuffer.h
1 /*
2     Copyright (C) 2000 Paul Davis & Benno Senoner
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 of the License, or
7     (at your option) 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; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18     $Id$
19 */
20
21 #ifndef ringbuffer_h
22 #define ringbuffer_h
23
24 #include <sys/mman.h>
25 #include <pbd/atomic.h>
26
27 template<class T>
28 class RingBuffer 
29 {
30   public:
31         RingBuffer (size_t sz) {
32                 size_t power_of_two;
33                 
34                 for (power_of_two = 1; 1U<<power_of_two < sz; power_of_two++);
35                 
36                 size = 1<<power_of_two;
37                 size_mask = size;
38                 size_mask -= 1;
39                 buf = new T[size];
40                 reset ();
41
42         };
43         
44         virtual ~RingBuffer() {
45                 delete [] buf;
46         }
47
48         void reset () {
49                 /* !!! NOT THREAD SAFE !!! */
50                 atomic_set (&write_ptr, 0);
51                 atomic_set (&read_ptr, 0);
52         }
53
54         void set (size_t r, size_t w) {
55                 /* !!! NOT THREAD SAFE !!! */
56                 atomic_set (&write_ptr, w);
57                 atomic_set (&read_ptr, r);
58         }
59         
60         size_t  read  (T *dest, size_t cnt);
61         size_t  write (T *src, size_t cnt);
62
63         struct rw_vector {
64             T *buf[2];
65             size_t len[2];
66         };
67
68         void get_read_vector (rw_vector *);
69         void get_write_vector (rw_vector *);
70         
71         void decrement_read_ptr (size_t cnt) {
72                 atomic_set (&read_ptr, (atomic_read(&read_ptr) - cnt) & size_mask);
73         }                
74
75         void increment_read_ptr (size_t cnt) {
76                 atomic_set (&read_ptr, (atomic_read(&read_ptr) + cnt) & size_mask);
77         }                
78
79         void increment_write_ptr (size_t cnt) {
80                 atomic_set (&write_ptr,  (atomic_read(&write_ptr) + cnt) & size_mask);
81         }                
82
83         size_t write_space () {
84                 size_t w, r;
85                 
86                 w = atomic_read (&write_ptr);
87                 r = atomic_read (&read_ptr);
88                 
89                 if (w > r) {
90                         return ((r - w + size) & size_mask) - 1;
91                 } else if (w < r) {
92                         return (r - w) - 1;
93                 } else {
94                         return size - 1;
95                 }
96         }
97         
98         size_t read_space () {
99                 size_t w, r;
100                 
101                 w = atomic_read (&write_ptr);
102                 r = atomic_read (&read_ptr);
103                 
104                 if (w > r) {
105                         return w - r;
106                 } else {
107                         return (w - r + size) & size_mask;
108                 }
109         }
110
111         T *buffer () { return buf; }
112         size_t get_write_ptr () const { return atomic_read (&write_ptr); }
113         size_t get_read_ptr () const { return atomic_read (&read_ptr); }
114         size_t bufsize () const { return size; }
115
116   protected:
117         T *buf;
118         size_t size;
119         atomic_t write_ptr;
120         atomic_t read_ptr;
121         size_t size_mask;
122 };
123
124 template<class T> size_t
125 RingBuffer<T>::read (T *dest, size_t cnt)
126 {
127         size_t free_cnt;
128         size_t cnt2;
129         size_t to_read;
130         size_t n1, n2;
131         size_t priv_read_ptr;
132
133         priv_read_ptr=atomic_read(&read_ptr);
134
135         if ((free_cnt = read_space ()) == 0) {
136                 return 0;
137         }
138
139         to_read = cnt > free_cnt ? free_cnt : cnt;
140         
141         cnt2 = priv_read_ptr + to_read;
142
143         if (cnt2 > size) {
144                 n1 = size - priv_read_ptr;
145                 n2 = cnt2 & size_mask;
146         } else {
147                 n1 = to_read;
148                 n2 = 0;
149         }
150         
151         memcpy (dest, &buf[priv_read_ptr], n1 * sizeof (T));
152         priv_read_ptr = (priv_read_ptr + n1) & size_mask;
153
154         if (n2) {
155                 memcpy (dest+n1, buf, n2 * sizeof (T));
156                 priv_read_ptr = n2;
157         }
158
159         atomic_set(&read_ptr, priv_read_ptr);
160         return to_read;
161 }
162
163 template<class T> size_t
164 RingBuffer<T>::write (T *src, size_t cnt)
165
166 {
167         size_t free_cnt;
168         size_t cnt2;
169         size_t to_write;
170         size_t n1, n2;
171         size_t priv_write_ptr;
172
173         priv_write_ptr=atomic_read(&write_ptr);
174
175         if ((free_cnt = write_space ()) == 0) {
176                 return 0;
177         }
178
179         to_write = cnt > free_cnt ? free_cnt : cnt;
180         
181         cnt2 = priv_write_ptr + to_write;
182
183         if (cnt2 > size) {
184                 n1 = size - priv_write_ptr;
185                 n2 = cnt2 & size_mask;
186         } else {
187                 n1 = to_write;
188                 n2 = 0;
189         }
190
191         memcpy (&buf[priv_write_ptr], src, n1 * sizeof (T));
192         priv_write_ptr = (priv_write_ptr + n1) & size_mask;
193
194         if (n2) {
195                 memcpy (buf, src+n1, n2 * sizeof (T));
196                 priv_write_ptr = n2;
197         }
198
199         atomic_set(&write_ptr, priv_write_ptr);
200         return to_write;
201 }
202
203 template<class T> void
204 RingBuffer<T>::get_read_vector (RingBuffer<T>::rw_vector *vec)
205
206 {
207         size_t free_cnt;
208         size_t cnt2;
209         size_t w, r;
210         
211         w = atomic_read (&write_ptr);
212         r = atomic_read (&read_ptr);
213         
214         if (w > r) {
215                 free_cnt = w - r;
216         } else {
217                 free_cnt = (w - r + size) & size_mask;
218         }
219
220         cnt2 = r + free_cnt;
221
222         if (cnt2 > size) {
223                 /* Two part vector: the rest of the buffer after the
224                    current write ptr, plus some from the start of 
225                    the buffer.
226                 */
227
228                 vec->buf[0] = &buf[r];
229                 vec->len[0] = size - r;
230                 vec->buf[1] = buf;
231                 vec->len[1] = cnt2 & size_mask;
232
233         } else {
234                 
235                 /* Single part vector: just the rest of the buffer */
236                 
237                 vec->buf[0] = &buf[r];
238                 vec->len[0] = free_cnt;
239                 vec->len[1] = 0;
240         }
241 }
242
243 template<class T> void
244 RingBuffer<T>::get_write_vector (RingBuffer<T>::rw_vector *vec)
245
246 {
247         size_t free_cnt;
248         size_t cnt2;
249         size_t w, r;
250         
251         w = atomic_read (&write_ptr);
252         r = atomic_read (&read_ptr);
253         
254         if (w > r) {
255                 free_cnt = ((r - w + size) & size_mask) - 1;
256         } else if (w < r) {
257                 free_cnt = (r - w) - 1;
258         } else {
259                 free_cnt = size - 1;
260         }
261         
262         cnt2 = w + free_cnt;
263
264         if (cnt2 > size) {
265                 
266                 /* Two part vector: the rest of the buffer after the
267                    current write ptr, plus some from the start of 
268                    the buffer.
269                 */
270
271                 vec->buf[0] = &buf[w];
272                 vec->len[0] = size - w;
273                 vec->buf[1] = buf;
274                 vec->len[1] = cnt2 & size_mask;
275         } else {
276                 vec->buf[0] = &buf[w];
277                 vec->len[0] = free_cnt;
278                 vec->len[1] = 0;
279         }
280 }
281
282
283 #endif /* __ringbuffer_h__ */