+/*-------------------------------------------------------*/
+
+static void
+free_per_thread_pool (void* ptr)
+{
+ /* Rather than deleting the CrossThreadPool now, we add it to our trash buffer.
+ * This prevents problems if other threads still require access to this CrossThreadPool.
+ * We assume that some other agent will clean out the trash buffer as required.
+ */
+ CrossThreadPool* cp = static_cast<CrossThreadPool*> (ptr);
+ assert (cp);
+
+ if (cp->empty()) {
+ /* This CrossThreadPool is already empty, and the thread is finishing so nothing
+ * more can be added to it. We can just delete the pool.
+ */
+ delete cp;
+ } else {
+ /* This CrossThreadPool is not empty, meaning that there's some Events in it
+ * which another thread may yet read, so we can't delete the pool just yet.
+ * Put it in the trash and hope someone deals with it at some stage.
+ */
+ cp->parent()->add_to_trash (cp);
+ }
+}
+
+PerThreadPool::PerThreadPool ()
+ : _trash (0)
+{
+ {
+ /* for some reason this appears necessary to get glib's thread private stuff to work */
+ GPrivate* key;
+ key = g_private_new (NULL);
+ }
+
+ _key = g_private_new (free_per_thread_pool);
+}
+
+/** Create a new CrossThreadPool and set the current thread's private _key to point to it.
+ * @param n Name.
+ * @param isize Size of each item in the pool.
+ * @param nitems Number of items in the pool.
+ */
+void
+PerThreadPool::create_per_thread_pool (string n, unsigned long isize, unsigned long nitems)
+{
+ CrossThreadPool* p = new CrossThreadPool (n, isize, nitems, this);
+ g_private_set (_key, p);
+}
+
+/** @return CrossThreadPool for the current thread, which must previously have been created by
+ * calling create_per_thread_pool in the current thread.
+ */
+CrossThreadPool*
+PerThreadPool::per_thread_pool ()
+{
+ CrossThreadPool* p = static_cast<CrossThreadPool*> (g_private_get (_key));
+ if (!p) {
+ fatal << "programming error: no per-thread pool \"" << _name << "\" for thread " << pthread_self() << endmsg;
+ /*NOTREACHED*/
+ }
+ return p;
+}
+
+void
+PerThreadPool::set_trash (RingBuffer<CrossThreadPool*>* t)
+{
+ Glib::Mutex::Lock lm (_trash_mutex);
+ _trash = t;
+}
+
+/** Add a CrossThreadPool to our trash, if we have one. If not, a warning is emitted. */
+void
+PerThreadPool::add_to_trash (CrossThreadPool* p)
+{
+ Glib::Mutex::Lock lm (_trash_mutex);
+
+ if (!_trash) {
+ warning << "Pool " << p->name() << " has no trash collector; a memory leak has therefore occurred" << endmsg;
+ return;
+ }
+
+ /* we have a lock here so that multiple threads can safely call add_to_trash (even though there
+ can only be one writer to the _trash RingBuffer)
+ */
+
+ _trash->write (&p, 1);
+}
+
+CrossThreadPool::CrossThreadPool (string n, unsigned long isize, unsigned long nitems, PerThreadPool* p)
+ : Pool (n, isize, nitems)
+ , pending (nitems)
+ , _parent (p)
+{
+
+}
+
+void*
+CrossThreadPool::alloc ()
+{
+ void* ptr;
+ while (pending.read (&ptr, 1) == 1) {
+ free_list.write (&ptr, 1);
+ }
+ return Pool::alloc ();
+}
+
+void
+CrossThreadPool::push (void* t)
+{
+ pending.write (&t, 1);
+}
+
+/** @return true if there is nothing in this pool */
+bool
+CrossThreadPool::empty ()
+{
+ return (free_list.write_space() == pending.read_space());
+}
+