Skip to content

Commit 1974839

Browse files
committed
Settings: Avoid static initialization fiasco
When building on macOS with clang 8 and ThreadSanitizer enabled, memcached_testapp crashes when destructing the Settings object: libc++abi.dylib: terminating with uncaught exception of type std::__1::system_error: mutex lock failed: Invalid argument With the following backtrace from where the system_error exception is thrown: * thread couchbase#1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1 frame #0: 0x00007fff7b6411f6 libc++abi.dylib` __cxa_throw frame couchbase#1: 0x00007fff7b6147af libc++.1.dylib` std::__1::__throw_system_error(int, char const*) + 77 frame couchbase#2: 0x00007fff7b606c93 libc++.1.dylib` std::__1::mutex::lock() + 29 frame couchbase#3: 0x00000001009c27ff memcached_testapp` folly::SharedMutexImpl<...>::annotateLazyCreate() [inlined] std::__1::unique_lock<std::__1::mutex>::unique_lock(__m=<unavailable>) + 191 at __mutex_base:131 frame couchbase#4: 0x00000001009c27f7 memcached_testapp` folly::SharedMutexImpl<...>::annotateLazyCreate() [inlined] std::__1::unique_lock<std::__1::mutex>::unique_lock(__m=<unavailable>) at __mutex_base:131 * frame couchbase#5: 0x00000001009c27f7 memcached_testapp` folly::SharedMutexImpl<...>::annotateLazyCreate() + 140 at SharedMutex.cpp:33 frame couchbase#6: 0x00000001009c276b memcached_testapp` folly::SharedMutexImpl<...>::annotateLazyCreate(this=0x0000000100c7ddc8) + 43 at SharedMutex.h:705 frame #7: 0x00000001009c191a memcached_testapp` folly::SharedMutexImpl<...>::~SharedMutexImpl() [inlined] folly::SharedMutexImpl<...>::annotateDestroy(this=<unavailable>) + 8 at SharedMutex.h:718 frame #8: 0x00000001009c1912 memcached_testapp` folly::SharedMutexImpl<...>::~SharedMutexImpl() + 24 at SharedMutex.h:324 frame #9: 0x00000001009c18fa memcached_testapp` folly::SharedMutexImpl<...>::~SharedMutexImpl(this=0x0000000100c7ddc8) + 26 at SharedMutex.h:300 frame #10: 0x000000010076b837 memcached_testapp` folly::Synchronized<...>::~Synchronized(this=0x0000000100c7ddb0) + 55 at Synchronized.h:484 frame #11: 0x000000010075d1d9 memcached_testapp` folly::Synchronized<...>::~Synchronized(this=0x0000000100c7ddb0) + 41 at Synchronized.h:484 frame #12: 0x000000010075e8fc memcached_testapp` Settings::~Settings(this=0x0000000100c7db10) + 76 at settings.h:51 frame #13: 0x000000010075c8b9 memcached_testapp` Settings::~Settings(this=0x0000000100c7db10) + 41 at settings.h:51 frame #14: 0x0000000102b7d5c1 libclang_rt.tsan_osx_dynamic.dylib` cxa_at_exit_wrapper(void*) + 33 frame #15: 0x00007fff7d72beed libsystem_c.dylib` __cxa_finalize_ranges + 351 frame #16: 0x00007fff7d72c1fe libsystem_c.dylib` exit + 55 frame #17: 0x00007fff7d67f01c libdyld.dylib` start + 8 The problem is at frame 5, where as part of the destruction of SharedMutex (as used by a member variable of Settings) we are attempting to acquire a mutex which has already been destructed (as it is itself a function-local static) - i.e. we have encountered the static initialization order fiasco :( Address this by changing `settings` to no longer be a plain static (global) variable, and instead be created on first use via a new Settings::instance() static method. Change-Id: I40bd44ed0ae32eb55271ce739fdf990d9869c32f Reviewed-on: http://review.couchbase.org/113640 Tested-by: Build Bot <build@couchbase.com> Reviewed-by: Jim Walker <jim@couchbase.com>
1 parent ac8385e commit 1974839

27 files changed

+208
-172
lines changed

daemon/cmdline.cc

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,19 +79,19 @@ void parse_arguments(int argc, char** argv) {
7979
std::exit(EXIT_FAILURE);
8080
}
8181

82-
load_config_file(config_file.c_str(), settings);
82+
load_config_file(config_file.c_str(), Settings::instance());
8383

8484
if (verbosity > 0) {
85-
settings.setVerbose(verbosity);
85+
Settings::instance().setVerbose(verbosity);
8686
}
8787

8888
if (threads > 0) {
89-
settings.setNumWorkerThreads(threads);
89+
Settings::instance().setNumWorkerThreads(threads);
9090
}
9191

9292
if (requests > 0) {
93-
settings.setRequestsPerEventNotification(requests,
94-
EventPriority::Default);
93+
Settings::instance().setRequestsPerEventNotification(
94+
requests, EventPriority::Default);
9595
}
9696
}
9797

daemon/config_parse.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ boost::optional<nlohmann::json> validate_proposed_config_changes(
4343
try {
4444
auto json = nlohmann::json::parse(new_cfg);
4545
Settings new_settings(json);
46-
settings.updateSettings(new_settings, false);
46+
Settings::instance().updateSettings(new_settings, false);
4747
return {};
4848
} catch (const std::exception& exception) {
4949
errors.push_back(exception.what());
@@ -58,7 +58,7 @@ void reload_config_file() {
5858
try {
5959
auto content = cb::io::loadFile(get_config_file());
6060
Settings new_settings(nlohmann::json::parse(content));
61-
settings.updateSettings(new_settings, true);
61+
Settings::instance().updateSettings(new_settings, true);
6262
} catch (const std::exception& exception) {
6363
LOG_WARNING(
6464
"Validation failed while reloading config file '{}'. Error: {}",

daemon/connection.cc

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,7 @@ cb::rbac::PrivilegeAccess Connection::checkPrivilege(
357357
const std::string privilege_string = cb::rbac::to_string(privilege);
358358
const std::string context = privilegeContext.to_string();
359359

360-
if (settings.isPrivilegeDebug()) {
360+
if (Settings::instance().isPrivilegeDebug()) {
361361
audit_privilege_debug(*this,
362362
command,
363363
all_buckets[bucketIndex].name,
@@ -743,7 +743,8 @@ int Connection::sslPreConnection() {
743743
disconnect = true;
744744
break;
745745
case cb::x509::Status::NotPresent:
746-
if (settings.getClientCertMode() == cb::x509::Mode::Mandatory) {
746+
if (Settings::instance().getClientCertMode() ==
747+
cb::x509::Mode::Mandatory) {
747748
disconnect = true;
748749
} else if (is_default_bucket_enabled()) {
749750
associate_bucket(*this, "default");
@@ -1122,7 +1123,7 @@ int Connection::sslRead(char* dest, size_t nbytes) {
11221123
int Connection::sslWrite(const char* src, size_t nbytes) {
11231124
int ret = 0;
11241125

1125-
int chunksize = settings.getBioDrainBufferSize();
1126+
int chunksize = Settings::instance().getBioDrainBufferSize();
11261127

11271128
while (ret < int(nbytes)) {
11281129
int n;
@@ -1249,7 +1250,7 @@ void Connection::ensureIovSpace() {
12491250

12501251
bool Connection::enableSSL(const std::string& cert, const std::string& pkey) {
12511252
if (ssl.enable(cert, pkey)) {
1252-
if (settings.getVerbose() > 1) {
1253+
if (Settings::instance().getVerbose() > 1) {
12531254
ssl.dumpCipherList(getId());
12541255
}
12551256

@@ -1267,7 +1268,7 @@ Connection::Connection(FrontEndThread& thr)
12671268
peername("unknown"),
12681269
sockname("unknown"),
12691270
stateMachine(*this),
1270-
max_reqs_per_event(settings.getRequestsPerEventNotification(
1271+
max_reqs_per_event(Settings::instance().getRequestsPerEventNotification(
12711272
EventPriority::Default)) {
12721273
updateDescription();
12731274
cookies.emplace_back(std::unique_ptr<Cookie>{new Cookie(*this)});
@@ -1286,7 +1287,7 @@ Connection::Connection(SOCKET sfd,
12861287
peername(cb::net::getpeername(socketDescriptor)),
12871288
sockname(cb::net::getsockname(socketDescriptor)),
12881289
stateMachine(*this),
1289-
max_reqs_per_event(settings.getRequestsPerEventNotification(
1290+
max_reqs_per_event(Settings::instance().getRequestsPerEventNotification(
12901291
EventPriority::Default)) {
12911292
setTcpNoDelay(true);
12921293
updateDescription();
@@ -1330,7 +1331,7 @@ void Connection::setState(StateMachine::State next_state) {
13301331
}
13311332

13321333
void Connection::runStateMachinery() {
1333-
if (settings.getVerbose() > 1) {
1334+
if (Settings::instance().getVerbose() > 1) {
13341335
do {
13351336
LOG_DEBUG("{} - Running task: {}",
13361337
getId(),
@@ -1557,15 +1558,18 @@ void Connection::setPriority(Connection::Priority priority) {
15571558
switch (priority) {
15581559
case Priority::High:
15591560
max_reqs_per_event =
1560-
settings.getRequestsPerEventNotification(EventPriority::High);
1561+
Settings::instance().getRequestsPerEventNotification(
1562+
EventPriority::High);
15611563
return;
15621564
case Priority::Medium:
15631565
max_reqs_per_event =
1564-
settings.getRequestsPerEventNotification(EventPriority::Medium);
1566+
Settings::instance().getRequestsPerEventNotification(
1567+
EventPriority::Medium);
15651568
return;
15661569
case Priority::Low:
15671570
max_reqs_per_event =
1568-
settings.getRequestsPerEventNotification(EventPriority::Low);
1571+
Settings::instance().getRequestsPerEventNotification(
1572+
EventPriority::Low);
15691573
return;
15701574
}
15711575
throw std::invalid_argument("Unkown priority: " +
@@ -1575,9 +1579,10 @@ void Connection::setPriority(Connection::Priority priority) {
15751579
bool Connection::selectedBucketIsXattrEnabled() const {
15761580
auto* bucketEngine = getBucketEngine();
15771581
if (bucketEngine) {
1578-
return settings.isXattrEnabled() && bucketEngine->isXattrEnabled();
1582+
return Settings::instance().isXattrEnabled() &&
1583+
bucketEngine->isXattrEnabled();
15791584
}
1580-
return settings.isXattrEnabled();
1585+
return Settings::instance().isXattrEnabled();
15811586
}
15821587

15831588
ENGINE_ERROR_CODE Connection::add_packet_to_send_pipe(

daemon/connections.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ Connection* conn_new(SOCKET sfd,
180180
bucket.name);
181181
}
182182

183-
if (settings.getVerbose() > 1) {
183+
if (Settings::instance().getVerbose() > 1) {
184184
LOG_DEBUG("<{} new client connection", sfd);
185185
}
186186

daemon/cookie.cc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ void Cookie::sendDynamicBuffer() {
251251
void Cookie::sendNotMyVBucket() {
252252
auto pair = connection.getBucket().clusterConfiguration.getConfiguration();
253253
if (pair.first == -1 || (pair.first == connection.getClustermapRevno() &&
254-
settings.isDedupeNmvbMaps())) {
254+
Settings::instance().isDedupeNmvbMaps())) {
255255
// We don't have a vbucket map, or we've already sent it to the
256256
// client
257257
mcbp_add_header(*this,
@@ -414,7 +414,7 @@ std::string Cookie::getPrintableRequestKey() const {
414414
}
415415

416416
void Cookie::logCommand() const {
417-
if (settings.getVerbose() == 0) {
417+
if (Settings::instance().getVerbose() == 0) {
418418
// Info is not enabled.. we don't want to try to format
419419
// output
420420
return;
@@ -437,7 +437,7 @@ void Cookie::logResponse(const char* reason) const {
437437
}
438438

439439
void Cookie::logResponse(ENGINE_ERROR_CODE code) const {
440-
if (settings.getVerbose() == 0) {
440+
if (Settings::instance().getVerbose() == 0) {
441441
// Info is not enabled.. we don't want to try to format
442442
// output
443443
return;

daemon/datatype.cc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@
2121
bool Datatype::isSupported(cb::mcbp::Feature feature) {
2222
switch (feature) {
2323
case cb::mcbp::Feature::XATTR:
24-
return settings.isXattrEnabled();
24+
return Settings::instance().isXattrEnabled();
2525
case cb::mcbp::Feature::JSON:
26-
return settings.isDatatypeJsonEnabled();
26+
return Settings::instance().isDatatypeJsonEnabled();
2727
case cb::mcbp::Feature::SNAPPY:
28-
return settings.isDatatypeSnappyEnabled();
28+
return Settings::instance().isDatatypeSnappyEnabled();
2929
case cb::mcbp::Feature::TLS:
3030
case cb::mcbp::Feature::TCPNODELAY:
3131
case cb::mcbp::Feature::MUTATION_SEQNO:

daemon/mcaudit.cc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -335,8 +335,8 @@ static void event_state_listener(uint32_t id, bool enabled) {
335335

336336
void initialize_audit() {
337337
/* Start the audit daemon */
338-
auditHandle = cb::audit::create_audit_daemon(settings.getAuditFile(),
339-
get_server_api()->cookie);
338+
auditHandle = cb::audit::create_audit_daemon(
339+
Settings::instance().getAuditFile(), get_server_api()->cookie);
340340
if (!auditHandle) {
341341
FATAL_ERROR(EXIT_FAILURE, "FATAL: Failed to start audit daemon");
342342
}
@@ -349,7 +349,7 @@ void shutdown_audit() {
349349
}
350350

351351
ENGINE_ERROR_CODE reconfigure_audit(Cookie& cookie) {
352-
if (auditHandle->configure_auditdaemon(settings.getAuditFile(),
352+
if (auditHandle->configure_auditdaemon(Settings::instance().getAuditFile(),
353353
static_cast<void*>(&cookie))) {
354354
return ENGINE_EWOULDBLOCK;
355355
}

daemon/mcbp.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ void mcbp_add_header(Cookie& cookie,
114114
header.getOpaque(),
115115
cookie.getCas());
116116

117-
if (settings.getVerbose() > 1) {
117+
if (Settings::instance().getVerbose() > 1) {
118118
auto* header = reinterpret_cast<const cb::mcbp::Header*>(wbuf.data());
119119
try {
120120
LOG_TRACE("<{} Sending: {}",

daemon/mcbp_executors.cc

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ static void verbosity_executor(Cookie& cookie) {
216216
if (level < 0 || level > MAX_VERBOSITY_LEVEL) {
217217
level = MAX_VERBOSITY_LEVEL;
218218
}
219-
settings.setVerbose(level);
219+
Settings::instance().setVerbose(level);
220220
cookie.sendResponse(cb::mcbp::Status::Success);
221221
}
222222

@@ -254,7 +254,7 @@ static void sasl_list_mech_executor(Cookie& cookie) {
254254
return;
255255
}
256256

257-
if (settings.isExternalAuthServiceEnabled()) {
257+
if (Settings::instance().isExternalAuthServiceEnabled()) {
258258
// If the system is configured to use ns_server for authentication
259259
// we should only advertise PLAIN as the mechanism (as it can't use
260260
// SCRAM to connect to LDAP)
@@ -267,16 +267,18 @@ static void sasl_list_mech_executor(Cookie& cookie) {
267267
return;
268268
}
269269

270-
if (connection.isSslEnabled() && settings.has.ssl_sasl_mechanisms) {
271-
const auto& mechs = settings.getSslSaslMechanisms();
270+
if (connection.isSslEnabled() &&
271+
Settings::instance().has.ssl_sasl_mechanisms) {
272+
const auto& mechs = Settings::instance().getSslSaslMechanisms();
272273
cookie.sendResponse(cb::mcbp::Status::Success,
273274
{},
274275
{},
275276
mechs,
276277
cb::mcbp::Datatype::Raw,
277278
0);
278-
} else if (!connection.isSslEnabled() && settings.has.sasl_mechanisms) {
279-
const auto& mechs = settings.getSaslMechanisms();
279+
} else if (!connection.isSslEnabled() &&
280+
Settings::instance().has.sasl_mechanisms) {
281+
const auto& mechs = Settings::instance().getSaslMechanisms();
280282
cookie.sendResponse(cb::mcbp::Status::Success,
281283
{},
282284
{},
@@ -453,11 +455,11 @@ static void config_reload_executor(Cookie& cookie) {
453455
// We need to audit that the privilege debug mode changed and
454456
// in order to do that we need the "connection" object so we can't
455457
// do this by using the common "changed_listener"-interface.
456-
const bool old_priv_debug = settings.isPrivilegeDebug();
458+
const bool old_priv_debug = Settings::instance().isPrivilegeDebug();
457459
reload_config_file();
458-
if (settings.isPrivilegeDebug() != old_priv_debug) {
460+
if (Settings::instance().isPrivilegeDebug() != old_priv_debug) {
459461
audit_set_privilege_debug_mode(cookie.getConnection(),
460-
settings.isPrivilegeDebug());
462+
Settings::instance().isPrivilegeDebug());
461463
}
462464
cookie.sendResponse(cb::mcbp::Status::Success);
463465
}
@@ -487,7 +489,7 @@ static void get_errmap_executor(Cookie& cookie) {
487489
auto value = cookie.getRequest(Cookie::PacketContent::Full).getValue();
488490
auto* req = reinterpret_cast<const cb::mcbp::request::GetErrmapPayload*>(
489491
value.data());
490-
auto const& errormap = settings.getErrorMap(req->getVersion());
492+
auto const& errormap = Settings::instance().getErrorMap(req->getVersion());
491493
if (errormap.empty()) {
492494
cookie.sendResponse(cb::mcbp::Status::KeyEnoent);
493495
} else {
@@ -543,7 +545,7 @@ static void rbac_refresh_executor(Cookie& cookie) {
543545
}
544546

545547
static void auth_provider_executor(Cookie& cookie) {
546-
if (!settings.isExternalAuthServiceEnabled()) {
548+
if (!Settings::instance().isExternalAuthServiceEnabled()) {
547549
cookie.setErrorContext(
548550
"Support for external authentication service is disabled");
549551
cookie.sendResponse(cb::mcbp::Status::NotSupported);
@@ -982,7 +984,7 @@ void try_read_mcbp_command(Cookie& cookie) {
982984
return;
983985
}
984986

985-
if (settings.getVerbose() > 1) {
987+
if (Settings::instance().getVerbose() > 1) {
986988
try {
987989
LOG_TRACE(">{} Read command {}",
988990
c.getId(),
@@ -997,13 +999,13 @@ void try_read_mcbp_command(Cookie& cookie) {
997999

9981000
// Protect ourself from someone trying to kill us by sending insanely
9991001
// large packets.
1000-
if (header.getBodylen() > settings.getMaxPacketSize()) {
1002+
if (header.getBodylen() > Settings::instance().getMaxPacketSize()) {
10011003
LOG_WARNING(
10021004
"{}: The package size ({}) exceeds the limit ({}) for what "
10031005
"the system accepts.. Disconnecting client",
10041006
c.getId(),
10051007
header.getBodylen(),
1006-
settings.getMaxPacketSize());
1008+
Settings::instance().getMaxPacketSize());
10071009
c.setState(StateMachine::State::closing);
10081010
return;
10091011
}

0 commit comments

Comments
 (0)