Previous: Module Misc, Up: Writing Dynamic Modules

E.8.5 Nonlocal Exits in Modules

Emacs Lisp supports nonlocal exits, whereby program control is transfered from one point in a program to another remote point. See Nonlocal Exits. Thus, Lisp functions called by your module might exit nonlocally by calling signal or throw, and your module functions must handle such nonlocal exits properly. Such handling is needed because C programs will not automatically release resources and perform other cleanups in these cases; your module code must itself do it. The module API provides facilities for that, described in this subsection. They are generally available since Emacs 25; those of them that became available in later releases explicitly call out the first Emacs version where they became part of the API.

When some Lisp code called by a module function signals an error or throws, the nonlocal exit is trapped, and the pending exit and its associated data are stored in the environment. Whenever a nonlocal exit is pending in the environment, any module API function called with a pointer to that environment will return immediately without any processing (the functions non_local_exit_check, non_local_exit_get, and non_local_exit_clear are exceptions from this rule). If your module function then does nothing and returns to Emacs, a pending nonlocal exit will cause Emacs to act on it: signal an error or throw to the corresponding catch.

So the simplest “handling” of nonlocal exits in module functions is to do nothing special and let the rest of your code to run as if nothing happened. However, this can cause two classes of problems:

Therefore, we recommend that your module functions check for nonlocal exit conditions and recover from them, using the functions described below.

— Function: enum emacs_funcall_exit non_local_exit_check (emacs_env *env)

This function returns the kind of nonlocal exit condition stored in env. The possible values are:

emacs_funcall_exit_return
The last API function exited normally.
emacs_funcall_exit_signal
The last API function signaled an error.
emacs_funcall_exit_throw
The last API function exited via throw.

— Function: emacs_funcall_exit non_local_exit_get (emacs_env *env, emacs_value *symbol, emacs_value *data)

This function returns the kind of nonlocal exit condition stored in env, like non_local_exit_check does, but it also returns the full information about the nonlocal exit, if any. If the return value is emacs_funcall_exit_signal, the function stores the error symbol in *symbol and the error data in *data (see Signaling Errors). If the return value is emacs_funcall_exit_throw, the function stores the catch tag symbol in *symbol and the throw value in *data. The function doesn't store anything in memory pointed by these arguments when the return value is emacs_funcall_exit_return.

You should check nonlocal exit conditions where it matters: before you allocated some resource or after you allocated a resource that might need freeing, or where a failure means further processing is impossible or infeasible.

Once your module function detected that a nonlocal exit is pending, it can either return to Emacs (after performing the necessary local cleanup), or it can attempt to recover from the nonlocal exit. The following API functions will help with these tasks.

— Function: void non_local_exit_clear (emacs_env *env)

This function clears the pending nonlocal exit conditions and data from env. After calling it, the module API functions will work normally. Use this function if your module function can recover from nonlocal exits of the Lisp functions it calls and continue, and also before calling any of the following two functions (or any other API functions, if you want them to perform their intended processing when a nonlocal exit is pending).

— Function: void non_local_exit_throw (emacs_env *env, emacs_value tag, emacs_value value)

This function throws to the Lisp catch symbol represented by tag, passing it value as the value to return. Your module function should in general return soon after calling this function. One use of this function is when you want to re-throw a non-local exit from one of the called API or Lisp functions.

— Function: void non_local_exit_signal (emacs_env *env, emacs_value error, emacs_value data)

This function signals the error represented by error with the specified error data data. The module function should return soon after calling this function. This function could be useful, e.g., for signaling errors from module functions to Emacs.