summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan Reams <jbreams@mongodb.com>2016-09-07 14:18:23 -0400
committerJonathan Reams <jbreams@mongodb.com>2016-09-16 10:18:34 -0400
commite3b42fd990070f48c11b233cec0c198098d1a48f (patch)
tree464665d664b18fe709ccf5e9d039a424b9489a12
parent7693fa59c4470db729d85af99fb9cc3b264fa8c8 (diff)
downloadmongo-e3b42fd990070f48c11b233cec0c198098d1a48f.tar.gz
SERVER-26002 Make sure javascript sleep isn't interrupted
-rw-r--r--jstests/auth/server-4892.js6
-rw-r--r--jstests/core/list_collections1.js3
-rw-r--r--jstests/core/list_indexes.js3
-rw-r--r--jstests/core/max_doc_size.js6
-rw-r--r--jstests/noPassthroughWithMongod/connections_opened.js6
-rw-r--r--jstests/sharding/auto2.js14
-rw-r--r--jstests/sharding/cursor1.js5
-rw-r--r--jstests/sharding/shard_kill_and_pooling.js4
-rw-r--r--jstests/sharding/sharding_rs2.js7
-rw-r--r--src/mongo/scripting/SConscript2
-rw-r--r--src/mongo/scripting/deadline_monitor.cpp43
-rw-r--r--src/mongo/scripting/deadline_monitor.h24
-rw-r--r--src/mongo/scripting/mozjs/mongo.cpp21
-rw-r--r--src/mongo/scripting/mozjs/mongo.h3
-rw-r--r--src/mongo/scripting/utils.cpp3
-rw-r--r--src/mongo/shell/shell_utils_launcher.cpp7
-rw-r--r--src/mongo/stdx/thread.h34
-rw-r--r--src/mongo/util/net/sock.cpp13
-rw-r--r--src/mongo/util/net/sock.h2
19 files changed, 145 insertions, 61 deletions
diff --git a/jstests/auth/server-4892.js b/jstests/auth/server-4892.js
index 4926ea7e131..a6bbf89e69e 100644
--- a/jstests/auth/server-4892.js
+++ b/jstests/auth/server-4892.js
@@ -72,11 +72,7 @@ withMongod({auth: ""}, function runTest(mongod) {
cursor.next();
expectNumLiveCursors(mongod, 1);
- cursor = null;
- // NOTE(schwerin): We assume that after setting cursor = null, there are no remaining
- // references
- // to the cursor, and that gc() will deterministically garbage collect it.
- gc();
+ cursor.close();
// NOTE(schwerin): dbKillCursors gets piggybacked on subsequent messages on the
// connection, so we
diff --git a/jstests/core/list_collections1.js b/jstests/core/list_collections1.js
index b6466ea4a87..1ccca1ba680 100644
--- a/jstests/core/list_collections1.js
+++ b/jstests/core/list_collections1.js
@@ -249,8 +249,7 @@
res = mydb.runCommand("listCollections", {cursor: {batchSize: 0}});
cursor = new DBCommandCursor(mydb.getMongo(), res, 2);
- cursor = null;
- gc(); // Shell will send a killCursors message when cleaning up underlying cursor.
+ cursor.close();
cursor = new DBCommandCursor(mydb.getMongo(), res, 2);
assert.throws(function() {
cursor.hasNext();
diff --git a/jstests/core/list_indexes.js b/jstests/core/list_indexes.js
index 520406be59f..de0f4473980 100644
--- a/jstests/core/list_indexes.js
+++ b/jstests/core/list_indexes.js
@@ -164,8 +164,7 @@
res = coll.runCommand("listIndexes", {cursor: {batchSize: 0}});
cursor = new DBCommandCursor(coll.getDB().getMongo(), res, 2);
- cursor = null;
- gc(); // Shell will send a killCursors message when cleaning up underlying cursor.
+ cursor.close();
cursor = new DBCommandCursor(coll.getDB().getMongo(), res, 2);
assert.throws(function() {
cursor.hasNext();
diff --git a/jstests/core/max_doc_size.js b/jstests/core/max_doc_size.js
index 383e4b4b76d..5180add4f87 100644
--- a/jstests/core/max_doc_size.js
+++ b/jstests/core/max_doc_size.js
@@ -1,11 +1,7 @@
var maxBsonObjectSize = db.isMaster().maxBsonObjectSize;
var docOverhead = Object.bsonsize({_id: new ObjectId(), x: ''});
var maxStrSize = maxBsonObjectSize - docOverhead;
-
-var maxStr = 'a';
-while (maxStr.length < maxStrSize)
- maxStr += 'a';
-
+var maxStr = 'a'.repeat(maxStrSize);
var coll = db.max_doc_size;
coll.drop();
diff --git a/jstests/noPassthroughWithMongod/connections_opened.js b/jstests/noPassthroughWithMongod/connections_opened.js
index 2ec192ed1e2..d3d141ab5a1 100644
--- a/jstests/noPassthroughWithMongod/connections_opened.js
+++ b/jstests/noPassthroughWithMongod/connections_opened.js
@@ -20,7 +20,8 @@ var signalCollection = 'keepRunning';
function createPersistentConnection() {
assert.soon(function() {
try {
- return new Mongo(db.getMongo().host);
+ permConns.push(new Mongo(db.getMongo().host));
+ return true;
} catch (x) {
return false;
}
@@ -95,7 +96,4 @@ jsTestLog("Testing that current connections counter went down after temporary co
waitForConnections(originalConnInfo.current + numPerTypeToCreate,
originalConnInfo.totalCreated + numPerTypeToCreate * 2);
-persistent = null;
-gc();
-
MongoRunner.stopMongod(mongo);
diff --git a/jstests/sharding/auto2.js b/jstests/sharding/auto2.js
index 3c8eb168d7b..f3d8bf299ec 100644
--- a/jstests/sharding/auto2.js
+++ b/jstests/sharding/auto2.js
@@ -98,8 +98,7 @@
for (i = 0; i < 100; i++) {
cursor = coll.find().batchSize(5);
cursor.next();
- cursor = null;
- gc();
+ cursor.close();
}
print("checkpoint D");
@@ -110,14 +109,12 @@
for (i = 0; i < 100; i++)
t.save({_id: i});
for (i = 0; i < 100; i++) {
- t.find().batchSize(2).next();
+ var cursor = t.find().batchSize(2);
+ cursor.next();
assert.lt(0, db.serverStatus().metrics.cursor.open.total, "cursor1");
- gc();
+ cursor.close();
}
- for (i = 0; i < 100; i++) {
- gc();
- }
assert.eq(0, db.serverStatus().metrics.cursor.open.total, "cursor2");
// Stop the balancer, otherwise it may grab some connections from the pool for itself
@@ -132,8 +129,7 @@
temp2 = conn.getDB("test2").foobar;
assert.eq(conn._fullNameSpace, t._fullNameSpace, "check close 1");
assert(temp2.findOne(), "check close 2");
- conn = null;
- gc();
+ conn.close();
}
print("checkpoint F");
diff --git a/jstests/sharding/cursor1.js b/jstests/sharding/cursor1.js
index 3aee9ff2cb5..d9b464512e7 100644
--- a/jstests/sharding/cursor1.js
+++ b/jstests/sharding/cursor1.js
@@ -43,8 +43,6 @@
assert.eq(numObjs, cursor3.itcount(), "c3");
// Test that a cursor with a 1 second timeout eventually times out.
- gc();
- gc();
var cur = db.foo.find().batchSize(2);
assert(cur.next(), "T1");
assert(cur.next(), "T2");
@@ -64,9 +62,6 @@
}
}, "cursor failed to time out", /*timeout*/ 30000, /*interval*/ 5000);
- gc();
- gc();
-
s.stop();
})();
diff --git a/jstests/sharding/shard_kill_and_pooling.js b/jstests/sharding/shard_kill_and_pooling.js
index 6b8397f9e37..369ebe6c3ee 100644
--- a/jstests/sharding/shard_kill_and_pooling.js
+++ b/jstests/sharding/shard_kill_and_pooling.js
@@ -44,10 +44,8 @@ for (var test = 0; test < 2; test++) {
jsTest.log("Returning the connections back to the pool.");
for (var i = 0; i < conns.length; i++) {
- conns[i] = null;
+ conns[i].close();
}
- // Make sure we return connections back to the pool
- gc();
// Don't make test fragile by linking to format of shardConnPoolStats, but this is useful if
// something goes wrong.
diff --git a/jstests/sharding/sharding_rs2.js b/jstests/sharding/sharding_rs2.js
index 7da1a239012..648512b051d 100644
--- a/jstests/sharding/sharding_rs2.js
+++ b/jstests/sharding/sharding_rs2.js
@@ -139,10 +139,9 @@
assert.eq(100, ts.find().itcount(), "B5");
assert.eq(100, ts.find().batchSize(5).itcount(), "B6");
- t.find().batchSize(3).next();
- gc();
- gc();
- gc();
+ var cursor = t.find().batchSize(3);
+ cursor.next();
+ cursor.close();
// --- sharded ----
diff --git a/src/mongo/scripting/SConscript b/src/mongo/scripting/SConscript
index 8c2604c76b6..18574fb2f16 100644
--- a/src/mongo/scripting/SConscript
+++ b/src/mongo/scripting/SConscript
@@ -10,6 +10,7 @@ Import([
env.Library(
target='scripting_common',
source=[
+ 'deadline_monitor.cpp',
'dbdirectclient_factory.cpp',
'engine.cpp',
'utils.cpp',
@@ -176,5 +177,6 @@ env.CppUnitTest(
'deadline_monitor_test.cpp',
],
LIBDEPS=[
+ 'scripting_common',
],
)
diff --git a/src/mongo/scripting/deadline_monitor.cpp b/src/mongo/scripting/deadline_monitor.cpp
new file mode 100644
index 00000000000..5eb0f52e5de
--- /dev/null
+++ b/src/mongo/scripting/deadline_monitor.cpp
@@ -0,0 +1,43 @@
+/**
+ * Copyright (C) 2016 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/scripting/deadline_monitor.h"
+
+#include "mongo/db/server_parameters.h"
+
+namespace mongo {
+
+MONGO_EXPORT_SERVER_PARAMETER(scriptingEngineInterruptIntervalMS, int, 1000);
+
+int getScriptingEngineInterruptInterval() {
+ return scriptingEngineInterruptIntervalMS.load();
+}
+
+} // namespace mongo
diff --git a/src/mongo/scripting/deadline_monitor.h b/src/mongo/scripting/deadline_monitor.h
index cd5c532c974..127bc81288d 100644
--- a/src/mongo/scripting/deadline_monitor.h
+++ b/src/mongo/scripting/deadline_monitor.h
@@ -30,6 +30,7 @@
#include <cstdint>
#include "mongo/base/disallow_copying.h"
+#include "mongo/platform/atomic_word.h"
#include "mongo/platform/unordered_map.h"
#include "mongo/stdx/condition_variable.h"
#include "mongo/stdx/mutex.h"
@@ -39,6 +40,9 @@
namespace mongo {
+// Returns the current interrupt interval from the setParameter value
+int getScriptingEngineInterruptInterval();
+
/**
* DeadlineMonitor
*
@@ -133,12 +137,21 @@ private:
while (!_inShutdown) {
// get the next interval to wait
const Date_t now = Date_t::now();
+ const auto interruptInterval = Milliseconds{getScriptingEngineInterruptInterval()};
+
+ if (now - lastInterruptCycle > interruptInterval) {
+ for (const auto& task : _tasks) {
+ if (task.second > now)
+ task.first->interrupt();
+ }
+ lastInterruptCycle = now;
+ }
// wait for a task to be added or a deadline to expire
if (_nearestDeadlineWallclock > now) {
if (_nearestDeadlineWallclock == Date_t::max() ||
- _nearestDeadlineWallclock - now > Seconds{1}) {
- _newDeadlineAvailable.wait_for(lk, Seconds{1}.toSystemDuration());
+ _nearestDeadlineWallclock - now > interruptInterval) {
+ _newDeadlineAvailable.wait_for(lk, interruptInterval.toSystemDuration());
} else {
_newDeadlineAvailable.wait_until(lk,
_nearestDeadlineWallclock.toSystemTimePoint());
@@ -162,13 +175,6 @@ private:
++i;
}
}
-
- if (now - lastInterruptCycle > Seconds{1}) {
- for (auto it : _tasks) {
- it.first->interrupt();
- }
- lastInterruptCycle = now;
- }
}
}
diff --git a/src/mongo/scripting/mozjs/mongo.cpp b/src/mongo/scripting/mozjs/mongo.cpp
index 8df2629c08f..2c887804203 100644
--- a/src/mongo/scripting/mozjs/mongo.cpp
+++ b/src/mongo/scripting/mozjs/mongo.cpp
@@ -46,12 +46,14 @@
#include "mongo/scripting/mozjs/valuewriter.h"
#include "mongo/scripting/mozjs/wrapconstrainedmethod.h"
#include "mongo/stdx/memory.h"
+#include "mongo/util/assert_util.h"
namespace mongo {
namespace mozjs {
const JSFunctionSpec MongoBase::methods[] = {
MONGO_ATTACH_JS_CONSTRAINED_METHOD_NO_PROTO(auth, MongoLocalInfo, MongoExternalInfo),
+ MONGO_ATTACH_JS_CONSTRAINED_METHOD_NO_PROTO(close, MongoExternalInfo),
MONGO_ATTACH_JS_CONSTRAINED_METHOD_NO_PROTO(
copyDatabaseWithSCRAM, MongoLocalInfo, MongoExternalInfo),
MONGO_ATTACH_JS_CONSTRAINED_METHOD_NO_PROTO(cursorFromId, MongoLocalInfo, MongoExternalInfo),
@@ -91,8 +93,12 @@ const JSFunctionSpec MongoExternalInfo::freeFunctions[4] = {
namespace {
DBClientBase* getConnection(JS::CallArgs& args) {
- return static_cast<std::shared_ptr<DBClientBase>*>(JS_GetPrivate(args.thisv().toObjectOrNull()))
- ->get();
+ auto ret =
+ static_cast<std::shared_ptr<DBClientBase>*>(JS_GetPrivate(args.thisv().toObjectOrNull()))
+ ->get();
+ uassert(
+ ErrorCodes::BadValue, "Trying to get connection for closed Mongo object", ret != nullptr);
+ return ret;
}
void setCursor(JS::HandleObject target,
@@ -166,6 +172,17 @@ void MongoBase::finalize(JSFreeOp* fop, JSObject* obj) {
}
}
+void MongoBase::Functions::close::call(JSContext* cx, JS::CallArgs args) {
+ getConnection(args);
+
+ auto thisv = args.thisv().toObjectOrNull();
+ auto conn = static_cast<std::shared_ptr<DBClientBase>*>(JS_GetPrivate(thisv));
+
+ conn->reset();
+
+ args.rval().setUndefined();
+}
+
void MongoBase::Functions::runCommand::call(JSContext* cx, JS::CallArgs args) {
if (args.length() != 3)
uasserted(ErrorCodes::BadValue, "runCommand needs 3 args");
diff --git a/src/mongo/scripting/mozjs/mongo.h b/src/mongo/scripting/mozjs/mongo.h
index c3a82d10998..297b4907c6d 100644
--- a/src/mongo/scripting/mozjs/mongo.h
+++ b/src/mongo/scripting/mozjs/mongo.h
@@ -46,6 +46,7 @@ struct MongoBase : public BaseInfo {
struct Functions {
MONGO_DECLARE_JS_FUNCTION(auth);
MONGO_DECLARE_JS_FUNCTION(copyDatabaseWithSCRAM);
+ MONGO_DECLARE_JS_FUNCTION(close);
MONGO_DECLARE_JS_FUNCTION(cursorFromId);
MONGO_DECLARE_JS_FUNCTION(cursorHandleFromId);
MONGO_DECLARE_JS_FUNCTION(find);
@@ -63,7 +64,7 @@ struct MongoBase : public BaseInfo {
MONGO_DECLARE_JS_FUNCTION(getMaxWireVersion);
};
- static const JSFunctionSpec methods[18];
+ static const JSFunctionSpec methods[19];
static const char* const className;
static const unsigned classFlags = JSCLASS_HAS_PRIVATE;
diff --git a/src/mongo/scripting/utils.cpp b/src/mongo/scripting/utils.cpp
index 91bd67a5e0f..e9c2e4aa477 100644
--- a/src/mongo/scripting/utils.cpp
+++ b/src/mongo/scripting/utils.cpp
@@ -26,9 +26,12 @@
* then also delete it in the license file.
*/
+#include "mongo/platform/basic.h"
+
#include "mongo/bson/json.h"
#include "mongo/scripting/engine.h"
#include "mongo/util/md5.hpp"
+#include "mongo/util/time_support.h"
namespace mongo {
diff --git a/src/mongo/shell/shell_utils_launcher.cpp b/src/mongo/shell/shell_utils_launcher.cpp
index d69bdc6452b..b95e814777e 100644
--- a/src/mongo/shell/shell_utils_launcher.cpp
+++ b/src/mongo/shell/shell_utils_launcher.cpp
@@ -656,7 +656,10 @@ bool wait_for_pid(ProcessId pid, bool block = true, int* exit_code = NULL) {
}
#else
int tmp;
- bool ret = (pid.toNative() == waitpid(pid.toNative(), &tmp, (block ? 0 : WNOHANG)));
+ int ret;
+ do {
+ ret = waitpid(pid.toNative(), &tmp, (block ? 0 : WNOHANG));
+ } while (ret == -1 && errno == EINTR);
if (ret && exit_code) {
if (WIFEXITED(tmp)) {
*exit_code = WEXITSTATUS(tmp);
@@ -666,7 +669,7 @@ bool wait_for_pid(ProcessId pid, bool block = true, int* exit_code = NULL) {
MONGO_UNREACHABLE;
}
}
- return ret;
+ return ret == pid.toNative();
#endif
}
diff --git a/src/mongo/stdx/thread.h b/src/mongo/stdx/thread.h
index d81dc1bf315..c85c654445a 100644
--- a/src/mongo/stdx/thread.h
+++ b/src/mongo/stdx/thread.h
@@ -28,6 +28,8 @@
#pragma once
+#include <chrono>
+#include <ctime>
#include <exception>
#include <thread>
#include <type_traits>
@@ -105,7 +107,37 @@ inline void swap(thread& lhs, thread& rhs) noexcept {
lhs.swap(rhs);
}
-namespace this_thread = ::std::this_thread; // NOLINT
+namespace this_thread {
+using std::this_thread::get_id;
+using std::this_thread::yield;
+
+#ifdef _WIN32
+using std::this_thread::sleep_for;
+using std::this_thread::sleep_until;
+#else
+template <class Rep, class Period>
+inline void sleep_for(const std::chrono::duration<Rep, Period>& sleep_duration) {
+ if (sleep_duration <= sleep_duration.zero())
+ return;
+
+ const auto seconds = std::chrono::duration_cast<std::chrono::seconds>(sleep_duration);
+ const auto nanoseconds =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(sleep_duration - seconds);
+ struct timespec sleepVal = {static_cast<std::time_t>(seconds.count()),
+ static_cast<long>(nanoseconds.count())};
+ struct timespec remainVal;
+ while (nanosleep(&sleepVal, &remainVal) == -1 && errno == EINTR) {
+ sleepVal = remainVal;
+ }
+}
+
+template <class Clock, class Duration>
+void sleep_until(const std::chrono::time_point<Clock, Duration>& sleep_time) {
+ const auto now = Clock::now();
+ sleep_for(sleep_time - now);
+}
+#endif
+} // namespace this_thread
} // namespace stdx
} // namespace mongo
diff --git a/src/mongo/util/net/sock.cpp b/src/mongo/util/net/sock.cpp
index 9e172335663..8202c28d2fa 100644
--- a/src/mongo/util/net/sock.cpp
+++ b/src/mongo/util/net/sock.cpp
@@ -433,9 +433,7 @@ int Socket::_send(const char* data, int len, const char* context) {
}
#endif
int ret = ::send(_fd, data, len, portSendFlags);
- if (ret < 0) {
- handleSendError(ret, context);
- }
+
return ret;
}
@@ -449,11 +447,15 @@ void Socket::send(const char* data, int len, const char* context) {
#else
errno = ENETUNREACH;
#endif
- handleSendError(ret, context);
} else {
ret = _send(data, len, context);
}
+ if (ret < 0) {
+ handleSendError(ret, context);
+ continue;
+ }
+
_bytesOut += ret;
fassert(16507, ret <= len);
@@ -594,7 +596,7 @@ void Socket::handleSendError(int ret, const char* context) {
#endif
LOG(_logLevel) << "Socket " << context << " send() timed out " << remoteString();
throw SocketException(SocketException::SEND_TIMEOUT, remoteString());
- } else {
+ } else if (mongo_errno != EINTR) {
LOG(_logLevel) << "Socket " << context << " send() " << errnoWithDescription(mongo_errno)
<< ' ' << remoteString();
throw SocketException(SocketException::SEND_ERROR, remoteString());
@@ -614,7 +616,6 @@ void Socket::handleRecvError(int ret, int len) {
int e = errno;
#if defined(EINTR)
if (e == EINTR) {
- LOG(_logLevel) << "EINTR returned from recv(), retrying";
return;
}
#endif
diff --git a/src/mongo/util/net/sock.h b/src/mongo/util/net/sock.h
index 3e4f0616ff9..44a83b6ef5b 100644
--- a/src/mongo/util/net/sock.h
+++ b/src/mongo/util/net/sock.h
@@ -232,7 +232,7 @@ public:
}
void handleRecvError(int ret, int len);
- MONGO_COMPILER_NORETURN void handleSendError(int ret, const char* context);
+ void handleSendError(int ret, const char* context);
std::string getSNIServerName() const;