Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Error destroying window in Mac when rendering in non-main thread  (Read 6745 times)

0 Members and 1 Guest are viewing this topic.

Pablo

  • Newbie
  • *
  • Posts: 8
    • View Profile
Hi,

I've been trying to run my game engine doing all the rendering in a different thread following the tutorial http://www.sfml-dev.org/tutorials/2.2/graphics-draw.php#drawing-from-threads

I've also read http://www.sfml-dev.org/tutorials/2.2/window-window.php#things-to-know-about-windows and if I understood correctly, in Mac there are these two main constraints:

  • Events must be polled in the window's thread
  • On OS X, windows and events must be managed in the main thread --> Mac OS X just won't agree if you try to create a window or handle events in a thread other than the main one.

I get I can't neither poll the events nor create the window in a different thread than the main one. What is happening to me, though, is that the moment I render something, on destruction of the window I have the following crash:

Crashed Thread:        0  Dispatch queue: com.apple.main-thread

Exception Type:        EXC_CRASH (SIGABRT)
Exception Codes:       0x0000000000000000, 0x0000000000000000

Application Specific Information:
Assertion failed: (localPool != NULL), function releasePool, file /Users/pablo/Game/ThirdParty/SFML/src/SFML/Window/OSX/AutoreleasePoolWrapper.mm, line 203.
 

Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0   libsystem_kernel.dylib              0x00007fff833f5282 __pthread_kill + 10
1   libsystem_c.dylib                   0x00007fff8d081b73 abort + 129
2   libsystem_c.dylib                   0x00007fff8d049c59 __assert_rtn + 321
3   libsfml-window-d.2.2.0.dylib        0x000000010c231f81 releasePool() + 305
4   libsfml-window-d.2.2.0.dylib        0x000000010c2285d2 sf::priv::SFContext::~SFContext() + 146
5   libsfml-window-d.2.2.0.dylib        0x000000010c228635 sf::priv::SFContext::~SFContext() + 21
6   libsfml-window-d.2.2.0.dylib        0x000000010c228658 sf::priv::SFContext::~SFContext() + 24
7   libsfml-window-d.2.2.0.dylib        0x000000010c206e95 sf::priv::GlContext::globalCleanup() + 693
8   libsfml-window-d.2.2.0.dylib        0x000000010c20ad66 sf::GlResource::~GlResource() + 70
9   libsfml-window-d.2.2.0.dylib        0x000000010c211ba3 sf::Window::~Window() + 51
10  libsfml-graphics-d.2.2.0.dylib      0x000000010c289aed sf::RenderWindow::~RenderWindow() + 45
11  libsfml-graphics-d.2.2.0.dylib      0x000000010c289b45 sf::RenderWindow::~RenderWindow() + 21
12  Game                                0x000000010c18de47 main + 679
13  libdyld.dylib                       0x00007fff87f7e5c9 start + 1
 

This is a minimal example to reproduce the issue:

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

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())
    {
        // 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");

    // 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;
}
 

If I don't draw anything, it doesn't crash. I changed slightly the AutoreleasePoolWrapper, SFContext and WindowImplCocoa to check what it was retaining and releasing from which thread adding a few log traces:

This is the output of the code provided at the end in debug:

Main thread 4217047789276772314 started
SFContext::SFContext 0x7fa603c1db30 retainPool from thread 4217047789276772314
AutoreleasePoolWrapper::retainPool: entered for 1 time from thread 4217047789276772314
AutoreleasePoolWrapper::retainPool: creating new pool from thread 4217047789276772314
SFContext::SFContext 0x7fa603e0a440 retainPool from thread 4217047789276772314
AutoreleasePoolWrapper::retainPool: entered for 2 time from thread 4217047789276772314
WindowImplCocoa::VideoMode 0x7fa603e0c230 retainPool from thread 4217047789276772314
AutoreleasePoolWrapper::retainPool: entered for 3 time from thread 4217047789276772314
SFContext::SFContext,WindowImpl 0x7fa603d0c680 retainPool from thread 4217047789276772314
AutoreleasePoolWrapper::retainPool: entered for 4 time from thread 4217047789276772314


Rendering thread 13536384218981027915 started
SFContext::SFContext 0x7fa6058649d0 retainPool from thread 13536384218981027915
AutoreleasePoolWrapper::retainPool: entered for 5 time from thread 13536384218981027915
AutoreleasePoolWrapper::retainPool: creating new pool from thread 13536384218981027915
SFContext::~SFContext 0x7fa6058649d0 releasePool from thread 13536384218981027915
AutoreleasePoolWrapper::releasePool: entered for 1 time from thread 13536384218981027915
AutoreleasePoolWrapper::releasePool: releasing pool for thread 13536384218981027915
SFContext::SFContext 0x7fa605859e80 retainPool from thread 13536384218981027915
AutoreleasePoolWrapper::retainPool: entered for 6 time from thread 13536384218981027915
AutoreleasePoolWrapper::retainPool: creating new pool from thread 13536384218981027915
Closing


SFContext::~SFContext 0x7fa603d0c680 releasePool from thread 4217047789276772314
AutoreleasePoolWrapper::releasePool: entered for 2 time from thread 4217047789276772314
WindowImplCocoa::~WindowImplCocoa 0x7fa603e0c230 releasePool from thread 4217047789276772314
AutoreleasePoolWrapper::releasePool: entered for 3 time from thread 4217047789276772314
SFContext::~SFContext 0x7fa603c1db30 releasePool from thread 4217047789276772314
AutoreleasePoolWrapper::releasePool: entered for 4 time from thread 4217047789276772314
SFContext::~SFContext 0x7fa603e0a440 releasePool from thread 4217047789276772314
AutoreleasePoolWrapper::releasePool: entered for 5 time from thread 4217047789276772314
AutoreleasePoolWrapper::releasePool: releasing pool for thread 4217047789276772314
SFContext::~SFContext 0x7fa605859e80 releasePool from thread 4217047789276772314
AutoreleasePoolWrapper::releasePool: entered for 6 time from thread 4217047789276772314
AutoreleasePoolWrapper::releasePool: has NULL localPool for thread 4217047789276772314. assert expected

Assertion failed: (localPool != NULL), function releasePool, file /Users/pablo/Game/ThirdParty/SFML/src/SFML/Window/OSX/AutoreleasePoolWrapper.mm, line 208.
Abort trap: 6

It seems pretty clear that since AutoreleasePoolWrapper is using localPool as a TLS variable, and the SFContext from the rendering thread is called from that thread whereas its deconstructor is called from the main one. When this happens, localPool from the main thread has already been released. The question I guess is whether I'm doing something wrong or this is the usual behavior and I'm missing something in here. Please feel free to give any feedback cause I've already tried tons of things.

Something as simple as adding a checking would "solve" the crash, but it doesn't solve the deep issue which is that the constructor is called from one thread whereas the desctructor is called from another one, making that the pool of "the other thread" (aka. rendering thread) is never released.

void releasePool(void)
{
    if (localPool == NULL) {
        return;
    }
    ...
 

My OS is Yosemite 10.10.1 and I'm using SFML 2.2 compiled by myself.

Sorry for the long post but I wanted to provide as much information as possible to help figuring out the problem. Also, I'm not sure if this is the proper place for this post or Help->Window is better. Please feel free to move it.

Thanks a million in advance  :D
« Last Edit: January 28, 2015, 09:35:34 am by Pablo »

Gambit

  • Sr. Member
  • ****
  • Posts: 283
    • View Profile
Re: Error destroying window in Mac when rendering in a different thread
« Reply #1 on: January 28, 2015, 12:15:04 am »
You need to synchronize the threads so that they know what is happening. Without it, ... Well you can guess.

(click to show/hide)

Pablo

  • Newbie
  • *
  • Posts: 8
    • View Profile
Re: Error destroying window in Mac when rendering in a different thread
« Reply #2 on: January 28, 2015, 12:19:35 am »
You need to synchronize the threads so that they know what is happening. Without it, ... Well you can guess.

(click to show/hide)

Thanks for the quick response. What kind of synchronization you are refering to? I took the example from the tutorials, and I tested that worked on Windows. One thing that I added is that instead of directly closing the window from the main thread, I change a flag and wait for the rendering thread to finish cleanly. I'd call that synchronization. Not an elegant way of synchronization, but synchronization after all. Anyway, if you're able to reproduce the issue and fix it synchronizing both threads, I'd like to know how.

Cheers

« Last Edit: January 28, 2015, 12:43:15 am by Pablo »

Gambit

  • Sr. Member
  • ****
  • Posts: 283
    • View Profile
Re: Error destroying window in Mac when rendering in a different thread
« Reply #3 on: January 28, 2015, 12:23:00 am »
Unfortunately I do not know how to synchronize threads. Multithreading in C++ increases code complexity by about a billion percent and you cannot tackle it head on like single threaded applications. It requires a completely different mindset.

I'm going to strongly suggest that you keep your application single threaded, at least until you are certain you can handle multithreading. It will solve this exact problem and keep your code a lot cleaner and more understandable (Providing you can understand your own code).

Jesper Juhl

  • Hero Member
  • *****
  • Posts: 1405
    • View Profile
    • Email
Re: Error destroying window in Mac when rendering in multithread
« Reply #4 on: January 28, 2015, 12:34:31 am »
Agreed. Multithreading is a useful tool in the toolbox, but it is very difficult to use correctly. If using a single thread can solve the problem then it is often the best aproach.
Threading has its place. Sure. But it is often overused and it complicates code enormously - use when you have to - avoid whenever you can.
« Last Edit: January 28, 2015, 12:39:25 am by Jesper Juhl »

Gambit

  • Sr. Member
  • ****
  • Posts: 283
    • View Profile
Re: Error destroying window in Mac when rendering in multithread
« Reply #5 on: January 28, 2015, 12:42:50 am »
Unless you are working for a software company or some sort of enterprise, it is very rare that you actually need mulithreading. You can use it for everything, even simple SFML applications, but you dont need it. Like Jesper said, if you can avoid it, by all means do it.

Pablo

  • Newbie
  • *
  • Posts: 8
    • View Profile
Re: Error destroying window in Mac when rendering in multithread
« Reply #6 on: January 28, 2015, 12:50:49 am »
Unfortunately I do not know how to synchronize threads. Multithreading in C++ increases code complexity by about a billion percent and you cannot tackle it head on like single threaded applications. It requires a completely different mindset.

Multithreading in C++ has become a lot easier with C++11. I guess you're more used to Java, but using sf::Mutex or std::mutex and lockers is not that difficult. The difficult part comes when you have to deal with race conditions, etc, but that's not something C++ related but multithreaded related.

I agree with both of you that multithreading complicates the code. I do. But there's an example in the tutorials explaining how you may use it. It should work, right? I'm not saying I'm going to use multithreading rendering, but I was just writing a PoC of how it would be and found what I think might be a bug (because it seems different OSs handle windows quite different) and wanted to report it here to know whether I'm missing something or is some issue.
« Last Edit: January 28, 2015, 12:53:34 am by Pablo »

Jesper Juhl

  • Hero Member
  • *****
  • Posts: 1405
    • View Profile
    • Email
Re: Error destroying window in Mac when rendering in multithread
« Reply #7 on: January 28, 2015, 01:08:00 am »
Guarding access to variables with a mutex/semaphore/spinlock etc is just the tip of the proverbial iceberg. In addition to that you have to consider things like;
Functions you call (including OS, stdlib or SFML functions) not being reentrant.
Priority inversions.
Deadlocks and livelocks.
Reordering of statements/instructions resulting in what you see in your source code not being what the CPU actually executes.
Access to resources like files not being synchronized.
Forgetting that all "const" functions should probably be thread safe or you are going to have nasty surprises on your hands.
There's a good reason why version 1 of Java deprecated thread.cancel() - and no, POSIX threads cancelation points/functions are not a solution.
Async events like signals can really screw you over (even with no threads) - you do remember to save and restore errno in your signal handlers; right?
Then you have problems like initializations of static globals which Microsofts compiler still doesn't get right.

And a ton of other issues.

In short; unless you really, really need threads - you are probably better off avoiding them.
« Last Edit: January 28, 2015, 01:12:07 am by Jesper Juhl »

Pablo

  • Newbie
  • *
  • Posts: 8
    • View Profile
Re: Error destroying window in Mac when rendering in non-main thread
« Reply #8 on: January 28, 2015, 01:13:54 am »
I agree 100% with you, Jesper. What I meant in my previous post is that multithreading is a difficult thing "per se", not because of the fact that C++ makes it much more difficult than other languages.

Having said that, my goal opening this post was not to start a war about multithreading, but about helping to identify what might be a bug.

Gambit

  • Sr. Member
  • ****
  • Posts: 283
    • View Profile
Re: Error destroying window in Mac when rendering in non-main thread
« Reply #9 on: January 28, 2015, 01:22:19 am »
Multithreading in C++ has become a lot easier with C++11. I guess you're more used to Java

I'm not a Java developer? In C++11/14, multithreading is easier, but that doesnt mean its easy. It means you now have a nicer abstraction so you dont blow yourself  up when you try it. SFML has "legacy" mutex and thread. I say legacy because C++ now has threads and mutexes. Just because SFML has these features, doesnt necessarily mean that everyone knows how to use them. I'm going to go on a limb here and say that I highly doubt you will get help continuing your multithreaded adventure due to its complexity and unfriendlyness to new comers, and not a lot of people have the patience to help with that.

Jesper Juhl

  • Hero Member
  • *****
  • Posts: 1405
    • View Profile
    • Email
Re: Error destroying window in Mac when rendering in non-main thread
« Reply #10 on: January 28, 2015, 01:23:24 am »
Regarding your question. If you read the documentation I believe you'll find that it clearly states that events must be processed in the Window's thread and I'm fairly certain that drawing must also happen in the main thread of the executable (although I can't find the documentation to back that up right now).

Pablo

  • Newbie
  • *
  • Posts: 8
    • View Profile
Re: Error destroying window in Mac when rendering in non-main thread
« Reply #11 on: January 28, 2015, 01:28:23 am »
Regarding your question. If you read the documentation I believe you'll find that it clearly states that events must be processed in the Window's thread and I'm fairly certain that drawing must also happen in the main thread of the executable (although I can't find the documentation to back that up right now).

Yes, indeed in the example I'm processing all the events in the main thread (which is the one that created the window). If you had to do all the drawing in the main thread then there'd be no point in creating a rendering thread at all. But as there's a tutorial for doing it so, it should mean it's possible.
« Last Edit: January 28, 2015, 01:38:44 am by Pablo »

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 10819
    • View Profile
    • development blog
    • Email
AW: Error destroying window in Mac when rendering in non-main thread
« Reply #12 on: January 28, 2015, 01:37:28 am »
Looks indeed like an issue in the OS X implementation. Time to wait for Hirua's comment. ;)

Two points about your code:
* bool is not an atomic type and operations on it require synchronization as well, or you use the C++11 atomic symbols.
* Keep in mind that std::hash's hashing algorithm is compiler/toolchain specific and it's not always very good.
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

Hiura

  • SFML Team
  • Hero Member
  • *****
  • Posts: 4321
    • View Profile
    • Email
Re: Error destroying window in Mac when rendering in non-main thread
« Reply #13 on: January 28, 2015, 09:58:31 am »
Hey Hey, thanks for the *proper* bug report. It's not everyday I get something that clear about a bug. So let's focus on the issue at hand and not digress on opinion based topic about MT programming, shall we? :-)

BTW, to debug things, use your debugger, it's way faster since you don't need to edit code to add prints. In this case, I added breakpoints in releasePool() and retainPool() that automatically continue executing code after executing bt -c 0 (print only the header of the backtrace) and I get the following:

Main thread 4382000746217841212 started
* thread #1: tid = 0x2d68f1, 0x000000010006f9cf libsfml-window-d.2.2.0.dylib`retainPool() + 15 at AutoreleasePoolWrapper.mm:177, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
* thread #1: tid = 0x2d68f1, 0x000000010006f9cf libsfml-window-d.2.2.0.dylib`retainPool() + 15 at AutoreleasePoolWrapper.mm:177, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
* thread #1: tid = 0x2d68f1, 0x000000010006f9cf libsfml-window-d.2.2.0.dylib`retainPool() + 15 at AutoreleasePoolWrapper.mm:177, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
* thread #1: tid = 0x2d68f1, 0x000000010006f9cf libsfml-window-d.2.2.0.dylib`retainPool() + 15 at AutoreleasePoolWrapper.mm:177, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
* thread #9: tid = 0x2d696a, 0x000000010006f9cf libsfml-window-d.2.2.0.dylib`retainPool() + 15 at AutoreleasePoolWrapper.mm:177, stop reason = breakpoint 1.1
* thread #9: tid = 0x2d696a, 0x000000010006fadf libsfml-window-d.2.2.0.dylib`releasePool() + 15 at AutoreleasePoolWrapper.mm:196, stop reason = breakpoint 2.1
Rendering thread 9965911431153986984 started
* thread #9: tid = 0x2d696a, 0x000000010006f9cf libsfml-window-d.2.2.0.dylib`retainPool() + 15 at AutoreleasePoolWrapper.mm:177, stop reason = breakpoint 1.1
Closing
* thread #1: tid = 0x2d68f1, 0x000000010006fadf libsfml-window-d.2.2.0.dylib`releasePool() + 15 at AutoreleasePoolWrapper.mm:196, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
* thread #1: tid = 0x2d68f1, 0x000000010006fadf libsfml-window-d.2.2.0.dylib`releasePool() + 15 at AutoreleasePoolWrapper.mm:196, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
* thread #1: tid = 0x2d68f1, 0x000000010006fadf libsfml-window-d.2.2.0.dylib`releasePool() + 15 at AutoreleasePoolWrapper.mm:196, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
* thread #1: tid = 0x2d68f1, 0x000000010006fadf libsfml-window-d.2.2.0.dylib`releasePool() + 15 at AutoreleasePoolWrapper.mm:196, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
* thread #1: tid = 0x2d68f1, 0x000000010006fadf libsfml-window-d.2.2.0.dylib`releasePool() + 15 at AutoreleasePoolWrapper.mm:196, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
Assertion failed: (localPool != NULL), function releasePool, file /Users/m/Prog/Libraries/C++/SFML/git/SFML/src/SFML/Window/OSX/AutoreleasePoolWrapper.mm, line 196.
 

I thought you might like to know that such feature existed.  ;)

Anyway, your conclusion is right: the number of retain/release calls per threads is not balanced.

I'll investigate and come back to you.
SFML / OS X developer

Hiura

  • SFML Team
  • Hero Member
  • *****
  • Posts: 4321
    • View Profile
    • Email
Re: Error destroying window in Mac when rendering in non-main thread
« Reply #14 on: January 28, 2015, 10:34:46 am »
Okay, so no proper fix for now but at least a workaround.

It happens that the last two pool releases are linked to internal context (one per thread). I was expecting them to be released on their original thread but since they are manually allocated and the global cleanup is happening on the main thread...

So, the workaround is simply adding sf::Shader::isAvailable(); in the main thread before launching the secondary thread.

Let me know if it works for you too. And keep in mind that with a different code, a different workaround may be needed.

SFML / OS X developer

 

anything