- use setjmp() and longjmp() to quit the simulation thread cleanly.

I use setjmp() to save the context just before calling
  bx_continue_after_config_interface().  Then, in
  bx_real_sim_c:quit_sim, I use longjmp() to jump back to that context.
  This happens in main.cc and in gui/wxmain.cc (wxWindows only).
  I haven't tested with the debugger yet.  Possibly with debugger
  the quit longjmp() should jump back to the debugger prompt loop
  instead of actually quitting the program.
- clean up BX_ASYNC_EVT_LOG_MSG implementation by creating a different,
  synchronous event called BX_SYNC_EVT_LOG_ASK.  The async event
  could be used to simply tell the CI that an event has occurred,
  for example if the user wanted to view the events on screen
  (not implemented).  The sync event is used when you want the user
  to respond before the simulation can continue, such as a for the
  "panic=ask" behavior.
- in wxmain.cc, move the updates to the Start,Stop,Pause,Resume menu
  items into a separate method simStatusChanged().  This makes the code that
  does important stuff more readable.
- remove wxMutexGuiEnter()/Leave() from MyFrame::OnSim2CuiEvent().
  This method is an event handler called in the gui thread, so it
  already has the gui lock.  This call caused thread lock on my linux
  box.
This commit is contained in:
Bryce Denney 2002-08-27 18:11:13 +00:00
parent 46140ac6ce
commit 6bbf7d5957
6 changed files with 103 additions and 58 deletions

View File

@ -1,5 +1,5 @@
/////////////////////////////////////////////////////////////////////////
// $Id: control.cc,v 1.56 2002-08-27 16:27:57 vruppert Exp $
// $Id: control.cc,v 1.57 2002-08-27 18:11:13 bdenney Exp $
/////////////////////////////////////////////////////////////////////////
//
// This is code for a text-mode configuration interfac. Note that this file
@ -627,7 +627,7 @@ config_interface_notify_callback (void *unused, BxEvent *event)
case BX_ASYNC_EVT_SHUTDOWN_GUI:
fprintf (stderr, "BX_ASYNC_EVT_SHUTDOWN_GUI\n");
return event;
case BX_ASYNC_EVT_LOG_MSG:
case BX_SYNC_EVT_LOG_ASK:
{
int level = event->u.logmsg.level;
fprintf (stderr, "========================================================================\n");

View File

@ -1,5 +1,5 @@
/////////////////////////////////////////////////////////////////////////
// $Id: siminterface.cc,v 1.45 2002-08-26 15:31:20 bdenney Exp $
// $Id: siminterface.cc,v 1.46 2002-08-27 18:11:13 bdenney Exp $
/////////////////////////////////////////////////////////////////////////
//
// See siminterface.h for description of the siminterface concept.
@ -32,9 +32,12 @@ class bx_real_sim_c : public bx_simulator_interface_c {
bx_param_c **param_registry;
int registry_alloc_size;
int enabled;
// save context to jump to if we must quit unexpectedly
jmp_buf *quit_context;
public:
bx_real_sim_c ();
virtual ~bx_real_sim_c ();
virtual void set_quit_context (jmp_buf *context) { quit_context = context; }
virtual int get_init_done () { return init_done; }
virtual int set_init_done (int n) { init_done = n; return 0;}
virtual void get_param_id_range (int *min, int *max) {
@ -136,6 +139,7 @@ bx_real_sim_c::bx_real_sim_c ()
param_registry = new bx_param_c* [registry_alloc_size];
for (i=0; i<registry_alloc_size; i++)
param_registry[i] = NULL;
quit_context = NULL;
}
// called by constructor of bx_param_c, so that every parameter that is
@ -210,20 +214,11 @@ bx_real_sim_c::get_max_log_level ()
void
bx_real_sim_c::quit_sim (int code) {
#if 0
if (!code)
BX_PANIC (("Quit simulation command"));
// tell bochs to shut down (includes vga screen)
bx_atexit ();
// tell the configuration interface to shut down
BxEvent *event = new BxEvent ();
event->type = BX_ASYNC_EVT_SHUTDOWN_GUI;
sim_to_cui_event (event);
// set something that will cause the cpu loop to exit.
// or use setjmp/longjmp, or something.
//FIXME!
#endif
BX_INFO (("quit_sim called"));
// use longjmp to quit cleanly, no matter where in the stack we are.
//fprintf (stderr, "using longjmp() to jump directly to the quit context!\n");
longjmp (*quit_context, 1);
BX_PANIC (("in bx_real_sim_c::quit_sim, longjmp should never return"));
#if BX_WITH_WX
// in wxWindows, the whole simulator is running in a separate thread.
// our only job is to end the thread as soon as possible, NOT to shut
@ -349,7 +344,7 @@ int
bx_real_sim_c::log_msg (const char *prefix, int level, char *msg)
{
BxEvent *be = new BxEvent ();
be->type = BX_ASYNC_EVT_LOG_MSG;
be->type = BX_SYNC_EVT_LOG_ASK;
be->u.logmsg.prefix = (char *)prefix;
be->u.logmsg.level = level;
be->u.logmsg.msg = msg;

View File

@ -1,5 +1,5 @@
/////////////////////////////////////////////////////////////////////////
// $Id: siminterface.h,v 1.47 2002-08-26 15:31:21 bdenney Exp $
// $Id: siminterface.h,v 1.48 2002-08-27 18:11:13 bdenney Exp $
/////////////////////////////////////////////////////////////////////////
//
// Before I can describe what this file is for, I have to make the
@ -306,6 +306,7 @@ typedef enum {
BX_SYNC_EVT_GET_PARAM, // CUI -> simulator -> CUI
BX_SYNC_EVT_ASK_PARAM, // simulator -> CUI -> simulator
BX_SYNC_EVT_TICK, // simulator -> CUI, wait for response.
BX_SYNC_EVT_LOG_ASK, // simulator -> CUI, wait for response.
__ALL_EVENTS_BELOW_ARE_ASYNC__,
BX_ASYNC_EVT_KEY, // vga window -> simulator
BX_ASYNC_EVT_MOUSE, // vga window -> simulator
@ -401,7 +402,7 @@ typedef struct {
// some kind of "flow control" since the simulator will be able to produce
// new events much faster than the gui can accept them.
// Event type: BX_ASYNC_EVT_LOG_MSG
// Event type: BX_ASYNC_EVT_LOG_MSG (unused)
//
// Asynchronous event from the simulator to the CUI. When a BX_PANIC,
// BX_ERROR, BX_INFO, or BX_DEBUG is found in the simulator code, this
@ -421,12 +422,20 @@ typedef struct {
// skip over huge bursts of log entries without allocating memory,
// synchronizing threads, etc. for each.
typedef struct {
// type is BX_EVT_LOG_MSG
Bit8u level;
char *prefix;
char *msg;
} BxLogMsgEvent;
// Event type: BX_SYNC_EVT_LOG_ASK
//
// This is a synchronous version of BX_ASYNC_EVT_LOG_MSG, which is used
// when the "action=ask" setting is used. If the simulator runs into a
// panic, it sends a synchronous BX_SYNC_EVT_LOG_ASK to the CUI to be
// displayed. The CUI shows a dialog that asks if the user wants to
// continue, quit, etc. and sends the answer back to the simulator.
// This event also uses BxLogMsgEvent.
// Event type: BX_EVT_TOOLBAR
// Asynchronous event from the VGAW to the simulator, sent when the user
// clicks on a toolbar button. This may one day become something more
@ -724,9 +733,12 @@ struct bx_cdrom_options
// I'm not longer sure that having a base class is going to be of any
// use... -Bryce
#include <setjmp.h>
class bx_simulator_interface_c {
public:
bx_simulator_interface_c ();
virtual void set_quit_context (jmp_buf *context) {}
virtual int get_init_done () { return -1; }
virtual int set_init_done (int n) {return -1;}
virtual void get_param_id_range (int *min, int *max) {}

View File

@ -1,5 +1,5 @@
/////////////////////////////////////////////////////////////////
// $Id: wxmain.cc,v 1.8 2002-08-26 15:31:22 bdenney Exp $
// $Id: wxmain.cc,v 1.9 2002-08-27 18:11:13 bdenney Exp $
/////////////////////////////////////////////////////////////////
//
// wxmain.cc implements the wxWindows frame, toolbar, menus, and dialogs.
@ -314,6 +314,39 @@ void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
wxMessageBox( str, "About Bochs", wxOK | wxICON_INFORMATION );
}
// update the menu items, status bar, etc.
void MyFrame::simStatusChanged (StatusChange change, Boolean popupNotify) {
switch (change) {
case Start: // running
menuConfiguration->Enable (ID_Config_New, FALSE);
menuConfiguration->Enable (ID_Config_Read, FALSE);
menuSimulate->Enable (ID_Simulate_Start, FALSE);
menuSimulate->Enable (ID_Simulate_PauseResume, TRUE);
menuSimulate->Enable (ID_Simulate_Stop, TRUE);
menuSimulate->SetLabel (ID_Simulate_PauseResume, "&Pause");
break;
case Stop: // not running
menuSimulate->Enable (ID_Simulate_Start, TRUE);
menuSimulate->Enable (ID_Simulate_PauseResume, FALSE);
menuSimulate->Enable (ID_Simulate_Stop, FALSE);
menuSimulate->SetLabel (ID_Simulate_PauseResume, "&Pause");
// This should only be used if the simulation stops due to error.
// Obviously if the user asked it to stop, they don't need to be told.
if (popupNotify)
wxMessageBox("Bochs simulation has stopped.", "Bochs Stopped",
wxOK | wxICON_INFORMATION);
break;
case Pause: // pause
SetStatusText ("Pausing the Bochs simulation");
menuSimulate->SetLabel (ID_Simulate_PauseResume, "&Resume");
break;
case Resume: // resume
SetStatusText ("Resuming the Bochs simulation");
menuSimulate->SetLabel (ID_Simulate_PauseResume, "&Pause");
break;
}
}
void MyFrame::OnStartSim(wxCommandEvent& WXUNUSED(event))
{
wxCriticalSectionLocker lock(sim_thread_lock);
@ -327,7 +360,7 @@ void MyFrame::OnStartSim(wxCommandEvent& WXUNUSED(event))
start_bochs_times++;
if (start_bochs_times>1) {
wxMessageBox (
"You have already started the simulator once this session. Due to memory leaks, you may get unstable behavior.",
"You have already started the simulator once this session. Due to memory leaks and bugs in init code, you may get unstable behavior.",
"2nd time warning", wxOK | wxICON_WARNING);
}
num_events = 0; // clear the queue of events for bochs to handle
@ -337,13 +370,7 @@ void MyFrame::OnStartSim(wxCommandEvent& WXUNUSED(event))
wxLogDebug ("Simulator thread has started.");
// set up callback for events from simulator thread
SIM->set_notify_callback (&SimThread::SiminterfaceCallback, sim_thread);
// fix up menu choices
menuConfiguration->Enable (ID_Config_New, FALSE);
menuConfiguration->Enable (ID_Config_Read, FALSE);
menuSimulate->Enable (ID_Simulate_Start, FALSE);
menuSimulate->Enable (ID_Simulate_PauseResume, TRUE);
menuSimulate->Enable (ID_Simulate_Stop, TRUE);
menuSimulate->SetLabel (ID_Simulate_PauseResume, "&Pause");
simStatusChanged (Start);
}
void MyFrame::OnPauseResumeSim(wxCommandEvent& WXUNUSED(event))
@ -351,13 +378,11 @@ void MyFrame::OnPauseResumeSim(wxCommandEvent& WXUNUSED(event))
wxCriticalSectionLocker lock(sim_thread_lock);
if (sim_thread) {
if (sim_thread->IsPaused ()) {
SetStatusText ("Resuming the Bochs simulation");
simStatusChanged (Resume);
sim_thread->Resume ();
menuSimulate->SetLabel (ID_Simulate_PauseResume, "&Pause");
} else {
SetStatusText ("Pausing the Bochs simulation");
simStatusChanged (Pause);
sim_thread->Pause ();
menuSimulate->SetLabel (ID_Simulate_PauseResume, "&Resume");
}
}
}
@ -367,13 +392,12 @@ void MyFrame::OnKillSim(wxCommandEvent& WXUNUSED(event))
// DON'T use a critical section here. Delete implicitly calls
// OnSimThreadExit, which also tries to lock sim_thread_lock.
// If we grab the lock at this level, deadlock results.
wxLogDebug ("OnKillSim()");
if (sim_thread) {
SetStatusText ("Killing the Bochs simulation");
sim_thread->Delete ();
menuSimulate->Enable (ID_Simulate_Start, TRUE);
menuSimulate->Enable (ID_Simulate_PauseResume, FALSE);
menuSimulate->Enable (ID_Simulate_Stop, FALSE);
menuSimulate->SetLabel (ID_Simulate_PauseResume, "&Pause");
sim_thread->Delete ();
// Next time the simulator reaches bx_real_sim_c::periodic() it
// will quit. This is better than killing the thread because it
// gives it a chance to clean up after itself.
}
}
@ -501,9 +525,9 @@ MyFrame::HandleAskParam (BxEvent *event)
return -1; // could not display
}
// This is called when a Sim2Cui event from the simulator thread is discovered
// on the GUI thread's event queue. (It got there via wxPostEvent in
// SiminterfaceCallback2, which is executed in the simulator Thread.)
// This is called from the wxWindows GUI thread, when a Sim2Cui event
// is found. (It got there via wxPostEvent in SiminterfaceCallback2, which is
// executed in the simulator Thread.)
void
MyFrame::OnSim2CuiEvent (wxCommandEvent& event)
{
@ -530,12 +554,16 @@ MyFrame::OnSim2CuiEvent (wxCommandEvent& event)
Close (TRUE);
wxExit ();
return;
case BX_SYNC_EVT_LOG_ASK:
case BX_ASYNC_EVT_LOG_MSG:
wxLogDebug ("log msg: level=%d, prefix='%s', msg='%s'",
be->u.logmsg.level,
be->u.logmsg.prefix,
be->u.logmsg.msg);
wxMutexGuiEnter();
if (be->type == BX_ASYNC_EVT_LOG_MSG) {
// don't ask for user response
return;
}
string.Printf ("%s", be->u.logmsg.msg);
choice = ::wxGetSingleChoiceIndex (
string,
@ -543,7 +571,7 @@ MyFrame::OnSim2CuiEvent (wxCommandEvent& event)
if (choice<0) choice = 2; // treat cancel the same as "die"
be->retcode = choice;
wxLogDebug ("you chose %d", choice);
wxMutexGuiLeave();
sim_thread->SendSyncResponse (be);
return;
default:
wxLogDebug ("OnSim2CuiEvent: event type %d ignored", (int)be->type);
@ -593,7 +621,6 @@ SimThread::Entry (void)
{
int argc=1;
char *argv[] = {"bochs"};
wxLogDebug ("in SimThread, starting at bx_continue_after_config_interface");
// run all the rest of the Bochs simulator code. This function will
// run forever, unless a "kill_bochs_request" is issued. The shutdown
// procedure is as follows:
@ -608,8 +635,18 @@ SimThread::Entry (void)
// bx_continue_after_config_interface(), which notices the
// kill_bochs_request and returns back to this Entry() function.
// - Entry() exits and the thread stops. Whew.
bx_continue_after_config_interface (argc, argv);
wxLogDebug ("in SimThread, bx_continue_after_config_interface exited");
wxLogDebug ("in SimThread, starting at bx_continue_after_config_interface");
static jmp_buf context; // this must not go out of scope. maybe static not needed
if (setjmp (context) == 0) {
SIM->set_quit_context (&context);
bx_continue_after_config_interface (argc, argv);
wxLogDebug ("in SimThread, bx_continue_after_config_interface exited normally");
} else {
wxLogDebug ("in SimThread, bx_continue_after_config_interface exited by longjmp");
}
wxMutexGuiEnter();
theFrame->simStatusChanged (theFrame->Stop, true);
wxMutexGuiLeave();
return NULL;
}
@ -672,14 +709,6 @@ SimThread::SiminterfaceCallback2 (BxEvent *event)
wxevent.SetEventObject ((wxEvent *)event);
wxLogDebug ("Sending an event to the window");
wxPostEvent (frame, wxevent);
if (event->type == BX_ASYNC_EVT_LOG_MSG) {
// wait until the GUI thread has processed the event and changed
// retcode. bbd: This is a strange case. Shouldn't this just be
// called a sync event?
event->retcode = -1;
while (event->retcode == -1) this->Sleep(500);
return event;
}
// if it is an asynchronous event, return immediately
if (async) return NULL;
wxLogDebug ("SiminterfaceCallback2: synchronous event; waiting for response");

View File

@ -1,5 +1,5 @@
/////////////////////////////////////////////////////////////////
// $Id: wxmain.h,v 1.4 2002-08-26 15:31:23 bdenney Exp $
// $Id: wxmain.h,v 1.5 2002-08-27 18:11:13 bdenney Exp $
/////////////////////////////////////////////////////////////////
// This file defines variables and classes that the wxWindows .cc files
// share. It should be included only by wx.cc and wxmain.cc.
@ -53,6 +53,8 @@ class MyFrame: public wxFrame
MyPanel *panel;
public:
MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size, const long style);
enum StatusChange { Start, Stop, Pause, Resume };
void simStatusChanged (StatusChange change, Boolean popupNotify=false);
void OnConfigRead(wxCommandEvent& event);
void OnConfigSave(wxCommandEvent& event);
void OnQuit(wxCommandEvent& event);

View File

@ -1,5 +1,5 @@
/////////////////////////////////////////////////////////////////////////
// $Id: main.cc,v 1.122 2002-08-27 16:43:40 bdenney Exp $
// $Id: main.cc,v 1.123 2002-08-27 18:11:13 bdenney Exp $
/////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2002 MandrakeSoft S.A.
@ -1099,7 +1099,14 @@ int main (int argc, char *argv[])
bx_init_main (argc, argv);
bx_do_text_config_interface (argc, argv);
bx_config_interface (BX_CI_INIT);
bx_continue_after_config_interface (argc, argv);
static jmp_buf context;
if (setjmp (context) == 0) {
SIM->set_quit_context (&context);
bx_continue_after_config_interface (argc, argv);
// function returned normally
} else {
// quit via longjmp
}
}
#endif