Nonlocal Jump: 두 판 사이의 차이

youngwiki
9번째 줄: 9번째 줄:
#include <setjmp.h>
#include <setjmp.h>
//Returns: 0 from setjmp, nonzero from longjmps
//Returns: 0 from setjmp, nonzero from longjmps
int setjmp(jmp_buf env);  
int setjmp(jmp_buf env); //세이브 포인트 찍기
int sigsetjmp(sigjmp_buf env, int savesigs);  
int sigsetjmp(sigjmp_buf env, int savesigs);  
</syntaxhighlight>
</syntaxhighlight>
16번째 줄: 16번째 줄:
#include <setjmp.h>
#include <setjmp.h>
//never returns
//never returns
void longjmp(jmp_buf env, int retval);
void longjmp(jmp_buf env, int retval); //문제가 생겼으니 세이브 포인트로 후퇴
void siglongjmp(sigjmp_buf env, int retval);  
void siglongjmp(sigjmp_buf env, int retval);  
</syntaxhighlight>
</syntaxhighlight>

2025년 4월 10일 (목) 17:15 판

상위 문서: Exceptional Control Flow

개요

Nonlocal jump는 사용자 수준의 exceptional control flow을 제공한다. 이는 정상적인 호출이나 반환 순서를 거치지 않고 현재 실행 중인 한 함수에서 다른 함수로 직접 control을 전이시키는 역할을 한다.

Nolocal functions

Nonlocal jump는 setjmp(), longjmp() 함수들에 의해 구현된다.

#include <setjmp.h>
//Returns: 0 from setjmp, nonzero from longjmps
int setjmp(jmp_buf env); //세이브 포인트 찍기
int sigsetjmp(sigjmp_buf env, int savesigs);

setjmp() 함수는 현재의 호출 환경(calling environment)를 env 버퍼에 저장하여 longjmp() 함수에서 사용하도록 한 후, 0을 반환한다. 이때 호출 환경은 프로그램 카운터, 스택 포인터, 범용 레지스터(general-purpose registers)들을 포함한다. 이때, setjmp()의 반환값을 변수에 할당해서는 안된다.

#include <setjmp.h>
//never returns
void longjmp(jmp_buf env, int retval); //문제가 생겼으니 세이브 포인트로 후퇴
void siglongjmp(sigjmp_buf env, int retval);

longjmp() 함수는 env 버퍼로부터 호출 환경을 복원한 다음 해당 env를 초기화한 다음, 해당 env를 초기화한 최신의 setjmp() 함수의 return을 야기한다. 이때 setjmp() 함수는 0이 아닌 retval 값을 return하며 복귀한다.
즉, setjmp() 함수는 한 번 호출되지만 여러 번 반환된다. 한 번은 setjmp() 함수가 처음 호출되어 호출 환경이 env 버퍼에 저장될 때이고, 나머지는 각각의 longjmp() 함수 호출에 대응하여 반환될 때이다. 반면, longjmp() 함수는 한 번 호출되지만 절대 반환되지 않는다.

비유를 하자면, setjmp(), longjmp() 함수들은 닥터 스트레인지의 마법과 비슷하다. setjmp()는 닥터 스트레인지가 도르마무와 맞서기 위해서 스폰 포인트를 찍어두는 곳이고, longjmp()는 닥터 스트레인지가 도르마무에게 죽는 곳이다. 즉, 코드가 longjmp()에 이르면 control이 강제로 setjmp() 지점으로 이동되며, 이를 통해서 setjmp()는 두 번 반환될 수 있다.

Nonlocal jump의 중요한 용도 중 하나는, 깊이 중첩된 함수 호출 내부에서 어떤 오류 조건이 감지되었을 때 즉시 return하도록 하는 것이다. 중첩된 함수 호출 내부 깊은 곳에서 오류 조건이 발생한 경우, 호출 스택(call stack)을 힘겹게 하나하나 풀어내는 대신, nonlocal jump를 이용해 common localized error handler로 직접 return할 수 있다.
Nonlocal jump의 또 다른 중요한 용도는, 시그널 핸들러(signal handler)로부터 인터럽트(interruted)된 명령어로 복귀하는 대신, 특정 코드 위치로 분기(branch out)하는 것이다.

Nonlocal examples

Nonlocal example 1

#include "csapp.h"
jmp_buf buf;
int error1 = 0;
int error2 = 1;

void foo(void), bar(void);
int main() {
    switch(setjmp(buf)) {
        case 0:  foo();  break;
        case 1:  printf("Detected an error1 condition in foo\n");  break;
        case 2:  printf("Detected an error2 condition in foo\n");  break;
        default:  printf("Unknown error condition in foo\n");
    }
    exit(0);
}
/* Deeply nested function foo */
void foo(void) {
    if (error1) longjmp(buf, 1);
    bar();
}
void bar(void) {
    if (error2) longjmp(buf, 2);
}

Nonlocal example 2

각주