[WORK-AROUND FOUND] Crash on Shutdown .. SubscriberSlots ..

For help with general CEGUI usage:
- Questions about the usage of CEGUI and its features, if not explained in the documentation.
- Problems with the CMAKE configuration or problems occuring during the build process/compilation.
- Errors or unexpected behaviour.

Moderators: CEGUI MVP, CEGUI Team

daves
Home away from home
Home away from home
Posts: 253
Joined: Thu Feb 02, 2006 20:12

[WORK-AROUND FOUND] Crash on Shutdown .. SubscriberSlots ..

Postby daves » Thu Jul 31, 2008 20:30

I have a shutdown issue that I'm trying to resolve. A particular widget subscribes to mouse clicks using the following call:

Code: Select all

mImgWindow->subscribeEvent(CEGUI::Window::EventMouseClick, CEGUI::Event::Subscriber(&DisplayedImage::handleMouseClick, this));


When the CEGUI is cleaning up the dead pool I get a crash when the "event slots" are being cleaned for this widget. Here is the calling stack at the time of the crash:

    > CEGUIBase_d.dll!CEGUI::SubscriberSlot::cleanup() Line 52 + 0x32 bytes C++
    CEGUIBase_d.dll!CEGUI::Event::~Event() Line 74 C++
    CEGUIBase_d.dll!CEGUI::Event::`scalar deleting destructor'() + 0x2b bytes C++
    CEGUIBase_d.dll!CEGUI::EventSet::removeAllEvents() Line 97 + 0x36 bytes C++
    CEGUIBase_d.dll!CEGUI::EventSet::~EventSet() Line 54 C++
    CEGUIBase_d.dll!CEGUI::Window::~Window() Line 245 + 0x11c bytes C++
    CEGUIBase_d.dll!CEGUI::GUISheet::~GUISheet() Line 75 + 0x3e bytes C++
    CEGUIBase_d.dll!CEGUI::GUISheet::`vector deleting destructor'() + 0x6c bytes C++
    CEGUIBase_d.dll!CEGUI::GUISheetFactory::destroyWindow(CEGUI::Window * window=0x123e0160) Line 69 + 0x57 bytes C++
    CEGUIBase_d.dll!CEGUI::WindowManager::cleanDeadPool() Line 256 + 0x1a bytes C++
    CEGUIBase_d.dll!CEGUI::System::~System() Line 343 C++
    svt_d.dll!CEGUI::System::`scalar deleting destructor'() + 0x2e bytes C++
    svt_d.dll!SVT::GUIManager::uninitialize() Line 913 + 0x40 bytes C++
    svt_d.dll!SVT::MainApp::~MainApp() Line 468 C++
    SvtMain.exe!SVT::MainApp::`scalar deleting destructor'() + 0x2e bytes C++
    SvtMain.exe!WinMain(HINSTANCE__ * hInst=0x00400000, HINSTANCE__ * __formal=0x00000000, char * strCmdLine=0x00151f0d, HINSTANCE__ * __formal=0x00000000) Line 36 + 0x34 bytes C++
    SvtMain.exe!__tmainCRTStartup() Line 589 + 0x35 bytes C
    SvtMain.exe!WinMainCRTStartup() Line 414 C


The text of the error is:
    Unhandled exception at 0x01e617b5 (CEGUIBase_d.dll) in SVTMain.exe:
    0xC0000005: Access violation reading location 0x0f3f2a48.


If I avoid subscribing to this event. I shutdown without error.
Last edited by daves on Fri Aug 01, 2008 08:46, edited 1 time in total.

daves
Home away from home
Home away from home
Posts: 253
Joined: Thu Feb 02, 2006 20:12

Postby daves » Fri Aug 01, 2008 07:37

I have a new clue. If I disconnect the connection just prior to destroying this window (on shutdown) then I no longer crash. What would cause this? Typically I do not have to disconnect. This just happens naturally.

Well, I just found another widget that seems to have the same issue. I applied the same work-around and I can shutdown cleanly.

Though I dont understand why I need to disconnect, in many ways that seems cleaner anyhow, and I certainly can live with forcing myself to do that, at least for those widgets that dont seem to cleanup by themselves.

User avatar
CrazyEddie
CEGUI Project Lead
Posts: 6760
Joined: Wed Jan 12, 2005 12:06
Location: England
Contact:

Postby CrazyEddie » Fri Aug 01, 2008 08:48

This is certainly an odd one. I've not had time to look into anything at the moment, and have no ideas at all off the top of my head.

What widget types are involved here?

I'll have to play around and try to reproduce it.

CE.

daves
Home away from home
Home away from home
Posts: 253
Joined: Thu Feb 02, 2006 20:12

Postby daves » Sat Aug 02, 2008 13:40

I'll post the particulars of both scenarios. Keep in mind that my "cegui usage environment" is fairly complex. I use cegui for a LOT of our application. It has been at the root of many of the features we have developed. There is a lot of dynamic creation/destruction of windows, registration of subscribers to be invoked on callback, dynamic creation of imagesets, etc. I mention this since the operating environment is not easily replicated, just based on a small snapshot of code. Nonetheless I'll highlight the specifics of the two scenarios.

Scenario 1:
I create a stack of "dispalyedimages". Each displayed image is initialized as follows:

The declaration:

Code: Select all

      CEGUI::DefaultWindow *mImgGroupWindow;
      CEGUI::DefaultWindow *mImgBoundingWindow;
      CEGUI::DefaultWindow *mImgWindow;
      CEGUI::Window *mImgLabelWindow;


The initialization/subscription:

Code: Select all

bool DisplayedImage::init(const std::string& imgName)
   {
      try {
         mImgGroupWindow = static_cast<CEGUI::DefaultWindow *>(WidgetMaker::getSingleton().createWindow(WidgetMaker::DEFAULTWINDOW, imgName+".grp", false));
         mImgBoundingWindow = static_cast<CEGUI::DefaultWindow *>(WidgetMaker::getSingleton().createWindow(WidgetMaker::STATICIMAGE, imgName+".bnd"));
         mImgLabelWindow = WidgetMaker::getSingleton().createWindow(WidgetMaker::DIALOGITEMLABEL, imgName+".lbl", true);
         mImgWindow = static_cast<CEGUI::DefaultWindow *>(WidgetMaker::getSingleton().createWindow(WidgetMaker::STATICIMAGE, imgName, false));

         mImgBoundingWindow->addChildWindow(mImgWindow);
         mImgGroupWindow->addChildWindow(mImgLabelWindow);
         mImgGroupWindow->addChildWindow(mImgBoundingWindow);

         mImgWindow->show();
         mImgBoundingWindow->setProperty("FrameEnabled", "true");
         mImgBoundingWindow->setProperty("BackgroundEnabled", "false");

         mImgWindow->setProperty("FrameEnabled", "false");
         mImgWindow->setProperty("BackgroundEnabled", "false");
         mImgWindow->setInheritsAlpha(false);
         mImgGroupWindow->hide();

         // Setup callbacks
         mImgMouseClickEventConnection = mImgWindow->subscribeEvent(CEGUI::DefaultWindow::EventMouseClick, CEGUI::Event::Subscriber(&DisplayedImage::handleMouseClick, this));
      }
      catch (CEGUI::Exception ce)
      {
         SVTConsole::getSingleton().printError(ce);
         uninit();
         return false;
      }
      return true;
   }

Notice the single call to subscribeEvent. I used to ignore the eventconnection returned by this call.

My uninitialize method:

Code: Select all

void DisplayedImage::uninit()
   {
      if(mImgMouseClickEventConnection->connected())
      {
         mImgMouseClickEventConnection->disconnect();
      }
      if(mImgWindow)
      {
         WidgetMaker::getSingleton().destroyWindow(mImgWindow);
         mImgWindow = 0;
      }
      if(mImgLabelWindow)
      {
         WidgetMaker::getSingleton().destroyWindow(mImgLabelWindow, true);
         mImgLabelWindow = 0;
      }
      if(mImgBoundingWindow)
      {
         WidgetMaker::getSingleton().destroyWindow(mImgBoundingWindow);
         mImgBoundingWindow = 0;
      }
      if(mImgGroupWindow)
      {
         WidgetMaker::getSingleton().destroyWindow(mImgGroupWindow);
         mImgGroupWindow = 0;
      }
   }


As you see now, I no longer ignore it. When I did ignore it then the access violation that you saw in the initial post occurred. This displayed image is part of an image browser plugin that is part of our application. Our application architecture is a "pluggable" architecture. Many features of the application are loaded (plugged in), as needed. The init method is called when the plugin library is loaded as part of application startup. The uninit method is called when the plugin objects are destroyed on application shutdown.

Scenario 2:
The second scenario is in a "Control Panel Management" class that is part of our base application. Plugins can register themselves with the manager so that they can be launched through pusbutton press (the pushbuttons are controls in the control panel).

These callbacks are registered as follows:

Code: Select all

void ControlPanelManager::registerCallback(const std::string& funcKey, CEGUI::Event::Subscriber *subscriber)
{
   SubscriberConnectionPair scp;
   scp.first = subscriber;
   mControlCallbackMap[funcKey] = scp;
}


Notice the structure of a "SubscriberConnectionPair"

Code: Select all

typedef std::pair<CEGUI::Event::Subscriber*, CEGUI::Event::Connection> SubscriberConnectionPair;


This structure has been introduced as part of my "work-around". I was ignoring the connection until I worked to solve this long-standing shutdown issue.

Clients of the manager register as follows:

Code: Select all

      CEGUI::Event::Subscriber *subscriber = (CEGUI::Event::Subscriber *)new CEGUI::Event::Subscriber(&IntellidarPluginManager::handleToggleInterfaceStatisticsMenu, this);
      appView.registerCallback(sControlPanelName, subscriber);


In this example the "appView" is a ControlPanelManager.

The actual subscription occurs subsequent to this registry action:

Code: Select all

ControlCallbackMap::iterator refitr = mControlCallbackMap.find(ctlItr->second.referenceName);
   if(refitr != mControlCallbackMap.end())
   {
      refitr->second.second = window->subscribeEvent(evtType, *(refitr->second.first));
      return 0;
   }


Code: Select all

   typedef std::map<std::string, SubscriberConnectionPair> ControlCallbackMap;



Here, again, you see that I am no longer ignoring the return from the subscription. My subsequent shutdown will release the connection:

Code: Select all

void ControlPanelManager::unregisterCallback(const std::string& funcKey)
{
   ControlCallbackMap::iterator it = mControlCallbackMap.find(funcKey);
   if(it != mControlCallbackMap.end())
   {
      if(it->second.second->connected())
      {
         it->second.second->disconnect();
      }
      mControlCallbackMap.erase(it);
   }
}
Last edited by daves on Sat Aug 02, 2008 14:38, edited 2 times in total.

daves
Home away from home
Home away from home
Posts: 253
Joined: Thu Feb 02, 2006 20:12

Postby daves » Sat Aug 02, 2008 13:51

Hmm. I just noticed that my label is not defined as a DEFAULTWINDOW (which it should be) when I posted that code. Let me post some of the particulars to the scheme file.. (while I compile a tweak to my code).

Code: Select all

  <FalagardMapping WindowType="SVTLook/DialogItemLabel"  TargetType="DefaultWindow" Renderer="Falagard/StaticText"  LookNFeel="SVTLook/DialogItemLabel" />
  <FalagardMapping WindowType="SVTLook/ControlPanelButton"      TargetType="CEGUI/PushButton"  Renderer="Falagard/Button"       LookNFeel="SVTLook/ControlPanelButton" />



Also I forgot to post the button creation call in the ControlPanelManager:

Code: Select all

         CEGUI::PushButton *button = static_cast<CEGUI::PushButton*>(WidgetMaker::getSingleton().createWindow(WidgetMaker::CONTROLPANELBUTTON, controlItr->second.name));
         assert(button && "ControlPanelManager::createControlPanel: Failed to create a button");



As you can tell the WidgetMaker is a simple wrapper class (around CEGUI::WindowManager):

Code: Select all

CEGUI::Window* WidgetMaker::createWindow(const WidgetType& type, const std::string& name, bool registerStyleManagement)
   {
      WidgetTypeId2NameMap::iterator it = mWidgetTypeId2NameMap.find(type);
      if(it == mWidgetTypeId2NameMap.end())
      {
         throw SVTException(ExceptionModules::WIDGET_MAKER | SVTException::ERR_INTERNAL_ERROR,
            "Widget type does not exist",   "WidgetMaker::createWindow");
      }

      CEGUI::Window *window;
      /*
      ** Create the window
      */
      window = CEGUI::WindowManager::getSingletonPtr()->createWindow(it->second, name.c_str());
      assert(window && "WidgetMaker::createWindow: Failed to create window");
}

daves
Home away from home
Home away from home
Posts: 253
Joined: Thu Feb 02, 2006 20:12

Postby daves » Sat Aug 02, 2008 14:30

By the way, I mentioned once that printing the pointer to a newly created CEGUI window to the CEGUI.log is invaluable. Without the following modification to CEGUI::WindowManager I would never have been able to isolate this issue to a specific cegui window.

Code: Select all

 Window* newWindow = factory->createWindow(finalName);
   std::stringstream ss; ss << newWindow;
   Logger::getSingleton().logEvent("Window '" + finalName +"' of type '" + type + "' has been created: "+ ss.str(), Informative);



The way that I used this is as follows. When the access violation occurred I would walk up the stack to the point that I could tell which window was being cleaned from the dead pool. I would compare that pointer to the pointers for every cegui window logged within cegui.log. Then I would know which window is the "culprit". By the way, I dont know if I'm doing something wrong, but for me its hit or miss as to whether I can "watch" a cegui window pointer in the debugger and discern its name. Sometimes the debugger will resolve the name in a human readable form, often it will not. I mention this, since if I could read the name then the pointer would not be so important. With this diagnostic tweak to the WindowManager, I can see both the pointer and the name in the cegui.log.

Anyways I'd recommend a tweak such as this to the createwindow method (to aid in such diagnostics). It has saved me valuable time (solving subtle problems) on several occasions.

User avatar
CrazyEddie
CEGUI Project Lead
Posts: 6760
Joined: Wed Jan 12, 2005 12:06
Location: England
Contact:

Postby CrazyEddie » Sat Aug 02, 2008 16:32

Hi,

Thanks for all the extra info, I've looked minimally at this so far, but did not manage to get a result. As you say, the specifics of your app are somewhat complex, though events are sufficiently low-level in CEGUI that reproducing this should have been pretty easy (yeah, I know - things never pan out the way you think!). Anyhow, I'll play around some more tomorrow and see if anything good happens (like a crash).

With regards to the logging of the window object addresses, I'd actually not forgotten about this (amazingly), and agree that it would be useful for others. I had intended getting all the 'managers' to log addresses for objects they create.

Edit: I've added this on mantis so it's now an 'official' feature request - http://www.cegui.org.uk/mantis/view.php?id=224

CE.

daves
Home away from home
Home away from home
Posts: 253
Joined: Thu Feb 02, 2006 20:12

Postby daves » Sat Aug 02, 2008 18:03

CrazyEddie wrote:
With regards to the logging of the window object addresses, I'd actually not forgotten about this (amazingly),
CE.


Doesn't amaze me. Your quite attentive.

CrazyEddie wrote:
Edit: I've added this on mantis so it's now an 'official' feature request - http://www.cegui.org.uk/mantis/view.php?id=224

CE.


Thats awesome. Thank you.

flashbk
Just popping in
Just popping in
Posts: 4
Joined: Sun Sep 13, 2009 12:56

Re: [WORK-AROUND FOUND] Crash on Shutdown .. SubscriberSlots ..

Postby flashbk » Mon Sep 28, 2009 14:57

FYI

in my case.
This problem is occured because SubscriberSlot DO NOT have copy constructor

do not use like this

Code: Select all


void AttachSubscriber( CEGUI::Event::Subscriber& subscriber )
{
     button1->subscribe( CEGUI::Window::EventDragDropItemDropped, subscriber );
     button2->subscribe( CEGUI::Window::EventDragDropItemDropped, subscriber );
}



it makes dangling pointer ( d_functor_impl )


Return to “Help”

Who is online

Users browsing this forum: No registered users and 6 guests