A clever assembler programmer could come up with code to accomplish these ``tricks'', but that would require different assembler modules for each machine architecture we wanted to run Condor on. Instead we use setjmp() and longjmp() for this purpose, and reduce the machine dependency to a single macro (one line of ``C'' code). To see how this works, first consider the actions of setjmp/longjmp in more mundane circumstances. One calls setjmp() with a pointer to a system defined type called a JMP_BUF. The setjmp() routine saves the current ``context'' into the JMP_BUF, and returns 0. The content of the JMP_BUF is generally considered to be opaque to the programmer, but it does contain all the information needed to return to the current point in the stack from any other procedure which is called by this one - possibly after many nested procedure calls. If somewhere in the called procedure longjmp() is called with a pointer to the JMP_BUF and some value other than 0, then we return to the point where the original setjmp() call was made. This time the return value is the one specified at the longjmp() call, i.e. something other than 0. Of course one of the contents of the JMP_BUF is the stack pointer as of the time of the setjmp() call. The single line of machine specific code mentioned earlier is a macro which places a value into the location corresponding to the stack pointer within a JMP_BUF.
To call the procedure restore_stack() we do a setjmp(), use the macro to switch the stack pointer stored in the JMP_BUF to a location in the data area, and then execute the corresponding longjmp(). It should be noted that this idea was not invented by us, and is in fact used for switching virtual stacks by a number of popular user-level threads packages. The return from the restore_stack() routine is similar. In this case the call to longjmp() uses a JMP_BUF which was saved in the data area at checkpoint time. Recall that the data area is restored before the stack, so the content of the JMP_BUF is valid at this time.