New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Memory pools don't work properly with threads #790

Closed
mantognini opened this Issue Jan 29, 2015 · 3 comments

Comments

Projects
None yet
2 participants
@mantognini
Member

mantognini commented Jan 29, 2015

As reported here, here and here, memory pools on OS X can crash the application at exit time.

The issue is that the number of retainPool()/releasePool() is not properly balances on a thread basis because some manually internal context are deallocated on another thread.

SSCCE

#include <SFML/Graphics.hpp>
#include <SFML/System.hpp>

#include <thread>

std::atomic<bool> exitThread { false };

void renderingThread(sf::RenderWindow* window)
{
    printf("Rendering thread %lu started\n", std::hash<std::thread::id>()(std::this_thread::get_id()));

    sf::CircleShape shape(50);
    shape.setFillColor(sf::Color(150, 50, 250));

    // set a 10-pixel wide orange outline
    shape.setOutlineThickness(10);
    shape.setOutlineColor(sf::Color(250, 150, 100));

    // the rendering loop
    while (!exitThread && window->isOpen())
    {
        window->clear();

        // draw...
        window->draw(shape); // if I comment this line, it works

        // end the current frame
        window->display();
    }
}

int main(int argc, char* argv[]) {
    printf("Main thread %lu started\n", std::hash<std::thread::id>()(std::this_thread::get_id()));

    //create the window (remember: it's safer to create it in the main thread due to OS limitations)
    sf::RenderWindow window(sf::VideoMode(800, 600), "OpenGL");

    // sf::Shader::isAvailable(); // <- Calling it here is a workaround.

    // deactivate its OpenGL context
    window.setActive(false);

    // launch the rendering thread
    sf::Thread thread(&renderingThread, &window);
    thread.launch();

    // the event/logic/whatever loop
    while (window.isOpen())
    {
        sf::Event event;
        while (window.pollEvent(event)) {
            if (event.type == sf::Event::Closed || (event.key.code == sf::Keyboard::Escape)) {
                printf("Closing\n");

                // closing the window when renderingThread still uses it causes lots of
                // GL_INVALID_FRAMEBUFFER_OPERATION, the object bound to FRAMEBUFFER_BINDING is not "framebuffer complete"
                //window.close();

                exitThread = true;
                thread.wait();
                window.close();
            }
        }
    }

    return 0;
}

Detailed debugger log

Main thread 4382000746217841212 started
* thread #1 retainPool() => create window, global shared context
  * frame #0: retainPool() + 15 at AutoreleasePoolWrapper.mm:177
    frame #1: sf::priv::SFContext::SFContext() + 70 at SFContext.mm:47
    frame #2: sf::priv::SFContext::SFContext() + 29 at SFContext.mm:53
    frame #3: sf::priv::GlContext::globalInit() + 46 at GlContext.cpp:125
    frame #4: sf::GlResource::GlResource() + 52 at GlResource.cpp:53
    frame #5: sf::Window::Window() + 28 at Window.cpp:49
    frame #6: sf::RenderWindow::RenderWindow() + 61 at RenderWindow.cpp:43
    frame #7: sf::RenderWindow::RenderWindow() + 87 at RenderWindow.cpp:46
    frame #8: main() + 265 at main.cpp:38
    frame #9: start + 52
* thread #1 retainPool() => create window, internal thread context
  * frame #0: retainPool() + 15 at AutoreleasePoolWrapper.mm:177
    frame #1: sf::priv::SFContext::SFContext() + 70 at SFContext.mm:47
    frame #2: sf::priv::SFContext::SFContext() + 29 at SFContext.mm:53
    frame #3: sf::priv::GlContext::create() + 49 at GlContext.cpp:162
    frame #4: (anonymous namespace)::getInternalContext() + 29 at GlContext.cpp:107
    frame #5: sf::priv::GlContext::setActive() + 166 at GlContext.cpp:244
    frame #6: sf::priv::GlContext::globalInit() + 97 at GlContext.cpp:131
    frame #7: sf::GlResource::GlResource() + 52 at GlResource.cpp:53
    frame #8: sf::Window::Window() + 28 at Window.cpp:49
    frame #9: sf::RenderWindow::RenderWindow() + 61 at RenderWindow.cpp:43
    frame #10: sf::RenderWindow::RenderWindow() + 87 at RenderWindow.cpp:46
    frame #11: main() + 265 at main.cpp:38
    frame #12: start + 52
* thread #1 retainPool() => create window, own pool
  * frame #0: retainPool() + 15 at AutoreleasePoolWrapper.mm:177
    frame #1: sf::priv::WindowImplCocoa::WindowImplCocoa() + 97 at WindowImplCocoa.mm:203
    frame #2: sf::priv::WindowImplCocoa::WindowImplCocoa() + 87 at WindowImplCocoa.mm:214
    frame #3: sf::priv::WindowImpl::create() + 127 at WindowImpl.cpp:71
    frame #4: sf::Window::create() + 419 at Window.cpp:124
    frame #5: sf::RenderWindow::RenderWindow() + 163 at RenderWindow.cpp:45
    frame #6: sf::RenderWindow::RenderWindow() + 87 at RenderWindow.cpp:46
    frame #7: main() + 265 at main.cpp:38
    frame #8: start + 52
* thread #1 retainPool() => create window, window context
  * frame #0: retainPool() + 15 at AutoreleasePoolWrapper.mm:177
    frame #1: sf::priv::SFContext::SFContext() + 79 at SFContext.mm:63
    frame #2: sf::priv::SFContext::SFContext() + 53 at SFContext.mm:71
    frame #3: sf::priv::GlContext::create() + 86 at GlContext.cpp:176
    frame #4: sf::Window::create() + 446 at Window.cpp:127
    frame #5: sf::RenderWindow::RenderWindow() + 163 at RenderWindow.cpp:45
    frame #6: sf::RenderWindow::RenderWindow() + 87 at RenderWindow.cpp:46
    frame #7: main() + 265 at main.cpp:38
    frame #8: start + 52
Rendering thread 9071305688082120336 started
* thread #9 retainPool() => check shader, temporary context
  * frame #0: retainPool() + 15 at AutoreleasePoolWrapper.mm:177
    frame #1: sf::priv::SFContext::SFContext() + 70 at SFContext.mm:47
    frame #2: sf::priv::SFContext::SFContext() + 29 at SFContext.mm:53
    frame #3: sf::priv::GlContext::create() + 49 at GlContext.cpp:162
    frame #4: sf::Context::Context() + 47 at Context.cpp:37
    frame #5: sf::Context::Context() + 21 at Context.cpp:39
    frame #6: (anonymous namespace)::checkShadersAvailable() + 17 at Shader.cpp:111
    frame #7: sf::Shader::isAvailable() + 65 at Shader.cpp:478
    frame #8: sf::RenderTarget::resetGLStates() + 25 at RenderTarget.cpp:347
    frame #9: sf::RenderTarget::draw() + 128 at RenderTarget.cpp:214
    frame #10: sf::VertexArray::draw() const + 192 at VertexArray.cpp:147
    frame #11: sf::RenderTarget::draw() + 136 at RenderTarget.cpp:188
    frame #12: sf::Shape::draw() const + 122 at Shape.cpp:219
    frame #13: sf::RenderTarget::draw() + 136 at RenderTarget.cpp:188
    frame #14: renderingThread() + 352 at main.cpp:27
    frame #15: sf::Thread::run() + 26 at Thread.cpp:83
    frame #16: sf::priv::ThreadImpl::entryPoint() + 54 at ThreadImpl.cpp:87
    frame #17: _pthread_body + 138
    frame #18: 0x00007fff8eaa572a libsystem_pthread.dylib`_pthread_start + 137
    frame #19: 0x00007fff8eaa9fc9 libsystem_pthread.dylib`thread_start + 13
* thread #9 releasePool() => check shader, dispose temporary context
  * frame #0: releasePool() + 15 at AutoreleasePoolWrapper.mm:196
    frame #1: sf::priv::SFContext::~SFContext() + 146 at SFContext.mm:110
    frame #2: sf::priv::SFContext::~SFContext() + 21 at SFContext.mm:103
    frame #3: sf::priv::SFContext::~SFContext() + 24 at SFContext.mm:103
    frame #4: sf::Context::~Context() + 55 at Context.cpp:45
    frame #5: sf::Context::~Context() + 21 at Context.cpp:44
    frame #6: (anonymous namespace)::checkShadersAvailable() + 136 at Shader.cpp:122
    frame #7: sf::Shader::isAvailable() + 65 at Shader.cpp:478
    frame #8: sf::RenderTarget::resetGLStates() + 25 at RenderTarget.cpp:347
    frame #9: sf::RenderTarget::draw() + 128 at RenderTarget.cpp:214
    frame #10: sf::VertexArray::draw() const + 192 at VertexArray.cpp:147
    frame #11: sf::RenderTarget::draw() + 136 at RenderTarget.cpp:188
    frame #12: sf::Shape::draw() const + 122 at Shape.cpp:219
    frame #13: sf::RenderTarget::draw() + 136 at RenderTarget.cpp:188
    frame #14: renderingThread() + 352 at main.cpp:27
    frame #15: sf::Thread::run() + 26 at Thread.cpp:83
    frame #16: sf::priv::ThreadImpl::entryPoint() + 54 at ThreadImpl.cpp:87
    frame #17: _pthread_body + 138
    frame #18: 0x00007fff8eaa572a libsystem_pthread.dylib`_pthread_start + 137
    frame #19: 0x00007fff8eaa9fc9 libsystem_pthread.dylib`thread_start + 13
* thread #9 retainPool() => create new internal thread context upon disposal of temporary
  * frame #0: retainPool() + 15 at AutoreleasePoolWrapper.mm:177
    frame #1: sf::priv::SFContext::SFContext() + 70 at SFContext.mm:47
    frame #2: sf::priv::SFContext::SFContext() + 29 at SFContext.mm:53
    frame #3: sf::priv::GlContext::create() + 49 at GlContext.cpp:162
    frame #4: (anonymous namespace)::getInternalContext() + 29 at GlContext.cpp:107
    frame #5: sf::priv::GlContext::setActive() + 166 at GlContext.cpp:244
    frame #6: sf::priv::GlContext::~GlContext() + 67 at GlContext.cpp:202
    frame #7: sf::priv::SFContext::~SFContext() + 163 at SFContext.mm:111
    frame #8: sf::priv::SFContext::~SFContext() + 21 at SFContext.mm:103
    frame #9: sf::priv::SFContext::~SFContext() + 24 at SFContext.mm:103
    frame #10: sf::Context::~Context() + 55 at Context.cpp:45
    frame #11: sf::Context::~Context() + 21 at Context.cpp:44
    frame #12: (anonymous namespace)::checkShadersAvailable() + 136 at Shader.cpp:122
    frame #13: sf::Shader::isAvailable() + 65 at Shader.cpp:478
    frame #14: sf::RenderTarget::resetGLStates() + 25 at RenderTarget.cpp:347
    frame #15: sf::RenderTarget::draw() + 128 at RenderTarget.cpp:214
    frame #16: sf::VertexArray::draw() const + 192 at VertexArray.cpp:147
    frame #17: sf::RenderTarget::draw() + 136 at RenderTarget.cpp:188
    frame #18: sf::Shape::draw() const + 122 at Shape.cpp:219
    frame #19: sf::RenderTarget::draw() + 136 at RenderTarget.cpp:188
    frame #20: renderingThread() + 352 at main.cpp:27
    frame #21: sf::Thread::run() + 26 at Thread.cpp:83
    frame #22: sf::priv::ThreadImpl::entryPoint() + 54 at ThreadImpl.cpp:87
    frame #23: _pthread_body + 138
    frame #24: 0x00007fff8eaa572a libsystem_pthread.dylib`_pthread_start + 137
    frame #25: 0x00007fff8eaa9fc9 libsystem_pthread.dylib`thread_start + 13
Closing
* thread #1 releasePool() => dispose window context
  * frame #0: releasePool() + 15 at AutoreleasePoolWrapper.mm:196
    frame #1: sf::priv::SFContext::~SFContext() + 146 at SFContext.mm:110
    frame #2: sf::priv::SFContext::~SFContext() + 21 at SFContext.mm:103
    frame #3: sf::priv::SFContext::~SFContext() + 24 at SFContext.mm:103
    frame #4: sf::Window::close() + 53 at Window.cpp:155
    frame #5: main() + 485 at main.cpp:61
    frame #6: start + 52
* thread #1 releasePool() => dispose window pool
  * frame #0: releasePool() + 15 at AutoreleasePoolWrapper.mm:196
    frame #1: sf::priv::WindowImplCocoa::~WindowImplCocoa() + 281 at WindowImplCocoa.mm:233
    frame #2: sf::priv::WindowImplCocoa::~WindowImplCocoa() + 21 at WindowImplCocoa.mm:219
    frame #3: sf::priv::WindowImplCocoa::~WindowImplCocoa() + 24 at WindowImplCocoa.mm:219
    frame #4: sf::Window::close() + 99 at Window.cpp:159
    frame #5: main() + 485 at main.cpp:61
    frame #6: start + 52
* thread #1 releasePool() => dispose shared context
  * frame #0: releasePool() + 15 at AutoreleasePoolWrapper.mm:196
    frame #1: sf::priv::SFContext::~SFContext() + 146 at SFContext.mm:110
    frame #2: sf::priv::SFContext::~SFContext() + 21 at SFContext.mm:103
    frame #3: sf::priv::SFContext::~SFContext() + 24 at SFContext.mm:103
    frame #4: sf::priv::GlContext::globalCleanup() + 53 at GlContext.cpp:139
    frame #5: sf::GlResource::~GlResource() + 70 at GlResource.cpp:75
    frame #6: sf::Window::~Window() + 51 at Window.cpp:80
    frame #7: sf::RenderWindow::~RenderWindow() + 45 at RenderWindow.cpp:61
    frame #8: sf::RenderWindow::~RenderWindow() + 21 at RenderWindow.cpp:59
    frame #9: main() + 546 at main.cpp:66
    frame #10: start + 52
* thread #1 releasePool() => dispose internal thread context
  * frame #0: releasePool() + 15 at AutoreleasePoolWrapper.mm:196
    frame #1: sf::priv::SFContext::~SFContext() + 146 at SFContext.mm:110
    frame #2: sf::priv::SFContext::~SFContext() + 21 at SFContext.mm:103
    frame #3: sf::priv::SFContext::~SFContext() + 24 at SFContext.mm:103
    frame #4: sf::priv::GlContext::globalCleanup() + 693 at GlContext.cpp:145
    frame #5: sf::GlResource::~GlResource() + 70 at GlResource.cpp:75
    frame #6: sf::Window::~Window() + 51 at Window.cpp:80
    frame #7: sf::RenderWindow::~RenderWindow() + 45 at RenderWindow.cpp:61
    frame #8: sf::RenderWindow::~RenderWindow() + 21 at RenderWindow.cpp:59
    frame #9: main() + 546 at main.cpp:66
    frame #10: start + 52
* thread #1 releasePool() => dispose internal thread context (from other thread!!)
  * frame #0: releasePool() + 15 at AutoreleasePoolWrapper.mm:196
    frame #1: sf::priv::SFContext::~SFContext() + 146 at SFContext.mm:110
    frame #2: sf::priv::SFContext::~SFContext() + 21 at SFContext.mm:103
    frame #3: sf::priv::SFContext::~SFContext() + 24 at SFContext.mm:103
    frame #4: sf::priv::GlContext::globalCleanup() + 693 at GlContext.cpp:145
    frame #5: sf::GlResource::~GlResource() + 70 at GlResource.cpp:75
    frame #6: sf::Window::~Window() + 51 at Window.cpp:80
    frame #7: sf::RenderWindow::~RenderWindow() + 45 at RenderWindow.cpp:61
    frame #8: sf::RenderWindow::~RenderWindow() + 21 at RenderWindow.cpp:59
    frame #9: main() + 546 at main.cpp:66
    frame #10: start + 52
Assertion failed: (localPool != NULL), function releasePool, file src/SFML/Window/OSX/AutoreleasePoolWrapper.mm, line 196.

Workaround

In this specific case, but it might not always be true, calling sf::Shader::isAvailable() from the main thread before launching another one prevents the crash.

@LaurentGomila

This comment has been minimized.

Show comment
Hide comment
@LaurentGomila

LaurentGomila Jan 30, 2015

Member

Wouldn't ARC be a solution now?

Member

LaurentGomila commented Jan 30, 2015

Wouldn't ARC be a solution now?

@mantognini

This comment has been minimized.

Show comment
Hide comment
@mantognini

mantognini Jan 30, 2015

Member

There were a few things that turned bad last time I tried switching to ARC. Beside dropping 32-bit machines, there were some leaks (I don't remember exactly what, but that's what I've written in b9f5f19).

Member

mantognini commented Jan 30, 2015

There were a few things that turned bad last time I tried switching to ARC. Beside dropping 32-bit machines, there were some leaks (I don't remember exactly what, but that's what I've written in b9f5f19).

@mantognini mantognini self-assigned this Sep 8, 2015

@mantognini mantognini changed the title from Memory pools not thread safe to Memory pools don't work properly with threads Sep 8, 2015

mantognini added a commit that referenced this issue Sep 8, 2015

@mantognini

This comment has been minimized.

Show comment
Hide comment
@mantognini

mantognini Sep 8, 2015

Member

Superseded by #962

Member

mantognini commented Sep 8, 2015

Superseded by #962

@mantognini mantognini closed this Sep 8, 2015

mantognini added a commit that referenced this issue Sep 18, 2015

mantognini added a commit that referenced this issue Sep 23, 2015

mantognini added a commit that referenced this issue Sep 25, 2015

@mantognini mantognini added this to the 2.4 milestone Aug 5, 2016

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment