Ein paar Fragen zur korrekten Verwendung von boost.context



  • Hallo Leute,

    Ich schreibe gerade an einer IRC-Bot Library und wuerde gerne boost.context dafuer verwenden. In der Doku befindet sich folgender Absatz: http://www.boost.org/doc/libs/1_51_0/libs/context/doc/html/context/context.html#context.context.stack_unwinding

    Stack unwinding

    Sometimes it is necessary to unwind the stack of an unfinished context to destroy local stack variables so they can release allocated resources (RAII pattern). The user is responsible for this task.

    Was heisst das fuer mich? Was muss ich machen, und wann? Was ist ueberhaupt ein "unfinished context"?

    Und dann gibt es auch noch folgenden Absatz: http://www.boost.org/doc/libs/1_51_0/libs/context/doc/html/context/context.html#context.context.exceptions_in__emphasis_context_function__emphasis_

    Exceptions in context-function

    If the context-function emits an exception, the application will terminate.

    D.h. ich muss also alle Exceptions fangen. Ich habe aber irgendwo gelesen, dass es eine abi::__forced_unwind Exception gibt, die ich nicht einfach fangen darf, da hiermit Stack unwinding erledigt wird. Trifft das hier auch zu? Sprich, muss ich sowas hier machen?

    try
    {
        ...
    }
    
    catch(abi::__forced_unwind const&)
    {
        throw;
    }
    
    catch(...)
    {
        ex_ptr = std::current_exception();
    }
    

    ... und dann die Exception im 'calling' Context mit std::rethrow_exception() weiterwerfen?

    Gruesse,
    Der Kellerautomat



  • Bist du sicher, dass du boost.context benutzen willst und nicht boost.thread? Fibers sind eine ziemlich widerliche Angelegenheit (im Grunde sind das notdürftig verpackte longjmps).

    Ein unfinished context ist eine Funktion, aus der du in der Mitte raus- und nie wieder zurückgeswitcht bist. Im Wesentlichen bedeutet das, dass du in die Funktion zurückswitchen musst, damit sie bis zum Ende durchlaufen und den Stack unwinden kann.

    Und fang um Stroustrups Willen nicht an, mit __forced_unwind herumzuspielen. Die sollten allerdings bei dir auch gar nicht ankommen, sofern du nicht innerhalb der Kontexte nochmal nackte longjmps benutzt (was du sein lassen solltest).



  • seldon schrieb:

    Bist du sicher, dass du boost.context benutzen willst und nicht boost.thread? Fibers sind eine ziemlich widerliche Angelegenheit (im Grunde sind das notdürftig verpackte longjmps).

    Ja, ich mir ganz sicher. 😉
    Wie jedes Chatprotokoll, ist auch IRC asynchroner Natur. Damit der Benutzer meine Library seinen Code trotzdem wie synchronen Code schreiben kann, moechte ich intern Fibers verwenden.

    seldon schrieb:

    Ein unfinished context ist eine Funktion, aus der du in der Mitte raus- und nie wieder zurückgeswitcht bist. Im Wesentlichen bedeutet das, dass du in die Funktion zurückswitchen musst, damit sie bis zum Ende durchlaufen und den Stack unwinden kann.

    Das verstehe ich nicht. Angenommen, ich springe in eine Funktion mit jump_fcontext hinein. Dann kann ich diese Funktion ja nicht einfach returnen lassen, sondern muss doch auch genauso mit jump_fcontext am Ende wieder herausspringen?

    seldon schrieb:

    Und fang um Stroustrups Willen nicht an, mit __forced_unwind herumzuspielen. Die sollten allerdings bei dir auch gar nicht ankommen, sofern du nicht innerhalb der Kontexte nochmal nackte longjmps benutzt (was du sein lassen solltest).

    Ich habe keine Ahnung, unter welchen Umstaenden diese Exception geworfen wird, im Internet habe ich dazu auch nicht wirklich etwas brauchbares gefunden. Hast du dazu vielleicht einen Link?



  • Ich sehe nicht, welchen Vorteil dir Fibers gegenüber Threads in diesem Zusammenhang bieten. Wenn du deinem Benutzer erlauben willst, ständig zu pollen (was mir eigentlich auch nicht so richtig sinnvoll erscheint, um mit einem asynchronen Protokoll umzugehen, aber sei's drum), kannst du auch stumpf 20 Threads aufmachen, die ihr Ding durchziehen, und die jedes mal der Reihe nach fragen, ob sie schon etwas für dich haben. Synchron aus asynchron ist simpel, umgekehrt ist schwierig.

    Wie dem auch sei, die RAII-Geschichte wird so gemeint sein:

    void foo() {
      {
        raii stuff(here());
    
        ...
      } // stack unwinding passiert hier.
    
      // statt return.
      boost::ctx::jump_fcontext(&c1, &c2, 0);
    }
    

    Was __forced_unwind angeht, forced unwinding passiert, wenn du einen Thread abschießt oder mit longjmp den Stack hochspringst. Letzteres ist übrigens vom C++-Standard nicht gedeckt (erzeugt undefiniertes Verhalten), sondern wird im Itanium-ABI beschrieben. Wie C++11 zum abschießen von Threads steht, müsste ich jetzt nachkucken -- kann gut sein, dass da ähnliches zu steht.

    Wenn du dich aber für Boost.Context schon entschieden hast, ist Plattformunabhängigkeit wohl eh kein Thema für dich -- Boost.Context zieht hinter den Kulissen eines an Assembler-Magie, um überhaupt zu funktionieren. Das wird genau an dieser Stack-Unwinding-Problematik liegen. Und wenn du jetzt kommst und etwas in die Exception-Tables schreibst, das das Verhalten von __forced_unwind verändert, nachdem boost.context grad den Stackframe verschoben hat, bin ich mir nicht sicher, was dabei herauskommt.

    Dummerweise sind die relevanten Terme etwas schwer zu googeln (personality routine wirft einem jede Menge Psycho- und HR-Zeug raus); aber http://mentorembedded.github.com/cxx-abi/abi-eh.html enthält eine technische Beschreibung.



  • Ich will, dass sowas hier funktioniert:

    session.on_connect
    (
        [&] (connect_event const& e)
        {
            if(session.join("#foo")) // asynchron. andere, eintreffende nachrichten sollen im hintergrund verarbeitet werden.
                cout << "joined #foo\n";
        }
    );
    

    Damit das geht, muss ich innerhalb von join() in den Message-Loop Context switchen, dort Nachrichten verarbeiten und sobald die Antwort da ist wieder zurueckswitchen.

    Und Boost.Context laueft zumindest unter den wichtigsten Plattformen, das reicht mir.


Log in to reply