[Documentation updates for #1177 Simon Marlow **20070726094030 We now have a section that describes what hs_exit() does (including the "wait for foreign calls to return" behaviour), and more documentation on creating libraries of Haskell code. I also imported the section "Beware of DllMain()!" from the haskell.org wiki, with some minor editing. ] { hunk ./docs/users_guide/ffi-chap.xml 13 - flag, or + flag, or hunk ./docs/users_guide/ffi-chap.xml 18 - The FFI support in GHC diverges from the Addendum in the following ways: - - - - Syntactic forms and library functions proposed in earlier versions - of the FFI are still supported for backwards compatibility. - - - - GHC implements a number of GHC-specific extensions to the FFI - Addendum. These extensions are described in , but please note that programs using - these features are not portable. Hence, these features should be - avoided where possible. - - + GHC implements a number of GHC-specific extensions to the FFI + Addendum. These extensions are described in , but please note that programs using + these features are not portable. Hence, these features should be + avoided where possible. hunk ./docs/users_guide/ffi-chap.xml 24 - documentation; see for example the Foreign - module. + documentation; see for example the + Foreign module. hunk ./docs/users_guide/ffi-chap.xml 31 - GHC. Avoid them where possible to not compromise the portability of the - resulting code. + GHC. Your code will not be portable to other compilers if you use them. hunk ./docs/users_guide/ffi-chap.xml 69 - hunk ./docs/users_guide/ffi-chap.xml 127 + When linking the program, remember to include + M_stub.o in the final link command line, or + you'll get link errors for the missing function(s) (this isn't + necessary when building your program with ghc + ––make, as GHC will automatically link in the + correct bits). + hunk ./docs/users_guide/ffi-chap.xml 185 - hs_init(): strange things will + hs_init(): bad things will hunk ./docs/users_guide/ffi-chap.xml 188 - We pass argc and + We pass references to argc and hunk ./docs/users_guide/ffi-chap.xml 248 - can call hs_exit(), which - terminates the RTS. It runs any outstanding finalizers and - generates any profiling or stats output that might have been - requested. + can call hs_exit(), which terminates the + RTS. hunk ./docs/users_guide/ffi-chap.xml 268 - - Using <literal>foreign import ccall "wrapper"</literal> with GHC + + Making a Haskell library that can be called from foreign + code hunk ./docs/users_guide/ffi-chap.xml 272 - foreign import - ccall "wrapper"with GHC - + The scenario here is much like in , except that the aim is not to link a complete program, but to + make a library from Haskell code that can be deployed in the same + way that you would deploy a library of C code. hunk ./docs/users_guide/ffi-chap.xml 277 - When foreign import ccall "wrapper" is used - in a Haskell module, The C stub file M_stub.c - generated by GHC contains small helper functions used by the code - generated for the imported wrapper, so it must be linked in to the - final program. When linking the program, remember to include - M_stub.o in the final link command line, or - you'll get link errors for the missing function(s) (this isn't - necessary when building your program with ghc - ––make, as GHC will automatically link in the - correct bits). + The main requirement here is that the runtime needs to be + initialized before any Haskell code can be called, so your library + should provide initialisation and deinitialisation entry points, + implemented in C or C++. For example: + + + HsBool mylib_init(void){ + int argc = ... + char *argv[] = ... + + // Initialize Haskell runtime + hs_init(&argc, &argv); + + // Tell Haskell about all root modules + hs_add_root(__stginit_Foo); + + // do any other initialization here and + // return false if there was a problem + return HS_BOOL_TRUE; + } + + void mylib_end(void){ + hs_exit(); + } + + + The intialisation routine, mylib_init, calls + hs_init() and hs_add_root() as + normal to initialise the Haskell runtime, and the corresponding + deinitialisation funtion mylib_end() calls + hs_exit() to shut down the runtime. + + + + On the use of <literal>hs_exit()</literal> + + hs_exit() normally causes the termination of + any running Haskell threads in the system, and when + hs_exit() returns, there will be no more Haskell + threads running. The runtime will then shut down the system in an + orderly way, generating profiling + output and statistics if necessary, and freeing all the memory it + owns. + + It isn't always possible to terminate a Haskell thread forcibly: + for example, the thread might be currently executing a foreign call, + and we have no way to force the foreign call to complete. What's + more, the runtime must + assume that in the worst case the Haskell code and runtime are about + to be removed from memory (e.g. if this is a Windows DLL, + hs_exit() is normally called before unloading the + DLL). So hs_exit() must wait + until all outstanding foreign calls return before it can return + itself. + + The upshot of this is that if you have Haskell threads that are + blocked in foreign calls, then hs_exit() may hang + (or possibly busy-wait) until the calls return. Therefore it's a + good idea to make sure you don't have any such threads in the system + when calling hs_exit(). This includes any threads + doing I/O, because I/O may (or may not, depending on the + type of I/O and the platform) be implemented using blocking foreign + calls. + + The GHC runtime treats program exit as a special case, to avoid + the need to wait for blocked threads when a standalone + executable exits. Since the program and all its threads are about to + terminate at the same time that the code is removed from memory, it + isn't necessary to ensure that the threads have exited first. + (Unofficially, if you want to use this fast and loose version of + hs_exit(), then call + shutdownHaskellAndExit() instead). hunk ./docs/users_guide/ffi-chap.xml 358 - directive), one can assist the C compiler in detecting type + flag), one can assist the C compiler in detecting type hunk ./docs/users_guide/ffi-chap.xml 395 -configuration, which GHC knows about. So if you are building a package, remember -to put all those options into the package configuration. -See the c_includes field in . - +configuration, which GHC knows about. So if you are building a package using + Cabal, remember to put all those include files in the package + description (see the includes field in the Cabal + documentation). hunk ./docs/users_guide/win32-dlls.xml 213 -moment; however, all the machinery is -still there. If you're interested, contact the GHC team. Note that +moment; we hope to re-instate this facility in the future. Note that hunk ./docs/users_guide/win32-dlls.xml 405 - hunk ./docs/users_guide/win32-dlls.xml 407 -know. The dirty details are in the Foreign Function -Interface definition, but it can be tricky to work out how to -combine this with DLL building, so here's an example: - +know. This is a special case of ; we'll deal with + the DLL-specific issues that arise below. Here's an example: hunk ./docs/users_guide/win32-dlls.xml 519 + + +Beware of DllMain()! + +The body of a DllMain() function is an +extremely dangerous place! This is because the order in which DLLs are +unloaded when a process is terminating is unspecified. This means that +the DllMain() for your DLL may be called when other DLLs containing +functions that you call when de-initializing your DLL have already +been unloaded. In other words, you can't put shutdown code inside +DllMain(), unless your shutdown code only requires use of certain +functions which are guaranteed to be available (see the Platform SDK +docs for more info). + +In particular, if you are writing a DLL that's statically +linked with Haskell, it is not safe to call +hs_exit() from DllMain(), since +hs_exit() may make use of other DLLs (see also ). What's more, if you +wait until program shutdown to execute your deinitialisation code, Windows will have +terminated all the threads in your program except the one calling +DllMain(), which can cause even more +problems. + +A solution is to always export Begin() and End() functions from your +DLL, and call these from the application that uses the DLL, so that +you can be sure that all DLLs needed by any shutdown code in your +End() function are available when it is called. + +The following example is untested but illustrates the idea (please let us + know if you find problems with this example or have a better one). Suppose we have a DLL called Lewis which makes use of 2 +Haskell modules Bar and Zap, +where Bar imports Zap and is +therefore the root module in the sense of . Then the main C++ unit for the DLL would +look something like: + + + // Lewis.cpp -- compiled using GCC + #include <Windows.h> + #include "HsFFI.h" + + #define __LEWIS_DLL_EXPORT + #include "Lewis.h" + + #include "Bar_stub.h" // generated by GHC + #include "Zap_stub.h" + + BOOL APIENTRY DllMain( HANDLE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved + ){ + return TRUE; + } + + extern "C"{ + + LEWIS_API HsBool lewis_Begin(){ + int argc = ... + char *argv[] = ... + + // Initialize Haskell runtime + hs_init(&argc, &argv); + + // Tell Haskell about all root modules + hs_add_root(__stginit_Bar); + + // do any other initialization here and + // return false if there was a problem + return HS_BOOL_TRUE; + } + + LEWIS_API void lewis_End(){ + hs_exit(); + } + + LEWIS_API HsInt lewis_Test(HsInt x){ + // use Haskell functions exported by + // modules Bar and/or Zap + + return ... + } + + } // extern "C" + +and some application which used the functions in the DLL would have a main() function like: + + // MyApp.cpp + #include "stdafx.h" + #include "Lewis.h" + + int main(int argc, char *argv[]){ + if (lewis_Begin()){ + // can now safely call other functions + // exported by Lewis DLL + + } + lewis_End(); + return 0; + } + + +Lewis.h would have to have some appropriate #ifndef to ensure that the +Haskell FFI types were defined for external users of the DLL (who +wouldn't necessarily have GHC installed and therefore wouldn't have +the include files like HsFFI.h etc). + + }