missing include for rand() in self-tests
[ardour.git] / libs / pbd / pool.cc
1 /*
2     Copyright (C) 1998-99 Paul Barton-Davis
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 #include <cstdlib>
22 #include <vector>
23 #include <cstdlib>
24 #include <cassert>
25
26 #include "pbd/pool.h"
27 #include "pbd/pthread_utils.h"
28 #include "pbd/error.h"
29 #include "pbd/debug.h"
30 #include "pbd/compose.h"
31
32 using namespace std;
33 using namespace PBD;
34
35 Pool::Pool (string n, unsigned long item_size, unsigned long nitems)
36         : free_list (nitems)
37         , _name (n)
38 #ifndef NDEBUG
39         , max_usage (0)
40 #endif
41 {
42         _name = n;
43
44         /* since some overloaded ::operator new() might use this,
45            its important that we use a "lower level" allocator to
46            get more space.
47         */
48
49         block = malloc (nitems * item_size);
50
51         void **ptrlist = (void **) malloc (sizeof (void *)  * nitems);
52
53         for (unsigned long i = 0; i < nitems; i++) {
54                 ptrlist[i] = static_cast<void *> (static_cast<char*>(block) + (i * item_size));
55         }
56
57         free_list.write (ptrlist, nitems);
58         free (ptrlist);
59 }
60
61 Pool::~Pool ()
62 {
63 #ifndef NDEBUG
64         // TODO: after collecting some stats, use DEBUG::PoolStats here
65         cerr << "Pool: '" << _name << "' max: " << max_usage << " / " << total() << endmsg;
66 #endif
67         free (block);
68 }
69
70 /** Allocate an item's worth of memory in the Pool by taking one from the free list.
71  *  @return Pointer to free item.
72  */
73 void *
74 Pool::alloc ()
75 {
76         void *ptr;
77
78 #ifndef NDEBUG
79         if (used () > max_usage) {
80                 max_usage = used () + 1;
81         }
82 #endif
83
84         if (free_list.read (&ptr, 1) < 1) {
85                 fatal << "CRITICAL: " << _name << " POOL OUT OF MEMORY - RECOMPILE WITH LARGER SIZE!!" << endmsg;
86                 abort(); /*NOTREACHED*/
87                 return 0;
88         } else {
89                 return ptr;
90         }
91 }
92
93 /** Release an item's memory by writing its location to the free list */
94 void
95 Pool::release (void *ptr)
96 {
97         free_list.write (&ptr, 1);
98 }
99
100 /*---------------------------------------------*/
101
102 MultiAllocSingleReleasePool::MultiAllocSingleReleasePool (string n, unsigned long isize, unsigned long nitems)
103         : Pool (n, isize, nitems)
104 {
105 }
106
107 MultiAllocSingleReleasePool::~MultiAllocSingleReleasePool ()
108 {
109 }
110
111 SingleAllocMultiReleasePool::SingleAllocMultiReleasePool (string n, unsigned long isize, unsigned long nitems)
112         : Pool (n, isize, nitems)
113 {
114 }
115
116 SingleAllocMultiReleasePool::~SingleAllocMultiReleasePool ()
117 {
118 }
119
120 void*
121 MultiAllocSingleReleasePool::alloc ()
122 {
123         void *ptr;
124         Glib::Threads::Mutex::Lock guard (m_lock);
125         ptr = Pool::alloc ();
126         return ptr;
127 }
128
129 void
130 MultiAllocSingleReleasePool::release (void* ptr)
131 {
132         Pool::release (ptr);
133 }
134
135 void*
136 SingleAllocMultiReleasePool::alloc ()
137 {
138         return Pool::alloc ();
139 }
140
141 void
142 SingleAllocMultiReleasePool::release (void* ptr)
143 {
144         Glib::Threads::Mutex::Lock guard (m_lock);
145         Pool::release (ptr);
146 }
147
148 /*-------------------------------------------------------*/
149
150 static void
151 free_per_thread_pool (void* ptr)
152 {
153         /* Rather than deleting the CrossThreadPool now, we add it to our trash buffer.
154          * This prevents problems if other threads still require access to this CrossThreadPool.
155          * We assume that some other agent will clean out the trash buffer as required.
156          */
157         CrossThreadPool* cp = static_cast<CrossThreadPool*> (ptr);
158         assert (cp);
159
160         if (cp->empty()) {
161                 /* This CrossThreadPool is already empty, and the thread is finishing so nothing
162                  * more can be added to it.  We can just delete the pool.
163                  */
164                 delete cp;
165         } else {
166                 /* This CrossThreadPool is not empty, meaning that there's some Events in it
167                  * which another thread may yet read, so we can't delete the pool just yet.
168                  * Put it in the trash and hope someone deals with it at some stage.
169                  */
170                 cp->parent()->add_to_trash (cp);
171         }
172 }
173
174 PerThreadPool::PerThreadPool ()
175         : _key (free_per_thread_pool)
176         , _trash (0)
177 {
178 }
179
180 /** Create a new CrossThreadPool and set the current thread's private _key to point to it.
181  *  @param n Name.
182  *  @param isize Size of each item in the pool.
183  *  @param nitems Number of items in the pool.
184  */
185 void
186 PerThreadPool::create_per_thread_pool (string n, unsigned long isize, unsigned long nitems)
187 {
188         _key.set (new CrossThreadPool (n, isize, nitems, this));
189 }
190
191 /** @return True if CrossThreadPool for the current thread exists,
192  *  False otherwise
193  */
194 bool
195 PerThreadPool::has_per_thread_pool ()
196 {
197         CrossThreadPool* p = _key.get();
198         if (p) {
199                 return true;
200         }
201         return false;
202 }
203
204
205 /** @return CrossThreadPool for the current thread, which must previously have been created by
206  *  calling create_per_thread_pool in the current thread.
207  */
208 CrossThreadPool*
209 PerThreadPool::per_thread_pool (bool must_exist)
210 {
211         CrossThreadPool* p = _key.get();
212         if (!p && must_exist) {
213                 fatal << "programming error: no per-thread pool \"" << _name << "\" for thread " << pthread_name() << endmsg;
214                 abort(); /*NOTREACHED*/
215         }
216         return p;
217 }
218
219 void
220 PerThreadPool::set_trash (RingBuffer<CrossThreadPool*>* t)
221 {
222         Glib::Threads::Mutex::Lock lm (_trash_mutex);
223         _trash = t;
224 }
225
226 /** Add a CrossThreadPool to our trash, if we have one.  If not, a warning is emitted. */
227 void
228 PerThreadPool::add_to_trash (CrossThreadPool* p)
229 {
230         Glib::Threads::Mutex::Lock lm (_trash_mutex);
231
232         if (!_trash) {
233                 warning << "Pool " << p->name() << " has no trash collector; a memory leak has therefore occurred" << endmsg;
234                 return;
235         }
236
237         /* we have a lock here so that multiple threads can safely call add_to_trash (even though there
238            can only be one writer to the _trash RingBuffer)
239         */
240
241         _trash->write (&p, 1);
242 }
243
244 CrossThreadPool::CrossThreadPool  (string n, unsigned long isize, unsigned long nitems, PerThreadPool* p)
245         : Pool (n, isize, nitems)
246         , pending (nitems)
247         , _parent (p)
248 {
249
250 }
251
252 void
253 CrossThreadPool::flush_pending_with_ev (void *ptr)
254 {
255         push (ptr);
256         flush_pending ();
257 }
258
259 void
260 CrossThreadPool::flush_pending ()
261 {
262         void* ptr;
263         bool did_release = false;
264
265         DEBUG_TRACE (DEBUG::Pool, string_compose ("%1 %2 has %3 pending free entries waiting, status size %4 free %5 used %6\n", pthread_name(), name(), pending.read_space(),
266                                                   total(), available(), used()));
267
268         while (pending.read (&ptr, 1) == 1) {
269                 DEBUG_TRACE (DEBUG::Pool, string_compose ("%1 %2 pushes back a pending free list entry before allocating\n", pthread_name(), name()));
270                 free_list.write (&ptr, 1);
271                 did_release = true;
272         }
273
274         if (did_release) {
275                 DEBUG_TRACE (DEBUG::Pool, string_compose ("Pool size: %1 free %2 used %3 pending now %4\n", total(), available(), used(), pending_size()));
276         }
277 }
278
279 void*
280 CrossThreadPool::alloc ()
281 {
282         /* process anything waiting to be deleted (i.e. moved back to the free list)  */
283         flush_pending ();
284         /* now allocate from the potentially larger free list */
285         return Pool::alloc ();
286 }
287
288 void
289 CrossThreadPool::push (void* t)
290 {
291         pending.write (&t, 1);
292 }
293
294 /** @return true if there is nothing in this pool */
295 bool
296 CrossThreadPool::empty ()
297 {
298         return (free_list.write_space() == pending.read_space());
299 }
300