summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeert Bosch <geert@mongodb.com>2015-01-22 12:09:54 -0500
committerGeert Bosch <geert@mongodb.com>2015-01-22 12:21:50 -0500
commit4c83a604004c329ac114c53df38ac96421ebcf83 (patch)
tree3a4aeca2c8f500ff199bdabdb0a8ae5054a2967b
parentab9ac2126075fec83c579113bc0c81ec08b797de (diff)
downloadmongo-4c83a604004c329ac114c53df38ac96421ebcf83.tar.gz
SERVER-16773: Mark threads idle for tcmalloc
-rw-r--r--src/mongo/SConscript1
-rw-r--r--src/mongo/db/concurrency/lock_state.cpp9
-rw-r--r--src/mongo/util/concurrency/synchronization.cpp28
-rw-r--r--src/mongo/util/concurrency/synchronization.h25
-rw-r--r--src/mongo/util/net/message_server_port.cpp18
-rw-r--r--src/mongo/util/tcmalloc_server_status_section.cpp28
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: