Prevent crash in pool destruction during session teardown.
[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 <iostream>
23 #include <vector>
24 #include <cstdlib>
25 #include <cassert>
26
27 #include "pbd/pool.h"
28 #include "pbd/error.h"
29
30 using namespace std;
31 using namespace PBD;
32
33 Pool::Pool (string n, unsigned long item_size, unsigned long nitems)
34         : free_list (nitems)
35         , _name (n)
36 {
37         _name = n;
38
39         /* since some overloaded ::operator new() might use this,
40            its important that we use a "lower level" allocator to
41            get more space.  
42         */
43
44         block = malloc (nitems * item_size);
45
46         void **ptrlist = (void **) malloc (sizeof (void *)  * nitems);
47
48         for (unsigned long i = 0; i < nitems; i++) {
49                 ptrlist[i] = static_cast<void *> (static_cast<char*>(block) + (i * item_size));
50         }
51
52         free_list.write (ptrlist, nitems);
53         free (ptrlist);
54 }
55
56 Pool::~Pool ()
57 {
58         free (block);
59 }
60
61 /** Allocate an item's worth of memory in the Pool by taking one from the free list.
62  *  @return Pointer to free item.
63  */
64 void *
65 Pool::alloc ()
66 {
67         void *ptr;
68
69         if (free_list.read (&ptr, 1) < 1) {
70                 fatal << "CRITICAL: " << _name << " POOL OUT OF MEMORY - RECOMPILE WITH LARGER SIZE!!" << endmsg;
71                 /*NOTREACHED*/
72                 return 0;
73         } else {
74                 return ptr;
75         }
76 }
77
78 /** Release an item's memory by writing its location to the free list */
79 void            
80 Pool::release (void *ptr)
81 {
82         free_list.write (&ptr, 1);
83 }
84
85 /*---------------------------------------------*/
86
87 MultiAllocSingleReleasePool::MultiAllocSingleReleasePool (string n, unsigned long isize, unsigned long nitems) 
88         : Pool (n, isize, nitems)
89         , m_lock(0)
90 {
91 }
92
93 MultiAllocSingleReleasePool::~MultiAllocSingleReleasePool ()
94 {
95     delete m_lock;
96 }
97
98 SingleAllocMultiReleasePool::SingleAllocMultiReleasePool (string n, unsigned long isize, unsigned long nitems) 
99         : Pool (n, isize, nitems)
100         , m_lock(0)
101 {
102 }
103
104 SingleAllocMultiReleasePool::~SingleAllocMultiReleasePool ()
105 {
106     delete m_lock;
107 }
108
109 void*
110 MultiAllocSingleReleasePool::alloc ()
111 {
112         void *ptr;
113     if(!m_lock) {
114         m_lock = new Glib::Mutex();
115         // umm, I'm not sure that this doesn't also allocate memory.
116         if(!m_lock) error << "cannot create Glib::Mutex in pool.cc" << endmsg;
117     }
118     
119     Glib::Mutex::Lock guard(*m_lock);
120         ptr = Pool::alloc ();
121         return ptr;
122 }
123
124 void
125 MultiAllocSingleReleasePool::release (void* ptr)
126 {
127         Pool::release (ptr);
128 }
129
130 void*
131 SingleAllocMultiReleasePool::alloc ()
132 {
133         return Pool::alloc ();
134 }
135
136 void
137 SingleAllocMultiReleasePool::release (void* ptr)
138 {
139     if(!m_lock) {
140         m_lock = new Glib::Mutex();
141         // umm, I'm not sure that this doesn't also allocate memory.
142         if(!m_lock) error << "cannot create Glib::Mutex in pool.cc" << endmsg;
143     }
144     Glib::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         cp->parent()->add_to_trash (cp);
161 }
162  
163 PerThreadPool::PerThreadPool ()
164         : _trash (0)
165 {
166         {
167                 /* for some reason this appears necessary to get glib's thread private stuff to work */
168                 GPrivate* key;
169                 key = g_private_new (NULL);
170         }
171
172         _key = g_private_new (free_per_thread_pool);
173 }
174
175 /** Create a new CrossThreadPool and set the current thread's private _key to point to it.
176  *  @param n Name.
177  *  @param isize Size of each item in the pool.
178  *  @param nitems Number of items in the pool.
179  */
180 void
181 PerThreadPool::create_per_thread_pool (string n, unsigned long isize, unsigned long nitems)
182 {
183         CrossThreadPool* p = new CrossThreadPool (n, isize, nitems, this);
184         g_private_set (_key, p);
185 }
186
187 /** @return CrossThreadPool for the current thread, which must previously have been created by
188  *  calling create_per_thread_pool in the current thread.
189  */
190 CrossThreadPool*
191 PerThreadPool::per_thread_pool ()
192 {
193         CrossThreadPool* p = static_cast<CrossThreadPool*> (g_private_get (_key));
194         if (!p) {
195                 fatal << "programming error: no per-thread pool \"" << _name << "\" for thread " << pthread_self() << endmsg;
196                 /*NOTREACHED*/
197         }
198         return p;
199 }
200
201 void
202 PerThreadPool::set_trash (RingBuffer<CrossThreadPool*>* t)
203 {
204         Glib::Mutex::Lock lm (_trash_mutex);
205         _trash = t;
206 }
207
208 /** Add a CrossThreadPool to our trash, if we have one.  If not, a warning is emitted. */
209 void
210 PerThreadPool::add_to_trash (CrossThreadPool* p)
211 {
212         Glib::Mutex::Lock lm (_trash_mutex);
213         
214         if (!_trash) {
215                 warning << "Pool " << p->name() << " has no trash collector; a memory leak has therefore occurred" << endmsg;
216                 return;
217         }
218
219         /* we have a lock here so that multiple threads can safely call add_to_trash (even though there
220            can only be one writer to the _trash RingBuffer)
221         */
222                 
223         _trash->write (&p, 1);
224 }
225
226 CrossThreadPool::CrossThreadPool  (string n, unsigned long isize, unsigned long nitems, PerThreadPool* p)
227         : Pool (n, isize, nitems)
228         , pending (nitems)
229         , _parent (p)
230 {
231         
232 }
233
234 void*
235 CrossThreadPool::alloc () 
236 {
237         void* ptr;
238         while (pending.read (&ptr, 1) == 1) {
239                 free_list.write (&ptr, 1);
240         }
241         return Pool::alloc ();
242 }
243
244 void
245 CrossThreadPool::push (void* t) 
246 {
247         pending.write (&t, 1);
248 }
249
250 /** @return true if there is nothing in this pool */
251 bool
252 CrossThreadPool::empty ()
253 {
254         return (free_list.write_space() == pending.read_space());
255 }
256