Question

J'ai une application FLTK soi-disant mono-thread avec un menu contextuel, créé avec fluide. J'ai une classe qui sous-classes Fl_Gl_Window et met en œuvre une méthode poignée (). La méthode handle () appelle une fonction qui crée une fenêtre pop-up en un clic droit. J'ai une longue opération que je fais pour l'un des éléments du menu. Mon application a créé un deuxième fil à d'autres fins. J'utilise des verrous pour protéger certaines sections critiques entre mon thread principal et que le deuxième fil. En particulier, doLongOperation () utilise les serrures.

Mon problème est que je peux le menu deux fois apparaitre et exécuter deux fois doLongOperation (), et avec lui-même puis serrures à pêne dormant, suspendu l'application. Pourquoi pas la première doLongOperation () décrochage de l'interface graphique et me empêcher de doLongOperation () à partir d'une seconde fois?

Je peux éviter le problème avec un drapeau que j'utilise pour désactiver l'élément de menu incriminé, mais je voudrais comprendre pourquoi il est possible, en premier lieu.

Voici le code, en abrégé bien sûr. Espérons que j'ai inclus tous les bits concernés.

class MyClass {
  void doLongOperation();
};

class MyApplication : public MyClass {
  MyApplication();
  void run();
  void popup_menu();
};

void MyClass::doLongOperation()
{
   this->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->leaveCriticalSection();
} 

MyApplication::MyApplication() : MyClass() 
{
  // ...
  { m_mainWindowPtr = new Fl_Double_Window(820, 935, "Title");
    m_mainWindowPtr->callback((Fl_Callback*)cb_m_mainWindowPtr, (void*)(this));
    { m_wireFrameViewPtr = new DerivedFrom_Fl_Gl_Window(10, 40, 800, 560);
      // ...
    }
    m_mainWindowPtr->end();
  } // Fl_Double_Window* m_mainWindowPtr

m_wireFrameViewPtr->setInteractive();

m_mainWindowPtr->position(7,54);
m_mainWindowPtr->show(1, &(argv[0]));

Fl::wait();
}

void MyApplication::run() {
  bool keepRunning = true;
  while(keepRunning) {

  m_wireFrameViewPtr->redraw();
  m_wireFrameView2Ptr->redraw();

  MyClass::Status result = this->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->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->m_selectedLandmarks.size() == 1) rclick_menu = single_rclick_menu;
  else rclick_menu = multiple_rclick_menu;

  const Fl_Menu_Item *m = rclick_menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, 0);

  if (!m) return;


  if (strcmp(m->label(), "Do long operation") == 0) {
    this->doLongOperation();
    return;
  }

  // Etc.

}
Était-ce utile?

La solution

Assurez-vous que vous n'êtes pas appeler Fl::wait(...) de plus d'un fil. Ai-je raison déduisant votre code qui run() exécute dans son propre fil?

Un premier appel à Fl::wait(), par exemple, du fil conducteur, attraperait et traiter le premier clic droit (blocage, comme prévu, alors que le premier appel à doLongOperation() produit); Entre-temps, les appels d'un second fil à, par exemple Fl::wait(timeout)/Fl::check() continuerait à rafraîchir l'affichage - et intercepter (service), le second clic droit, appelant handle() (dans le second fil), alors que la première opération longue est encore trimballe. Cela donnerait l'apparence d'une impasse, même si je pense que l'interface utilisateur reprendrait redessiner (à travers le second fil) lorsque les deux longues opérations complètes.

Valider ci-dessus en se connectant l'ID de fil à l'intérieur popup_menu().

Vous devriez choisir un seul thread d'appeler Fl::wait(...) dans une boucle, et vous ne devriez pas bloquer cette boucle - engendrer des tâches non modales ou non-UI comme des threads séparés. C'est à dire. quand popup_menu() est appelée, coup d'envoi de la longue opération dans son propre fil; si popup_menu() est déclenchée (à nouveau), tandis que le long fil d'opération est toujours en cours d'exécution, soit marquer l'élément de menu contextuel comme désactivé (analogue à votre solution de contournement), ou signaler simplement le long fil d'opération pour redémarrer avec un nouveau paramètre.

Autres conseils

Par hasard, votre doLongOperation faire tout ce qui pourrait avoir besoin de la pompe de message (ou VBTT, certaines API de fichiers Windows utilise ces dessous) pour être en cours d'exécution (en supposant que vous voyez ce comportement sous Windows)? Par exemple, si le doLongOperation tente de mettre à jour l'interface graphique qui utilise SendMessage dessous, vous obtiendrez l'impasse même dans un scénario mono-thread.

En outre, un autre thread n'ont la section critique déjà fait? Vous devriez être en mesure de briser le débogueur pendant le coup et voir, espérons qui attend sur ce.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top