add abort() to non-reached code
[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/pthread_utils.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                 abort(); /*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 {
92 }
93
94 MultiAllocSingleReleasePool::~MultiAllocSingleReleasePool ()
95 {
96 }
97
98 SingleAllocMultiReleasePool::SingleAllocMultiReleasePool (string n, unsigned long isize, unsigned long nitems) 
99         : Pool (n, isize, nitems)
100 {
101 }
102
103 SingleAllocMultiReleasePool::~SingleAllocMultiReleasePool ()
104 {
105 }
106
107 void*
108 MultiAllocSingleReleasePool::alloc ()
109 {
110         void *ptr;
111         Glib::Threads::Mutex::Lock guard (m_lock);
112         ptr = Pool::alloc ();
113         return ptr;
114 }
115
116 void
117 MultiAllocSingleReleasePool::release (void* ptr)
118 {
119         Pool::release (ptr);
120 }
121
122 void*
123 SingleAllocMultiReleasePool::alloc ()
124 {
125         return Pool::alloc ();
126 }
127
128 void
129 SingleAllocMultiReleasePool::release (void* ptr)
130 {
131         Glib::Threads::Mutex::Lock guard (m_lock);
132         Pool::release (ptr);
133 }
134
135 /*-------------------------------------------------------*/
136
137 static void 
138 free_per_thread_pool (void* ptr)
139 {
140         /* Rather than deleting the CrossThreadPool now, we add it to our trash buffer.
141          * This prevents problems if other threads still require access to this CrossThreadPool.
142          * We assume that some other agent will clean out the trash buffer as required.
143          */
144         CrossThreadPool* cp = static_cast<CrossThreadPool*> (ptr);
145         assert (cp);
146
147         if (cp->empty()) {
148                 /* This CrossThreadPool is already empty, and the thread is finishing so nothing
149                  * more can be added to it.  We can just delete the pool.
150                  */
151                 delete cp;
152         } else {
153                 /* This CrossThreadPool is not empty, meaning that there's some Events in it
154                  * which another thread may yet read, so we can't delete the pool just yet.
155                  * Put it in the trash and hope someone deals with it at some stage.
156                  */
157                 cp->parent()->add_to_trash (cp);
158         }
159 }
160  
161 PerThreadPool::PerThreadPool ()
162         : _key (free_per_thread_pool)
163         , _trash (0)
164 {
165 }
166
167 /** Create a new CrossThreadPool and set the current thread's private _key to point to it.
168  *  @param n Name.
169  *  @param isize Size of each item in the pool.
170  *  @param nitems Number of items in the pool.
171  */
172 void
173 PerThreadPool::create_per_thread_pool (string n, unsigned long isize, unsigned long nitems)
174 {
175         _key.set (new CrossThreadPool (n, isize, nitems, this));
176 }
177
178 /** @return CrossThreadPool for the current thread, which must previously have been created by
179  *  calling create_per_thread_pool in the current thread.
180  */
181 CrossThreadPool*
182 PerThreadPool::per_thread_pool ()
183 {
184         CrossThreadPool* p = _key.get();
185         if (!p) {
186                 fatal << "programming error: no per-thread pool \"" << _name << "\" for thread " << pthread_name() << endmsg;
187                 abort(); /*NOTREACHED*/
188         }
189         return p;
190 }
191
192 void
193 PerThreadPool::set_trash (RingBuffer<CrossThreadPool*>* t)
194 {
195         Glib::Threads::Mutex::Lock lm (_trash_mutex);
196         _trash = t;
197 }
198
199 /** Add a CrossThreadPool to our trash, if we have one.  If not, a warning is emitted. */
200 void
201 PerThreadPool::add_to_trash (CrossThreadPool* p)
202 {
203         Glib::Threads::Mutex::Lock lm (_trash_mutex);
204         
205         if (!_trash) {
206                 warning << "Pool " << p->name() << " has no trash collector; a memory leak has therefore occurred" << endmsg;
207                 return;
208         }
209
210         /* we have a lock here so that multiple threads can safely call add_to_trash (even though there
211            can only be one writer to the _trash RingBuffer)
212         */
213                 
214         _trash->write (&p, 1);
215 }
216
217 CrossThreadPool::CrossThreadPool  (string n, unsigned long isize, unsigned long nitems, PerThreadPool* p)
218         : Pool (n, isize, nitems)
219         , pending (nitems)
220         , _parent (p)
221 {
222         
223 }
224
225 void*
226 CrossThreadPool::alloc () 
227 {
228         void* ptr;
229
230         DEBUG_TRACE (DEBUG::Pool, string_compose ("%1 %2 has %3 pending free entries waiting\n", pthread_name(), name(), pending.read_space()));
231         while (pending.read (&ptr, 1) == 1) {
232                 DEBUG_TRACE (DEBUG::Pool, string_compose ("%1 %2 pushes back a pending free list entry before allocating\n", pthread_name(), name()));
233                 free_list.write (&ptr, 1);
234         }
235         return Pool::alloc ();
236 }
237
238 void
239 CrossThreadPool::push (void* t) 
240 {
241         pending.write (&t, 1);
242 }
243
244 /** @return true if there is nothing in this pool */
245 bool
246 CrossThreadPool::empty ()
247 {
248         return (free_list.write_space() == pending.read_space());
249 }
250