Implementation of SIGNAL and ERROR in CLRHack
In CLRHack, the condition signaling system is implemented in the Lisp.HandlerControl class within the
LispBase library. It leverages .NET's [ThreadStatic] storage to maintain a per-thread dynamic stack of
active condition handlers.
SIGNAL Implementation
The Signal(object condition) method performs the following logic:
- Retrieval: It fetches the
activeHandlerslist for the current thread. This list is a chain of[LispBase]Lisp.Handlerobjects maintained byhandler-bind. - Iteration: It iterates linearly through the list from the most recently bound handler to the oldest.
- Type Matching: For each handler, it calls
IsType(condition, handler.ConditionType).- If the condition is a symbol, it checks for symbol equality (supporting simple symbol-based conditions).
- If the condition is a .NET object, it checks if the handler's type is assignable from the condition's runtime type (supporting interop with system exceptions).
- It treats the symbols
TorEXCEPTIONas catch-all types.
- Handler Invocation: If a match is found:
- Recursive Signal Protection: Before calling the handler function, the current handler list is
temporarily shadowed.
activeHandlersis set tocell.rest(the handlers bound outside the current one). This ensures that if the handler itself callssignal, it won't trigger itself recursively. - Execution: The handler's
Closureis invoked with the condition object as its argument. - Restoration: A
finallyblock ensures the originalactiveHandlerslist is restored if the handler returns normally.
- Recursive Signal Protection: Before calling the handler function, the current handler list is
temporarily shadowed.
- Signaling Pass: It first invokes
Signal(condition). If a handler performs a non-local exit (e.g., viahandler-case), theErrormethod never returns. - Debugger Entry: If
Signalreturns normally (meaning all handlers declined),ErrorcallsEnterDebugger(condition). - Interactive Debugging: The debugger:
- Prints the condition and a list of available restarts (retrieved via
RestartControl.GetActiveRestarts()). - Provides a prompt for the user to select a restart, launch the system-level debugger (Visual Studio/Rider), or abort.
- If a restart is selected, it is invoked interactively (potentially gathering arguments from the user).
- Prints the condition and a list of available restarts (retrieved via
- Final Fallback: If the debugger is exited without invoking a restart,
Errorthrows a C#Exceptionto ensure that execution does not continue on an invalid path. - Handler Shadowing: The decision to pop the handler list during invocation is critical for maintaining Common Lisp semantics. It prevents infinite loops and ensures that "outer" handlers can handle errors raised within "inner" handlers.
- Unified Exception Model: CLRHack attempts to unify Lisp conditions and .NET exceptions.
IsTypeallows Lisp handlers to catch C# exceptions by their class name or Type object. - Thread Isolation: By using
[ThreadStatic]foractiveHandlers, CLRHack ensures that condition signaling is thread-safe. One thread signaling an error will not interfere with the handler state of another thread. - Debugger Capability: The
SYSTEM-DEBUGGERoption inEnterDebuggeris a bridge to the underlying .NET environment, allowing developers to use professional IDE tools to inspect the state of the Lisp VM when an unhandled error occurs.
ERROR Implementation
The Error(object condition) method build upon Signal:
Notable Implementation Decisions and Edge Cases
signal and error complete the Common Lisp condition system implementation for CLRHack
No comments:
Post a Comment