Improved memory management on OS X (related to #790)
This commit is contained in:
parent
c4be28bd0f
commit
0c9ce3bef3
@ -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);
|
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user