diff options
author | Jonathan Reams <jbreams@mongodb.com> | 2016-09-07 14:18:23 -0400 |
---|---|---|
committer | Jonathan Reams <jbreams@mongodb.com> | 2016-09-16 10:18:34 -0400 |
commit | e3b42fd990070f48c11b233cec0c198098d1a48f (patch) | |
tree | 464665d664b18fe709ccf5e9d039a424b9489a12 | |
parent | 7693fa59c4470db729d85af99fb9cc3b264fa8c8 (diff) | |
download | mongo-e3b42fd990070f48c11b233cec0c198098d1a48f.tar.gz |
SERVER-26002 Make sure javascript sleep isn't interrupted
-rw-r--r-- | jstests/auth/server-4892.js | 6 | ||||
-rw-r--r-- | jstests/core/list_collections1.js | 3 | ||||
-rw-r--r-- | jstests/core/list_indexes.js | 3 | ||||
-rw-r--r-- | jstests/core/max_doc_size.js | 6 | ||||
-rw-r--r-- | jstests/noPassthroughWithMongod/connections_opened.js | 6 | ||||
-rw-r--r-- | jstests/sharding/auto2.js | 14 | ||||
-rw-r--r-- | jstests/sharding/cursor1.js | 5 | ||||
-rw-r--r-- | jstests/sharding/shard_kill_and_pooling.js | 4 | ||||
-rw-r--r-- | jstests/sharding/sharding_rs2.js | 7 | ||||
-rw-r--r-- | src/mongo/scripting/SConscript | 2 | ||||
-rw-r--r-- | src/mongo/scripting/deadline_monitor.cpp | 43 | ||||
-rw-r--r-- | src/mongo/scripting/deadline_monitor.h | 24 | ||||
-rw-r--r-- | src/mongo/scripting/mozjs/mongo.cpp | 21 | ||||
-rw-r--r-- | src/mongo/scripting/mozjs/mongo.h | 3 | ||||
-rw-r--r-- | src/mongo/scripting/utils.cpp | 3 | ||||
-rw-r--r-- | src/mongo/shell/shell_utils_launcher.cpp | 7 | ||||
-rw-r--r-- | src/mongo/stdx/thread.h | 34 | ||||
-rw-r--r-- | src/mongo/util/net/sock.cpp | 13 | ||||
-rw-r--r-- | src/mongo/util/net/sock.h | 2 |
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; |