2011-12-25 40 views
5

Tôi muốn sử dụng setjmp()/longjmp() để triển khai hệ thống coroutine. Sau đó, tôi quyết định mã một tập tin .c nhỏ để kiểm tra nó. Trong MinGW, không sao đâu; Tôi có kết quả tôi muốn. Nhưng khi tôi biên dịch nó trong MSVC++, chương trình bị treo: "access violation"Tại sao xảy ra sự cố setjmp/longjmp trên MSVC khi nó không có trong MinGW?

#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; 
    } 
+0

Tôi đã đọc một bài viết một thời gian trước về cách setjmp/longjmp thực sự làm hỏng trình tối ưu hóa và khi họ thoát khỏi chương trình họ đang làm việc để tăng tốc đáng kể. – Bill

+1

Q: Phiên bản nào của MSVC++? Q: Chính xác nơi nó đã sụp đổ? Bạn đã thử chạy trong trình gỡ lỗi Visual Studio? Q: Có bất kỳ cảnh báo hoặc lỗi nào trong bản dịch MSVC++ của bạn không? – paulsm4

+0

Phiên bản MSVC của tôi là năm 2008. sự cố trong longjmp. Vâng. không có lỗi và cảnh báo khi biên dịch. đó là một lỗi thời gian chạy – TZW

Trả lời

7

Các trang người đàn ông cho setjmp nói:

setjmp() tiết kiệm stack bối cảnh/môi trường trong env để sử dụng sau bởi longjmp(). Ngữ cảnh ngăn xếp sẽ bị vô hiệu nếu hàm được gọi là trả về setjmp().

Trong triển khai đơn giản, bạn có thể giả sử rằng jmp_buf chứa địa chỉ để đặt lại con trỏ ngăn xếp và địa chỉ để chuyển đến. Ngay sau khi bạn quay trở lại từ hàm đã lưu jmp_buf, khung ngăn xếp được trỏ đến bởi jmp_buf không còn hợp lệ và có thể bị hỏng ngay lập tức.

Hay nói cách khác, bạn chỉ có thể dựa vào longjmp để hoạt động như một loại siêu câu hỏi return - không bao giờ đi sâu hơn.

Tôi nghĩ lý do này phù hợp với bạn trong việc kết nối (và đối với tôi trên Linux) là thực hiện cụ thể và có thể làm giảm sự may mắn. Có một cách khác - bạn đã đọc bài tiểu luận Simon Tatham's evil coroutine macros chưa?

+0

ok ~ i kiểm tra wiki của setjmp: "Tương tự, C99 không yêu cầu longjmp bảo vệ khung ngăn xếp hiện tại.", Nhưng MSVC không hỗ trợ tốt c99 --- vậy, đó có phải là lý do cho chương trình của tôi bị lỗi không? – TZW

+0

@TZW: Không; rắc rối là bạn trở về từ hàm gọi là 'setjmp()' trước khi gọi 'longjmp()'. –

+0

@ Jonathan Leffler: vậy bạn có thể nói với tôi là có cách đơn giản để kiểm tra ngữ cảnh của jmp_buf là hợp lệ hay không, thưa bạn? – TZW

1

Bạn không thể sử dụng setjmp/longjmp cho coroutines. Sử dụng makecontext/swapcontext trên POSIX hoặc các sợi (CreateFiber, vv) trên các cửa sổ.

4

Vì bạn đang gọi hành vi chưa được xác định, nên một trình biên dịch bị lỗi và một trình biên dịch khác xuất hiện để hoạt động. Cả hai đều đúng - đó là vẻ đẹp của hành vi không xác định.

Sự cố là ngữ cảnh đã lưu - jmp_buf - chỉ còn hiệu lực miễn là chức năng được gọi là setjmp() để đặt nó chưa được trả lại.

Tiêu chuẩn C99 (không còn là tiêu chuẩn hiện hành, nhưng từ ngữ này dường như không có thay đổi đáng kể) nói:

§7.13.2.1 Các longjmp chức năng

Chức năng longjmp phục hồi môi trường lưu theo yêu cầu gần đây nhất của macro setjmp trong cùng một lời gọi chương trình với đối số jmp_buf tương ứng. Nếu chưa có gọi như vậy, hoặc nếu chức năng chứa invocation của setjmp vĩ mô đã chấm dứt thực hiện 208) trong thời gian chuyển tiếp, hoặc nếu gọi của setjmp vĩ mô là trong phạm vi của một định danh với variably sửa đổi loại và thực hiện đã để lại phạm vi đó trong thời gian tạm thời, hành vi là không xác định.

208) Ví dụ, bằng cách thực hiện một tuyên bố return hoặc vì một longjmp cuộc gọi đã gây ra một chuyển đến một invocation setjmp trong một chức năng trước đó trong tập hợp các cuộc gọi lồng nhau.

Mã của bạn đang thoát khỏi action_1() gần như ngay lập tức, hiển thị jmp_buf được lưu bởi setjmp() vô giá trị.


Tôi tạo ra cuộc biểu tình này ít setjmp()longjmp() một vài năm trước đây. Nó có thể giúp bạn.

/* 
@(#)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); 
} 
Các vấn đề liên quan