title: Why can I deadlock in a single-threaded FLTK application?
body: <p>I have a supposedly single-threaded FLTK application with a popup menu, created with Fluid.  I have a class that sub-classes Fl_Gl_Window and implements a handle() method.  The handle() method calls a function that creates a popup window on right-click.  I have a long operation that I do for one of the menu items.  My application has created a second thread for some other purpose.  I use locks to protect some critical sections between my main thread and that second thread.  In particular, doLongOperation() uses the locks.</p>
  
  <p>My problem is that I can popup the menu twice and run doLongOperation() twice, and it then deadlocks with itself, hanging the application.  <strong>Why doesn't the first doLongOperation() stall the GUI and prevent me from starting doLongOperation() a second time?</strong></p>
  
  <p>I can avoid the problem with a flag that I use to disable the offending menu item, but I would like to understand why it is possible in the first place.</p>
  
  <p>Here's the code, abbreviated of course.  Hopefully I've included all the relevant bits.</p>
  
  <pre><code>class MyClass {
    void doLongOperation();
  };
  
  class MyApplication : public MyClass {
    MyApplication();
    void run();
    void popup_menu();
  };
  
  void MyClass::doLongOperation()
  {
     this-&gt;enterCriticalSection();
     // stuff
     // EDIT
     // @vladr I did leave out a relevant bit.
     // Inside this critical section, I was calling Fl::check().
     // That let the GUI handle a new popup and dispatch a new
     // doLongOperation() which is what lead to deadlock.
     // END EDIT
     this-&gt;leaveCriticalSection();
  } 
  
  MyApplication::MyApplication() : MyClass() 
  {
    // ...
    { m_mainWindowPtr = new Fl_Double_Window(820, 935, "Title");
      m_mainWindowPtr-&gt;callback((Fl_Callback*)cb_m_mainWindowPtr, (void*)(this));
      { m_wireFrameViewPtr = new DerivedFrom_Fl_Gl_Window(10, 40, 800, 560);
        // ...
      }
      m_mainWindowPtr-&gt;end();
    } // Fl_Double_Window* m_mainWindowPtr
  
  m_wireFrameViewPtr-&gt;setInteractive();
  
  m_mainWindowPtr-&gt;position(7,54);
  m_mainWindowPtr-&gt;show(1, &amp;(argv[0]));
  
  Fl::wait();
  }
  
  void MyApplication::run() {
    bool keepRunning = true;
    while(keepRunning) {
  
    m_wireFrameViewPtr-&gt;redraw();
    m_wireFrameView2Ptr-&gt;redraw();
  
    MyClass::Status result = this-&gt;runOneIteration();
    switch(result) {
    case DONE: keepRunning = false; break;
    case NONE: Fl::wait(0.001); break;
    case MORE: Fl::check(); break;
    default: keepRunning = false;
    }
  
  }
  
  void MyApplication::popup_menu() {
    Fl_Menu_Item *rclick_menu;
  
  
    int longOperationFlag = 0;
    // To avoid the deadlock I can set the flag when I'm "busy".
    //if (this-&gt;isBusy()) longOperationFlag = FL_MENU_INACTIVE;
  
    Fl_Menu_Item single_rclick_menu[] = {
       { "Do long operation", 0, 0, 0, longOperationFlag },
       // etc. ...
       { 0 }
    };
  
    // Define multiple_rclick_menu...
  
    if (this-&gt;m_selectedLandmarks.size() == 1) rclick_menu = single_rclick_menu;
    else rclick_menu = multiple_rclick_menu;
  
    const Fl_Menu_Item *m = rclick_menu-&gt;popup(Fl::event_x(), Fl::event_y(), 0, 0, 0);
  
    if (!m) return;
  
  
    if (strcmp(m-&gt;label(), "Do long operation") == 0) {
      this-&gt;doLongOperation();
      return;
    }
  
    // Etc.
  
  }
  </code></pre>