diff options
author | Geert Bosch <geert@mongodb.com> | 2015-01-22 12:09:54 -0500 |
---|---|---|
committer | Geert Bosch <geert@mongodb.com> | 2015-01-22 12:21:50 -0500 |
commit | 4c83a604004c329ac114c53df38ac96421ebcf83 (patch) | |
tree | 3a4aeca2c8f500ff199bdabdb0a8ae5054a2967b | |
parent | ab9ac2126075fec83c579113bc0c81ec08b797de (diff) | |
download | mongo-4c83a604004c329ac114c53df38ac96421ebcf83.tar.gz |
SERVER-16773: Mark threads idle for tcmalloc
-rw-r--r-- | src/mongo/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/db/concurrency/lock_state.cpp | 9 | ||||
-rw-r--r-- | src/mongo/util/concurrency/synchronization.cpp | 28 | ||||
-rw-r--r-- | src/mongo/util/concurrency/synchronization.h | 25 | ||||
-rw-r--r-- | src/mongo/util/net/message_server_port.cpp | 18 | ||||
-rw-r--r-- | src/mongo/util/tcmalloc_server_status_section.cpp | 28 |
6 files changed, 105 insertions, 4 deletions
diff --git a/src/mongo/SConscript b/src/mongo/SConscript index a6474dd1281..b51b1238fd6 100644 --- a/src/mongo/SConscript +++ b/src/mongo/SConscript @@ -74,6 +74,7 @@ env.Library('foundation', "util/startup_test.cpp", ], LIBDEPS=['stacktrace', + 'synchronization', '$BUILD_DIR/mongo/base/base', '$BUILD_DIR/mongo/logger/logger', '$BUILD_DIR/mongo/platform/platform', diff --git a/src/mongo/db/concurrency/lock_state.cpp b/src/mongo/db/concurrency/lock_state.cpp index a37ddc2d8fe..84ff751d2b1 100644 --- a/src/mongo/db/concurrency/lock_state.cpp +++ b/src/mongo/db/concurrency/lock_state.cpp @@ -35,6 +35,7 @@ #include "mongo/db/global_environment_experiment.h" #include "mongo/db/namespace_string.h" #include "mongo/platform/compiler.h" +#include "mongo/util/concurrency/synchronization.h" #include "mongo/util/debug_util.h" #include "mongo/util/log.h" #include "mongo/util/mongoutils/str.h" @@ -726,7 +727,10 @@ namespace { } // If infinite timeout was requested, just keep waiting - if (timeoutMs == UINT_MAX) continue; + if (timeoutMs == UINT_MAX) { + markThreadIdle(); + continue; + } const unsigned elapsedTimeMs = elapsedTimeMicros / 1000; waitTimeMs = (elapsedTimeMs < timeoutMs) ? @@ -735,6 +739,9 @@ namespace { if (waitTimeMs == 0) { break; } + + // We have waited for a while and may likely be waiting even longer, mark us as idle + markThreadIdle(); } // Cleanup the state, since this is an unused lock now diff --git a/src/mongo/util/concurrency/synchronization.cpp b/src/mongo/util/concurrency/synchronization.cpp index b33bb2eeef9..3121989123a 100644 --- a/src/mongo/util/concurrency/synchronization.cpp +++ b/src/mongo/util/concurrency/synchronization.cpp @@ -27,13 +27,39 @@ * then also delete it in the license file. */ +#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kDefault + #include "synchronization.h" #include <boost/date_time/posix_time/posix_time.hpp> +#include "mongo/util/log.h" + namespace mongo { - Notification::Notification() : _mutex ( "Notification" ){ +namespace { + ThreadIdleCallback threadIdleCallback; +} // namespace + + void registerThreadIdleCallback(ThreadIdleCallback callback) { + invariant(!threadIdleCallback); + threadIdleCallback = callback; + } + + void markThreadIdle() { + if (!threadIdleCallback) { + return; + } + try { + threadIdleCallback(); + } + catch (...) { + severe() << "Exception escaped from threadIdleCallback"; + fassertFailedNoTrace(28603); + } + } + + Notification::Notification() : _mutex ( "Notification" ) { lookFor = 1; cur = 0; } diff --git a/src/mongo/util/concurrency/synchronization.h b/src/mongo/util/concurrency/synchronization.h index 7446817c309..39ddc4edce5 100644 --- a/src/mongo/util/concurrency/synchronization.h +++ b/src/mongo/util/concurrency/synchronization.h @@ -36,9 +36,30 @@ namespace mongo { + /** + * Type of callback functions that can be invoked when markThreadIdle() runs. + * These functions *must not throw*. + */ + typedef void(*ThreadIdleCallback)(); + + /** + * Informs the registered listener that this thread believes it may go idle for an extended + * period. The caller should avoid calling markThreadIdle at a high rate, as it can both be + * moderately costly itself and in terms of distributed overhead for subsequent malloc/free + * calls. + */ + void markThreadIdle(); + + /** + * Allows for registering callbacks for when threads go idle and become active. This is used + * by TCMalloc to return freed memory to its central freelist at appropriate points, so it + * won't happen during critical sections while holding locks. Calling this is not thread-safe. + */ + void registerThreadIdleCallback(ThreadIdleCallback callback); + /* - * A class to establish a synchronization point between two threads. One thread is the waiter and one is - * the notifier. After the notification event, both proceed normally. + * A class to establish a synchronization point between two threads. One thread is the waiter + * and one is the notifier. After the notification event, both proceed normally. * * This class is thread-safe. */ diff --git a/src/mongo/util/net/message_server_port.cpp b/src/mongo/util/net/message_server_port.cpp index c5a9d4fb4d6..f536cb394f5 100644 --- a/src/mongo/util/net/message_server_port.cpp +++ b/src/mongo/util/net/message_server_port.cpp @@ -40,6 +40,7 @@ #include "mongo/db/server_options.h" #include "mongo/db/stats/counters.h" #include "mongo/stdx/functional.h" +#include "mongo/util/concurrency/synchronization.h" #include "mongo/util/concurrency/thread_name.h" #include "mongo/util/concurrency/ticketholder.h" #include "mongo/util/debug_util.h" @@ -203,6 +204,8 @@ namespace { portWithHandler->psock->setLogLevel(logger::LogSeverity::Debug(1)); Message m; + int64_t lastIdle = Listener::getElapsedTimeMillis(); + int64_t avgMillisBetweenIdle = 0; try { LastError * le = new LastError(); lastError.reset( le ); // lastError now has ownership @@ -227,6 +230,21 @@ namespace { handler->process(m, portWithHandler.get(), le); networkCounter.hit(portWithHandler->psock->getBytesIn(), portWithHandler->psock->getBytesOut()); + + // Connections that don't run at a high rate should mark an idle point + // between operations to allow cleanup of the thread-local malloc cache. + // Just before a receive is a reasonable point, as we may overlap with + // the processing of a command response. Avoid doing this in very active + // threads as they are actively using their memory and not experiencing + // resource starvation. Use the course clock with averaging for efficiency. + + const int64_t now = Listener::getElapsedTimeMillis(); + const int64_t millisSinceIdle = now - lastIdle; + avgMillisBetweenIdle = (7 * avgMillisBetweenIdle + millisSinceIdle) / 8; + if (avgMillisBetweenIdle >= 10) { + markThreadIdle(); + } + lastIdle = now; } } catch ( AssertionException& e ) { diff --git a/src/mongo/util/tcmalloc_server_status_section.cpp b/src/mongo/util/tcmalloc_server_status_section.cpp index 42993ffed3a..36de581ed01 100644 --- a/src/mongo/util/tcmalloc_server_status_section.cpp +++ b/src/mongo/util/tcmalloc_server_status_section.cpp @@ -29,10 +29,38 @@ #include <third_party/gperftools-2.2/src/gperftools/malloc_extension.h> +#include "mongo/base/init.h" #include "mongo/db/commands/server_status.h" +#include "mongo/util/concurrency/synchronization.h" +#include "mongo/util/net/listen.h" namespace mongo { + namespace { + // If many clients are used, the per-thread caches become smaller and chances of + // rebalancing of free space during critical sections increases. In such situations, + // it is better to release memory when it is likely the thread will be blocked for + // a long time. + const int kManyClients = 40; + + /** + * Callback to allow TCMalloc to release freed memory to the central list at + * favorable times. Ideally would do some milder cleanup or scavenge... + */ + void threadStateChange() { + if (Listener::globalTicketHolder.used() > kManyClients) { + // Mark thread busy while we're idle, so that overhead is incurred now. + MallocExtension::instance()->MarkThreadIdle(); + MallocExtension::instance()->MarkThreadBusy(); + } + } + + // Register threadStateChange callback + MONGO_INITIALIZER(TCMallocThreadIdleListener)(InitializerContext*) { + registerThreadIdleCallback(&threadStateChange); + return Status::OK(); + } + class TCMallocServerStatusSection : public ServerStatusSection { public: |