2008-06-02 17:41:35 -04:00
/*
2019-08-02 23:10:55 -04:00
* Copyright ( C ) 2006 Taybin Rutkin < taybin @ taybin . com >
* Copyright ( C ) 2008 - 2009 David Robillard < d @ drobilla . net >
* Copyright ( C ) 2008 - 2011 Carl Hetherington < carl @ carlh . net >
* Copyright ( C ) 1998 - 2015 Paul Davis < paul @ linuxaudiosystems . com >
2022-07-06 22:42:48 -04:00
* Copyright ( C ) 2014 - 2022 Robin Gareus < robin @ gareus . org >
2019-08-02 23:10:55 -04:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License along
* with this program ; if not , write to the Free Software Foundation , Inc . ,
* 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA .
*/
2008-06-02 17:41:35 -04:00
2022-07-06 22:47:08 -04:00
# include <cassert>
2022-07-06 22:42:48 -04:00
# include <cstdio>
2008-06-02 17:41:35 -04:00
# include <cstdlib>
# include <vector>
2022-07-06 22:47:08 -04:00
# include "pbd/compose.h"
# include "pbd/debug.h"
# include "pbd/error.h"
2009-02-25 13:26:51 -05:00
# include "pbd/pool.h"
2013-07-11 11:53:24 -04:00
# include "pbd/pthread_utils.h"
2022-07-06 22:42:48 -04:00
# include "pbd/stacktrace.h"
2008-06-02 17:41:35 -04:00
using namespace std ;
using namespace PBD ;
2022-07-06 22:42:48 -04:00
Pool : : Pool ( string n , unsigned long item_size , unsigned long nitems , PoolDumpCallback cb )
2009-12-04 14:24:09 -05:00
: free_list ( nitems )
, _name ( n )
2022-07-06 22:42:48 -04:00
, _dump ( cb )
2015-09-11 18:29:02 -04:00
# ifndef NDEBUG
, max_usage ( 0 )
# endif
2008-06-02 17:41:35 -04:00
{
_name = n ;
2022-07-06 22:42:48 -04:00
/* adjust to actual size (power-of-two) */
nitems = free_list . bufsize ( ) ;
2008-06-02 17:41:35 -04:00
/* since some overloaded ::operator new() might use this,
its important that we use a " lower level " allocator to
2015-10-04 14:51:05 -04:00
get more space .
2008-06-02 17:41:35 -04:00
*/
2022-07-06 22:42:48 -04:00
_block = malloc ( nitems * item_size ) ;
2008-06-02 17:41:35 -04:00
2022-07-06 22:47:08 -04:00
void * * ptrlist = ( void * * ) calloc ( nitems , sizeof ( void * ) ) ;
2008-06-02 17:41:35 -04:00
for ( unsigned long i = 0 ; i < nitems ; i + + ) {
2022-07-06 22:47:08 -04:00
ptrlist [ i ] = static_cast < void * > ( static_cast < char * > ( _block ) + ( i * item_size ) ) ;
2008-06-02 17:41:35 -04:00
}
2009-12-04 14:24:09 -05:00
free_list . write ( ptrlist , nitems ) ;
2008-06-02 17:41:35 -04:00
free ( ptrlist ) ;
}
Pool : : ~ Pool ( )
{
2022-07-06 22:47:08 -04:00
DEBUG_TRACE ( DEBUG : : Pool , string_compose ( " Pool: '%1' max: %2 / %3 \n " , name ( ) , max_usage , total ( ) ) ) ;
2022-07-06 22:42:48 -04:00
free ( _block ) ;
2008-06-02 17:41:35 -04:00
}
2010-04-14 18:06:12 -04:00
/** Allocate an item's worth of memory in the Pool by taking one from the free list.
* @ return Pointer to free item .
*/
2022-07-06 22:47:08 -04:00
void *
2008-06-02 17:41:35 -04:00
Pool : : alloc ( )
{
2022-07-06 22:47:08 -04:00
void * ptr ;
2008-06-02 17:41:35 -04:00
2015-09-11 18:29:02 -04:00
# ifndef NDEBUG
if ( used ( ) > max_usage ) {
max_usage = used ( ) + 1 ;
}
# endif
2009-12-04 14:24:09 -05:00
if ( free_list . read ( & ptr , 1 ) < 1 ) {
2022-07-06 22:42:48 -04:00
PBD : : stacktrace ( std : : cerr , 20 ) ;
if ( _dump ) {
2023-02-17 02:31:21 -05:00
std : : cout < < " RingBuffer write-idx: " < < free_list . get_write_idx ( ) < < " read-idx: " < < free_list . get_read_idx ( ) < < ' \n ' ;
2022-07-06 22:42:48 -04:00
void * * _block = free_list . buffer ( ) ;
for ( size_t i = 0 ; i < free_list . bufsize ( ) ; + + i ) {
_dump ( i , _block [ i ] ) ;
}
}
2008-06-02 17:41:35 -04:00
fatal < < " CRITICAL: " < < _name < < " POOL OUT OF MEMORY - RECOMPILE WITH LARGER SIZE!! " < < endmsg ;
2022-07-06 22:47:08 -04:00
abort ( ) ; /*NOTREACHED*/
2008-06-02 17:41:35 -04:00
return 0 ;
} else {
return ptr ;
}
}
2010-04-14 18:06:12 -04:00
/** Release an item's memory by writing its location to the free list */
2015-10-05 10:17:49 -04:00
void
2022-07-06 22:47:08 -04:00
Pool : : release ( void * ptr )
2008-06-02 17:41:35 -04:00
{
2009-12-04 14:24:09 -05:00
free_list . write ( & ptr , 1 ) ;
2008-06-02 17:41:35 -04:00
}
/*---------------------------------------------*/
2015-10-04 14:51:05 -04:00
MultiAllocSingleReleasePool : : MultiAllocSingleReleasePool ( string n , unsigned long isize , unsigned long nitems )
2010-04-14 18:06:12 -04:00
: Pool ( n , isize , nitems )
2008-06-02 17:41:35 -04:00
{
}
MultiAllocSingleReleasePool : : ~ MultiAllocSingleReleasePool ( )
{
}
2015-10-04 14:51:05 -04:00
SingleAllocMultiReleasePool : : SingleAllocMultiReleasePool ( string n , unsigned long isize , unsigned long nitems )
2010-04-14 18:06:12 -04:00
: Pool ( n , isize , nitems )
2008-06-02 17:41:35 -04:00
{
}
SingleAllocMultiReleasePool : : ~ SingleAllocMultiReleasePool ( )
{
}
void *
MultiAllocSingleReleasePool : : alloc ( )
{
2012-07-25 13:48:55 -04:00
Glib : : Threads : : Mutex : : Lock guard ( m_lock ) ;
2022-07-06 22:47:08 -04:00
return Pool : : alloc ( ) ;
2008-06-02 17:41:35 -04:00
}
void
MultiAllocSingleReleasePool : : release ( void * ptr )
{
Pool : : release ( ptr ) ;
}
void *
SingleAllocMultiReleasePool : : alloc ( )
{
return Pool : : alloc ( ) ;
}
void
SingleAllocMultiReleasePool : : release ( void * ptr )
{
2012-07-25 13:48:55 -04:00
Glib : : Threads : : Mutex : : Lock guard ( m_lock ) ;
2008-06-02 17:41:35 -04:00
Pool : : release ( ptr ) ;
}
2009-12-04 14:24:09 -05:00
/*-------------------------------------------------------*/
2015-10-04 14:51:05 -04:00
static void
2009-12-04 14:24:09 -05:00
free_per_thread_pool ( void * ptr )
{
2010-04-14 18:06:12 -04:00
/* 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 ) ;
2022-07-06 22:47:08 -04:00
if ( cp - > empty ( ) ) {
2010-04-29 18:55:27 -04:00
/* 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 .
*/
2022-07-06 22:47:08 -04:00
cp - > parent ( ) - > add_to_trash ( cp ) ;
2010-04-29 18:55:27 -04:00
}
2009-12-04 14:24:09 -05:00
}
2015-10-04 14:51:05 -04:00
2009-12-04 14:24:09 -05:00
PerThreadPool : : PerThreadPool ( )
2012-07-25 13:48:55 -04:00
: _key ( free_per_thread_pool )
, _trash ( 0 )
2009-12-04 14:24:09 -05:00
{
}
2010-04-14 18:06:12 -04:00
/** 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 .
*/
2009-12-04 14:24:09 -05:00
void
2022-07-06 22:42:48 -04:00
PerThreadPool : : create_per_thread_pool ( string n , unsigned long isize , unsigned long nitems , PoolDumpCallback cb )
2009-12-04 14:24:09 -05:00
{
2022-07-06 22:42:48 -04:00
_key . set ( new CrossThreadPool ( n , isize , nitems , this , cb ) ) ;
2009-12-04 14:24:09 -05:00
}
2015-02-24 07:27:36 -05:00
/** @return True if CrossThreadPool for the current thread exists,
* False otherwise
*/
bool
PerThreadPool : : has_per_thread_pool ( )
{
2022-07-06 22:47:08 -04:00
CrossThreadPool * p = _key . get ( ) ;
2015-05-13 17:32:10 -04:00
if ( p ) {
return true ;
}
return false ;
2015-02-24 07:27:36 -05:00
}
2010-04-14 18:06:12 -04:00
/** @return CrossThreadPool for the current thread, which must previously have been created by
* calling create_per_thread_pool in the current thread .
*/
2009-12-04 14:24:09 -05:00
CrossThreadPool *
2015-03-10 19:25:10 -04:00
PerThreadPool : : per_thread_pool ( bool must_exist )
2009-12-04 14:24:09 -05:00
{
2022-07-06 22:47:08 -04:00
CrossThreadPool * p = _key . get ( ) ;
2015-03-10 19:25:10 -04:00
if ( ! p & & must_exist ) {
2022-07-06 22:47:08 -04:00
fatal < < " programming error: no per-thread pool \" " < < _name < < " \" for thread " < < pthread_name ( ) < < endmsg ;
abort ( ) ; /*NOTREACHED*/
2009-12-04 14:24:09 -05:00
}
return p ;
}
2010-04-14 19:58:20 -04:00
void
PerThreadPool : : set_trash ( RingBuffer < CrossThreadPool * > * t )
{
2012-07-25 13:48:55 -04:00
Glib : : Threads : : Mutex : : Lock lm ( _trash_mutex ) ;
2010-04-14 19:58:20 -04:00
_trash = t ;
}
2010-04-14 18:06:12 -04:00
/** Add a CrossThreadPool to our trash, if we have one. If not, a warning is emitted. */
void
PerThreadPool : : add_to_trash ( CrossThreadPool * p )
{
2012-07-25 13:48:55 -04:00
Glib : : Threads : : Mutex : : Lock lm ( _trash_mutex ) ;
2015-10-05 10:17:49 -04:00
2010-04-14 18:06:12 -04:00
if ( ! _trash ) {
2022-07-06 22:47:08 -04:00
warning < < " Pool " < < p - > name ( ) < < " has no trash collector; a memory leak has therefore occurred " < < endmsg ;
2010-04-14 18:06:12 -04:00
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 )
*/
2015-10-05 10:17:49 -04:00
2010-04-14 18:06:12 -04:00
_trash - > write ( & p , 1 ) ;
}
2022-07-06 22:47:08 -04:00
CrossThreadPool : : CrossThreadPool ( string n , unsigned long isize , unsigned long nitems , PerThreadPool * p , PoolDumpCallback cb )
2022-07-06 22:42:48 -04:00
: Pool ( n , isize , nitems , cb )
2009-12-04 14:24:09 -05:00
, pending ( nitems )
2010-04-14 18:06:12 -04:00
, _parent ( p )
2009-12-04 14:24:09 -05:00
{
}
2015-02-05 16:13:24 -05:00
void
2022-07-06 22:47:08 -04:00
CrossThreadPool : : flush_pending_with_ev ( void * ptr )
2009-12-04 14:24:09 -05:00
{
2015-02-05 16:13:24 -05:00
push ( ptr ) ;
flush_pending ( ) ;
}
2011-01-08 10:19:32 -05:00
2015-02-05 16:13:24 -05:00
void
CrossThreadPool : : flush_pending ( )
{
void * ptr ;
2022-07-06 22:47:08 -04:00
bool did_release = false ;
2015-10-05 10:17:49 -04:00
2022-07-06 22:47:08 -04:00
DEBUG_TRACE ( DEBUG : : Pool , string_compose ( " %1 %2 has %3 pending free entries waiting, status size %4 free %5 used %6 \n " , pthread_name ( ) , name ( ) , pending . read_space ( ) ,
total ( ) , available ( ) , used ( ) ) ) ;
2015-10-05 10:17:49 -04:00
2009-12-04 14:24:09 -05:00
while ( pending . read ( & ptr , 1 ) = = 1 ) {
2022-07-06 22:47:08 -04:00
DEBUG_TRACE ( DEBUG : : Pool , string_compose ( " %1 %2 pushes back a pending free list entry before allocating \n " , pthread_name ( ) , name ( ) ) ) ;
2009-12-04 14:24:09 -05:00
free_list . write ( & ptr , 1 ) ;
2015-02-05 16:13:24 -05:00
did_release = true ;
}
if ( did_release ) {
2022-07-06 22:47:08 -04:00
DEBUG_TRACE ( DEBUG : : Pool , string_compose ( " Pool size: %1 free %2 used %3 pending now %4 \n " , total ( ) , available ( ) , used ( ) , pending_size ( ) ) ) ;
2009-12-04 14:24:09 -05:00
}
2015-02-05 16:13:24 -05:00
}
void *
2015-10-04 14:51:05 -04:00
CrossThreadPool : : alloc ( )
2015-02-05 16:13:24 -05:00
{
/* process anything waiting to be deleted (i.e. moved back to the free list) */
flush_pending ( ) ;
/* now allocate from the potentially larger free list */
2009-12-04 14:24:09 -05:00
return Pool : : alloc ( ) ;
}
void
2015-10-04 14:51:05 -04:00
CrossThreadPool : : push ( void * t )
2009-12-04 14:24:09 -05:00
{
pending . write ( & t , 1 ) ;
}
2010-04-14 18:06:12 -04:00
/** @return true if there is nothing in this pool */
bool
CrossThreadPool : : empty ( )
{
2022-07-06 22:47:08 -04:00
return ( free_list . write_space ( ) = = pending . read_space ( ) ) ;
2010-04-14 18:06:12 -04:00
}