Improved memory management on OS X (related to #790)

This commit is contained in:
Marco Antognini 2015-09-08 10:18:50 +02:00
parent c4be28bd0f
commit 0c9ce3bef3
4 changed files with 52 additions and 168 deletions

View File

@ -24,26 +24,14 @@
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
/// \brief Ensure at least one autorelease pool is available on this thread
///
/// Increment a retain count for *this* thread.
/// \brief Ensure one autorelease pool is available on this thread
///
////////////////////////////////////////////////////////////
void retainPool(void);
void ensureThreadHasPool(void);
////////////////////////////////////////////////////////////
/// \brief Drain the pool
///
/// The pool retain count should be absolutely positive before calling this function on this thread.
/// \brief Drain the thread's pool but keep it alive
///
////////////////////////////////////////////////////////////
void drainCurrentPool(void);
////////////////////////////////////////////////////////////
/// \brief Release the pool.
///
/// Decrease the retain count for *this* thread.
///
////////////////////////////////////////////////////////////
void releasePool(void);
void drainThreadPool(void);

View File

@ -26,11 +26,8 @@
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/System/Err.hpp>
#include <SFML/System/NonCopyable.hpp>
#include <SFML/System/ThreadLocalPtr.hpp>
#include <cassert>
#include <pthread.h>
#import <SFML/Window/OSX/AutoreleasePoolWrapper.h>
#import <Foundation/Foundation.h>
@ -41,165 +38,68 @@
/// pool and making other pools invalid which can lead to a crash on 10.5 and an
/// annoying message on 10.6 (*** attempt to pop an unknown autorelease pool).
///
/// Because NSAutoreleasePool cannot be retain we have to do it ourself.
/// We use an sf::ThreadLocalPtr to have one PoolWrapper in each thread.
///
/// SPECIAL CONSIDERATION:
/// ======================
/// This implies that if retainPool is called X times in a thread Y then
/// releasePool must be called X times too in the same thread Y.
///
////////////////////////////////////////////////////////////
namespace sf
{
namespace priv
{
////////////////////////////////////////////////////////////
/// \brief C++ Wrapper of Obj-C Autorelease Pool
///
////////////////////////////////////////////////////////////
class PoolWrapper : NonCopyable
{
public:
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
////////////////////////////////////////////////////////////
PoolWrapper();
////////////////////////////////////////////////////////////
/// \brief Default destructor
///
/// Make sure the pool is drained (if appropriate)
///
////////////////////////////////////////////////////////////
~PoolWrapper();
////////////////////////////////////////////////////////////
/// \brief Increment retain count and allocate memory if needed
///
////////////////////////////////////////////////////////////
void retain();
////////////////////////////////////////////////////////////
/// \brief Decrement retain count and releasing memory if needed
///
/// \return true if the pool wrapper can be released
///
////////////////////////////////////////////////////////////
bool release();
////////////////////////////////////////////////////////////
/// \brief Drain the pool
///
////////////////////////////////////////////////////////////
void drain();
private:
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
int m_count; ///< How many times was the pool retained?
NSAutoreleasePool* m_pool; ///< Our dedicated pool
};
////////////////////////////////////////////////////////////
PoolWrapper::PoolWrapper() :
m_count(0),
m_pool(nil)
{
/* Nothing else */
}
////////////////////////////////////////////////////////////
PoolWrapper::~PoolWrapper()
{
// Make sure everything is drained
m_count = 0;
drain();
}
////////////////////////////////////////////////////////////
void PoolWrapper::retain()
{
// Increase counter
++m_count;
// Allocate pool if required
if (m_pool == nil)
m_pool = [[NSAutoreleasePool alloc] init];
}
////////////////////////////////////////////////////////////
bool PoolWrapper::release()
{
// Decrease counter
--m_count;
return m_count == 0;
}
void PoolWrapper::drain()
{
[m_pool drain];
m_pool = nil;
if (m_count != 0)
m_pool = [[NSAutoreleasePool alloc] init];
}
} // namespace priv
} // namespace sf
////////////////////////////////////////////////////////////
// Private data
////////////////////////////////////////////////////////////
namespace
static pthread_key_t poolKey;
static pthread_once_t initOnceToken = PTHREAD_ONCE_INIT;
////////////////////////////////////////////////////////////
/// \brief (local function) Drain one more time the pool
/// but this time don't create a new one.
///
////////////////////////////////////////////////////////////
static void destroyPool(void* data)
{
// This per-thread variable holds the current autorelease pool for each thread
sf::ThreadLocalPtr<sf::priv::PoolWrapper> localPool;
NSAutoreleasePool* pool = (NSAutoreleasePool*)data;
[pool drain];
}
////////////////////////////////////////////////////////////
void retainPool(void)
/// \brief (local function) Init the pthread key for the pool
///
////////////////////////////////////////////////////////////
static void createPoolKey(void)
{
// First, Check that we have a valid PoolWrapper object in our local pool.
if (localPool == NULL)
localPool = new sf::priv::PoolWrapper();
// Then retains!
localPool->retain();
pthread_key_create(&poolKey, destroyPool);
}
////////////////////////////////////////////////////////////
void drainCurrentPool(void)
/// \brief (local function) Store a new pool for this thread
///
////////////////////////////////////////////////////////////
static void createNewPool(void)
{
assert(localPool != NULL);
localPool->drain();
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
pthread_setspecific(poolKey, pool);
}
////////////////////////////////////////////////////////////
void releasePool(void)
void ensureThreadHasPool(void)
{
assert(localPool != NULL);
// If we're done with the pool, let's release the memory
if (localPool->release())
pthread_once(&initOnceToken, createPoolKey);
if (pthread_getspecific(poolKey) == NULL)
{
delete localPool;
localPool = NULL;
createNewPool();
}
}
////////////////////////////////////////////////////////////
void drainThreadPool(void)
{
void* data = pthread_getspecific(poolKey);
assert(data != NULL);
// Drain the pool but keep it alive by creating a new one
destroyPool(data);
createNewPool();
}

View File

@ -46,7 +46,7 @@ m_view(0),
m_window(0)
{
// Ask for a pool.
retainPool();
ensureThreadHasPool();
// Create the context
createContext(shared,
@ -62,7 +62,7 @@ m_view(0),
m_window(0)
{
// Ask for a pool.
retainPool();
ensureThreadHasPool();
// Create the context.
createContext(shared, bitsPerPixel, settings);
@ -83,7 +83,7 @@ m_window(0)
WindowImplCocoa::setUpProcess();
// Ask for a pool.
retainPool();
ensureThreadHasPool();
// Create the context.
createContext(shared, VideoMode::getDesktopMode().bitsPerPixel, settings);
@ -108,8 +108,6 @@ SFContext::~SFContext()
[m_view release]; // Might be nil but we don't care.
[m_window release]; // Idem.
releasePool();
}

View File

@ -155,7 +155,7 @@ WindowImplCocoa::WindowImplCocoa(WindowHandle handle) :
m_showCursor(true)
{
// Ask for a pool.
retainPool();
ensureThreadHasPool();
// Treat the handle as it real type
id nsHandle = (id)handle;
@ -200,7 +200,7 @@ m_showCursor(true)
setUpProcess();
// Ask for a pool.
retainPool();
ensureThreadHasPool();
// Use backing size
scaleInWidthHeight(mode, nil);
@ -226,11 +226,9 @@ WindowImplCocoa::~WindowImplCocoa()
if ([windows count] > 0)
[[windows objectAtIndex:0] makeKeyAndOrderFront:nil];
drainCurrentPool(); // Make sure everything was freed
drainThreadPool(); // Make sure everything was freed
// This solve some issue when sf::Window::Create is called for the
// second time (nothing was render until the function was called again)
releasePool();
}
@ -467,7 +465,7 @@ void WindowImplCocoa::textEntered(unichar charcode)
void WindowImplCocoa::processEvents()
{
[m_delegate processEvent];
drainCurrentPool(); // Reduce memory footprint
drainThreadPool(); // Reduce memory footprint
}
#pragma mark