diff options
author | Jonathan Reams <jbreams@mongodb.com> | 2016-09-07 14:18:23 -0400 |
---|---|---|
committer | Jonathan Reams <jbreams@mongodb.com> | 2017-06-14 16:28:42 -0400 |
commit | 1f0a8a0fcc5441627e74b6ac9bea9e730eee6da1 (patch) | |
tree | c26237e8d0a4e3f066b555779a091fff5360b598 | |
parent | 37d41b0b07404a391a1c23ef0dfa91fae499a20d (diff) | |
download | mongo-1f0a8a0fcc5441627e74b6ac9bea9e730eee6da1.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/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 | 20 | ||||
-rw-r--r-- | src/mongo/scripting/mozjs/mongo.h | 3 | ||||
-rw-r--r-- | src/mongo/scripting/utils.cpp | 4 | ||||
-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 |
18 files changed, 140 insertions, 52 deletions
diff --git a/jstests/auth/server-4892.js b/jstests/auth/server-4892.js index ef0c95c868e..4ca6318230f 100644 --- a/jstests/auth/server-4892.js +++ b/jstests/auth/server-4892.js @@ -74,11 +74,7 @@ withMongod({auth: ""}, 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 04acb82290b..897ebfa1ca9 100644 --- a/jstests/core/list_collections1.js +++ b/jstests/core/list_collections1.js @@ -277,8 +277,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 03deeafb307..11bdc66d398 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/cursor1.js b/jstests/sharding/cursor1.js index e7d54ac85e4..eccc9a70c31 100644 --- a/jstests/sharding/cursor1.js +++ b/jstests/sharding/cursor1.js @@ -44,8 +44,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"); @@ -65,9 +63,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 ac186e6478b..c8663ee8dc8 100644 --- a/jstests/sharding/sharding_rs2.js +++ b/jstests/sharding/sharding_rs2.js @@ -136,10 +136,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 3f1e034fa40..ef524aa852e 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', 'engine.cpp', 'utils.cpp', ], @@ -183,5 +184,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 c4923e4aab9..b1c2855dd46 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/thread.h" @@ -38,6 +39,9 @@ namespace mongo { +// Returns the current interrupt interval from the setParameter value +int getScriptingEngineInterruptInterval(); + /** * DeadlineMonitor * @@ -132,12 +136,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}); + _nearestDeadlineWallclock - now > interruptInterval) { + _newDeadlineAvailable.wait_for(lk, interruptInterval); } else { _newDeadlineAvailable.wait_until(lk, _nearestDeadlineWallclock.toSystemTimePoint()); @@ -161,13 +174,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 095622e47cc..39dc350d7d5 100644 --- a/src/mongo/scripting/mozjs/mongo.cpp +++ b/src/mongo/scripting/mozjs/mongo.cpp @@ -44,12 +44,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, 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), @@ -89,8 +91,11 @@ 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, @@ -120,6 +125,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 10d8fcfe020..a9a9bf0a696 100644 --- a/src/mongo/scripting/utils.cpp +++ b/src/mongo/scripting/utils.cpp @@ -26,8 +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 81793143ec0..d01b2d9ec98 100644 --- a/src/mongo/shell/shell_utils_launcher.cpp +++ b/src/mongo/shell/shell_utils_launcher.cpp @@ -546,7 +546,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); @@ -556,7 +559,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 9b1d2f5ae10..1a991bda63a 100644 --- a/src/mongo/stdx/thread.h +++ b/src/mongo/stdx/thread.h @@ -29,6 +29,8 @@ #pragma once #include <boost/config.hpp> +#include <chrono> +#include <ctime> #include <exception> #include <thread> #include <type_traits> @@ -109,7 +111,37 @@ inline void swap(thread& lhs, thread& rhs) BOOST_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 9a628a04930..4ecde553370 100644 --- a/src/mongo/util/net/sock.cpp +++ b/src/mongo/util/net/sock.cpp @@ -691,9 +691,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; } @@ -707,11 +705,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); @@ -844,7 +846,7 @@ void Socket::handleSendError(int ret, const char* context) { #endif LOG(_logLevel) << "Socket " << context << " send() timed out " << remoteString() << endl; throw SocketException(SocketException::SEND_TIMEOUT, remoteString()); - } else { + } else if (mongo_errno != EINTR) { LOG(_logLevel) << "Socket " << context << " send() " << errnoWithDescription(mongo_errno) << ' ' << remoteString() << endl; throw SocketException(SocketException::SEND_ERROR, remoteString()); @@ -864,7 +866,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 74a36c38c09..399cbc09e9f 100644 --- a/src/mongo/util/net/sock.h +++ b/src/mongo/util/net/sock.h @@ -348,7 +348,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); private: void _init(); |