mirror of
https://github.com/mongodb/mongo.git
synced 2024-11-28 07:59:02 +01:00
SERVER-17570: Fix NT Service shutdown race condition
This commit is contained in:
parent
7854198178
commit
0f6f2e9a19
@ -241,7 +241,7 @@ env.CppUnitTest('sock_test', ['util/net/sock_test.cpp'],
|
||||
|
||||
env.CppUnitTest('curop_test',
|
||||
['db/curop_test.cpp'],
|
||||
LIBDEPS=['serveronly', 'coredb', 'coreserver'],
|
||||
LIBDEPS=['serveronly', 'coredb', 'coreserver', 'ntservice_mock'],
|
||||
NO_CRUTCH=True)
|
||||
|
||||
env.Library('index_names',["db/index_names.cpp"])
|
||||
@ -583,7 +583,10 @@ env.Library('ntservice', ['util/ntservice.cpp'],
|
||||
if windows:
|
||||
env.CppUnitTest('ntservice_test', 'util/ntservice_test.cpp',
|
||||
LIBDEPS=['ntservice'],
|
||||
LIBS=['shell32', env['LIBS']])
|
||||
LIBS=['shell32', env['LIBS']],
|
||||
NO_CRUTCH=True)
|
||||
|
||||
env.Library('ntservice_mock', ['util/ntservice_mock.cpp'])
|
||||
|
||||
env.Library(
|
||||
target='scripting_common',
|
||||
@ -895,7 +898,8 @@ env.CppUnitTest("scoped_db_conn_test", [ "client/scoped_db_conn_test.cpp" ],
|
||||
"coreshard",
|
||||
"mongocommon",
|
||||
"message_server_port",
|
||||
"mongoscore"],
|
||||
"mongoscore",
|
||||
"ntservice_mock"],
|
||||
NO_CRUTCH=True)
|
||||
|
||||
env.CppUnitTest("shard_test", [ "s/shard_test.cpp" ],
|
||||
@ -936,6 +940,7 @@ env.CppUnitTest(
|
||||
"$BUILD_DIR/mongo/serveronly",
|
||||
"$BUILD_DIR/mongo/coreserver",
|
||||
"$BUILD_DIR/mongo/coredb",
|
||||
"$BUILD_DIR/mongo/ntservice_mock",
|
||||
],
|
||||
NO_CRUTCH = True,
|
||||
)
|
||||
@ -949,6 +954,7 @@ env.CppUnitTest(
|
||||
"$BUILD_DIR/mongo/serveronly",
|
||||
"$BUILD_DIR/mongo/coreserver",
|
||||
"$BUILD_DIR/mongo/coredb",
|
||||
"$BUILD_DIR/mongo/ntservice_mock",
|
||||
],
|
||||
NO_CRUTCH = True,
|
||||
)
|
||||
@ -962,6 +968,7 @@ env.CppUnitTest(
|
||||
"$BUILD_DIR/mongo/serveronly",
|
||||
"$BUILD_DIR/mongo/coreserver",
|
||||
"$BUILD_DIR/mongo/coredb",
|
||||
"$BUILD_DIR/mongo/ntservice_mock",
|
||||
],
|
||||
NO_CRUTCH = True,
|
||||
)
|
||||
@ -1124,6 +1131,7 @@ env.Install(
|
||||
"tools/mongobridge_options_init.cpp"
|
||||
],
|
||||
LIBDEPS=[
|
||||
"ntservice_mock",
|
||||
"serveronly",
|
||||
"coreserver",
|
||||
"coredb",
|
||||
@ -1135,6 +1143,7 @@ env.Install(
|
||||
"client/examples/mongoperf.cpp",
|
||||
],
|
||||
LIBDEPS=[
|
||||
"ntservice_mock",
|
||||
"serveronly",
|
||||
"coreserver",
|
||||
"coredb",
|
||||
@ -1227,6 +1236,7 @@ env.Install('$BUILD_ROOT/', env.Program('file_allocator_bench',
|
||||
'serveronly',
|
||||
'coredb',
|
||||
'coreserver',
|
||||
"ntservice_mock",
|
||||
]))
|
||||
|
||||
env.Alias('file_allocator_bench', "$BUILD_ROOT/" + add_exe("file_allocator_bench"))
|
||||
|
@ -77,6 +77,8 @@ namespace mongo {
|
||||
return shuttingDown;
|
||||
}
|
||||
|
||||
void signalShutdown() {}
|
||||
|
||||
DBClientBase* createDirectClient(OperationContext* txn) { return NULL; }
|
||||
|
||||
void dbexit(ExitCode rc, const char *why){
|
||||
|
@ -58,6 +58,7 @@
|
||||
#include "mongo/util/fail_point_service.h"
|
||||
#include "mongo/util/log.h"
|
||||
#include "mongo/util/md5.hpp"
|
||||
#include "mongo/util/ntservice.h"
|
||||
#include "mongo/util/processinfo.h"
|
||||
#include "mongo/util/ramlog.h"
|
||||
#include "mongo/util/version_reporting.h"
|
||||
@ -323,8 +324,23 @@ namespace mongo {
|
||||
|
||||
log() << "terminating, shutdown command received" << endl;
|
||||
|
||||
exitCleanly(EXIT_CLEAN); // this never returns
|
||||
invariant(false);
|
||||
#if defined(_WIN32)
|
||||
// Signal the ServiceMain thread to shutdown.
|
||||
if(ntservice::shouldStartService()) {
|
||||
signalShutdown();
|
||||
|
||||
// Client expects us to abruptly close the socket as part of exiting
|
||||
// so this function is not allowed to return.
|
||||
// The ServiceMain thread will quit for us so just sleep until it does.
|
||||
while (true)
|
||||
sleepsecs(60); // Loop forever
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
exitCleanly(EXIT_CLEAN); // this never returns
|
||||
invariant(false);
|
||||
}
|
||||
}
|
||||
|
||||
/* for testing purposes only */
|
||||
|
@ -87,6 +87,7 @@ env.CppUnitTest(
|
||||
"$BUILD_DIR/mongo/coreserver",
|
||||
"$BUILD_DIR/mongo/coredb",
|
||||
"$BUILD_DIR/mongo/mocklib",
|
||||
"$BUILD_DIR/mongo/ntservice_mock",
|
||||
],
|
||||
NO_CRUTCH = True,
|
||||
)
|
||||
@ -102,6 +103,7 @@ env.CppUnitTest(
|
||||
"$BUILD_DIR/mongo/coreserver",
|
||||
"$BUILD_DIR/mongo/coredb",
|
||||
"$BUILD_DIR/mongo/mocklib",
|
||||
"$BUILD_DIR/mongo/ntservice_mock",
|
||||
],
|
||||
NO_CRUTCH = True,
|
||||
)
|
||||
@ -116,6 +118,7 @@ env.CppUnitTest(
|
||||
"$BUILD_DIR/mongo/serveronly",
|
||||
"$BUILD_DIR/mongo/coreserver",
|
||||
"$BUILD_DIR/mongo/coredb",
|
||||
"$BUILD_DIR/mongo/ntservice_mock",
|
||||
],
|
||||
NO_CRUTCH = True,
|
||||
)
|
||||
|
@ -1105,12 +1105,25 @@ namespace mongo {
|
||||
getGlobalEnvironment()->shutdownGlobalStorageEngineCleanly();
|
||||
}
|
||||
|
||||
// shutdownLock
|
||||
//
|
||||
// Protects:
|
||||
// Ensures shutdown is single threaded.
|
||||
// Lock Ordering:
|
||||
// No restrictions
|
||||
boost::mutex shutdownLock;
|
||||
|
||||
void signalShutdown() {
|
||||
// Notify all threads shutdown has started
|
||||
shutdownInProgress.fetchAndAdd(1);
|
||||
}
|
||||
|
||||
void exitCleanly(ExitCode code) {
|
||||
if (shutdownInProgress.compareAndSwap(0, 1) != 0) {
|
||||
while (true) {
|
||||
sleepsecs(1000);
|
||||
}
|
||||
}
|
||||
// Notify all threads shutdown has started
|
||||
shutdownInProgress.fetchAndAdd(1);
|
||||
|
||||
// Grab the shutdown lock to prevent concurrent callers
|
||||
boost::lock_guard<boost::mutex> lockguard(shutdownLock);
|
||||
|
||||
// Global storage engine may not be started in all cases before we exit
|
||||
if (getGlobalEnvironment()->getGlobalStorageEngine() == NULL) {
|
||||
|
@ -61,6 +61,7 @@ env.CppUnitTest(
|
||||
"$BUILD_DIR/mongo/coreserver",
|
||||
"$BUILD_DIR/mongo/coredb",
|
||||
"$BUILD_DIR/mongo/mocklib",
|
||||
"$BUILD_DIR/mongo/ntservice_mock",
|
||||
],
|
||||
NO_CRUTCH = True,
|
||||
)
|
||||
@ -236,6 +237,7 @@ env.CppUnitTest(
|
||||
"$BUILD_DIR/mongo/coreserver",
|
||||
"$BUILD_DIR/mongo/coredb",
|
||||
"$BUILD_DIR/mongo/mocklib",
|
||||
"$BUILD_DIR/mongo/ntservice_mock",
|
||||
],
|
||||
NO_CRUTCH = True,
|
||||
)
|
||||
|
@ -209,5 +209,6 @@ env.CppUnitTest('isself_test',
|
||||
'$BUILD_DIR/mongo/serveronly',
|
||||
'$BUILD_DIR/mongo/coreserver',
|
||||
'$BUILD_DIR/mongo/coredb',
|
||||
"$BUILD_DIR/mongo/ntservice_mock",
|
||||
],
|
||||
NO_CRUTCH = True)
|
||||
|
@ -89,6 +89,7 @@ env.CppUnitTest(
|
||||
'$BUILD_DIR/mongo/serveronly',
|
||||
'$BUILD_DIR/mongo/coreserver',
|
||||
'$BUILD_DIR/mongo/coredb',
|
||||
'$BUILD_DIR/mongo/ntservice_mock',
|
||||
],
|
||||
NO_CRUTCH=True,
|
||||
)
|
||||
|
@ -88,6 +88,7 @@ if wiredtiger:
|
||||
'$BUILD_DIR/mongo/serveronly',
|
||||
'$BUILD_DIR/mongo/coreserver',
|
||||
'$BUILD_DIR/mongo/coredb',
|
||||
'$BUILD_DIR/mongo/ntservice_mock',
|
||||
],
|
||||
NO_CRUTCH=True,
|
||||
)
|
||||
|
@ -135,6 +135,14 @@ namespace mongo {
|
||||
}
|
||||
} // namespace dbtests
|
||||
|
||||
#ifdef _WIN32
|
||||
namespace ntservice {
|
||||
bool shouldStartService() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace mongo
|
||||
|
||||
void mongo::unittest::onCurrentTestNameChange( const std::string &testName ) {
|
||||
|
@ -475,6 +475,11 @@ int main(int argc, char* argv[], char** envp) {
|
||||
|
||||
#undef exit
|
||||
|
||||
void mongo::signalShutdown() {
|
||||
// Notify all threads shutdown has started
|
||||
dbexitCalled = true;
|
||||
}
|
||||
|
||||
void mongo::exitCleanly(ExitCode code) {
|
||||
// TODO: do we need to add anything?
|
||||
mongo::dbexit( code );
|
||||
@ -483,7 +488,11 @@ void mongo::exitCleanly(ExitCode code) {
|
||||
void mongo::dbexit( ExitCode rc, const char *why ) {
|
||||
dbexitCalled = true;
|
||||
audit::logShutdown(ClientBasic::getCurrent());
|
||||
|
||||
#if defined(_WIN32)
|
||||
// Windows Service Controller wants to be told when we are done shutting down
|
||||
// and call quickExit itself.
|
||||
//
|
||||
if ( rc == EXIT_WINDOWS_SERVICE_STOP ) {
|
||||
log() << "dbexit: exiting because Windows service was stopped" << endl;
|
||||
return;
|
||||
|
@ -64,4 +64,14 @@ namespace mongo {
|
||||
invariant(!"unittests shouldn't call exitCleanly");
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
void signalShutdown() {}
|
||||
|
||||
namespace ntservice {
|
||||
bool shouldStartService() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace mongo
|
||||
|
@ -65,4 +65,12 @@ namespace mongo {
|
||||
*/
|
||||
void exitCleanly(ExitCode code);
|
||||
|
||||
/**
|
||||
* Signal main or ServiceMain thread to exit
|
||||
* Important for the ServiceMain thread to do the exit when mongod/s are running as NT Services
|
||||
* on Windows.
|
||||
* It is not required to be called before exitCleanly in the general case, only for
|
||||
* proper NT Service shutdown.
|
||||
*/
|
||||
void signalShutdown();
|
||||
} // namespace mongo
|
||||
|
@ -525,12 +525,12 @@ namespace {
|
||||
|
||||
// Stop the process
|
||||
// During clean shutdown, ie NT SCM signals us, _serviceCallback returns here
|
||||
// as part of the listener loop terminating so we do not have to stop twice in this case
|
||||
if ( ! inShutdown() ) {
|
||||
// TODO: SERVER-5703, separate the "cleanup for shutdown" functionality from
|
||||
// the "terminate process" functionality in exitCleanly.
|
||||
exitCleanly( EXIT_WINDOWS_SERVICE_STOP );
|
||||
}
|
||||
// as part of the listener loop terminating.
|
||||
// exitCleanly is supposed to return. If it blocks, some other thread must be exiting.
|
||||
//
|
||||
// TODO: SERVER-5703, separate the "cleanup for shutdown" functionality from
|
||||
// the "terminate process" functionality in exitCleanly.
|
||||
exitCleanly( EXIT_WINDOWS_SERVICE_STOP );
|
||||
|
||||
reportStatus(SERVICE_STOPPED, 0, exitCode);
|
||||
}
|
||||
@ -543,12 +543,9 @@ namespace {
|
||||
|
||||
reportStatus( SERVICE_STOP_PENDING );
|
||||
|
||||
if ( ! inShutdown() ) {
|
||||
// TODO: SERVER-5703, separate the "cleanup for shutdown" functionality from
|
||||
// the "terminate process" functionality in exitCleanly.
|
||||
// Note: This triggers _serviceCallback to stop by setting inShutdown() == true
|
||||
exitCleanly( EXIT_WINDOWS_SERVICE_STOP );
|
||||
}
|
||||
// Note: This triggers _serviceCallback, ie ServiceMain,
|
||||
// to stop by setting inShutdown() == true
|
||||
signalShutdown();
|
||||
|
||||
// Note: we will report exit status in initService
|
||||
}
|
||||
|
37
src/mongo/util/ntservice_mock.cpp
Normal file
37
src/mongo/util/ntservice_mock.cpp
Normal file
@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (C) 2015 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.
|
||||
*/
|
||||
|
||||
namespace mongo {
|
||||
namespace ntservice {
|
||||
|
||||
bool shouldStartService() {
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace ntservice
|
||||
} // namespace mongo
|
@ -117,4 +117,7 @@ namespace mongo {
|
||||
void Client::initThread(const char* desc, AbstractMessagingPort* mp) {
|
||||
}
|
||||
void removeControlCHandler() {}
|
||||
void signalShutdown() {}
|
||||
bool inShutdown() { return false; }
|
||||
void exitCleanly(ExitCode code) { }
|
||||
} // namespace mongo
|
||||
|
Loading…
Reference in New Issue
Block a user