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 /// \brief Ensure one autorelease pool is available on this thread
///
/// Increment a retain count for *this* thread.
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void retainPool(void); void ensureThreadHasPool(void);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Drain the pool /// \brief Drain the thread's pool but keep it alive
///
/// The pool retain count should be absolutely positive before calling this function on this thread.
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void drainCurrentPool(void); void drainThreadPool(void);
////////////////////////////////////////////////////////////
/// \brief Release the pool.
///
/// Decrease the retain count for *this* thread.
///
////////////////////////////////////////////////////////////
void releasePool(void);

View File

@ -26,11 +26,8 @@
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
// Headers // Headers
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
#include <SFML/System/Err.hpp>
#include <SFML/System/NonCopyable.hpp>
#include <SFML/System/ThreadLocalPtr.hpp>
#include <cassert> #include <cassert>
#include <pthread.h>
#import <SFML/Window/OSX/AutoreleasePoolWrapper.h> #import <SFML/Window/OSX/AutoreleasePoolWrapper.h>
#import <Foundation/Foundation.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 /// 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). /// 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 // 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 NSAutoreleasePool* pool = (NSAutoreleasePool*)data;
sf::ThreadLocalPtr<sf::priv::PoolWrapper> localPool; [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. pthread_key_create(&poolKey, destroyPool);
if (localPool == NULL)
localPool = new sf::priv::PoolWrapper();
// Then retains!
localPool->retain();
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void drainCurrentPool(void) /// \brief (local function) Store a new pool for this thread
///
////////////////////////////////////////////////////////////
static void createNewPool(void)
{ {
assert(localPool != NULL); NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
localPool->drain(); pthread_setspecific(poolKey, pool);
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void releasePool(void) void ensureThreadHasPool(void)
{ {
assert(localPool != NULL); pthread_once(&initOnceToken, createPoolKey);
if (pthread_getspecific(poolKey) == NULL)
// If we're done with the pool, let's release the memory
if (localPool->release())
{ {
delete localPool; createNewPool();
localPool = NULL;
} }
} }
////////////////////////////////////////////////////////////
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) m_window(0)
{ {
// Ask for a pool. // Ask for a pool.
retainPool(); ensureThreadHasPool();
// Create the context // Create the context
createContext(shared, createContext(shared,
@ -62,7 +62,7 @@ m_view(0),
m_window(0) m_window(0)
{ {
// Ask for a pool. // Ask for a pool.
retainPool(); ensureThreadHasPool();
// Create the context. // Create the context.
createContext(shared, bitsPerPixel, settings); createContext(shared, bitsPerPixel, settings);
@ -83,7 +83,7 @@ m_window(0)
WindowImplCocoa::setUpProcess(); WindowImplCocoa::setUpProcess();
// Ask for a pool. // Ask for a pool.
retainPool(); ensureThreadHasPool();
// Create the context. // Create the context.
createContext(shared, VideoMode::getDesktopMode().bitsPerPixel, settings); createContext(shared, VideoMode::getDesktopMode().bitsPerPixel, settings);
@ -108,8 +108,6 @@ SFContext::~SFContext()
[m_view release]; // Might be nil but we don't care. [m_view release]; // Might be nil but we don't care.
[m_window release]; // Idem. [m_window release]; // Idem.
releasePool();
} }

View File

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