trying to track down why undo doesn't remove xfade rendering on OS X
[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 <vector>
23 #include <cstdlib>
24 #include <cassert>
25
26 #include "pbd/pool.h"
27 #include "pbd/error.h"
28 #include "pbd/debug.h"
29 #include "pbd/compose.h"
30
31 using namespace std;
32 using namespace PBD;
33
34 Pool::Pool (string n, unsigned long item_size, unsigned long nitems)
35         : free_list (nitems)
36         , _name (n)
37 {
38         _name = n;
39
40         /* since some overloaded ::operator new() might use this,
41            its important that we use a "lower level" allocator to
42            get more space.  
43         */
44
45         block = malloc (nitems * item_size);
46
47         void **ptrlist = (void **) malloc (sizeof (void *)  * nitems);
48
49         for (unsigned long i = 0; i < nitems; i++) {
50                 ptrlist[i] = static_cast<void *> (static_cast<char*>(block) + (i * item_size));
51         }
52
53         free_list.write (ptrlist, nitems);
54         free (ptrlist);
55 }
56
57 Pool::~Pool ()
58 {
59         free (block);
60 }
61
62 /** Allocate an item's worth of memory in the Pool by taking one from the free list.
63  *  @return Pointer to free item.
64  */
65 void *
66 Pool::alloc ()
67 {
68         void *ptr;
69
70         if (free_list.read (&ptr, 1) < 1) {
71                 fatal << "CRITICAL: " << _name << " POOL OUT OF MEMORY - RECOMPILE WITH LARGER SIZE!!" << endmsg;
72                 /*NOTREACHED*/
73                 return 0;
74         } else {
75                 return ptr;
76         }
77 }
78
79 /** Release an item's memory by writing its location to the free list */
80 void            
81 Pool::release (void *ptr)
82 {
83         free_list.write (&ptr, 1);
84 }
85
86 /*---------------------------------------------*/
87
88 MultiAllocSingleReleasePool::MultiAllocSingleReleasePool (string n, unsigned long isize, unsigned long nitems) 
89         : Pool (n, isize, nitems)
90 {
91 }
92
93 MultiAllocSingleReleasePool::~MultiAllocSingleReleasePool ()
94 {
95 }
96
97 SingleAllocMultiReleasePool::SingleAllocMultiReleasePool (string n, unsigned long isize, unsigned long nitems) 
98         : Pool (n, isize, nitems)
99 {
100 }
101
102 SingleAllocMultiReleasePool::~SingleAllocMultiReleasePool ()
103 {
104 }
105
106 void*
107 MultiAllocSingleReleasePool::alloc ()
108 {
109         void *ptr;
110         Glib::Threads::Mutex::Lock guard (m_lock);
111         ptr = Pool::alloc ();
112         return ptr;
113 }
114
115 void
116 MultiAllocSingleReleasePool::release (void* ptr)
117 {
118         Pool::release (ptr);
119 }
120
121 void*
122 SingleAllocMultiReleasePool::alloc ()
123 {
124         return Pool::alloc ();
125 }
126
127 void
128 SingleAllocMultiReleasePool::release (void* ptr)
129 {
130         Glib::Threads::Mutex::Lock guard (m_lock);
131         Pool::release (ptr);
132 }
133
134 /*-------------------------------------------------------*/
135
136 static void 
137 free_per_thread_pool (void* ptr)
138 {
139         /* Rather than deleting the CrossThreadPool now, we add it to our trash buffer.
140          * This prevents problems if other threads still require access to this CrossThreadPool.
141          * We assume that some other agent will clean out the trash buffer as required.
142          */
143         CrossThreadPool* cp = static_cast<CrossThreadPool*> (ptr);
144         assert (cp);
145
146         if (cp->empty()) {
147                 /* This CrossThreadPool is already empty, and the thread is finishing so nothing
148                  * more can be added to it.  We can just delete the pool.
149                  */
150                 delete cp;
151         } else {
152                 /* This CrossThreadPool is not empty, meaning that there's some Events in it
153                  * which another thread may yet read, so we can't delete the pool just yet.
154                  * Put it in the trash and hope someone deals with it at some stage.
155                  */
156                 cp->parent()->add_to_trash (cp);
157         }
158 }
159  
160 PerThreadPool::PerThreadPool ()
161         : _key (free_per_thread_pool)
162         , _trash (0)
163 {
164 }
165
166 /** Create a new CrossThreadPool and set the current thread's private _key to point to it.
167  *  @param n Name.
168  *  @param isize Size of each item in the pool.
169  *  @param nitems Number of items in the pool.
170  */
171 void
172 PerThreadPool::create_per_thread_pool (string n, unsigned long isize, unsigned long nitems)
173 {
174         _key.set (new CrossThreadPool (n, isize, nitems, this));
175 }
176
177 /** @return CrossThreadPool for the current thread, which must previously have been created by
178  *  calling create_per_thread_pool in the current thread.
179  */
180 CrossThreadPool*
181 PerThreadPool::per_thread_pool ()
182 {
183         CrossThreadPool* p = _key.get();
184         if (!p) {
185                 fatal << "programming error: no per-thread pool \"" << _name << "\" for thread " << pthread_self() << endmsg;
186                 /*NOTREACHED*/
187         }
188         return p;
189 }
190
191 void
192 PerThreadPool::set_trash (RingBuffer<CrossThreadPool*>* t)
193 {
194         Glib::Threads::Mutex::Lock lm (_trash_mutex);
195         _trash = t;
196 }
197
198 /** Add a CrossThreadPool to our trash, if we have one.  If not, a warning is emitted. */
199 void
200 PerThreadPool::add_to_trash (CrossThreadPool* p)
201 {
202         Glib::Threads::Mutex::Lock lm (_trash_mutex);
203         
204         if (!_trash) {
205                 warning << "Pool " << p->name() << " has no trash collector; a memory leak has therefore occurred" << endmsg;
206                 return;
207         }
208
209         /* we have a lock here so that multiple threads can safely call add_to_trash (even though there
210            can only be one writer to the _trash RingBuffer)
211         */
212                 
213         _trash->write (&p, 1);
214 }
215
216 CrossThreadPool::CrossThreadPool  (string n, unsigned long isize, unsigned long nitems, PerThreadPool* p)
217         : Pool (n, isize, nitems)
218         , pending (nitems)
219         , _parent (p)
220 {
221         
222 }
223
224 void*
225 CrossThreadPool::alloc () 
226 {
227         void* ptr;
228
229         DEBUG_TRACE (DEBUG::Pool, string_compose ("%1 %2 has %3 pending free entries waiting\n", pthread_self(), name(), pending.read_space()));
230         while (pending.read (&ptr, 1) == 1) {
231                 DEBUG_TRACE (DEBUG::Pool, string_compose ("%1 %2 pushes back a pending free list entry before allocating\n", pthread_self(), name()));
232                 free_list.write (&ptr, 1);
233         }
234         return Pool::alloc ();
235 }
236
237 void
238 CrossThreadPool::push (void* t) 
239 {
240         pending.write (&t, 1);
241 }
242
243 /** @return true if there is nothing in this pool */
244 bool
245 CrossThreadPool::empty ()
246 {
247         return (free_list.write_space() == pending.read_space());
248 }
249