Sfml: Crash after closing wxWidgets with SFML-window

Created on 23 Jan 2019  路  7Comments  路  Source: SFML/SFML

*System specification are as follow...
macOS Mojave -> Mac OS X 10.14.2
wxWidgets 3.1.2 -> --with-osx_cocoa --with-macosx-version-min=10.14 --with-macosx=MacOSX10.14.sdk
SFML 2.5.1 -> macOS OS X 10.7+, compatible with C++11 and libc++

The following code snippet, shown below, comes from a GitHub project that you can find at this link https://gist.github.com/eriknelson/e4ccf32534eb3d25a1ea. Please give Eriknelson credit for his SFML 2.3+ into wxWidgets 3.0 integration demo. This demo is not prefect but it only has one flaw. The application crashes after closing the wxWidgets window.

#include <iostream>
#include <wx/wx.h>
#include <memory>
#include <SFML/Graphics.hpp>

#ifdef __WXGTK__
#include <gdk/gdkx.h>
#include <gtk/gtk.h>
#endif

using namespace std;

static const int kDefaultWindowWidth = 1280;
static const int kDefaultWindowHeight = 720;
static const int kCanvasMargin = 0;

struct wxSfmlCanvas : public wxControl, public sf::RenderWindow
{
  wxSfmlCanvas(
    wxWindow *parent = nullptr,
    wxWindowID windowId = -1,
    const wxPoint &position = wxDefaultPosition,
    const wxSize &size = wxDefaultSize,
    long style = 0) :
  wxControl(parent, windowId, position, size, style)
  {
    createRenderWindow();
  }

  virtual void onUpdate(){};

  void onIdle(wxIdleEvent& event)
  {
    // Send a paint message when control is idle, to ensure max framerate
    Refresh();
  }

  void onPaint(wxPaintEvent& event)
  {
    wxPaintDC dc(this);     // Prepare control to be repainted
    onUpdate();             // Tick update
    display();              // Draw
  }

  // Explicitly overriding prevents wxWidgets from drawing, which could result in flicker
  void onEraseBackground(wxEraseEvent& event){}

  void createRenderWindow()
  {
#ifdef __WXGTK__
    // gtk_widget_realize(m_wxwindow);
    // gtk_widget_set_double_buffered(m_wxwindow, false);

    // GdkWindow *gdkWindow = gtk_widget_get_window((GtkWidget*)GetHandle());
    // XFlush(GDK_WINDOW_XDISPLAY(gdkWindow));

    // sf::RenderWindow::create(GDK_WINDOW_XWINDOW(gdkWindow));
#else
    sf::RenderWindow::create(GetHandle());
#endif
  }

  void setwxWindowSize(const wxSize& size)
  {
    this->SetSize(size);
  }

  void setRenderWindowSize(const sf::Vector2u& size)
  {
    this->setSize(size);
  }

  virtual ~wxSfmlCanvas(){};

wxDECLARE_EVENT_TABLE();
};

struct Canvas : public wxSfmlCanvas
{
  Canvas(
    wxWindow* parent,
    wxWindowID id,
    wxPoint position,
    wxSize size,
    long style = 0) :
  wxSfmlCanvas(parent, id, position, size, style)
  {
  }

  virtual void onUpdate()
  {
    clear(sf::Color::Yellow);
    sf::CircleShape shape(50);
    shape.setFillColor(sf::Color(150, 50, 250));
    shape.setOutlineThickness(10);
    shape.setOutlineColor(sf::Color(250, 150, 100));
    sf::RectangleShape line(sf::Vector2f(150, 5));
    line.rotate(45);
    this->draw(shape);
    this->draw(line);
  }

  void onResize(wxSizeEvent& event)
  {
    auto size = event.GetSize();
    auto newCanvasWidth = size.x - (2 * kCanvasMargin);
    auto newCanvasHeight = size.y - (2 * kCanvasMargin);
    sf::Vector2u v(newCanvasWidth,newCanvasHeight);
    wxSize s(newCanvasWidth,newCanvasHeight);
    this->SetSize(s); //wx setSize
    this->setSize(v); //SFML setSize
  }
};

struct AppFrame : public wxFrame
{
  AppFrame(const wxString& title, const wxPoint& pos, const wxSize& size) :
    wxFrame(NULL, wxID_ANY, title, pos, size),
    _panel(new wxPanel(this)),
    _canvas(new Canvas(
      _panel.get(),
      wxID_ANY,
      wxPoint(kCanvasMargin, kCanvasMargin),
      wxSize(kDefaultWindowWidth - (2 * kCanvasMargin), kDefaultWindowHeight - (2 * kCanvasMargin))
    ))
  {
    _panel->SetBackgroundColour(*wxCYAN);

    ////////////////////////////////////////////////////////////////////////////////
    // Probably due to some RTTI, IDE is getting confused by this dynamic call
    // and doesn't understand the correct Bind overload. Causes non sequitur errors
    // to display in the inspector. Suppress.
    //
    // Dynamic binding is cleanest here, since we don't want to hook up our resize
    // handler until our dependencies (Canvas namely) have finished their initialization
    ////////////////////////////////////////////////////////////////////////////////
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wint-conversion"
    Bind(wxEVT_SIZE, &AppFrame::onResize, this);
    #pragma clang diagnostic pop
    ////////////////////////////////////////////////////////////////////////////////

  }

  void onResize(wxSizeEvent& event)
  {
    _canvas->onResize(event);
    event.Skip();
  }

  unique_ptr<wxPanel> _panel;
  unique_ptr<Canvas> _canvas;
};

struct App : public wxApp
{
  virtual bool OnInit()
  {
    auto frame = new AppFrame("SFML Canvas Demo", wxPoint(100, 100),
                              wxSize(kDefaultWindowWidth, kDefaultWindowHeight));
    frame->Show(true);
    return true;
  }
};

wxBEGIN_EVENT_TABLE(wxSfmlCanvas, wxControl)
  EVT_IDLE(wxSfmlCanvas::onIdle)
  EVT_PAINT(wxSfmlCanvas::onPaint)
  EVT_ERASE_BACKGROUND(wxSfmlCanvas::onEraseBackground)
wxEND_EVENT_TABLE()

wxIMPLEMENT_APP(App);

His example runs as expected but upon closing the wxWidgets windows it crashes with the following error shown below.

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

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

Termination Reason:    Namespace OBJC, Code 0x1

Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0   libsystem_kernel.dylib          0x00007fff64e7c01e __abort_with_payload + 10
1   libsystem_kernel.dylib          0x00007fff64e77541 abort_with_payload_wrapper_internal + 82
2   libsystem_kernel.dylib          0x00007fff64e774ef abort_with_reason + 22
3   libobjc.A.dylib                 0x00007fff63c58674 _objc_fatalv(unsigned long long, unsigned long long, char const*, __va_list_tag*) + 108
4   libobjc.A.dylib                 0x00007fff63c58526 _objc_fatal(char const*, ...) + 135
5   libobjc.A.dylib                 0x00007fff63c62525 (anonymous namespace)::AutoreleasePoolPage::fastcheck(bool) + 125
6   libobjc.A.dylib                 0x00007fff63c4a9f7 (anonymous namespace)::AutoreleasePoolPage::pop(void*) + 65
7   com.apple.CoreFoundation        0x00007fff37ab9ee6 _CFAutoreleasePoolPop + 22
8   com.apple.Foundation            0x00007fff39e66a5e -[NSAutoreleasePool drain] + 144
9   org.sfml-dev.sfml-window        0x000000010ffc86a5 drainThreadPool() + 37
10  org.sfml-dev.sfml-window        0x000000010ffc76e4 sf::priv::WindowImplCocoa::~WindowImplCocoa() + 148
11  org.sfml-dev.sfml-window        0x000000010ffc772f sf::priv::WindowImplCocoa::~WindowImplCocoa() + 15
12  org.sfml-dev.sfml-window        0x000000010ffb85ca sf::Window::~Window() + 58
13  org.wxwindows.                  0x000000010fe8dc75 wxSfmlCanvas::~wxSfmlCanvas() + 37 (main.cpp:73)
14  org.wxwindows.                  0x000000010fe8dc45 Canvas::~Canvas() + 21 (main.cpp:78)
15  org.wxwindows.                  0x000000010fe8d505 Canvas::~Canvas() + 21 (main.cpp:78)
16  org.wxwindows.                  0x000000010fe8d529 Canvas::~Canvas() + 25 (main.cpp:78)
17  org.wxwindows.                  0x000000010fe8e740 AppFrame::~AppFrame() + 304 (memory:2285)
18  org.wxwindows.                  0x000000010fe8b335 AppFrame::~AppFrame() + 21 (main.cpp:125)
19  org.wxwindows.                  0x000000010fe8b359 AppFrame::~AppFrame() + 25 (main.cpp:125)
20  libwx_osx_cocoau-3.1.2.0.0.dylib    0x000000010fffdd3d wxAppConsoleBase::ProcessIdle() + 157
21  libwx_osx_cocoau-3.1.2.0.0.dylib    0x0000000110238476 wxAppBase::ProcessIdle() + 22
22  libwx_osx_cocoau-3.1.2.0.0.dylib    0x000000011018550a wxApp::ProcessIdle() + 26
23  libwx_osx_cocoau-3.1.2.0.0.dylib    0x00000001100e41fc wxCFEventLoop::OSXCommonModeObserverCallBack(__CFRunLoopObserver*, int, void*) + 92
24  com.apple.CoreFoundation        0x00007fff37b459cd __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 23
25  com.apple.CoreFoundation        0x00007fff37b45902 __CFRunLoopDoObservers + 452
26  com.apple.CoreFoundation        0x00007fff37ae7558 __CFRunLoopRun + 1469
27  com.apple.CoreFoundation        0x00007fff37ae6d48 CFRunLoopRunSpecific + 463
28  com.apple.HIToolbox             0x00007fff36d7dab5 RunCurrentEventLoopInMode + 293
29  com.apple.HIToolbox             0x00007fff36d7d7eb ReceiveNextEventCommon + 618
30  com.apple.HIToolbox             0x00007fff36d7d568 _BlockUntilNextEventMatchingListInModeWithFilter + 64
31  com.apple.AppKit                0x00007fff35038363 _DPSNextEvent + 997
32  com.apple.AppKit                0x00007fff35037102 -[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 1362
33  com.apple.AppKit                0x00007fff35031165 -[NSApplication run] + 699
34  libwx_osx_cocoau-3.1.2.0.0.dylib    0x00000001101f0dff wxGUIEventLoop::OSXDoRun() + 207
35  libwx_osx_cocoau-3.1.2.0.0.dylib    0x00000001100e4ab1 wxCFEventLoop::DoRun() + 49
36  libwx_osx_cocoau-3.1.2.0.0.dylib    0x000000011002fcae wxEventLoopBase::Run() + 158
37  libwx_osx_cocoau-3.1.2.0.0.dylib    0x000000010fffda13 wxAppConsoleBase::MainLoop() + 99
38  libwx_osx_cocoau-3.1.2.0.0.dylib    0x000000011018555a wxApp::OnRun() + 26
39  libwx_osx_cocoau-3.1.2.0.0.dylib    0x0000000110067763 wxEntry(int&, wchar_t**) + 131
40  org.wxwindows.                  0x000000010fe883b6 main + 38 (main.cpp:182)
41  libdyld.dylib                   0x00007fff64d24ed9 start + 1

Thread 1:
0   libsystem_pthread.dylib         0x00007fff64f173f8 start_wqthread + 0
1   ???                             0x0000000054485244 0 + 1414025796

Thread 2:: Dispatch queue: NSCGSDisableUpdates
0   libsystem_kernel.dylib          0x00007fff64e5e17a mach_msg_trap + 10
1   libsystem_kernel.dylib          0x00007fff64e5e6d0 mach_msg + 60
2   com.apple.SkyLight              0x00007fff5dc44bc5 CGSUpdateManager::enable_updates_common() + 577
3   com.apple.SkyLight              0x00007fff5dbe7e3b CGSUpdateManager::enable_update(unsigned long long) + 317
4   libdispatch.dylib               0x00007fff64cd5d53 _dispatch_call_block_and_release + 12
5   libdispatch.dylib               0x00007fff64cd6dcf _dispatch_client_callout + 8
6   libdispatch.dylib               0x00007fff64cdd124 _dispatch_lane_serial_drain + 618
7   libdispatch.dylib               0x00007fff64cddbdc _dispatch_lane_invoke + 388
8   libdispatch.dylib               0x00007fff64ce6090 _dispatch_workloop_worker_thread + 603
9   libsystem_pthread.dylib         0x00007fff64f1760b _pthread_wqthread + 409
10  libsystem_pthread.dylib         0x00007fff64f17405 start_wqthread + 13

Thread 3:
0   libsystem_pthread.dylib         0x00007fff64f173f8 start_wqthread + 0
1   ???                             0x0000000054485244 0 + 1414025796

Thread 4:: com.apple.NSEventThread
0   libsystem_kernel.dylib          0x00007fff64e5e17a mach_msg_trap + 10
1   libsystem_kernel.dylib          0x00007fff64e5e6d0 mach_msg + 60
2   com.apple.CoreFoundation        0x00007fff37ae80c2 __CFRunLoopServiceMachPort + 337
3   com.apple.CoreFoundation        0x00007fff37ae7611 __CFRunLoopRun + 1654
4   com.apple.CoreFoundation        0x00007fff37ae6d48 CFRunLoopRunSpecific + 463
5   com.apple.AppKit                0x00007fff35040f89 _NSEventThread + 160
6   libsystem_pthread.dylib         0x00007fff64f18305 _pthread_body + 126
7   libsystem_pthread.dylib         0x00007fff64f1b26f _pthread_start + 70
8   libsystem_pthread.dylib         0x00007fff64f17415 thread_start + 13

Thread 5:
0   libsystem_pthread.dylib         0x00007fff64f173f8 start_wqthread + 0

Thread 0 crashed with X86 Thread State (64-bit):
  rax: 0x0000000002000209  rbx: 0x0000000000000080  rcx: 0x00007ffedfd767c8  rdx: 0x0000000000000000
  rdi: 0x0000000000000008  rsi: 0x0000000000000001  rbp: 0x00007ffedfd76810  rsp: 0x00007ffedfd767c8
   r8: 0x00007febce519d50   r9: 0x0000000000000080  r10: 0x0000000000000000  r11: 0x0000000000000246
  r12: 0x0000000000000000  r13: 0x0000000000000000  r14: 0x0000000000000001  r15: 0x0000000000000008
  rip: 0x00007fff64e7c01e  rfl: 0x0000000000000246  cr2: 0x0000000116681000

Logical CPU:     0
Error Code:      0x02000209
Trap Number:     133

I suspect wxControl and sf::RenderWindows are trying the close the NSView, which comes from the GetHandle() call, at the same time.

-James Harris

bug sfml-window macos undecided

Most helpful comment

As expected, the objects destruction order doesn't matter. As a result, reversing the inheritance had no impact.

For the second test, I tried separating the inheritance to isolate this issue to the NSView. So, I made sf::RenderWindow into a member of wxSfmlCanvas. Interesting enough, after closing the application, the debug console now outputs the following message.

@"Cannot close SFML area when SFML is integrated in a NSView.\r\n"
@"Cannot close SFML area when SFML is integrated in a NSView.\r\n"
@"objc[6991]: autorelease pool page 0x1018b2000 corrupted\r\n"
@"  magic     0x81b800c0 0x00007fff 0x018dfcd8 0x00000001\r\n"
@"  should be 0xa1a1a1a1 0x4f545541 0x454c4552 0x21455341\r\n"
@"  pthread   0x7fff21f7e158\r\n"
@"  should be 0x1001025c0\r\n"
@"\r\n"

It appears that the NSView is being closed before SFML has a chance to shutdown, I think. So, my next test will be to wrap sf::RenderWindow into a singleton class to isolate wx. I'm suspecting wx is closing the NSView while SFML is still using it, possibly releasing resources too soon.

-James Harris

All 7 comments

I pinged @mantognini already based on your forum post, as he'd know best what is going on here. I tried to understand it, but I don't really following the whole logic and workarounds...

I quickly looked into it.

objc[32687]: autorelease pool page 0x10189b000 corrupted
  magic     0x02610f00 0x00006000 0xa2904f60 0x00007fff
  should be 0xa1a1a1a1 0x4f545541 0x454c4552 0x21455341
  pthread   0x0
  should be 0x1000d35c0

Is wx corrupting pthread state?

The crash happens in AutoreleasePoolWrapper.mm's destroyPool() function. But I have no idea what's happening. I tried removing all the pthread related stuff from this file but this hasn't solve the issue (I was expecting to see the bug go away without introducing another one because all allocations are done in the main thread with this test). Instead, I got different values:

objc[36432]: autorelease pool page 0x102071000 corrupted
  magic     0x9846a19d 0x00000000 0xfecc1b72 0x4447417b
  should be 0xa1a1a1a1 0x4f545541 0x454c4552 0x21455341
  pthread   0x10f8a72c272bc602
  should be 0x1000d35c0

Maybe someone more familiar with wx could help?

Separating the multiple inheritance of wxSfmlCanvas is something I would like to try in the coming weeks. Because maybe this crash is caused by destructing the wx stuff prior to the sfml::window? After a second thought, maybe switching the order of the inheritance could yield different results? Another approach, I would like to try is rolling back wxWidgets to an earlier version, such as 2.8.12. Maybe this approach could work for now until this one is resolved later?

I will let you know what I find in the coming weeks. In advanced, thank you for your patience and help.

-James Harris

As expected, the objects destruction order doesn't matter. As a result, reversing the inheritance had no impact.

For the second test, I tried separating the inheritance to isolate this issue to the NSView. So, I made sf::RenderWindow into a member of wxSfmlCanvas. Interesting enough, after closing the application, the debug console now outputs the following message.

@"Cannot close SFML area when SFML is integrated in a NSView.\r\n"
@"Cannot close SFML area when SFML is integrated in a NSView.\r\n"
@"objc[6991]: autorelease pool page 0x1018b2000 corrupted\r\n"
@"  magic     0x81b800c0 0x00007fff 0x018dfcd8 0x00000001\r\n"
@"  should be 0xa1a1a1a1 0x4f545541 0x454c4552 0x21455341\r\n"
@"  pthread   0x7fff21f7e158\r\n"
@"  should be 0x1001025c0\r\n"
@"\r\n"

It appears that the NSView is being closed before SFML has a chance to shutdown, I think. So, my next test will be to wrap sf::RenderWindow into a singleton class to isolate wx. I'm suspecting wx is closing the NSView while SFML is still using it, possibly releasing resources too soon.

-James Harris

I wrapped sf::RenderWindow into the singleton class, and I was able to isolate the shutdown routines to run three tests.

Shutdown SFML before wx didn't stop the crash from occurring, but instead caused a 'invalid or prematurely-freed autorelease pool' during wx's shutdown. If wx was allowed to shutdown completely while sf::RenderWindow is wrapped in a singleton class the crash also still occurred. The final test actually eliminated the crash and implies a problem with compiler destructor priority between NSView and SFML.

For the last test, I took out the singleton, and declared sf::RenderWindow as a global variable wrapped inside a namespace. Long behold, the application stopped crashing after closing the application. Somewhere in the operating system's shutdown process the destructor order is off.

To conclude, adding __attribute__((destructor[(priority)])) function attributes could be a possible solution.

To conclude, adding attribute((destructor[(priority)])) function attributes could be a possible solution.

Would that be an SFML modification or one in your own code?

This would be a modification to SFML, but I would like to take back my statement about using attribute. After further research today into what NSAutoreleasePool is doing. I found this resource which may help https://github.com/bkaradzic/bgfx/issues/1607 . There may be an issue with NSAutoreleasePool and how SFML'd usage is interfering with wx on a single thread. The resource says it appears to work on multi-thread. Their solution was to move NSAutoreleasePool to another location to prevent conflicts.

Was this page helpful?
0 / 5 - 0 ratings