- Setjmp.h
-
.
Plantilla:Lowercase setjmp.h es un encabezado definido en la biblioteca estándar de C para proporcionar "saltos no locales": control de flujo que se desvía de la llamada a subrutina habitual y la secuencia de retorno. Las funciones complementarias
setjmp
ylongjmp
proporcionan esta funcionalidad.Un uso típico de
setjmp
/longjmp
es la implementación de un mecanismo de excepción que utiliza la capacidad delongjmp
para restablecer el estado del programa o hilo, incluso a través de múltiples niveles de las llamadas a funciones. Un uso menos común desetjmp
es crear una sintaxis similar a co-rutinas.Plantilla:Biblioteca Estandar C
Contenido
Funciones miembro
int setjmp(jmp_buf env)
Establece el buffer jmp_buf
local y lo inicializa para el salto. Esta rutina[1] guarda el contexto de la llamada del programa en el búfer de contexto especificado por el argumentoenv
para su uso posterior porlongjmp
. Si el retorno es de una invocación directa,setjmp
devuelve 0. Si el retorno es de una llamada alongjmp
,setjmp
devuelve un valor distinto de cero.void longjmp(jmp_buf env, int value)
Restaura el contexto del buffer de entorno env
que se salvó por invocación de la rutina[1] en la misma invocación del programa. Invocando longjmp desde un manejador de señales anidado no-definido. El valor especificado porvalor
es pasado desdelongjmp
asetjmp
. Después de quelongjmp
se completa, la ejecución del programa continúa como si la invocación correspondiente desetjmp
acabara de regresar. Si elvalor
pasado alongjmp
es 0,setjmp
se comportará como si hubiera devuelto 1;, de lo contrario, se comportará como si hubiera devuelto unvalor
.setjmp
guarda el contexto actual (Ej., 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 puede ser usada, en un punto más lejano de la ejecución del programa, porlongjmp
para restaurar el estado del programa que fue guardado porsetjmp
enjmp_buf
. Este proceso puede representarse como un "salto" atras hacia el punto de la ejecución del programa dondesetjmp
guardo el contexto. El (aparente) valor de retorno desetjmp
indica si el control alcanzado el punto de normalidad o de una llamada alongjmp
. Esto lleva a un común [[lenguaje de programación |] idioma]:if( setjmp(x) ){/* manejar longjmp(x) */}
.POSIX.1 no especifica si
setjmp
ylongjmp
guardan o restauran el conjunto actual de bloqueo de señales — si un programa cuenta con manejo de señales que deberia usarlas instrucciones POSIXsigsetjmp
/siglongjmp
.Tipos Miembro
jmp_buf
Un tipo de array, como struct __jmp_buf_tag[1]
,[2] apropiado para manejar la información necesaria para restaurar el contexto desde una llamada a sistema.The C99 Rationale describes
jmp_buf
as being an array type for backwards compatibility; existing code refers tojmp_buf
storage locations by name (without the&
address-of operator), which is only possible for array types.[3]Advertencias y limitaciones
Cuando un "no-local goto" se ejecuta a través de setjmp / longjmp, normal "desenredo de pila" no ocurre y por lo tanto, las acciones de limpieza necesarios, tales como el cierre de descriptores de archivos, vaciar los búferes, la liberación de heap asignado de memoria, etc, no ocurrir.
Si la función en la que fue llamada setjmp regresa, ya no es posible utilizar de forma segura longjmp con el objeto jmp_buf correspondiente. Esto es así porque el marco de pila se invalida cuando se devuelve la función. Llamar a longjmp restablece el puntero de pila, que-ya que la función devuelve-apuntaría a una pila no existe y posiblemente sobrescribir / dañado frame.4 5
Del mismo modo, C99 no requiere que longjmp preservar el marco de pila actual. Esto significa que saltar a una función que se sale a través de una llamada a longjmp es undefined.6 Sin embargo, la mayoría de las implementaciones de longjmp dejar intacto el marco de pila, lo que permite setjmp y longjmp que se utiliza para saltar hacia atrás y hacia adelante entre dos o más funciones -una característica explotada para la multitarea.
En comparación con los mecanismos de lenguajes de programación de alto nivel como Python, Java, C + +, C #, e incluso pre-C lenguajes como Algol 60, la técnica del uso de setjmp / longjmp para implementar un mecanismo de excepción es aburrida. Estos lenguajes proporcionan las técnicas de manejo de excepción más potente, mientras que lenguas como el esquema, Smalltalk, y Haskell proporcionar más generales de manipulación de continuación construcciones.
Ejemplos de uso
Ejemplo simple
Este ejemplo muestra la idea básica de setjmp. Main calls first, which in turn calls second. The "second" function jumps back into main, skipping "first"'s print statement.
#include <stdio.h> #include <setjmp.h> static jmp_buf buf; void second(void) { printf("second\n"); // prints longjmp(buf,1); // jumps back to where setjmp was called - making setjmp now return 1 } void first(void) { second(); printf("first\n"); // does not print } int main() { if ( ! setjmp(buf) ) { first(); // when executed, setjmp returns 0 } else { // when longjmp jumps back, setjmp returns 1 printf("main\n"); // prints } return 0; }
When executed, the above program will output:
second main
Notice that although the
first()
subroutine gets called, "first
" never is printed. "main
" gets printed as the conditional statementif ( ! setjmp(buf) )
is executed a second time.Manejo de excepciones
En este ejemplo,
setjmp
is used to bracket exception handling, like 1999 ISO C standard and Single UNIX Specification by invokingsetjmp
in a limited range of contexts:[4]- As the condition to an
if
,switch
or iteration statement - As above in conjunction with a single
!
or comparison with an integer constant - As a statement (with the return value unused)
Following these rules can make it easier for the implementation to create the environment buffer, which can be a sensitive operation.[3] More general use of
setjmp
can cause undefined behaviour, such as corruption of local variables; conforming compilers and environments are not required to protect or even warn against such usage. However, slightly more sophisticated idioms such asswitch ((exception_type = setjmp(env))) { }
are common in literature and practice, and remain relatively portable. A simple conforming methodology is presented below, where an additional variable is maintained along with the state buffer. This variable could be elaborated into a structure incorporating the buffer itself.#include <stdio.h> #include <stdlib.h> #include <string.h> #include <setjmp.h> void first(void); void second(void); /* This program's output is: calling first calling second entering second second failed with type 3 exception; remapping to type 1. first failed, exception type 1 */ /* Use a file scoped static variable for the exception stack so we can access * it anywhere within this translation unit. */ static jmp_buf exception_env; static int exception_type; int main() { void *volatile mem_buffer; mem_buffer = NULL; if (setjmp(exception_env)) { /* if we get here there was an exception */ printf("first failed, exception type %d\n", exception_type); } else { /* Run code that may signal failure via longjmp. */ printf("calling first\n"); first(); mem_buffer = malloc(300); /* allocate a resource */ printf(strcpy((char*) mem_buffer, "first succeeded!")); /* ... this will not happen */ } if (mem_buffer) free((void*) mem_buffer); /* carefully deallocate resource */ return 0; } void first(void) { jmp_buf my_env; printf("calling second\n"); memcpy(my_env, exception_env, sizeof(jmp_buf)); switch (setjmp(exception_env)) { case 3: /* if we get here there was an exception. */ printf("second failed with type 3 exception; remapping to type 1.\n"); exception_type = 1; default: /* fall through */ memcpy(exception_env, my_env, sizeof(jmp_buf)); /* restore exception stack */ longjmp(exception_env, exception_type); /* continue handling the exception */ case 0: /* normal, desired operation */ second(); printf("second succeeded\n"); /* not reached */ } memcpy(exception_env, my_env, sizeof(jmp_buf)); /* restore exception stack */ } void second(void) { printf("entering second\n" ); /* reached */ exception_type = 3; longjmp(exception_env, exception_type); /* declare that the program has failed */ printf("leaving second\n"); /* not reached */ }
Multitarea cooperativa
C99 provee que
longjmp
is guaranteed to work only when the destination is a calling function, i.e., that the destination scope is guaranteed to be intact. Jumping to a function that has already terminated byreturn
orlongjmp
is undefined.[5] However, most implementations oflongjmp
do not specifically destroy local variables when performing the jump. Since the context survives until its local variables are erased, it could actually be restored bysetjmp
. In many environments (such as Really Simple Threads and TinyTimbers), idioms such asif(!setjmp(child_env)) longjmp(caller_env);
can allow a called function to effectively pause-and-resume at asetjmp
.This is exploited by thread libraries to provide cooperative multitasking facilities without using
setcontext
or other fiber facilities. Whereassetcontext
is a library service which can create an execution context in heap-allocated memory and can support other services such as buffer overflow protection[cita requerida], abuse ofsetjmp
is implemented by the programmer, who may reserve memory on the stack and fail to notify the library or operating system of the new operating context. On the other hand, a library's implementation ofsetcontext
may internally usesetjmp
in a fashion similar to this example to save and restore a context, after it has been initialised somehow.Considering that
setjmp
to a child function will generally work unless sabotaged, andsetcontext
, as part of POSIX, is not required to be provided by C implementations, this mechanism may be portable where thesetcontext
alternative fails.Since no exception will be generated upon overflow of one of the multiple stacks in such a mechanism, it is essential to overestimate the space required for each context, including the one containing
main()
and including space for any signal handlers that might interrupt regular execution. Exceeding the allocated space will corrupt the other contexts, usually with the outermost functions first. Unfortunately, systems requiring this kind of programming strategy are often also small ones with limited resources.#include <setjmp.h> #include <stdio.h> jmp_buf mainTask, childTask; void call_with_cushion(void); void child(void); int main(void) { if (!setjmp(mainTask)) { call_with_cushion(); /* child never returns */ /* yield */ } /* execution resumes after this "}" after first time that child yields */ for (;;) { printf("Parent\n"); if (!setjmp(mainTask)) { longjmp(childTask, 1); /* yield - note that this is undefined under C99 */ } } } void call_with_cushion (void) { char space[1000]; /* Reserve enough space for main to run */ space[999] = 1; /* Do not optimize array out of existence */ child(); } void child (void) { for (;;) { printf("Child loop begin\n"); if (!setjmp(childTask)) longjmp(mainTask, 1); /* yield - invalidates childTask in C99 */ printf("Child loop end\n"); if (!setjmp(childTask)) longjmp(mainTask, 1); /* yield - invalidates childTask in C99 */ } /* Don't return. Instead we should set a flag to indicate that main() should stop yielding to us and then longjmp(mainTask, 1) */ }
Véase también
- setcontext
- continuation
Referencias
- ↑ a b ISO C establece que
setjmp</ code> debe ser implementado como una macro, pero POSIX establece explícitamente que no está definido si
setjmp</ code> es una macro o una función.
↑ Este es el tipo usado por la Biblioteca GNU C, version 2.7
↑ a b C99 Rationale, version 5.10, April 2003, section 7.13
↑ Plantilla:Man
↑ Error en la cita: El elemento
<ref>
no es válido; pues no hay una referencia con texto llamadaISO.2FIEC_9899:1999
Enalces Externos
- Plantilla:Man
- Exceptions in C with Longjmp and Setjmp
Categorías:- Articulos con ejemplos en código C
- Bibliotecas Cabeceras de C standard
- Control de flujo
- As the condition to an
Wikimedia foundation. 2010.