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