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