Opened 18 years ago

Closed 18 years ago

Last modified 18 years ago

#10 closed defect (fixed)

Trap issuing stdlib's exit() when QApplication instance is created on the stack in main()

Reported by: dmik Owned by: dmik
Priority: normal Milestone: qt-os2-3.3.1-rc07
Component: kernel Version: 3.3.1-rc06
Severity: normal Keywords: trap exit()
Cc:

Description

If we have a QApplication instance created on the stack in main() (a very common practice for 99% of Qt apps), then an attempt to call the exit() function from stdlib will produce an application trap with a popuplog entry (SYS3170/c0010001, most likely in DOSCALL1.DLL).

A simple example to reproduce:

#include <qapplication.h>
#include <stdlib.h>
int main (int argc, char **argv)
{
    QApplication a (argc, argv);
    exit (-1);
}

From what I've already found, this happens when a global QFontCache (internal QObject subclass) instance is being destroyed (by a static func from the exit list). It is created as a child of QApplication, so its QObject desctructor calls parentObject->removeChild (this); but for some reason the vtable of the QApplocation instance is already corrupted by that time (but no QApplocation destructor has been called yet!).

Attachments (1)

exit_trap.zip (2.2 KB) - added by dmik 18 years ago.
A clean (Qt-less) demonstration of the trap

Download all attachments as: .zip

Change History (6)

comment:1 Changed 18 years ago by dmik

Status: newassigned

comment:2 Changed 18 years ago by dmik

Btw, the same trap occurs if we just DosKillProcess() any Qt application. Obviously, for the same reason -- unusual order of object destruction.

Changed 18 years ago by dmik

Attachment: exit_trap.zip added

A clean (Qt-less) demonstration of the trap

comment:3 Changed 18 years ago by dmik

As it turned out, the trap happens because destructors of static objects in a DLL (static QSingleCleanupHandler<QFontCache> cleanup_fontcache in our case, see source:trunk/src/kernel/qfont.cpp) are called by DosExit that seems to reuse the stack of the main thread by the time it calls _DLL_InitTerm (where destructors are actually called from by libc).

In our case, QFontCache is a child of QApplication, and both are subclasses of QObject. Therefore, when destructed, QFontCache tries to remove itself from the parent's child list by calling its removeChild method (see QObject::~QObject), but the parent located on the stack in main() is already overwritten.

This is not a GCC or LIBC problem. As I was told by Bird, the behavior of GCC related to the destruction of static objects has been done after IBM Visual Age C/C++. And indeed, VAC 3.08 behaves exactly the same as GCC and produces the same trap -- you can try a simple testcase (that doesn't use Qt at all) attached above.

There are few possible workarounds:

  1. Don't allocate global objects used by destructors of static objects defined in a DLL on the stack. In case of Qt, it's enough to define a QApplication instance outside main().
  1. Don't use global objects that can be allocated on the stack from destructors of static objects defined in a DLL.
  1. Supply your own _DLL_InitTerm implementation where you will not call _ctordtorTerm() upon DLL termination, but instead call it from a function registered in the libc exit list (using atexit() when the DLL is being initialized). This will, however, not help if the application is being terminated using _Exit(), DosExit(), DosKillProcess() or in other words, by any means other than libc's exit().

For Qt, I will choose the workaround N2 because there is only a single place so far where a stack-based QApplication instance can be called from the static destructor, and because it will also allow to kill Qt applications w/o producing a guaranteed trap.

comment:4 Changed 18 years ago by anonymous

Resolution: fixed
Status: assignedclosed

QFontCache is now cleaned up from a static atexit() exit handler (changeset:80). No traps any more, but there is one drawback (not consireded to be significant though): the cleanup will only occur on a normal program termination (i.e. a usual return from main()), or when exit() is called. Doing DosExit() or DosKillProcess() will leave the object not deleted since stdlib's exit list are not processed in these cases.

comment:5 Changed 18 years ago by dmik

In the last message, anonymous = dmik, as usual :)

Note: See TracTickets for help on using tickets.