From: Francis Dupont Date: Tue, 22 Oct 2019 22:48:28 +0000 (+0200) Subject: [947-mt-compatibility-for-hooks] checkpoint X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6eb38c0219418d2da980ddaa59669cc9fe18bbf9;p=thirdparty%2Fkea.git [947-mt-compatibility-for-hooks] checkpoint --- diff --git a/src/lib/hooks/hooks.h b/src/lib/hooks/hooks.h index 99c4547f88..39b2e17b3b 100644 --- a/src/lib/hooks/hooks.h +++ b/src/lib/hooks/hooks.h @@ -19,11 +19,14 @@ const int KEA_HOOKS_VERSION = 11; const char* const LOAD_FUNCTION_NAME = "load"; const char* const UNLOAD_FUNCTION_NAME = "unload"; const char* const VERSION_FUNCTION_NAME = "version"; +const char* const MULTI_THREADING_COMPATIBLE_FUNCTION_NAME = + "multi_threading_compatible"; // Typedefs for pointers to the framework functions. typedef int (*version_function_ptr)(); typedef int (*load_function_ptr)(isc::hooks::LibraryHandle&); typedef int (*unload_function_ptr)(); +typedef int (*multi_threading_compatible_function_ptr)(); } // Anonymous namespace diff --git a/src/lib/hooks/hooks_messages.cc b/src/lib/hooks/hooks_messages.cc index ca5d96214a..4d003afa14 100644 --- a/src/lib/hooks/hooks_messages.cc +++ b/src/lib/hooks/hooks_messages.cc @@ -1,4 +1,4 @@ -// File created from ../../../src/lib/hooks/hooks_messages.mes on Fri Feb 08 2019 20:16 +// File created from ../../../src/lib/hooks/hooks_messages.mes on Wed Oct 23 2019 00:13 #include #include @@ -21,6 +21,8 @@ extern const isc::log::MessageID HOOKS_HOOK_LIST_RESET = "HOOKS_HOOK_LIST_RESET" extern const isc::log::MessageID HOOKS_INCORRECT_VERSION = "HOOKS_INCORRECT_VERSION"; extern const isc::log::MessageID HOOKS_LIBRARY_LOADED = "HOOKS_LIBRARY_LOADED"; extern const isc::log::MessageID HOOKS_LIBRARY_LOADING = "HOOKS_LIBRARY_LOADING"; +extern const isc::log::MessageID HOOKS_LIBRARY_MULTI_THREADING_COMPATIBLE = "HOOKS_LIBRARY_MULTI_THREADING_COMPATIBLE"; +extern const isc::log::MessageID HOOKS_LIBRARY_MULTI_THREADING_NOT_COMPATIBLE = "HOOKS_LIBRARY_MULTI_THREADING_NOT_COMPATIBLE"; extern const isc::log::MessageID HOOKS_LIBRARY_UNLOADED = "HOOKS_LIBRARY_UNLOADED"; extern const isc::log::MessageID HOOKS_LIBRARY_UNLOADING = "HOOKS_LIBRARY_UNLOADING"; extern const isc::log::MessageID HOOKS_LIBRARY_VERSION = "HOOKS_LIBRARY_VERSION"; @@ -28,6 +30,7 @@ extern const isc::log::MessageID HOOKS_LOAD_ERROR = "HOOKS_LOAD_ERROR"; extern const isc::log::MessageID HOOKS_LOAD_EXCEPTION = "HOOKS_LOAD_EXCEPTION"; extern const isc::log::MessageID HOOKS_LOAD_FRAMEWORK_EXCEPTION = "HOOKS_LOAD_FRAMEWORK_EXCEPTION"; extern const isc::log::MessageID HOOKS_LOAD_SUCCESS = "HOOKS_LOAD_SUCCESS"; +extern const isc::log::MessageID HOOKS_MULTI_THREADING_COMPATIBLE_EXCEPTION = "HOOKS_MULTI_THREADING_COMPATIBLE_EXCEPTION"; extern const isc::log::MessageID HOOKS_NO_LOAD = "HOOKS_NO_LOAD"; extern const isc::log::MessageID HOOKS_NO_UNLOAD = "HOOKS_NO_UNLOAD"; extern const isc::log::MessageID HOOKS_NO_VERSION = "HOOKS_NO_VERSION"; @@ -59,6 +62,8 @@ const char* values[] = { "HOOKS_INCORRECT_VERSION", "hook library %1 is at version %2, require version %3", "HOOKS_LIBRARY_LOADED", "hooks library %1 successfully loaded", "HOOKS_LIBRARY_LOADING", "loading hooks library %1", + "HOOKS_LIBRARY_MULTI_THREADING_COMPATIBLE", "hooks library %1 reports its multi-threading compatibility as %2", + "HOOKS_LIBRARY_MULTI_THREADING_NOT_COMPATIBLE", "hooks library %1 is not compatible with multi-threading", "HOOKS_LIBRARY_UNLOADED", "hooks library %1 successfully unloaded", "HOOKS_LIBRARY_UNLOADING", "unloading library %1", "HOOKS_LIBRARY_VERSION", "hooks library %1 reports its version as %2", @@ -66,6 +71,7 @@ const char* values[] = { "HOOKS_LOAD_EXCEPTION", "'load' function in hook library %1 threw an exception", "HOOKS_LOAD_FRAMEWORK_EXCEPTION", "'load' function in hook library %1 threw an exception: reason %2", "HOOKS_LOAD_SUCCESS", "'load' function in hook library %1 returned success", + "HOOKS_MULTI_THREADING_COMPATIBLE_EXCEPTION", "'multi_threading_compatible' function in hook library %1 threw an exception", "HOOKS_NO_LOAD", "no 'load' function found in hook library %1", "HOOKS_NO_UNLOAD", "no 'unload' function found in hook library %1", "HOOKS_NO_VERSION", "no 'version' function found in hook library %1", diff --git a/src/lib/hooks/hooks_messages.h b/src/lib/hooks/hooks_messages.h index 288034665e..baf24659ac 100644 --- a/src/lib/hooks/hooks_messages.h +++ b/src/lib/hooks/hooks_messages.h @@ -1,4 +1,4 @@ -// File created from ../../../src/lib/hooks/hooks_messages.mes on Fri Feb 08 2019 20:16 +// File created from ../../../src/lib/hooks/hooks_messages.mes on Wed Oct 23 2019 00:13 #ifndef HOOKS_MESSAGES_H #define HOOKS_MESSAGES_H @@ -22,6 +22,8 @@ extern const isc::log::MessageID HOOKS_HOOK_LIST_RESET; extern const isc::log::MessageID HOOKS_INCORRECT_VERSION; extern const isc::log::MessageID HOOKS_LIBRARY_LOADED; extern const isc::log::MessageID HOOKS_LIBRARY_LOADING; +extern const isc::log::MessageID HOOKS_LIBRARY_MULTI_THREADING_COMPATIBLE; +extern const isc::log::MessageID HOOKS_LIBRARY_MULTI_THREADING_NOT_COMPATIBLE; extern const isc::log::MessageID HOOKS_LIBRARY_UNLOADED; extern const isc::log::MessageID HOOKS_LIBRARY_UNLOADING; extern const isc::log::MessageID HOOKS_LIBRARY_VERSION; @@ -29,6 +31,7 @@ extern const isc::log::MessageID HOOKS_LOAD_ERROR; extern const isc::log::MessageID HOOKS_LOAD_EXCEPTION; extern const isc::log::MessageID HOOKS_LOAD_FRAMEWORK_EXCEPTION; extern const isc::log::MessageID HOOKS_LOAD_SUCCESS; +extern const isc::log::MessageID HOOKS_MULTI_THREADING_COMPATIBLE_EXCEPTION; extern const isc::log::MessageID HOOKS_NO_LOAD; extern const isc::log::MessageID HOOKS_NO_UNLOAD; extern const isc::log::MessageID HOOKS_NO_VERSION; diff --git a/src/lib/hooks/hooks_messages.mes b/src/lib/hooks/hooks_messages.mes index 41ec538085..dbe318fee9 100644 --- a/src/lib/hooks/hooks_messages.mes +++ b/src/lib/hooks/hooks_messages.mes @@ -88,6 +88,17 @@ This is a debug message output just before the specified library is loaded. If the action is successfully, it will be followed by the HOOKS_LIBRARY_LOADED informational message. +% HOOKS_LIBRARY_MULTI_THREADING_COMPATIBLE hooks library %1 reports its multi-threading compatibility as %2 +A debug message issued when the "multi_threading_compatible" function was +called. The returned value (0 means not compatible, others compatible) +is displayed. + +% HOOKS_LIBRARY_MULTI_THREADING_NOT_COMPATIBLE hooks library %1 is not compatible with multi-threading +When multi-threading is enabled and the library is not compatible (either +because the "multi_threading_compatible" function returned 0 or was not +implemented) this error message is issued. The library must be removed +from the configuration or the multi-threading disabled. + % HOOKS_LIBRARY_UNLOADED hooks library %1 successfully unloaded This information message is issued when a user-supplied hooks library has been successfully unloaded. @@ -125,6 +136,11 @@ installed. This is a debug message issued when the "load" function has been found in a hook library and has been successfully called. +% HOOKS_MULTI_THREADING_COMPATIBLE_EXCEPTION 'multi_threading_compatible' function in hook library %1 threw an exception +This error message is issued if the multi_threading_compatible() +function in the specified hooks library was called and generated an +exception. The library is considered unusable and will not be loaded. + % HOOKS_NO_LOAD no 'load' function found in hook library %1 This is a debug message saying that the specified library was loaded but no function called "load" was found in it. Providing the library diff --git a/src/lib/hooks/hooks_user.dox b/src/lib/hooks/hooks_user.dox index 470be9249d..b9da7552be 100644 --- a/src/lib/hooks/hooks_user.dox +++ b/src/lib/hooks/hooks_user.dox @@ -1,4 +1,4 @@ -// Copyright (C) 2013-2017 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2013-2019 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -113,8 +113,10 @@ of three (user-supplied) functions: is built - load - called when the library is loaded by the server. - unload - called when the library is unloaded by the server. +- multi_threading_compatible - defines the compatibility (or not) of +the user-library and a multi-threaded DHCP service. -Of these, only "version" is mandatory, although in our example, all three +Of these, only "version" is mandatory, although in our example, all four are used. @subsubsection hooksdgVersionFunction The "version" Function @@ -251,6 +253,40 @@ on an error. The hooks framework will record a non-zero status return as an error in the current Kea log but otherwise ignore it. - As before, the function definitions are enclosed in 'extern "C"' braces. +@subsubsection hooksdgMultiThreadingCompatibleFuntion The +"multi_threading_compatible" function + +"multi_threading_compatible" is used by the hooks framework to check +if the libraries it is loading are compatible with the DHCPv4 or DHCPv6 +erver multi-threading configuration. The value 0 means not compatible +and is the default when the function is not implemented. not 0 values +mean compatible. + +To be compatible means: +- the code associated with DHCP packet processing callouts e.g. +pkt4_receive or pkt6_send must be reentrant so the multi-threaded DHCP service +can simultaneously calls more than once on of these callouts. +- commands a library registers must be reentrant +- when a library implements a backend API (e.g. host data source) the service +methods must be reentrant +- (shall be modified later) a library must not modify the internal +configuration of the server, e.g. create or delete a subnet. + +In the tutoral, we'll put "multi_threading_compatible" in its own file, +multi_threading_compatible.cc. The contents are: + +@code +// multi_threading_compatible.cc + +extern "C" { + +int multi_threading_compatible() { + return (0); +} + +} +@endcode + @subsection hooksdgCallouts Callouts Having sorted out the framework, we now come to the functions that diff --git a/src/lib/hooks/library_manager.cc b/src/lib/hooks/library_manager.cc index ab10837da9..b7f719aaa2 100644 --- a/src/lib/hooks/library_manager.cc +++ b/src/lib/hooks/library_manager.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2013-2017 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2013-2019 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -7,6 +7,9 @@ #include #include +#if 0 +#include +#endif #include #include #include @@ -53,7 +56,7 @@ LibraryManager::LibraryManager(const std::string& name) : dl_handle_(NULL), index_(-1), manager_(), library_name_(name) {} -// Destructor. +// Destructor. LibraryManager::~LibraryManager() { if (manager_) { // LibraryManager instantiated to load a library, so ensure that @@ -134,6 +137,42 @@ LibraryManager::checkVersion() const { return (false); } +// Check the multi-threading compatibility of the library + +bool +LibraryManager::checkMultiThreadingCompatible() const { + + // Compatible with single-threaded. +#if 0 + if (!MultiThreadingMgr::instance().getMode()) { + return (true); + } +#endif + + // Get the pointer to the "multi_threading_compatible" function. + PointerConverter pc(dlsym(dl_handle_, MULTI_THREADING_COMPATIBLE_FUNCTION_NAME)); + int compatible = 0; + if (pc.multiThreadingCompatiblePtr()) { + try { + compatible = (*pc.multiThreadingCompatiblePtr())(); + } catch (...) { + LOG_ERROR(hooks_logger, HOOKS_MULTI_THREADING_COMPATIBLE_EXCEPTION) + .arg(library_name_); + return (false); + } + + LOG_DEBUG(hooks_logger, HOOKS_DBG_CALLS, + HOOKS_LIBRARY_MULTI_THREADING_COMPATIBLE) + .arg(library_name_) + .arg(compatible); + } + if (compatible == 0) { + LOG_ERROR(hooks_logger, HOOKS_LIBRARY_MULTI_THREADING_NOT_COMPATIBLE) + .arg(library_name_); + } + return (compatible != 0); +} + // Register the standard callouts void @@ -275,9 +314,12 @@ LibraryManager::loadLibrary() { isc::log::LoggerManager::logDuplicatedMessages(); // Library opened OK, see if a version function is present and if so, - // check what value it returns. + // check what value it returns. Check multi-threading compatibility. +#if 0 + if (checkVersion() && checkMultiThreadingCompatible()) { +#else if (checkVersion()) { - +#endif // Version OK, so now register the standard callouts and call the // library's load() function if present. registerStandardCallouts(); @@ -360,7 +402,12 @@ LibraryManager::validateLibrary(const std::string& name) { LibraryManager manager(name); // Try to open it and, if we succeed, check the version. +#if 0 + bool validated = manager.openLibrary() && manager.checkVersion() && + checkMultiThreadingCompatible(); +#else bool validated = manager.openLibrary() && manager.checkVersion(); +#endif // Regardless of whether the version checked out, close the library. (This // is a no-op if the library failed to open.) diff --git a/src/lib/hooks/library_manager.h b/src/lib/hooks/library_manager.h index 5f6bc4cac7..14acf8a64d 100644 --- a/src/lib/hooks/library_manager.h +++ b/src/lib/hooks/library_manager.h @@ -1,4 +1,4 @@ -// Copyright (C) 2013-2017 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2013-2019 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -99,7 +99,7 @@ public: /// /// A static method that is used to validate a library. Validation checks /// that the library can be opened, that "version" exists, and that it - /// returns the right number. + /// returns the right number, and the multi-threading compatibility. /// /// @param name Name of the library to validate /// @@ -109,15 +109,17 @@ public: /// @brief Loads a library /// - /// Open the library and check the version. If all is OK, load all standard - /// symbols then call "load" if present. + /// Open the library, check the version and the multi-threading + /// compatibility. If all is OK, load all standard symbols then + /// call "load" if present. /// - /// It also calls the @c isc::log::MessageInitializer::loadDictionary, prior - /// to invoking the @c version function of the library, to update the global - /// logging dictionary with the log messages registered by the loaded library. + /// It also calls the @c isc::log::MessageInitializer::loadDictionary, + /// prior to invoking the @c version function of the library, to + /// update the global logging dictionary with the log messages + /// registered by the loaded library. /// - /// @return true if the library loaded successfully, false otherwise. In the - /// latter case, the library will be unloaded if possible. + /// @return true if the library loaded successfully, false otherwise. + /// In the latter case, the library will be unloaded if possible. bool loadLibrary(); /// @brief Unloads a library @@ -176,6 +178,16 @@ protected: /// @return bool true if the check succeeded bool checkVersion() const; + /// @brief Check multi-threading compatibility + /// + /// If the multi-threading mode is false returns true, else with + /// the library open, accesses the "multi_threading_compatible()" + /// function and returns false if not exists or has value 0, returns + /// true otherwise. + /// + /// @return bool true if the check succeeded + bool checkMultiThreadingCompatible() const; + /// @brief Register standard callouts /// /// Loops through the list of hook names and searches the library for diff --git a/src/lib/hooks/pointer_converter.h b/src/lib/hooks/pointer_converter.h index d160affd55..56eb7e2f2e 100644 --- a/src/lib/hooks/pointer_converter.h +++ b/src/lib/hooks/pointer_converter.h @@ -1,4 +1,4 @@ -// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2013-2019 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -92,6 +92,13 @@ public: return (pointers_.version_ptr); } + /// @brief Return pointer to multi_threading_compatible function + /// + /// @return Pointer to the multi_threading_compatible function + multi_threading_compatible_function_ptr multiThreadingCompatiblePtr() const { + return (pointers_.multi_threading_compatible_ptr); + } + ///@} private: @@ -103,6 +110,8 @@ private: load_function_ptr load_ptr; // Pointer to load function unload_function_ptr unload_ptr; // Pointer to unload function version_function_ptr version_ptr; // Pointer to version function + multi_threading_compatible_function_ptr multi_threading_compatible_ptr; + // Pointer to multi_threading_compatible function } pointers_; }; diff --git a/src/lib/hooks/tests/full_callout_library.cc b/src/lib/hooks/tests/full_callout_library.cc index 35bc2f7e88..8d3784d708 100644 --- a/src/lib/hooks/tests/full_callout_library.cc +++ b/src/lib/hooks/tests/full_callout_library.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2013-2017 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2013-2019 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -13,9 +13,10 @@ /// /// The characteristics of this library are: /// -/// - All three framework functions are supplied (version(), load() and -/// unload()), with unload() creating a marker file. The test code checks -/// for the presence of this file, so verifying that unload() has been run. +/// - All four framework functions are supplied (version(), load(), +/// unload() and multi_threading_compatible()), with unload() +/// creating a marker file. The test code checks for the presence +/// of this file, so verifying that unload() has been run. /// /// - One standard and two non-standard callouts are supplied, with the latter /// being registered by the load() function. @@ -167,5 +168,10 @@ unload() { return (0); } +int +multi_threading_compatible() { + return (1); +} + }; diff --git a/src/lib/hooks/tests/library_manager_unittest.cc b/src/lib/hooks/tests/library_manager_unittest.cc index 07202b26d4..d10b447582 100644 --- a/src/lib/hooks/tests/library_manager_unittest.cc +++ b/src/lib/hooks/tests/library_manager_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2013-2017 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2013-2019 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -18,6 +18,10 @@ #include #include +#if 0 +#include +#endif + #include #include @@ -135,6 +139,7 @@ public: using LibraryManager::openLibrary; using LibraryManager::closeLibrary; using LibraryManager::checkVersion; + using LibraryManager::checkMultiThreadingCompatible; using LibraryManager::registerStandardCallouts; using LibraryManager::runLoad; using LibraryManager::runUnload; @@ -236,6 +241,96 @@ TEST_F(LibraryManagerTest, CorrectVersionReturned) { EXPECT_TRUE(lib_manager.closeLibrary()); } +// Checks that the code handles the case of a library with no +// multi_threading_compatible function. + +TEST_F(LibraryManagerTest, NoMultiThreadingCompatible) { + PublicLibraryManager lib_manager(std::string(BASIC_CALLOUT_LIBRARY), + 0, callout_manager_); + + // Open should succeed. + EXPECT_TRUE(lib_manager.openLibrary()); + +#if 0 + // Not multi-threading compatible: does not matter without MT. + EXPECT_TRUE(lib_manager.checkMultiThreadingCompatible()); + + // Not multi-threading compatible: does matter with MT. + //// set MT + EXPECT_FALSE(lib_manager.checkMultiThreadingCompatible()); +#endif + + // Tidy up. + EXPECT_TRUE(lib_manager.closeLibrary()); +} + +// Checks that the code handles the case of a library with a +// multi_threading_compatible function returning 0 (not compatible). + +TEST_F(LibraryManagerTest, multiThreadingNotCompatible) { + PublicLibraryManager lib_manager(std::string(LOAD_ERROR_CALLOUT_LIBRARY), + 0, callout_manager_); + + // Open should succeed. + EXPECT_TRUE(lib_manager.openLibrary()); + +#if 0 + // Not multi-threading compatible: does not matter without MT. + EXPECT_TRUE(lib_manager.checkMultiThreadingCompatible()); + + // Not multi-threading compatible: does matter with MT. + //// set MT + EXPECT_FALSE(lib_manager.checkMultiThreadingCompatible()); +#endif + + // Tidy up. + EXPECT_TRUE(lib_manager.closeLibrary()); +} + +// Checks that the code handles the case of a library with a +// multi_threading_compatible function returning 1 (compatible) + +TEST_F(LibraryManagerTest, multiThreadingCompatible) { + PublicLibraryManager lib_manager(std::string(FULL_CALLOUT_LIBRARY), + 0, callout_manager_); + + // Open should succeed. + EXPECT_TRUE(lib_manager.openLibrary()); + + // Multi-threading compatible: does not matter without MT. + EXPECT_TRUE(lib_manager.checkMultiThreadingCompatible()); + + // Multi-threading compatible: does matter with MT. + //// set MT + EXPECT_TRUE(lib_manager.checkMultiThreadingCompatible()); + + // Tidy up. + EXPECT_TRUE(lib_manager.closeLibrary()); +} + +// Checks that the code handles the case of a library with a +// multi_threading_compatible function returning 1 (compatible) + +TEST_F(LibraryManagerTest, multiThreadingCompatibleException) { + PublicLibraryManager lib_manager(std::string(FRAMEWORK_EXCEPTION_LIBRARY), + 0, callout_manager_); + + // Open should succeed. + EXPECT_TRUE(lib_manager.openLibrary()); + +#if 0 + // Throw exception: does not matter without MT. + EXPECT_FALSE(lib_manager.checkMultiThreadingCompatible()); +#endif + + // Throw exception: does matter with MT. + //// set MT + EXPECT_FALSE(lib_manager.checkMultiThreadingCompatible()); + + // Tidy up. + EXPECT_TRUE(lib_manager.closeLibrary()); +} + // Checks the registration of standard callouts. TEST_F(LibraryManagerTest, RegisterStandardCallouts) { diff --git a/src/lib/hooks/tests/load_error_callout_library.cc b/src/lib/hooks/tests/load_error_callout_library.cc index 87bcd6c1fa..26f0d9e2b6 100644 --- a/src/lib/hooks/tests/load_error_callout_library.cc +++ b/src/lib/hooks/tests/load_error_callout_library.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2013-2019 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -38,5 +38,10 @@ unload() { return (1); } +int +multi_threading_compatible() { + return (0); +} + };