# HG changeset patch # User Sebastien Jodogne # Date 1607589159 -3600 # Node ID 6a39ca7083b9f22e024db4be002eaf675f86cf64 # Parent 3150306fb4ad74a05aabd0abf901df7d869c5975 New config option "MallocArenaMax" to control memory usage on GNU/Linux diff -r 3150306fb4ad -r 6a39ca7083b9 NEWS --- a/NEWS Wed Dec 09 16:46:10 2020 +0100 +++ b/NEWS Thu Dec 10 09:32:39 2020 +0100 @@ -5,6 +5,7 @@ ------- * ZIP archives containing DICOM files can be uploaded using WebDAV +* New config option "MallocArenaMax" to control memory usage on GNU/Linux REST API -------- diff -r 3150306fb4ad -r 6a39ca7083b9 OrthancServer/CMakeLists.txt --- a/OrthancServer/CMakeLists.txt Wed Dec 09 16:46:10 2020 +0100 +++ b/OrthancServer/CMakeLists.txt Thu Dec 10 09:32:39 2020 +0100 @@ -284,6 +284,15 @@ ## Configuration of the C/C++ macros ##################################################################### +check_symbol_exists(mallopt "malloc.h" HAVE_MALLOPT) + +if (HAVE_MALLOPT) + add_definitions(-DHAVE_MALLOPT=1) +else() + add_definitions(-DHAVE_MALLOPT=0) +endif() + + if (STATIC_BUILD) add_definitions(-DORTHANC_STATIC=1) else() diff -r 3150306fb4ad -r 6a39ca7083b9 OrthancServer/Resources/Configuration.json --- a/OrthancServer/Resources/Configuration.json Wed Dec 09 16:46:10 2020 +0100 +++ b/OrthancServer/Resources/Configuration.json Thu Dec 10 09:32:39 2020 +0100 @@ -618,5 +618,16 @@ // immediately written to the disk. This option only makes sense if // the builtin filesystem storage area is used. It defaults to // "false" in Orthanc <= 1.7.3, and to "true" in Orthanc >= 1.7.4. - "SyncStorageArea" : true + "SyncStorageArea" : true, + + // If specified, on compatible systems, call "mallopt(M_ARENA_MAX, + // ...)" while starting Orthanc. This has the same effect at setting + // the environment variable "MALLOC_ARENA_MAX". This avoids large + // growth in RES memory if the threads of the embedded HTTP server + // have to allocate large chunks of memory (typically the case with + // large DICOM files). By setting "MallocArenaMax" to "N", these + // threads share "N" memory pools (known as "arenas"). Setting this + // option to "0" doesn't call mallopt()", which was the behavior of + // Orthanc <= 1.8.1. + "MallocArenaMax" : 5 } diff -r 3150306fb4ad -r 6a39ca7083b9 OrthancServer/Sources/OrthancInitialization.cpp --- a/OrthancServer/Sources/OrthancInitialization.cpp Wed Dec 09 16:46:10 2020 +0100 +++ b/OrthancServer/Sources/OrthancInitialization.cpp Thu Dec 10 09:32:39 2020 +0100 @@ -38,6 +38,14 @@ # include #endif +#if !defined(HAVE_MALLOPT) +# error Macro HAVE_MALLOPT must be defined +#endif + +#if HAVE_MALLOPT == 1 +# include +#endif + #include "OrthancInitialization.h" #include "../../OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.h" @@ -227,6 +235,11 @@ void OrthancInitialize(const char* configurationFile) { + static const char* LOCALE = "Locale"; + static const char* PKCS11 = "Pkcs11"; + static const char* DEFAULT_ENCODING = "DefaultEncoding"; + static const char* MALLOC_ARENA_MAX = "MallocArenaMax"; + OrthancConfiguration::WriterLock lock; InitializeServerEnumerations(); @@ -237,9 +250,9 @@ { std::string locale; - if (lock.GetJson().isMember("Locale")) + if (lock.GetJson().isMember(LOCALE)) { - locale = lock.GetConfiguration().GetStringParameter("Locale", ""); + locale = lock.GetConfiguration().GetStringParameter(LOCALE, ""); } bool loadPrivate = lock.GetConfiguration().GetBooleanParameter("LoadPrivateDictionary", true); @@ -248,9 +261,9 @@ // The Orthanc framework is now initialized - if (lock.GetJson().isMember("DefaultEncoding")) + if (lock.GetJson().isMember(DEFAULT_ENCODING)) { - std::string encoding = lock.GetConfiguration().GetStringParameter("DefaultEncoding", ""); + std::string encoding = lock.GetConfiguration().GetStringParameter(DEFAULT_ENCODING, ""); SetDefaultDicomEncoding(StringToEncoding(encoding.c_str())); } else @@ -258,9 +271,9 @@ SetDefaultDicomEncoding(ORTHANC_DEFAULT_DICOM_ENCODING); } - if (lock.GetJson().isMember("Pkcs11")) + if (lock.GetJson().isMember(PKCS11)) { - ConfigurePkcs11(lock.GetJson()["Pkcs11"]); + ConfigurePkcs11(lock.GetJson()[PKCS11]); } RegisterUserMetadata(lock.GetJson()); @@ -269,6 +282,28 @@ LoadCustomDictionary(lock.GetJson()); lock.GetConfiguration().RegisterFont(ServerResources::FONT_UBUNTU_MONO_BOLD_16); + +#if HAVE_MALLOPT == 1 + // New in Orthanc 1.9.0 + // https://book.orthanc-server.com/faq/scalability.html#controlling-memory-usage + unsigned int maxArena = lock.GetConfiguration().GetUnsignedIntegerParameter(MALLOC_ARENA_MAX, 5); + if (maxArena != 0) + { + // https://man7.org/linux/man-pages/man3/mallopt.3.html + LOG(INFO) << "Calling mallopt(M_ARENA_MAX, " << maxArena << ")"; + if (mallopt(M_ARENA_MAX, maxArena) != 1 /* success */) + { + throw OrthancException(ErrorCode_InternalError, "The call to mallopt(M_ARENA_MAX, " + + boost::lexical_cast(maxArena) + ") has failed"); + } + } +#else + if (lock.GetJson().isMember(MALLOC_ARENA_MAX)) + { + LOG(INFO) << "Your platform does not support mallopt(), ignoring configuration option \"" + << MALLOC_ARENA_MAX << "\""; + } +#endif }