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>
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.
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.
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.
29 #include "pbd/pthread_utils.h"
30 #include "pbd/error.h"
31 #include "pbd/debug.h"
32 #include "pbd/compose.h"
37 Pool::Pool (string n, unsigned long item_size, unsigned long nitems)
46 /* since some overloaded ::operator new() might use this,
47 its important that we use a "lower level" allocator to
51 block = malloc (nitems * item_size);
53 void **ptrlist = (void **) malloc (sizeof (void *) * nitems);
55 for (unsigned long i = 0; i < nitems; i++) {
56 ptrlist[i] = static_cast<void *> (static_cast<char*>(block) + (i * item_size));
59 free_list.write (ptrlist, nitems);
65 DEBUG_TRACE (DEBUG::Pool, string_compose ("Pool: '%1' max: %2 / %3", name(), max_usage, total()));
69 /** Allocate an item's worth of memory in the Pool by taking one from the free list.
70 * @return Pointer to free item.
78 if (used () > max_usage) {
79 max_usage = used () + 1;
83 if (free_list.read (&ptr, 1) < 1) {
84 fatal << "CRITICAL: " << _name << " POOL OUT OF MEMORY - RECOMPILE WITH LARGER SIZE!!" << endmsg;
85 abort(); /*NOTREACHED*/
92 /** Release an item's memory by writing its location to the free list */
94 Pool::release (void *ptr)
96 free_list.write (&ptr, 1);
99 /*---------------------------------------------*/
101 MultiAllocSingleReleasePool::MultiAllocSingleReleasePool (string n, unsigned long isize, unsigned long nitems)
102 : Pool (n, isize, nitems)
106 MultiAllocSingleReleasePool::~MultiAllocSingleReleasePool ()
110 SingleAllocMultiReleasePool::SingleAllocMultiReleasePool (string n, unsigned long isize, unsigned long nitems)
111 : Pool (n, isize, nitems)
115 SingleAllocMultiReleasePool::~SingleAllocMultiReleasePool ()
120 MultiAllocSingleReleasePool::alloc ()
123 Glib::Threads::Mutex::Lock guard (m_lock);
124 ptr = Pool::alloc ();
129 MultiAllocSingleReleasePool::release (void* ptr)
135 SingleAllocMultiReleasePool::alloc ()
137 return Pool::alloc ();
141 SingleAllocMultiReleasePool::release (void* ptr)
143 Glib::Threads::Mutex::Lock guard (m_lock);
147 /*-------------------------------------------------------*/
150 free_per_thread_pool (void* ptr)
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.
156 CrossThreadPool* cp = static_cast<CrossThreadPool*> (ptr);
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.
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.
169 cp->parent()->add_to_trash (cp);
173 PerThreadPool::PerThreadPool ()
174 : _key (free_per_thread_pool)
179 /** Create a new CrossThreadPool and set the current thread's private _key to point to it.
181 * @param isize Size of each item in the pool.
182 * @param nitems Number of items in the pool.
185 PerThreadPool::create_per_thread_pool (string n, unsigned long isize, unsigned long nitems)
187 _key.set (new CrossThreadPool (n, isize, nitems, this));
190 /** @return True if CrossThreadPool for the current thread exists,
194 PerThreadPool::has_per_thread_pool ()
196 CrossThreadPool* p = _key.get();
204 /** @return CrossThreadPool for the current thread, which must previously have been created by
205 * calling create_per_thread_pool in the current thread.
208 PerThreadPool::per_thread_pool (bool must_exist)
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*/
219 PerThreadPool::set_trash (RingBuffer<CrossThreadPool*>* t)
221 Glib::Threads::Mutex::Lock lm (_trash_mutex);
225 /** Add a CrossThreadPool to our trash, if we have one. If not, a warning is emitted. */
227 PerThreadPool::add_to_trash (CrossThreadPool* p)
229 Glib::Threads::Mutex::Lock lm (_trash_mutex);
232 warning << "Pool " << p->name() << " has no trash collector; a memory leak has therefore occurred" << endmsg;
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)
240 _trash->write (&p, 1);
243 CrossThreadPool::CrossThreadPool (string n, unsigned long isize, unsigned long nitems, PerThreadPool* p)
244 : Pool (n, isize, nitems)
252 CrossThreadPool::flush_pending_with_ev (void *ptr)
259 CrossThreadPool::flush_pending ()
262 bool did_release = false;
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()));
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);
274 DEBUG_TRACE (DEBUG::Pool, string_compose ("Pool size: %1 free %2 used %3 pending now %4\n", total(), available(), used(), pending_size()));
279 CrossThreadPool::alloc ()
281 /* process anything waiting to be deleted (i.e. moved back to the free list) */
283 /* now allocate from the potentially larger free list */
284 return Pool::alloc ();
288 CrossThreadPool::push (void* t)
290 pending.write (&t, 1);
293 /** @return true if there is nothing in this pool */
295 CrossThreadPool::empty ()
297 return (free_list.write_space() == pending.read_space());