next up previous contents index
Next: B. Sources Up: A. Operating System Concepts Previous: A.4 Pipes   Contents   Index

Subsections


A.5 Longjmp

A.5.1 Concept

Longjump is a programming concept used in C to manipulate the flow of the execution sequence. Roughly it can be described as setting a mark by saving the current state of the processor of a program - in this case a thread or a process. After the state has been saved it can be restored at a further point in the sequence. To understand this concept it is essential to know that each process or thread has a stack for subroutine calls. Whenever the processor executes a subroutine call, it saves the program counter to the stack together with the parameters for the procedure, local variables are also put on the stack. At the end of a subroutine, the processor reads the saved program counter and discards all data put on the stack after this entry. The program continues with the next statement. The Longjump concept uses the fact that the stack contains this information.

When calling the function setjmp() the current processor's registers including the stack pointer is saved with the mark. Therefore the mark can be used to determine at which position the execution continues after setjmp(). When at any following point in the program execution the longjmp() function is used with the mark as an argument, the processor's registers including stack pointer is set to the position where it was before. The program will then continue as if the setjmp() call returned again. Only the return value of this call will be different. Therefore the Longjump concept is very similar to using "goto". However as opposed to longjmp(), "goto" will not restore any of the program's state. Therefore longjmp() can be used for jumps without corrupting the call stack. The drawback of using Longjump is that one has to be aware of the underlying systems technology to fully understand the concept, and of course that the program becomes more fuzzy as non standard programming concepts are used. A typical application for Longjumps is exception handling.

A.5.2 Example Longjump

In this example a single thread or process is considered. At a certain point of the execution sequence the function sub1() is called. As described above a new element containing information about the calling routine is put on the stack. During the execution of sub1(), setjmp() is called to mark the current position of the execution sequence and to save the state information. Setjmp() returns 0 as it is called to mark the current position. Therefore the execution continues in the normal branch of the execution sequence. Sub1() then calls sub2() which will call sub3(). Within sub3() the longjmp() system call is issued. Longjmp() requires two parameters. The first is the mark, used to define the position to jump to. The second parameter is the return value that the imaginary setjmp() call will return when being jumped to using the longjmp() call.

The execution will continue with the instruction following the setjmp call. That will usually be a decision based on the return value of setjmp to be able to differentiate whether setjmp() was just initialised or jumped to using longjmp().

The code for the example above that illustrates the use of Longjump and also resemble the petri net of figure A.8 is:

void sub3 (jmp_buf buffer)

     longjmp (buffer,1); /* Jump to the point in sequence 

                          * which buffer marks and have

                          * setjmp return 1 */ 

  

void sub2(jmp_buf buffer)

     sub3(buffer); /* call another sub function to fill up the 

                    * stack*/ 

  

int sub1()

     jmp_buf buffer; // we can save a stack point in here 

     if (setjump(buffer)) 

     { 

              // when setjmp is jumped to we will end up here 

              exit(0); 

     } 

     else

     { 

              /* when setjmp is called to save the stack 

               * pointer we will enter this branch */ 

              sub2(buffer); // call any sub function 

      }

} 

Figure A.8: longjmp behaviour (View PDF)
longjmp_PN.gif

Figure A.9: Stack content before calling longjmp() in the example above (View PDF)
Longjmp_Stack_example1.gif

A.5.3 Common application

One of the major uses for the Longjump concept is exception handling Usually when using Longjump for exception handling the program will save a mark using setjmp() right at the beginning of the execution sequence. It is followed by a decision that will branch to the normal program execution sequence based on the return value of setjmp(). A return value of 0 indicates that setjmp() was initially called to save a mark and execution is therefore continued normally. However, if the return value of setjmp() indicates that setjmp() was jumped to then it usually includes information about the type of the exception that occurred. The corresponding exception handling routine can therefore be started. The advantage of such error handling is its centralization and that it can be used from any point in the program execution sequence as the mark was saved at the lowest point of the subroutine execution hierarchy. Implementing exception handling this way very much resembles exception handling in higher level languages like Java and C++. The difference is that in C++ and Java exceptions can be caught in any procedure along the hierarchy of procedure calls. To implement such behaviour via Longjump multiple marks and additional logic to decide to which mark to jump to is required.

While exception handling is the major application area for this concept, Longjump can also be used to maximize performance in skipping multiple subroutine returns. A recursive algorithm for example might need a large amount function calls to compute the final result within the deepest function of the hierarchy. There the result is known but multiple function returns are needed to transfer the result back to the function originally triggering the recursion. In this case one single longjump can transfer the result by simply skipping the returns and restoring the original stack pointer that was current before the recursive algorithm was started.

Longjump can be used to jump within different stacks or even stacks of different threads. In an environment where multiple stacks exist to keep track of execution sequences - a stack might have been manually created or multiple threads that all have an own stack exist - Longjump can be used to set the stack pointer of a thread to a stack that originally does not belong to that thread. This can be used to implement a scheduling algorithm within a process. However modeling that kind of behaviour is rather difficult and does have many pitfalls. As you can imagine using one thread to jump to a stack position of a thread that is still active can corrupt the second thread's stack and will likely lead to undetermined behaviour or crashes.

A.5.4 Common Pitfalls

Even though the Longjump concept is a very powerful tool, it still demands a very detailed understanding of the operating system concept it is based on. Again different operating systems differ and might not support the functionality equally. Different flavours of Unix or Linux handle concurrency differently and some save more information on the stack than others. The existence of siglongjmp() function that has the same behaviour as longjmp with the addition of making sure that also the current signalling settings are saved and restored underlines that fact.

Figure A.10: Calling longjmp() can cause errors when used without caution (View PDF)
Longjmp_Stack_example2.gif

Additionally certain thread properties, like open file descriptors are simply not stored in the stack and can therefore be lost when jumping to a different thread. Local variables on the other hand are stored on the stack. When jumping from a point in the program sequence that involves any local pointer variables the developer has to make sure that no memory is still allocated before longjmp() is called to avoid memory leaks, as these pointer variables will not exist at the destination of the jump, but the memory will remain allocated until the program terminates.

The stack pointer value stored at any point of the programs execution should not be used any further after the current stack pointer has pointed or still points to a lower stack element than the marked one. At such a point the procedure that contained the setjmp call has already returned. The stack pointer was or is set to a lower level and any intermediate subsequent procedure call might have overwritten the element the saved pointer value originally pointed to. Therefore undetermined behaviour or crashes are likely to occur.

In the end Longjump is a very powerful tool when used with great care. On the other hand the excessive use of longjump can create very confusing execution sequences and procedure call hierarchies that are hard to model and keep track of. The code will also become harder to read the more Longjump is used.


next up previous contents index
Next: B. Sources Up: A. Operating System Concepts Previous: A.4 Pipes   Contents   Index
Apache Modeling Portal Home Apache Modeling Portal
2004-10-29