stringtranslate.com

conjuntojmp.h

setjmp.h es un encabezado definido en la biblioteca estándar de C para proporcionar "saltos no locales": flujo de control que se desvía de la secuencia habitual de llamada y retorno de subrutinasetjmp . Las funciones complementarias y longjmpproporcionan esta funcionalidad.

Un uso típico de setjmp/ longjmpes la implementación de un mecanismo de excepción que aprovecha la capacidad de longjmppara restablecer el estado del programa o del subproceso, incluso en múltiples niveles de llamadas de función. Un uso menos común de setjmpes crear una sintaxis similar a las corrutinas .

Funciones miembro

setjmpguarda el entorno actual (el estado del programa), en algún punto de la ejecución del programa, en una estructura de datos específica de la plataforma ( jmp_buf) que se puede usar en algún punto posterior de la ejecución del programa para longjmprestaurar el estado del programa al que guardó setjmpinto jmp_buf. Este proceso se puede imaginar como un "salto" de regreso al punto de ejecución del programa donde setjmpguardó el entorno. El valor de retorno (aparente) de setjmpindica si el control llegó a ese punto normalmente (cero) o desde una llamada a longjmp(distinto de cero). Esto conduce a un modismo común : .if( setjmp(x) ){/* handle longjmp(x) */}

POSIX .1 no especifica si se debe guardar setjmpy longjmprestaurar el conjunto actual de señales bloqueadas ; si un programa emplea manejo de señales, debe utilizar sigsetjmp/ de POSIX siglongjmp.

Tipos de miembros

El fundamento C99 lo describe jmp_bufcomo un tipo de matriz para compatibilidad con versiones anteriores ; el código existente se refiere a jmp_buflas ubicaciones de almacenamiento por nombre (sin el &operador de dirección), lo que solo es posible para los tipos de matriz. [2] Señala que simplemente puede ser una matriz de un solo miembro con su único miembro siendo los datos reales; de hecho, este es el enfoque empleado por la biblioteca GNU C , que define el tipo como struct __jmp_buf_tag[1].

Advertencias y limitaciones

Cuando se ejecuta un "goto no local" a través de setjmp/ longjmpen C++ , no se produce el " desenrollado de pila " normal. Por lo tanto, tampoco se realizarán las acciones de limpieza necesarias. Esto podría incluir el cierre de descriptores de archivos , el vaciado de búferes o la liberación de memoria asignada al montón .

Si la función en la que setjmpse llamó retorna, ya no es posible usarla de manera segura longjmpcon el jmp_bufobjeto correspondiente. Esto se debe a que el marco de pila se invalida cuando la función retorna. La llamada longjmprestaura el puntero de pila , que, debido a que la función retorna, apuntaría a un marco de pila inexistente y potencialmente sobrescrito o dañado. [3] [4]

De manera similar, C99 no exige que longjmpse conserve el marco de pila actual. Esto significa que saltar a una función de la que se salió mediante una llamada a longjmpno está definido. [5]

Ejemplo de uso

Ejemplo sencillo

El ejemplo siguiente muestra la idea básica de setjmp. Allí, main()llama a first(), que a su vez llama a second(). Luego, second()vuelve a saltar a main(), saltando first()la llamada de printf().

#incluir <stdio.h> #incluir <setjmp.h>  estático jmp_buf buf ;  void second () { printf ( "second \n " ); // imprime longjmp ( buf , 1 ); // salta de nuevo al lugar donde se llamó a setjmp - haciendo que setjmp ahora devuelva 1 }       void primero () { segundo (); printf ( "primero \n " ); // no imprime }     int main () { if ( ! setjmp ( buf )) first (); // cuando se ejecuta, setjmp devuelve 0 else // cuando longjmp salta hacia atrás, setjmp devuelve 1 printf ( "main \n " ); // imprime           devuelve 0 ; } 

Al ejecutarse, el programa anterior generará el siguiente resultado:

segundoprincipal

Tenga en cuenta que, aunque first()se llama a la subrutina, " first" nunca se imprime, ya que second()nunca devuelve el control a first(). En cambio, " " se imprime cuando se verifica mainla declaración condicional una segunda vez.if (!setjmp(buf))

Manejo de excepciones

En este ejemplo, setjmpse utiliza para incluir el manejo de excepciones, como tryen otros lenguajes. La llamada a longjmpes análoga a una throwdeclaración, lo que permite que una excepción devuelva un estado de error directamente a setjmp. El siguiente código se adhiere al estándar ISO C de 1999 y a la Especificación Única de UNIX al invocarlo setjmpen un rango limitado de contextos: [6]

El cumplimiento de estas reglas puede facilitar la implementación para crear el búfer de entorno, lo que puede ser una operación delicada. [2] El uso más general de setjmppuede causar un comportamiento indefinido, como la corrupción de variables locales; los compiladores y entornos conformes no están obligados a proteger o incluso advertir contra dicho uso. Sin embargo, expresiones idiomáticas ligeramente más sofisticadas como switch ((exception_type = setjmp(env))) { }son comunes en la literatura y la práctica, y siguen siendo relativamente portables. A continuación se presenta una metodología de conformidad simple, donde se mantiene una variable adicional junto con el búfer de estado. Esta variable podría elaborarse en una estructura que incorpore el propio búfer.

En un ejemplo de apariencia más moderna, el bloque "try" habitual se implementaría como un setjmp (con algún código de preparación para saltos multinivel, como se ve en first), el "throw" como longjmp con el parámetro opcional como excepción y el "catch" como el bloque "else" bajo "try".

#include <setjmp.h> #include <stdio.h> #include <stdlib.h> #include <string.h>    void estático primero (); void estático segundo ();    /* Utilice una variable estática con ámbito de archivo para la pila de excepciones de modo que podamos acceder a ella * en cualquier lugar dentro de esta unidad de traducción. */ static jmp_buf exception_env ; static int exception_type ;    int main ( void ) { char * volátil mem_buffer = NULL ;        if ( setjmp ( exception_env )) { // si llegamos aquí hubo una excepción printf ( "first falló, tipo de excepción: %d \n " , exception_type ); } else { // Ejecutar código que puede señalar una falla a través de longjmp. puts ( "calling first" ); first ();            mem_buffer = malloc ( 300 ); // asignar un recurso printf ( "%s \n " , strcpy ( mem_buffer , "primer éxito" )); // no alcanzado }         free ( mem_buffer ); // Se puede pasar NULL a free, no se realiza ninguna operación  devuelve 0 ; } vacío estático primero () { jmp_buf my_env ;      pone ( "entrando primero" ); // alcanzado  memcpy ( my_env , exception_env , sizeof my_env ); // almacena el valor de exception_env en my_env ya que exception_env será reutilizado     switch ( setjmp ( exception_env )) { caso 3 : // si llegamos aquí hubo una excepción. puts ( "segundo error, tipo de excepción: 3; reasignando al tipo 1" ); exception_type = 1 ;          predeterminado : // pasar por memcpy ( exception_env , my_env , sizeof exception_env ); // restaurar la pila de excepciones longjmp ( exception_env , exception_type ); // continuar manejando la excepción          caso 0 : // operación normal, deseada puts ( "llamando segundo" ); // alcanzado segundo (); puts ( "segundo exitoso" ); // no alcanzado }         memcpy ( exception_env , my_env , sizeof exception_env ); // restaurar la pila de excepciones     pone ( "saliendo primero" ); // nunca alcanzado } static void second () { puts ( "ingresando segundo" ); // alcanzado       exception_type = 3 ; longjmp ( exception_env , exception_type ); // declara que el programa ha fallado      pone ( "saliendo segundo" ); // no alcanzado } 

El resultado de este programa es:

llamando primeroentrando primerollamando segundoentrando segundoSegundo error, tipo de excepción: 3; reasignación al tipo 1primer error, tipo de excepción: 1

Multitarea cooperativa

C99 establece que longjmpse garantiza que funcione solo cuando el destino es una función que realiza la llamada, es decir, que se garantiza que el ámbito de destino esté intacto. Saltar a una función que ya ha finalizado por returno longjmpno está definida. [5] Sin embargo, la mayoría de las implementaciones de longjmpno destruyen específicamente las variables locales al realizar el salto. Dado que el contexto sobrevive hasta que se borran sus variables locales, en realidad podría restaurarse mediante setjmp. En muchos entornos (como Really Simple Threads y TinyTimbers), expresiones idiomáticas como if(!setjmp(child_env)) longjmp(caller_env);pueden permitir que una función llamada pause y reanude de manera efectiva en un setjmp.

Las bibliotecas de subprocesos aprovechan esto para proporcionar funciones de multitarea cooperativa sin utilizar setcontextotras funciones de fibra .

Teniendo en cuenta que setjmpuna función secundaria generalmente funcionará a menos que sea saboteada y que setcontext, como parte de POSIX, no es necesario que la proporcionen las implementaciones de C, este mecanismo puede ser portátil cuando la setcontextalternativa falla.

Dado que no se generará ninguna excepción en caso de desbordamiento de una de las múltiples pilas de un mecanismo de este tipo, es esencial sobreestimar el espacio necesario para cada contexto, incluido el que contiene main()e incluye espacio para cualquier manejador de señales que pueda interrumpir la ejecución normal. Si se excede el espacio asignado, se dañarán los demás contextos, generalmente con las funciones más externas primero. Desafortunadamente, los sistemas que requieren este tipo de estrategia de programación también suelen ser pequeños y con recursos limitados.

#incluir <setjmp.h> #incluir <stdio.h>  jmp_buf tarea principal , tarea secundaria ;  void llamada_con_cojin (); void hijo ();  int main () { if ( ! setjmp ( mainTask )) { call_with_cushion (); // el hijo nunca retorna, cede } // la ejecución se reanuda después de este "}" después de la primera vez que el hijo cede          mientras ( 1 ) { printf ( "Padre \n " ); si ( ! setjmp ( mainTask )) longjmp ( childTask , 1 ); // rendimiento - tenga en cuenta que esto no está definido en C99 } }          void call_with_cushion () { char space [ 1000 ]; // Reserva suficiente espacio para que se ejecute main space [ 999 ] = 1 ; // No optimice la matriz hasta que exista child (); }          void child () { while ( 1 ) { printf ( "El bucle secundario comienza \n " ); if ( ! setjmp ( childTask )) longjmp ( mainTask , 1 ); // yield - invalida childTask en C99             printf ( "Fin del bucle secundario \n " ); if ( ! setjmp ( childTask )) longjmp ( mainTask , 1 ); // yield - invalida childTask en C99 }      /* No retornar. En su lugar, deberíamos establecer un indicador para indicar que main()  debe dejar de cedernos el paso y luego longjmp(mainTask, 1) */ }

Referencias

  1. ^ ab ISO C establece que setjmpdebe implementarse como una macro, pero POSIX establece explícitamente que no está definido si setjmpes una macro o una función.
  2. ^ Fundamento del C99, versión 5.10, abril de 2003, sección 7.13
  3. ^ Notas de clase de CS360: Setjmp y Longjmp
  4. ^ setjmp(3) Archivado el 26 de julio de 2009 en Wayback Machine.
  5. ^ ab ISO/IEC 9899:1999, 2005, 7.13.2.1:2 y nota al pie 211
  6. ^ setjmp : establecer punto de salto para un goto no local – Referencia de interfaces del sistema, La especificación única de UNIX , versión 4 de The Open Group

Lectura adicional

Enlaces externos