This page is READ-ONLY. It is generated from the old site.
All timestamps are relative to 2013 (when this page is generated).
If you are looking for TeX support, please go to VietTUG.org

Nonlocal Jumps

how does exception mechanism work?
Added by bronzeboyvn over 2 years ago  »  Votes: 1/2

Có thể nói ngắn gọn cơ chế exception: luồng chương trình đang ở trong function C, khi mắc một lỗi nào đó luồng chương trình nhảy về function A. Giả sử ta có function A gọi function B, function B gọi function C, tức là ta có stack A sinh ra stack B, stack B sinh ra stack C trong virtual memory space.

  • Bằng cách nào con trỏ đang trỏ vào stack C chuyển sang trỏ vào stack A, trong khi chưa hủy stack C (function C chưa trả về), chưa hủy stack B (function B chưa trả về)?
  • Bằng cách lưu và phục hồi stack context.

1. setjmp/longjmp

#include <setjmp.h>

int setjmp(imp_buf env);
void longjmp(jmp_buf env, int retval);

Hàm setjmp được gọi một lần, nhưng trả về nhiều lần:

  • lần đầu tiên: do lần đầu tiên setjmp được gọi. Stack context được lưu vào buffer env. Và chỉ duy nhất lần này giá trị trả về rc = 0.
  • những lần sau: do longjmp(env, retval) gây ra. Stack được phục hồi lại như khi gọi setjmp(env) gần nhất và giá trị hàm setjmp(env) trả về chính là retval.

Trong khi đó longjmp được gọi một lần và không bao giờ trả về. Ví dụ

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <setjmp.h>
 4 
 5 jmp_buf buf;
 6 
 7 int error1 = 0;
 8 int error2 = 1;
 9 
10 void foo(void), bar(void);
11 
12 int main()
13 {
14     int rc;
15     rc = setjmp(buf);
16     if (rc == 0)
17         foo();
18     else if (rc == 1)
19         printf("there is error1 in foo\n");
20     else if (rc == 2)
21         printf("there is error1 in bar\n");
22     else
23         printf("unknow error\n");
24     exit(0);
25 }
26 
27 void foo(void)
28 {
29     if (error1)
30         longjmp(buf, 1);
31     bar();
32 }
33 
34 void bar(void)
35 {
36     if (error2)
37         longjmp(buf, 2);
38 }

Hàm main đầu tiên gọi setjmp để lưu lại stack context, và sau đó gọi foo, đến lượt mình foo gọi bar. Trong bar dính lỗi, luồng chương trình quay lại ngay setjmp, lúc này giá trị trả về rc = 2.

2. sigsetjmp/siglongjmp

#include <setjmp.h>

int sigsetjmp(imp_buf env, int savesigs);
void siglongjmp(jmp_buf env, int retval);

sigsetjmp/siglongjmp là một phiên bản khác của setjmp/longjmp dành cho signal handler khi ta bắt được tín hiệu. sigsetjmp(env, savesigs) được gọi với savesigs khác 0, ngoài chuyện stack context được lưu lại (để sau này phục hồi), thì mask signal cũng được lưu lại.

Ví dụ sau ta mô phỏng chương trình "soft restart". Khi chương trình chạy, in ra dòng đầu tiên là "starting", sau đó in đều đặn từng dòng "processing...". Hễ ta nhấn tổ hợp Ctr-C thì chương trình in ra "restarting".

 1 #include <stdio.h>
 2 #include <setjmp.h>
 3 #include <signal.h>
 4 
 5 sigjmp_buf buf;
 6 
 7 void handler(int sig)
 8 {
 9     siglongjmp(buf, 101);
10 }
11 
12 int main()
13 {
14     int restore;
15     signal(SIGINT, handler);
16     restore = sigsetjmp(buf, 1);
17     if (restore == 0)
18         printf("starting\n");
19     else if (restore == 101)
20         printf("restarting\n");
21     else
22         printf("who know?\n");
23 
24     while (1) {
25         sleep(1);
26         printf("processing...\n");
27     }
28     return 0;
29 }

Không phải bắt buộc cứng nhắc khi dùng cho signal handler, thì phải sử dụng sigsetjmp/siglongjmp. Ở source code trên, thay vì dùng sigsetjmp/siglongjmp, bạn có thể dùng setjmp/longjmp. Nhưng nếu dùng vậy, kết quả sẽ không như mong đợi: ở lần nhấn Ctr-C đầu tiên "restart" được in ra, nhưng lần sau nhấn thì vẫn cứ trơ trơ in "processing...". Đó là vì signal mask không được phục hồi (không lưu lấy gì mà phục hồi!!!). Stack context được phục hồi, luồng chương trình quay về nơi setjmp, nhưng chương trình vẫn cho rằng nó đang xư lý tín hiệu SIGINT, những tín hiệu SIGINT được gởi tới sau đó sẽ bị bỏ qua.

Thay vì gọi sigsetjmp(buf, 1), nếu bạn gọi sigsetjmp(buf, 0), cũng sẽ nhận lấy hậu quả tương tự, vì signal mask không được lưu lại để phục hồi.

Software exception in C++ and Java

Cơ chế exception mà C++ và Java cung cấp thực chất là một phiên bản high-level (dựa trên ý tưởng) của hàm setjmplongjmp của C. Ta có thể hình dung:

rc = setjmp(env) giống try
if (rc == 1) {} giống catch (Exception1 &e) {}
if (rc == 2) {} giống catch (Exception2 &e) {}
longjmp giống throw.

Comments

Added by hoangtran over 2 years ago

Đụng hàng :-D

Added by bronzeboyvn over 2 years ago

đụng hàng của ai ở đây anh ? Em nhớ bài của anh đâu có đề cập tới chuyện signal handler, đúng không ?