OS/2 codes: How to emulate COPY-ON-WRITE #3 Install exception handler
To activate the exception handler, it's needed to install it. By the way, the handler works only on the thread on which it is installed. After, we should install the handler on every threads.
Let's see the way to install a handler first:
EXCEPTIONREGISTRATIONRECORD is a type to register handler. Generally, it should be initialized to 0 except ExceptionHandler field. ExceptionHandler should be set to the handler to be registered. Finally, register the handler with DosSetExceptionHandler(). Here, note that EXCEPTIONREGISTRATIONRECORD variables should be placed in the stack.
Uninstalling the installed handler is simple:
regRec is the variable which was used when installing the handler. DosUnsetExceptionHandler() uninstalls the handler.
The codes which may cause exceptions should be placed between DosSetExceptionHandler() and DosUnsetExceptionHandler(). After all, it will look like this:
What we want is to install the handler to every threads. Therefore, the above codes should be placed in a thread. How ?
Generally, the way starting a new thread is to use _beginthread(). We passes a function to be executed in a thread to it. Here, there is nothing that we can for a thread function. Instead, it's possible to override _beginthread(). For details, see "Alias with DLL" of <How to override standard functions in kLIBC>. By overriding _beginthread(), it's possible to pass a wrapper function of a thread function to _beginthread() of kLIBC. The wrapper function will look like the above codes. A thread function will be called in [CODES WHICH MAY CAUSES EXCEPTIONS] part.
However, _beginthread() can cover only new threads whose thread id is >= 2. That is, it's not possible to install an exception handler to a main thread whose thread is 1 with _beginthread().
There are several ways to solve this problem. First, put installation and uninstallation codes in main() manually. Second, override main() with a macro. In this case, a header containing a macro overriding main() should be included. Both cases requires additional works. Aren't there ways to override main() without such additional works ? Unfortunately, no. Instead, it's possible to override the function calling main(). ^^
There are two functions related to call of main(). One is __init_app() in lib/sys/386/appinit.s. The other one is __init() in lib/sys/__init.c which is called by __init_app(). Both functions are exported from kLIBC. However, it's not possible to override __init(). The only coice is to override __init_app().
If we override __init_app() and install the exception handler before calling main(), then it's possible to catch exceptions caused in a main thread without additional works.
With these, it's possible to install the exception handler to every thread automatically. Of course, it's needed to link the replacement codes of _beginthread() and __init_app().
At last, we can now use copy-on-write feature in all threads.
Let's see the way to install a handler first:
EXCEPTIONREGISTRATIONRECORD regRec = { 0 };
/* Register an exception handler for SIGSEGV */
regRec.ExceptionHandler = ( ERR )sigsegv;
DosSetExceptionHandler( ®Rec );
EXCEPTIONREGISTRATIONRECORD is a type to register handler. Generally, it should be initialized to 0 except ExceptionHandler field. ExceptionHandler should be set to the handler to be registered. Finally, register the handler with DosSetExceptionHandler(). Here, note that EXCEPTIONREGISTRATIONRECORD variables should be placed in the stack.
Uninstalling the installed handler is simple:
/* Deregister an exception handler */
DosUnsetExceptionHandler( ®Rec );
regRec is the variable which was used when installing the handler. DosUnsetExceptionHandler() uninstalls the handler.
The codes which may cause exceptions should be placed between DosSetExceptionHandler() and DosUnsetExceptionHandler(). After all, it will look like this:
EXCEPTIONREGISTRATIONRECORD regRec = { 0 };
/* Register an exception handler for SIGSEGV */
regRec.ExceptionHandler = ( ERR )sigsegv;
DosSetExceptionHandler( ®Rec );
CODES WHICH MAY CAUSES EXCEPTIONS
/* Deregister an exception handler */
DosUnsetExceptionHandler( ®Rec );
What we want is to install the handler to every threads. Therefore, the above codes should be placed in a thread. How ?
Generally, the way starting a new thread is to use _beginthread(). We passes a function to be executed in a thread to it. Here, there is nothing that we can for a thread function. Instead, it's possible to override _beginthread(). For details, see "Alias with DLL" of <How to override standard functions in kLIBC>. By overriding _beginthread(), it's possible to pass a wrapper function of a thread function to _beginthread() of kLIBC. The wrapper function will look like the above codes. A thread function will be called in [CODES WHICH MAY CAUSES EXCEPTIONS] part.
However, _beginthread() can cover only new threads whose thread id is >= 2. That is, it's not possible to install an exception handler to a main thread whose thread is 1 with _beginthread().
There are several ways to solve this problem. First, put installation and uninstallation codes in main() manually. Second, override main() with a macro. In this case, a header containing a macro overriding main() should be included. Both cases requires additional works. Aren't there ways to override main() without such additional works ? Unfortunately, no. Instead, it's possible to override the function calling main(). ^^
There are two functions related to call of main(). One is __init_app() in lib/sys/386/appinit.s. The other one is __init() in lib/sys/__init.c which is called by __init_app(). Both functions are exported from kLIBC. However, it's not possible to override __init(). The only coice is to override __init_app().
If we override __init_app() and install the exception handler before calling main(), then it's possible to catch exceptions caused in a main thread without additional works.
With these, it's possible to install the exception handler to every thread automatically. Of course, it's needed to link the replacement codes of _beginthread() and __init_app().
At last, we can now use copy-on-write feature in all threads.
댓글
댓글 쓰기