I am uisng a QStateMachine in a multithreaded console based application that executes
under Windows and Linux, both 32 and 64-bit. The DEBUG build works fine, while in the
RELEASE build, the state machine fails to execute properly. This is true for both the
windows and Linux platforms. I have a file trace capability, and I am not able to enter into
the root state of the state machine at all. The secondary threads in the application all seem to
work, and there is no apparent problem viewable in terms of inappropriate linked libraries or
console output suggesting any build or runtime issues or links.

I have verified the dependencies to be correct. The necessary Qt libraries (console and library)
as on the path. All seems well with the application other than the state machine never starts
up in the RELEASE mode. This behavior is true under Windows XP, 7 and Linux Fedora 64-bit, so there
must be something I am doing wrong.

I am interested in any suggestions folks might have to try and determine what the issue might be.

Attached is the source code for the QStateMachine related lines of code.


My application is derived from QCoreApplication, and the c'tor is as follows

Qt Code:
  1. App::App(int &argc, char **argv, int ver) :
  2. QCoreApplication(argc, argv),
  3. m_appName(""),
  4. m_appPath(""),
  5. m_stateNumber(unknown),
  6. m_roleID(""),
  7. m_ftpTimeoutMs(5000),
  8. m_tmaMode(eTmaModeStandalone),
  9. m_pXmlParserSettings(0),
  10. m_pSettings(0),
  11. m_bit(false),
  12. m_configured(false),
  13. m_running(false),
  14. ...
  15. {
  16. TRACE();
  17.  
  18. Q_UNUSED(ver);
  19.  
  20. // Create state machine states
  21. m_machine = new QStateMachine(this);
  22. m_stateRoot = new QState();
  23. m_stateInitialize = new QState(m_stateRoot);
  24. m_stateConfiguring = new QState(m_stateRoot);
  25. m_stateUnconfigured = new QState(m_stateRoot);
  26. m_stateRunning = new QState(m_stateRoot);
  27. m_stateIdle = new QState(m_stateRoot);
  28. m_stateInitiatedBIT = new QState(m_stateRoot);
  29. m_stateDone = new QFinalState();
  30. }
To copy to clipboard, switch view to plain text mode 

Prior to calling a.exec(), I perform the following to prepare and execute the state machine

Qt Code:
  1. bool App::aboutToExec(xmlParserSettings *pXmlParserSettings, Settings* pSettings)
  2. {
  3. TRACE();
  4.  
  5. FILE_LOG(logINFO) << "App: created: thread id = " << QThread::currentThreadId();
  6.  
  7. // Save off pointers to local XML file parser and content
  8. m_pXmlParserSettings = pXmlParserSettings;
  9. m_pSettings = pSettings;
  10.  
  11. // Check for multiple instances of lmd
  12. QStringList listOfPids;
  13. int countOfProcesses = System::getProcessIdsByProcessName(szLmdProgramName, listOfPids);
  14. if (countOfProcesses > 1)
  15. {
  16. FILE_LOG(logERROR) << "Only one instance of the Link Manager can run at the same time";
  17. return false;
  18. }
  19.  
  20. // Process contents of the local configuration XML file
  21. processLocalSettings(pSettings);
  22.  
  23. // Process command line arguments (will override local XML file settings)
  24. if (!parseArgs())
  25. {
  26. return false;
  27. }
  28.  
  29. // Allow user to ask for help without being root, but must be root hereafter
  30. #ifdef Q_OS_LINUX
  31. if (geteuid() != 0)
  32. {
  33. // NOTE: SNMP Subagent connection and PTP operation when controlled requires root privilege
  34. FILE_LOG(logERROR) << "Link Manager requires ROOT permisssions to execute";
  35. printf("Link Manager requires ROOT permisssions to execute\n");
  36. return false;
  37. }
  38. #endif
  39.  
  40. // Perform lazy initialization of singletons prior to thread execution (to ensure thread safe creation)
  41. pSnmpInterface->onReset(restoreDefaults);
  42. pMdlInterface->onReset(restoreDefaults);
  43. pPtpdInterface->onReset(restoreDefaults);
  44. pRfnmProcessor->onReset(restoreDefaults);
  45.  
  46. // Create state machine content
  47. FILE_LOG(logINFO) << "INIT: Creating state machine...";
  48.  
  49. // Hook up state entry and exit routines (TODO: remove some of these if eventually not used)
  50. Q_ASSERT(QObject::connect(m_stateRoot, SIGNAL(entered()), this, SLOT(onStateRootEntered())));
  51. Q_ASSERT(QObject::connect(m_stateRoot, SIGNAL(exited()), this, SLOT(onStateRootExited())));
  52. Q_ASSERT(QObject::connect(m_stateInitialize, SIGNAL(entered()), this, SLOT(onStateInitializeEntered())));
  53. Q_ASSERT(QObject::connect(m_stateInitialize, SIGNAL(exited()), this, SLOT(onStateInitializeExited())));
  54. Q_ASSERT(QObject::connect(m_stateConfiguring, SIGNAL(entered()), this, SLOT(onStateConfiguringEntered())));
  55. Q_ASSERT(QObject::connect(m_stateConfiguring, SIGNAL(exited()), this, SLOT(onStateConfiguringExited())));
  56. Q_ASSERT(QObject::connect(m_stateUnconfigured, SIGNAL(entered()), this, SLOT(onStateUnconfiguredEntered())));
  57. Q_ASSERT(QObject::connect(m_stateUnconfigured, SIGNAL(exited()), this, SLOT(onStateUnconfiguredExited())));
  58. Q_ASSERT(QObject::connect(m_stateRunning, SIGNAL(entered()), this, SLOT(onStateRunningEntered())));
  59. Q_ASSERT(QObject::connect(m_stateRunning, SIGNAL(exited()), this, SLOT(onStateRunningExited())));
  60. Q_ASSERT(QObject::connect(m_stateIdle, SIGNAL(entered()), this, SLOT(onStateIdleEntered())));
  61. Q_ASSERT(QObject::connect(m_stateIdle, SIGNAL(exited()), this, SLOT(onStateIdleExited())));
  62. Q_ASSERT(QObject::connect(m_stateInitiatedBIT, SIGNAL(entered()), this, SLOT(onStateInitiatedBITEntered())));
  63. Q_ASSERT(QObject::connect(m_stateInitiatedBIT, SIGNAL(exited()), this, SLOT(onStateInitiatedBITExited())));
  64. Q_ASSERT(QObject::connect(m_stateDone, SIGNAL(entered()), this, SLOT(onStateDoneEntered())));
  65. Q_ASSERT(QObject::connect(m_stateDone, SIGNAL(exited()), this, SLOT(onStateDoneExited())));
  66.  
  67. // Set initial state of root state with common exit for all children states
  68. m_stateRoot->setInitialState(m_stateInitialize);
  69.  
  70. // Make done state the transition target for application exit from any state
  71. Q_ASSERT(m_stateRoot->addTransition(this, SIGNAL(aboutToQuit()), m_stateDone) != 0);
  72.  
  73. // Add states to machine
  74. m_machine->addState(m_stateRoot);
  75. m_machine->addState(m_stateDone);
  76.  
  77. // Add state transitions:
  78.  
  79. // Transition from stateInitialize to stateUnconfigured after initialization. This ensures SNMP
  80. // thread is active to send out traps
  81. //Q_ASSERT(m_stateInitialize->addTransition(m_stateInitialize, SIGNAL(entered()), m_stateUnconfigured) != 0);
  82. Q_ASSERT(m_stateInitialize->addTransition(this, SIGNAL(initializationDone()), m_stateUnconfigured) != 0);
  83.  
  84. // SNMP configured() signal responsive states
  85. Q_ASSERT(m_stateUnconfigured->addTransition(pSnmpInterface, SIGNAL(configured()), m_stateConfiguring) != 0);
  86. Q_ASSERT(m_stateUnconfigured->addTransition(this, SIGNAL(configurationSucceeded()), m_stateIdle) != 0); // MDL file load at boot
  87. Q_ASSERT(m_stateIdle->addTransition(pSnmpInterface, SIGNAL(configured()), m_stateConfiguring) != 0);
  88. Q_ASSERT(m_stateRunning->addTransition(pSnmpInterface, SIGNAL(configured()), m_stateConfiguring) != 0);
  89.  
  90. // Configuration success or failure internal signals from Configuring state
  91. Q_ASSERT(m_stateConfiguring->addTransition(this, SIGNAL(configurationSucceeded()), m_stateIdle) != 0);
  92. Q_ASSERT(m_stateConfiguring->addTransition(this, SIGNAL(configurationFailed()), m_stateUnconfigured) != 0);
  93.  
  94. // SNMP bitInitiated() signal responsive states
  95. // NOTE: call setBitReturnState in each state entry to specify return state from BIT
  96. Q_ASSERT(m_stateUnconfigured->addTransition(pSnmpInterface, SIGNAL(bitInitiated()), m_stateInitiatedBIT) != 0);
  97. Q_ASSERT(m_stateConfiguring->addTransition(pSnmpInterface, SIGNAL(bitInitiated()), m_stateInitiatedBIT) != 0);
  98. Q_ASSERT(m_stateIdle->addTransition(pSnmpInterface, SIGNAL(bitInitiated()), m_stateInitiatedBIT) != 0);
  99. Q_ASSERT(m_stateRunning->addTransition(pSnmpInterface, SIGNAL(bitInitiated()), m_stateInitiatedBIT) != 0);
  100.  
  101. // Transition to and from run state
  102. Q_ASSERT(m_stateIdle->addTransition(pRfnmProcessor, SIGNAL(run()), m_stateRunning) != 0);
  103. Q_ASSERT(m_stateRunning->addTransition(pRfnmProcessor, SIGNAL(stop()), m_stateIdle) != 0);
  104.  
  105. // SNMP reset() responsive states
  106. Q_ASSERT(m_stateConfiguring->addTransition(pSnmpInterface, SIGNAL(reset()), m_stateInitialize) != 0);
  107. Q_ASSERT(m_stateUnconfigured->addTransition(pSnmpInterface, SIGNAL(reset()), m_stateInitialize) != 0);
  108. Q_ASSERT(m_stateRunning->addTransition(pSnmpInterface, SIGNAL(reset()), m_stateInitialize) != 0);
  109. Q_ASSERT(m_stateIdle->addTransition(pSnmpInterface, SIGNAL(reset()), m_stateInitialize) != 0);
  110. Q_ASSERT(m_stateInitiatedBIT->addTransition(pSnmpInterface, SIGNAL(reset()), m_stateInitialize) != 0);
  111.  
  112. // SNMP set epoch
  113. Q_ASSERT(QObject::connect(pSnmpInterface, SIGNAL(setEpochMicroseconds(unsigned int)), this, SLOT(setEpochMicroseconds(unsigned int))));
  114.  
  115. // Connect state machine exit to application exit
  116. Q_ASSERT(QObject::connect(m_machine, SIGNAL(finished()), this, SLOT(quit())));
  117.  
  118. // Start up state machine
  119. m_machine->setInitialState(m_stateRoot);
  120. m_machine->start();
  121.  
  122. // Create processes and secondary thread objects (TODO: add others here)
  123.  
  124. .
  125. .
  126. .
  127. // Create secondary threads (TODO: add others here)
  128. FILE_LOG(logINFO) << "INIT: Creating secondary threads...";
  129. m_snmpSubAgentThread = new SnmpSubAgentThread(0);
  130. m_tdmaThread = new TdmaThread(0);
  131.  
  132. // Start up secondary threads (TODO: add others here)
  133. FILE_LOG(logINFO) << "INIT: Starting secondary threads...";
  134. m_snmpSubAgentThread->start(QThread::NormalPriority);
  135.  
  136. return true;
  137. }
To copy to clipboard, switch view to plain text mode 

Interested in ways to debug the RELEASE build.