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