Frage Warum stürzt der setjmp / longjmp auf MSVC ab, wenn nicht in MinGW?


Ich möchte verwenden setjmp()/longjmp() ein Coroutinsystem implementieren. Dann entscheide ich mich, eine kleine .c-Datei zu schreiben, um es zu testen. In MinGW ist es in Ordnung; Ich habe das gewünschte Ergebnis. Aber wenn ich es in MSVC ++ kompiliere, stürzt das Programm ab: "Zugriffsverletzung"

    #include <stdio.h>
    #include <stdlib.h>
    #include <setjmp.h>

    jmp_buf a;
    int is_invoke=0;

    void 
    action_1()
    {
        for ( ;; ) {
          printf("hello~~~A\n");
          if(!setjmp(a)) {
            is_invoke=1;
            return;
          }  
        }
    }

    void 
    func()
    {
      if (is_invoke) {
        longjmp(a,1);
      }
      action_1();
      printf("end\n");
    }

    void 
    dummy()
    {
      ;
    }

    int 
    main(int argc, char *argv[])
    {
        for ( ;; )  {
          func();
          dummy();
        }
        return 0;
    }

5
2017-12-25 03:32


Ursprung


Antworten:


Die man-Seite für setjmp sagt:

setjmp() speichert den Stapel Kontext / Umgebung in env zur späteren Verwendung durch    longjmp(). Der Stapelkontext wird ungültig, wenn die Funktion   welcher angerufen hat setjmp() kehrt zurück.

In einer einfachen Implementierung könnte man annehmen, dass a jmp_buf enthält eine Adresse zum Zurücksetzen des Stapelzeigers auf und eine Adresse, zu der gesprungen werden soll. Sobald Sie von der Funktion zurückkehren, die das gespeichert hat jmp_bufder Stapelrahmen, auf den die jmp_buf ist nicht mehr gültig und kann sofort beschädigt werden.

Oder mit anderen Worten, man kann sich nur auf longjmp verlassen, um als eine Art Super-return Aussage - niemals tiefer zu gehen.

Ich denke, der Grund, warum dies bei mingw (und bei mir unter Linux) für Sie funktioniert, ist implementierungsspezifisch und möglicherweise glücklicher Natur. Es gibt einen anderen Weg - hast du gelesen Simon Tathams böse Coroutine-Makros Aufsatz?


7
2017-12-25 05:00



Da Sie ein nicht definiertes Verhalten aufrufen, ist es in Ordnung, wenn ein Compiler abstürzt und ein anderer scheinbar funktioniert. Beide sind richtig - das ist die Schönheit von undefiniertem Verhalten.

Das Problem ist, dass ein gespeicherter Kontext - der jmp_buf - bleibt nur solange gültig wie die Funktion, die aufgerufen hat setjmp() es einzustellen ist nicht zurückgekehrt.

Der C99-Standard (nicht mehr der derzeitige Standard, aber dieser Wortlaut wird sich wahrscheinlich nicht wesentlich geändert haben) lautet:

§7.13.2.1 longjmp Funktion

Das longjmp Funktion stellt die Umgebung wieder her, die beim letzten Aufruf von   das setjmp Makro im selben Aufruf des Programms mit dem entsprechenden    jmp_buf Streit. Wenn es keinen solchen Aufruf gegeben hat oder wenn die Funktion enthält   die Anrufung der setjmp Makro hat die Ausführung beendet208) in der Zwischenzeit oder wenn die   Anrufung der setjmp Makro war im Rahmen einer Kennung mit variabel   Der modifizierte Typ und die Ausführung haben diesen Bereich in der Zwischenzeit verlassen, das Verhalten ist nicht definiert.

208) Zum Beispiel durch Ausführen von a return Aussage oder weil ein anderer longjmp Anruf hat verursacht   Transfer zu einem setjmp Aufruf in einer früheren Funktion in der Menge der verschachtelten Aufrufe.

Dein Code wird beendet action_1() fast sofort, Rendering der jmp_buf gespeichert von setjmp() wertlos.


Ich habe diese kleine Demonstration von erstellt setjmp() und longjmp() vor ein paar Jahren. Es kann dir helfen.

/*
@(#)File:           $RCSfile: setjmp.c,v $
@(#)Version:        $Revision: 1.1 $
@(#)Last changed:   $Date: 2009/10/01 16:41:04 $
@(#)Purpose:        Demonstrate setjmp() and longjmp()
@(#)Author:         J Leffler
@(#)Copyright:      (C) JLSS 2009
*/


#include <stdio.h>
#include <setjmp.h>
#include <stdlib.h>

static jmp_buf target_location;

static void do_something(void)
{
    static int counter = 0;
    if (++counter % 10 == 0)
        printf("---- doing something: %3d\n", counter);
    if (counter % 1000 == 0)
    {
        printf("||-- doing_something: calling longjmp() with value -1\n");
        longjmp(target_location, -1);
    }
}

static void do_something_else(int i, int j)
{
    printf("-->> do_something_else: (%d,%d)\n", i, j);
    do_something();
    if (i > 2 && j > 2 && j % i == 2)
    {
        printf("||-- do_something_else: calling longjmp() with value %d\n", (i + j) % 100);
        longjmp(target_location, (i + j) % 100);
    }
    printf("<<-- do_something_else: (%d,%d)\n", i, j);
}

static void doing_stuff(void)
{
    int i;
    printf("-->> doing_stuff()\n");
    for (i = rand() % 15; i < 30; i++)
    {
        int j;
        do_something();
        for (j = rand() % 10; j < 20; j++)
        {
            do_something_else(i, j);
        }
    }
    printf("<<-- doing_stuff()\n");
}

static void manage_setjmp(void)
{
    printf("-->> manage_setjmp()\n");
    switch (setjmp(target_location))
    {
    case 0:
        /* Initial return - get on with doing stuff */
        doing_stuff();
        break;
    case -1:
        /* Error return - terminate */
        printf("<<-- manage_setjmp() - error return from setjmp()\n");
        return;
    default:
        /* NB: not officially possible to assign the return from setjmp() */
        printf("---- manage_setjmp() - non-error return from setjmp()\n");
        doing_stuff();
        break;
    }
    printf("<<-- manage_setjmp()\n");
}

int main(void)
{
    printf("-->> main()\n");
    manage_setjmp();
    printf("<<-- main()\n");
    return(0);
}

4
2017-12-25 07:42



Sie können setjmp / longjmp nicht für Korotinen verwenden. Verwenden Sie macekontext / swapcontext für POSIX oder Fasern (CreateFiber usw.) für Windows.


1
2017-12-25 07:11