Boost logo

Boost Users :

Subject: [Boost-users] [Statechart] Why does this state machine enter unstable state only in debug mode?
From: Zhu Bicen (zhubicen_at_[hidden])
Date: 2011-03-18 22:21:03


I am trying to use statechart in a player application. And found
a weird issue,
so I write following code to reproduce the issue and ask for help.

1. call the MciWndPlay win32API to play a music file within a state entry
action
2. Windows msg , which indicate the player current player position, is
coming
3. call process_event within Windows procedure
4. but assert failed: get_pointer(pOutermostUnstableState_) == 0

It seems the PlayingState entry action does not execute completely,
The debug message after MciWndPlay call is not also printed out, but
another event is coming,
But This is a single thread application,
the MciWndPlay is just Send Message to something and return immediately.

I also print out the thread ID, all is the same.
Another thing is this assert failure is only occurred in Debug Mode, in
release mode all goes well.

I am using Visual C++ 2008 express, and boost 1.46.1.

Any comments and indication is welcome. I am confused by this issue several
days.
Thanks in advance.

// BugFix.cpp : Defines the entry point for the application.
//

#include "stdafx.h"
#include "BugFix.h"
#include "Vfw.h"
#include <iostream>
#include <boost/statechart/event.hpp>
#include <boost/statechart/state_machine.hpp>
#include <boost/statechart/simple_state.hpp>
#include <boost/statechart/state.hpp>
#include <boost/statechart/custom_reaction.hpp>
#include <boost/statechart/transition.hpp>
#include <boost/statechart/result.hpp>
#include <mmsystem.h>
#pragma comment(lib, "vfw32.lib")

namespace fsm = boost::statechart;

#define MAX_LOADSTRING 100

// Global Variables:
HINSTANCE hInst;
TCHAR szTitle[MAX_LOADSTRING];
TCHAR szWindowClass[MAX_LOADSTRING];
std::wstring file_name = L"D:\\VOA\\5. Deep Six It Is Well Hidden.mp3";

// Forward declarations of functions included in this code module:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);

static std::wstring FILE_NAME = L"";

struct OpenFileEvent: public fsm::event< OpenFileEvent >{
    std::wstring file_name;
    OpenFileEvent(const std::wstring p_file_name):file_name(p_file_name){}
};

struct UpdatePosEvent: public fsm::event< UpdatePosEvent >{
    long pos;
    UpdatePosEvent(long position): pos(position){}
};

class WaitingForFileState;
class PlayingState;

class PlayerFsm : public fsm::state_machine< PlayerFsm, WaitingForFileState
>
{
public:
    friend class WaitingForFileState;
    friend class HandlingFileState;
    friend class PlayingState;
};

class WaitingForFileState:
    public fsm::simple_state< WaitingForFileState, PlayerFsm >
{
public:
    typedef fsm::custom_reaction< OpenFileEvent > reactions;
    fsm::result react( const OpenFileEvent& evt)
        {
            std::cout << "OpenFileEvent is received " << std::endl;
            FILE_NAME = evt.file_name;
            return transit< HandlingFileState >();
        }
};
class HandlingFileState:
    public fsm::state< HandlingFileState, PlayerFsm, PlayingState>
{
public:
    HandlingFileState( my_context ctx ): my_base( ctx){
        MCIWndOpen(_playerWnd, FILE_NAME.c_str(), NULL);
    }
};

class PlayingState:
    public fsm::simple_state< PlayingState, HandlingFileState>
{
public:
    PlayingState() {
        std::cout << "Entering PlayingState" << std::endl;
        std::cout << "fsm threadId = " << GetCurrentThreadId() << std::endl;
        assert(MCIWndPlay(_playerWnd) == 0);
// below message is not printed out in debug mode,
// and another message UpdatePosEvent come
// and StateChart assert failure
        std::cout << "After MCIWndPlay" << std::endl;
    }
    typedef fsm::custom_reaction< UpdatePosEvent > reactions;
    fsm::result react( const UpdatePosEvent& evt){
        std::cout << "UpdatePosEvent is received in PlayingState" <<
std::endl;
        return fsm::detail::result_utility::
            make_result( fsm::detail::consumed );
        }
private:
    long current_pos_;
};

HWND _playerWnd;
PlayerFsm _player_fsm;

int APIENTRY _tWinMain(HINSTANCE hInstance,
                       HINSTANCE hPrevInstance,
                       LPTSTR lpCmdLine,
                       int nCmdShow)
{
    AllocConsole();
    freopen("CONOUT$", "w+t", stdout);
    freopen("CONOUT$", "w+t", stderr);

    std::cout << "threadId = " << GetCurrentThreadId() << std::endl;
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // TODO: Place code here.
    MSG msg;
    HACCEL hAccelTable;

    // Initialize global strings
    LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadString(hInstance, IDC_BUGFIX, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    // Perform application initialization:
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }

    hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_BUGFIX));

    // Main message loop:
    while (GetMessage(&msg, NULL, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return (int) msg.wParam;
}

ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEX wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;
    wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_BUGFIX));
    wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName = MAKEINTRESOURCE(IDC_BUGFIX);
    wcex.lpszClassName = szWindowClass;
    wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassEx(&wcex);
}

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
    HWND hWnd;

    hInst = hInstance; // Store instance handle in our global variable

    hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
               CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance,
NULL);

    if (!hWnd)
    {
        return FALSE;
    }

    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    return TRUE;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM
lParam)
{
    int wmId, wmEvent;
    PAINTSTRUCT ps;
    HDC hdc;

    switch (message)
    {
    case WM_COMMAND:
        wmId = LOWORD(wParam);
        wmEvent = HIWORD(wParam);
        // Parse the menu selections:
        switch (wmId)
        {
        case IDM_ABOUT:
            _player_fsm.process_event(OpenFileEvent(file_name));
            //DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
            break;
        case IDM_EXIT:
            DestroyWindow(hWnd);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
        break;
    case WM_CREATE:
        _player_fsm.initiate();
        _playerWnd = MCIWndCreate(
            hWnd,
            hInst,
            MCIWNDF_NOTIFYPOS|MCIWNDF_NOMENU|MCIWNDF_NOTIFYMODE,
            NULL);
        break;
    case MCIWNDM_NOTIFYPOS:
        std::cout << "received MCIWNDM_NOTIFYPOS msg, threadId = "
                  << GetCurrentThreadId() << std::endl;
        _player_fsm.process_event(UpdatePosEvent(lParam));
        break;

    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        EndPaint(hWnd, &ps);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

-- 
Best Regards
Bicen Zhu




asert_failure.png

Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net