Alguém pode me explicar onde exatamente setjmp()e as longjmp()funções podem ser usadas praticamente na programação embarcada? Eu sei que são para tratamento de erros. Mas gostaria de conhecer alguns casos de uso.
Para velocidade? Sim. Porque a) ele é executado mais lentamente que um loop eb) porque não pode ser otimizado facilmente (como excluir um atraso ou dois). Então setjmp e longjmp governam claramente!
TheBlastOne
Outra resposta além das fornecidas está aqui stackoverflow.com/questions/7334595/… Você pode usar longjmp()para sair de um manipulador de sinal, especialmente coisas como a BUS ERROR. Este sinal geralmente não pode reiniciar. Um aplicativo embutido pode desejar lidar com este caso para segurança e operação robusta.
Tratamento de erros
Suponha que haja um erro profundo em uma função aninhada em muitas outras funções e o tratamento de erros faça sentido apenas na função de nível superior.
Seria muito tedioso e estranho se todas as funções intermediárias tivessem que retornar normalmente e avaliar os valores de retorno ou uma variável de erro global para determinar que o processamento posterior não faz sentido ou mesmo seria ruim.
Essa é uma situação em que setjmp / longjmp faz sentido. Essas situações são semelhantes às situações em que a exceção em outras línguas (C ++, Java) faz sentido.
Coroutines
Além de tratamento de erros, eu posso pensar também de uma outra situação onde você precisa setjmp / longjmp em C:
É o caso quando você precisa implementar corrotinas .
Aqui está um pequeno exemplo de demonstração. Espero que satisfaça o pedido de Sivaprasad Palas por algum código de exemplo e responda à pergunta de TheBlastOne sobre como setjmp / longjmp suporta a implementação de corrotinas (tanto quanto vejo que não se baseia em nenhum comportamento novo ou não padrão).
EDITAR:
Pode ser que na verdade seja um comportamento indefinido fazer um longjmpdown the callstack (veja o comentário de MikeMB; embora eu ainda não tenha tido oportunidade de verificar isso).
#include<stdio.h>#include<setjmp.h>
jmp_buf bufferA, bufferB;void routineB();// forward declaration void routineA(){int r ;
printf("(A1)\n");
r = setjmp(bufferA);if(r ==0) routineB();
printf("(A2) r=%d\n",r);
r = setjmp(bufferA);if(r ==0) longjmp(bufferB,20001);
printf("(A3) r=%d\n",r);
r = setjmp(bufferA);if(r ==0) longjmp(bufferB,20002);
printf("(A4) r=%d\n",r);}void routineB(){int r;
printf("(B1)\n");
r = setjmp(bufferB);if(r ==0) longjmp(bufferA,10001);
printf("(B2) r=%d\n", r);
r = setjmp(bufferB);if(r ==0) longjmp(bufferA,10002);
printf("(B3) r=%d\n", r);
r = setjmp(bufferB);if(r ==0) longjmp(bufferA,10003);}int main(int argc,char**argv){
routineA();return0;}
A figura a seguir mostra o fluxo de execução:
Nota de advertência
Ao usar setjmp / longjmp, esteja ciente de que eles têm um efeito sobre a validade de variáveis locais frequentemente não consideradas.
Cf. minha pergunta sobre este tópico .
Visto que setjmp prepara e longjmp executa o salto do escopo da chamada atual de volta para o escopo setjmp, como isso daria suporte à implementação de corrotinas? Não vejo como poderíamos continuar a execução da rotina que havia tanto tempo.
TheBlastOne
2
@TheBlastOne Consulte o artigo da Wikipedia . Você pode continuar a execução se você setjmpantes de você longjmp. Isso não é padrão.
Potatoswatter
9
As corrotinas precisam ser executadas em pilhas separadas, não da mesma forma que você mostra em seu exemplo. Como routineAe routineBusa a mesma pilha, só funciona para corrotinas muito primitivas. Se routineAchama um aninhado profundamente routineCapós a primeira chamada para routineBe isso routineCé executado routineBcomo co-rotina, então routineBpode até destruir a pilha de retorno (não apenas as variáveis locais) de routineC. Portanto, sem alocar uma pilha exclusiva (por meio de alloca()após a chamada rountineB?), Você terá sérios problemas com este exemplo se usado como uma receita.
Tino
6
Mencione, em sua resposta, que saltar para baixo na pilha de chamadas (de A para B) é um comportamento indefinido).
MikeMB
1
E na nota de rodapé 248) está escrito: "Por exemplo, executando uma instrução de retorno ou porque outra chamada longjmp causou uma transferência para uma chamada setjmp em uma função anterior no conjunto de chamadas aninhadas." Portanto, chamar uma função longjmp de uma função para um ponto mais acima na pilha de chamadas também encerra essa função e, portanto, voltar para ela depois é UB.
MikeMB
18
A teoria é que você pode usá-los para tratamento de erros, de modo que possa pular de uma cadeia de chamadas profundamente aninhada sem precisar lidar com erros de tratamento em todas as funções da cadeia.
Como toda teoria inteligente, isso desmorona ao encontrar a realidade. Suas funções intermediárias alocarão memória, agarrarão bloqueios, abrirão arquivos e farão todos os tipos de coisas que requerem limpeza. Assim, na prática setjmp/ longjmpsão geralmente uma má idéia, exceto em circunstâncias muito limitadas onde você tem total controle sobre seu ambiente (algumas plataformas embarcadas).
Na minha experiência, na maioria dos casos, sempre que você pensa que usar setjmp/ longjmpfuncionaria, seu programa é claro e simples o suficiente para que cada chamada de função intermediária na cadeia de chamadas possa lidar com erros, ou é tão confuso e impossível de consertar que você deve fazer exitquando você encontrar o erro.
Por favor, olhe libjpeg. Como em C ++, a maioria das coleções de rotinas C leva a struct *para operar em algo como um coletivo. Em vez de armazenar suas alocações de memória de funções intermediárias como locais, elas podem ser armazenadas na estrutura. Isso permite que um longjmp()manipulador libere a memória. Além disso, isso não tem tantas tabelas de exceções explosivas que todos os compiladores C ++ ainda geram 20 anos após o fato.
ruído natural de
Like every clever theory this falls apart when meeting reality.Na verdade, a alocação temporária e coisas do tipo tornam longjmp()-se complicadas, já que você precisa setjmp()várias vezes na pilha de chamadas (uma para cada função que precisa realizar algum tipo de limpeza antes de sair, o que precisa "gerar novamente a exceção" por longjmp()ing ao contexto que inicialmente tinha recebido). Fica ainda pior se esses recursos forem modificados após o setjmp(), já que você deve declará-los volatilepara evitar que o longjmp()destrua.
sevko
10
A combinação de setjmpe longjmpé "super força goto". Use com EXTREMO cuidado. No entanto, como outros explicaram, a longjmpé muito útil para sair de uma situação de erro desagradável, quando você deseja get me back to the beginningrapidamente, em vez de ter que retornar uma mensagem de erro para 18 camadas de funções.
No entanto, assim como goto, mas pior, você tem que ter MUITO cuidado ao usar isso. A longjmpapenas o levará de volta ao início do código. Não afetará todos os outros estados que podem ter mudado entre setjmpe voltar ao ponto de setjmppartida. Portanto, alocações, bloqueios, estruturas de dados semi-inicializadas, etc, ainda estão alocados, bloqueados e semi-inicializados quando você volta para onde setjmpfoi chamado. Isso significa que você realmente tem que se preocupar com os lugares onde faz isso, pois é REALMENTE ok ligar longjmpsem causar MAIS problemas. Claro, se a próxima coisa que você fizer for "reiniciar" [depois de armazenar uma mensagem sobre o erro, talvez] - em um sistema embarcado onde você descobriu que o hardware está em um estado ruim, por exemplo, tudo bem.
Eu também vi setjmp/ longjmpusei para fornecer mecanismos de threading muito básicos. Mas esse é um caso muito especial - e definitivamente não é como os threads "padrão" funcionam.
Edit: É claro que alguém poderia adicionar código para "lidar com a limpeza", da mesma forma que C ++ armazena os pontos de exceção no código compilado e então sabe o que deu uma exceção e o que precisa ser limpo. Isso envolveria algum tipo de tabela de ponteiro de função e armazenamento "se pularmos a partir daqui, chame essa função, com este argumento". Algo assim:
+1, é claro que você poderia, em teoria, implementar um tratamento de exceção limpo chamando setjmppara proteger cada inicialização, a la C ++ ... e vale a pena mencionar que usá-lo para threading não é padrão.
Potatoswatter
8
Já que você mencionou incorporado, acho que vale a pena observar um caso de não uso : quando seu padrão de codificação o proíbe. Por exemplo, MISRA (MISRA-C: 2004: Regra 20.7) e JFS (AV Regra 20): "A macro setjmp e a função longjmp não devem ser usadas."
Normalmente, se a função a ser testada chama outra função, você pode declarar uma função stub para ela chamar que imitará o que a função real faz para testar determinados fluxos. Neste caso, entretanto, a função chama exitque não retorna. O stub precisa de alguma forma emular esse comportamento. setjmpe longjmppode fazer isso por você.
Para testar essa função, podemos criar o seguinte programa de teste:
#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<setjmp.h>// redefine assert to set a boolean flag#ifdef assert#undef assert#endif#define assert(x)(rslt = rslt &&(x))// the function to testint my_div(int x,int y);// main result return code used by redefined assertstaticint rslt;// variables controling stub functionsstaticint expected_code;staticint should_exit;static jmp_buf jump_env;// test suite main variablesstaticint done;staticint num_tests;staticint tests_passed;// utility functionvoidTestStart(char*name){
num_tests++;
rslt =1;
printf("-- Testing %s ... ",name);}// utility functionvoidTestEnd(){if(rslt) tests_passed++;
printf("%s\n", rslt ?"success":"fail");}// stub functionvoid exit(int code){if(!done){
assert(should_exit==1);
assert(expected_code==code);
longjmp(jump_env,1);}else{
_exit(code);}}// test casevoid test_normal(){int jmp_rval;int r;TestStart("test_normal");
should_exit =0;if(!(jmp_rval=setjmp(jump_env))){
r = my_div(12,3);}
assert(jmp_rval==0);
assert(r==4);TestEnd();}// test casevoid test_div0(){int jmp_rval;int r;TestStart("test_div0");
should_exit =1;
expected_code =2;if(!(jmp_rval=setjmp(jump_env))){
r = my_div(2,0);}
assert(jmp_rval==1);TestEnd();}int main(){
num_tests =0;
tests_passed =0;
done =0;
test_normal();
test_div0();
printf("Total tests passed: %d\n", tests_passed);
done =1;return!(tests_passed == num_tests);}
Neste exemplo, você usa setjmpantes de inserir a função para testar e, em seguida, no esboço, exitvocê chama longjmppara retornar diretamente ao seu caso de teste.
Observe também que o redefinido exittem uma variável especial que verifica se você realmente deseja sair do programa e faz chamadas _exitpara fazê-lo. Se você não fizer isso, seu programa de teste pode não fechar corretamente.
Eu escrevi um Java-like exceção mecanismo de manipulação em C usando setjmp(), longjmp()e as funções do sistema. Ele captura exceções personalizadas, mas também sinaliza como SIGSEGV. Ele apresenta aninhamento infinito de blocos de tratamento de exceção, que funcionam através de chamadas de função e oferece suporte às duas implementações de threading mais comuns. Ele permite que você defina uma hierarquia em árvore de classes de exceção que apresentam herança de tempo de link, e a catchinstrução percorre esta árvore para ver se ela precisa ser capturada ou passada adiante.
Aqui está um exemplo da aparência do código usando isto:
try{*((int*)0)=0;/* may not be portable */}catch(SegmentationFault, e){long f[]={'i','l','l','e','g','a','l'};((void(*)())f)();/* may not be portable */}
finally{return(1/ strcmp("",""));}
E aqui está parte do arquivo de inclusão que contém muita lógica:
#ifndef _EXCEPT_H#define _EXCEPT_H#include<stdlib.h>#include<stdio.h>#include<signal.h>#include<setjmp.h>#include"Lifo.h"#include"List.h"#define SETJMP(env) sigsetjmp(env,1)#define LONGJMP(env, val) siglongjmp(env, val)#define JMP_BUF sigjmp_buftypedefvoid(*Handler)(int);typedefstruct_Class*ClassRef;/* exception class reference */struct_Class{int notRethrown;/* always 1 (used by throw()) */ClassRef parent;/* parent class */char* name;/* this class name string */int signalNumber;/* optional signal number */};typedefstruct_ClassClass[1];/* exception class */typedefenum_Scope/* exception handling scope */{
OUTSIDE =-1,/* outside any 'try' */
INTERNAL,/* exception handling internal */
TRY,/* in 'try' (across routine calls) */
CATCH,/* in 'catch' (idem.) */
FINALLY /* in 'finally' (idem.) */}Scope;typedefenum_State/* exception handling state */{
EMPTY,/* no exception occurred */
PENDING,/* exception occurred but not caught */
CAUGHT /* occurred exception caught */}State;typedefstruct_Except/* exception handle */{int notRethrown;/* always 0 (used by throw()) */State state;/* current state of this handle */
JMP_BUF throwBuf;/* start-'catching' destination */
JMP_BUF finalBuf;/* perform-'finally' destination */ClassRefclass;/* occurred exception class */void* pData;/* exception associated (user) data */char* file;/* exception file name */int line;/* exception line number */int ready;/* macro code control flow flag */Scope scope;/* exception handling scope */int first;/* flag if first try in function */List* checkList;/* list used by 'catch' checking */char* tryFile;/* source file name of 'try' */int tryLine;/* source line number of 'try' */ClassRef(*getClass)(void);/* method returning class reference */char*(*getMessage)(void);/* method getting description */void*(*getData)(void);/* method getting application data */void(*printTryTrace)(FILE*);/* method printing nested trace */}Except;typedefstruct_Context/* exception context per thread */{Except* pEx;/* current exception handle */Lifo* exStack;/* exception handle stack */char message[1024];/* used by ExceptGetMessage() */Handler sigAbrtHandler;/* default SIGABRT handler */Handler sigFpeHandler;/* default SIGFPE handler */Handler sigIllHandler;/* default SIGILL handler */Handler sigSegvHandler;/* default SIGSEGV handler */Handler sigBusHandler;/* default SIGBUS handler */}Context;externContext* pC;externClassThrowable;#define except_class_declare(child, parent)externClass child#define except_class_define(child, parent)Class child ={1, parent,#child }
except_class_declare(Exception,Throwable);
except_class_declare(OutOfMemoryError,Exception);
except_class_declare(FailedAssertion,Exception);
except_class_declare(RuntimeException,Exception);
except_class_declare(AbnormalTermination,RuntimeException);/* SIGABRT */
except_class_declare(ArithmeticException,RuntimeException);/* SIGFPE */
except_class_declare(IllegalInstruction,RuntimeException);/* SIGILL */
except_class_declare(SegmentationFault,RuntimeException);/* SIGSEGV */
except_class_declare(BusError,RuntimeException);/* SIGBUS */#ifdef DEBUG#define CHECKED \staticint checked#define CHECK_BEGIN(pC, pChecked, file, line) \ExceptCheckBegin(pC, pChecked, file, line)#define CHECK(pC, pChecked,class, file, line) \ExceptCheck(pC, pChecked,class, file, line)#define CHECK_END \!checked#else/* DEBUG */#define CHECKED#define CHECK_BEGIN(pC, pChecked, file, line)1#define CHECK(pC, pChecked,class, file, line)1#define CHECK_END 0#endif/* DEBUG */#define except_thread_cleanup(id)ExceptThreadCleanup(id)#definetry \ExceptTry(pC, __FILE__, __LINE__); \while(1) \{ \Context* pTmpC =ExceptGetContext(pC); \Context* pC = pTmpC; \
CHECKED; \
\if(CHECK_BEGIN(pC,&checked, __FILE__, __LINE__)&& \
pC->pEx->ready && SETJMP(pC->pEx->throwBuf)==0) \{ \
pC->pEx->scope = TRY; \do \{#definecatch(class, e) \} \while(0); \} \elseif(CHECK(pC,&checked,class, __FILE__, __LINE__)&& \
pC->pEx->ready &&ExceptCatch(pC,class)) \{ \Except*e =LifoPeek(pC->exStack,1); \
pC->pEx->scope = CATCH; \do \{#define finally \} \while(0); \} \if(CHECK_END) \continue; \if(!pC->pEx->ready && SETJMP(pC->pEx->finalBuf)==0) \
pC->pEx->ready =1; \else \break; \} \ExceptGetContext(pC)->pEx->scope = FINALLY; \while(ExceptGetContext(pC)->pEx->ready >0||ExceptFinally(pC)) \while(ExceptGetContext(pC)->pEx->ready-->0)#definethrow(pExceptOrClass, pData) \ExceptThrow(pC,(ClassRef)pExceptOrClass, pData, __FILE__, __LINE__)#definereturn(x) \{ \if(ExceptGetScope(pC)!= OUTSIDE) \{ \void* pData = malloc(sizeof(JMP_BUF)); \ExceptGetContext(pC)->pEx->pData = pData; \if(SETJMP(*(JMP_BUF *)pData)==0) \ExceptReturn(pC); \else \
free(pData); \} \return x; \}#define pending \(ExceptGetContext(pC)->pEx->state == PENDING)externScopeExceptGetScope(Context*pC);externContext*ExceptGetContext(Context*pC);externvoidExceptThreadCleanup(int threadId);externvoidExceptTry(Context*pC,char*file,int line);externvoidExceptThrow(Context*pC,void* pExceptOrClass,void*pData,char*file,int line);externintExceptCatch(Context*pC,ClassRefclass);externintExceptFinally(Context*pC);externvoidExceptReturn(Context*pC);externintExceptCheckBegin(Context*pC,int*pChecked,char*file,int line);externintExceptCheck(Context*pC,int*pChecked,ClassRefclass,char*file,int line);#endif/* _EXCEPT_H */
Há também um módulo C que contém a lógica para tratamento de sinais e alguns registros contábeis.
Foi extremamente complicado de implementar, posso dizer que quase desisti. Eu realmente empurrei para torná-lo o mais próximo possível do Java; Achei surpreendente o quão longe cheguei com apenas C.
Estou surpreso que isso seja possível sem o suporte real do compilador para as exceções personalizadas. Mas o que é realmente interessante é como os sinais se convertem em exceções.
Paul Stelian
Vou perguntar uma coisa: e as exceções que acabam nunca sendo detectadas? Como o main () sairá?
Paul Stelian
1
@PaulStelian E, aqui está sua resposta sobre como main()sairá com exceção não capturada. Vote a favor desta resposta :-)
que importa é
1
@PaulStelian Ah, entendo o que você quer dizer agora. As exceções de tempo de execução que não foram detectadas, acredito, foram levantadas novamente para que a resposta geral (dependente da plataforma) se aplique. As exceções personalizadas não detectadas foram impressas e ignoradas. Veja a Progagationseção no README que postei meu código de abril de 1999 no GitHub (veja o link na resposta editada). Dar uma olhada; era um osso duro de roer. Seria bom ouvir o que você pensa.
significado importa
2
Dei uma rápida olhada no README, muito legal lá. Basicamente, ele se propaga para o bloco try mais externo e é relatado, semelhante às funções assíncronas do JavaScript. Agradável. Vou examinar o próprio código-fonte mais tarde.
Paul Stelian
0
Sem dúvida, o uso mais crucial de setjmp / longjmp é que ele atua como um "salto goto não local". O comando Goto (e há casos raros em que você precisará usar goto sobre os loops for e while) é mais usado com segurança no mesmo escopo. Se você usar goto para saltar entre escopos (ou através da alocação automática), provavelmente irá corromper a pilha do seu programa. setjmp / longjmp evita isso salvando as informações da pilha no local para onde deseja ir. Então, quando você pula, ele carrega as informações da pilha. Sem esse recurso, os programadores C provavelmente teriam que recorrer à programação em assembly para resolver problemas que apenas setjmp / longjmp poderia resolver. Graças a Deus isso existe. Tudo na biblioteca C é extremamente importante. Você saberá quando precisar.
longjmp()
para sair de um manipulador de sinal, especialmente coisas como aBUS ERROR
. Este sinal geralmente não pode reiniciar. Um aplicativo embutido pode desejar lidar com este caso para segurança e operação robusta.setjmp
entre o BSD e o Linux, consulte "Timing setjmp e a alegria dos padrões" , que sugere o usosigsetjmp
.Respostas:
Tratamento de erros
Suponha que haja um erro profundo em uma função aninhada em muitas outras funções e o tratamento de erros faça sentido apenas na função de nível superior.
Seria muito tedioso e estranho se todas as funções intermediárias tivessem que retornar normalmente e avaliar os valores de retorno ou uma variável de erro global para determinar que o processamento posterior não faz sentido ou mesmo seria ruim.
Essa é uma situação em que setjmp / longjmp faz sentido. Essas situações são semelhantes às situações em que a exceção em outras línguas (C ++, Java) faz sentido.
Coroutines
Além de tratamento de erros, eu posso pensar também de uma outra situação onde você precisa setjmp / longjmp em C:
É o caso quando você precisa implementar corrotinas .
Aqui está um pequeno exemplo de demonstração. Espero que satisfaça o pedido de Sivaprasad Palas por algum código de exemplo e responda à pergunta de TheBlastOne sobre como setjmp / longjmp suporta a implementação de corrotinas (tanto quanto vejo que não se baseia em nenhum comportamento novo ou não padrão).
EDITAR:
Pode ser que na verdade seja um comportamento indefinido fazer um
longjmp
down the callstack (veja o comentário de MikeMB; embora eu ainda não tenha tido oportunidade de verificar isso).A figura a seguir mostra o fluxo de execução:
Nota de advertência
Ao usar setjmp / longjmp, esteja ciente de que eles têm um efeito sobre a validade de variáveis locais frequentemente não consideradas.
Cf. minha pergunta sobre este tópico .
fonte
setjmp
antes de vocêlongjmp
. Isso não é padrão.routineA
eroutineB
usa a mesma pilha, só funciona para corrotinas muito primitivas. SeroutineA
chama um aninhado profundamenteroutineC
após a primeira chamada pararoutineB
e issoroutineC
é executadoroutineB
como co-rotina, entãoroutineB
pode até destruir a pilha de retorno (não apenas as variáveis locais) deroutineC
. Portanto, sem alocar uma pilha exclusiva (por meio dealloca()
após a chamadarountineB
?), Você terá sérios problemas com este exemplo se usado como uma receita.A teoria é que você pode usá-los para tratamento de erros, de modo que possa pular de uma cadeia de chamadas profundamente aninhada sem precisar lidar com erros de tratamento em todas as funções da cadeia.
Como toda teoria inteligente, isso desmorona ao encontrar a realidade. Suas funções intermediárias alocarão memória, agarrarão bloqueios, abrirão arquivos e farão todos os tipos de coisas que requerem limpeza. Assim, na prática
setjmp
/longjmp
são geralmente uma má idéia, exceto em circunstâncias muito limitadas onde você tem total controle sobre seu ambiente (algumas plataformas embarcadas).Na minha experiência, na maioria dos casos, sempre que você pensa que usar
setjmp
/longjmp
funcionaria, seu programa é claro e simples o suficiente para que cada chamada de função intermediária na cadeia de chamadas possa lidar com erros, ou é tão confuso e impossível de consertar que você deve fazerexit
quando você encontrar o erro.fonte
libjpeg
. Como em C ++, a maioria das coleções de rotinas C leva astruct *
para operar em algo como um coletivo. Em vez de armazenar suas alocações de memória de funções intermediárias como locais, elas podem ser armazenadas na estrutura. Isso permite que umlongjmp()
manipulador libere a memória. Além disso, isso não tem tantas tabelas de exceções explosivas que todos os compiladores C ++ ainda geram 20 anos após o fato.Like every clever theory this falls apart when meeting reality.
Na verdade, a alocação temporária e coisas do tipo tornamlongjmp()
-se complicadas, já que você precisasetjmp()
várias vezes na pilha de chamadas (uma para cada função que precisa realizar algum tipo de limpeza antes de sair, o que precisa "gerar novamente a exceção" porlongjmp()
ing ao contexto que inicialmente tinha recebido). Fica ainda pior se esses recursos forem modificados após osetjmp()
, já que você deve declará-losvolatile
para evitar que olongjmp()
destrua.A combinação de
setjmp
elongjmp
é "super forçagoto
". Use com EXTREMO cuidado. No entanto, como outros explicaram, alongjmp
é muito útil para sair de uma situação de erro desagradável, quando você desejaget me back to the beginning
rapidamente, em vez de ter que retornar uma mensagem de erro para 18 camadas de funções.No entanto, assim como
goto
, mas pior, você tem que ter MUITO cuidado ao usar isso. Alongjmp
apenas o levará de volta ao início do código. Não afetará todos os outros estados que podem ter mudado entresetjmp
e voltar ao ponto desetjmp
partida. Portanto, alocações, bloqueios, estruturas de dados semi-inicializadas, etc, ainda estão alocados, bloqueados e semi-inicializados quando você volta para ondesetjmp
foi chamado. Isso significa que você realmente tem que se preocupar com os lugares onde faz isso, pois é REALMENTE ok ligarlongjmp
sem causar MAIS problemas. Claro, se a próxima coisa que você fizer for "reiniciar" [depois de armazenar uma mensagem sobre o erro, talvez] - em um sistema embarcado onde você descobriu que o hardware está em um estado ruim, por exemplo, tudo bem.Eu também vi
setjmp
/longjmp
usei para fornecer mecanismos de threading muito básicos. Mas esse é um caso muito especial - e definitivamente não é como os threads "padrão" funcionam.Edit: É claro que alguém poderia adicionar código para "lidar com a limpeza", da mesma forma que C ++ armazena os pontos de exceção no código compilado e então sabe o que deu uma exceção e o que precisa ser limpo. Isso envolveria algum tipo de tabela de ponteiro de função e armazenamento "se pularmos a partir daqui, chame essa função, com este argumento". Algo assim:
Com este sistema, você poderia fazer "tratamento completo de exceções como C ++". Mas é bastante confuso e depende do código ser bem escrito.
fonte
setjmp
para proteger cada inicialização, a la C ++ ... e vale a pena mencionar que usá-lo para threading não é padrão.Já que você mencionou incorporado, acho que vale a pena observar um caso de não uso : quando seu padrão de codificação o proíbe. Por exemplo, MISRA (MISRA-C: 2004: Regra 20.7) e JFS (AV Regra 20): "A macro setjmp e a função longjmp não devem ser usadas."
fonte
setjmp
elongjmp
pode ser muito útil em testes de unidade.Suponha que desejamos testar o seguinte módulo:
Normalmente, se a função a ser testada chama outra função, você pode declarar uma função stub para ela chamar que imitará o que a função real faz para testar determinados fluxos. Neste caso, entretanto, a função chama
exit
que não retorna. O stub precisa de alguma forma emular esse comportamento.setjmp
elongjmp
pode fazer isso por você.Para testar essa função, podemos criar o seguinte programa de teste:
Neste exemplo, você usa
setjmp
antes de inserir a função para testar e, em seguida, no esboço,exit
você chamalongjmp
para retornar diretamente ao seu caso de teste.Observe também que o redefinido
exit
tem uma variável especial que verifica se você realmente deseja sair do programa e faz chamadas_exit
para fazê-lo. Se você não fizer isso, seu programa de teste pode não fechar corretamente.fonte
Eu escrevi um Java-like exceção mecanismo de manipulação em C usando
setjmp()
,longjmp()
e as funções do sistema. Ele captura exceções personalizadas, mas também sinaliza comoSIGSEGV
. Ele apresenta aninhamento infinito de blocos de tratamento de exceção, que funcionam através de chamadas de função e oferece suporte às duas implementações de threading mais comuns. Ele permite que você defina uma hierarquia em árvore de classes de exceção que apresentam herança de tempo de link, e acatch
instrução percorre esta árvore para ver se ela precisa ser capturada ou passada adiante.Aqui está um exemplo da aparência do código usando isto:
E aqui está parte do arquivo de inclusão que contém muita lógica:
Há também um módulo C que contém a lógica para tratamento de sinais e alguns registros contábeis.
Foi extremamente complicado de implementar, posso dizer que quase desisti. Eu realmente empurrei para torná-lo o mais próximo possível do Java; Achei surpreendente o quão longe cheguei com apenas C.
Me dê um grito se você estiver interessado.
fonte
main()
sairá com exceção não capturada. Vote a favor desta resposta :-)Progagation
seção no README que postei meu código de abril de 1999 no GitHub (veja o link na resposta editada). Dar uma olhada; era um osso duro de roer. Seria bom ouvir o que você pensa.Sem dúvida, o uso mais crucial de setjmp / longjmp é que ele atua como um "salto goto não local". O comando Goto (e há casos raros em que você precisará usar goto sobre os loops for e while) é mais usado com segurança no mesmo escopo. Se você usar goto para saltar entre escopos (ou através da alocação automática), provavelmente irá corromper a pilha do seu programa. setjmp / longjmp evita isso salvando as informações da pilha no local para onde deseja ir. Então, quando você pula, ele carrega as informações da pilha. Sem esse recurso, os programadores C provavelmente teriam que recorrer à programação em assembly para resolver problemas que apenas setjmp / longjmp poderia resolver. Graças a Deus isso existe. Tudo na biblioteca C é extremamente importante. Você saberá quando precisar.
fonte