tvl-depot/absl/synchronization/lifetime_test.cc
Abseil Team 389ec3f906 Export of internal Abseil changes.
--
636137f6f0de910691a3950387fefacfa4909fb8 by Abseil Team <absl-team@google.com>:

Add move semantics to absl::container_internal::CompressedTuple

PiperOrigin-RevId: 225394165

--
43da91e4f95a196b2e6b76f1c2f4158817b0ebb0 by Greg Falcon <gfalcon@google.com>:

Add a constructor to allow for global absl::Mutex instances.

This adds a new constexpr constructor to absl::Mutex, invoked with the absl::kConstInit tag value, which is intended to be used to construct Mutex instances with static storage duration.

What's tricky about is absl::Mutex (like std::mutex) is not a trivially destructible class, so by the letter of the law, accessing a global Mutex instance after it is destroyed results in undefined behavior.  Despite this, we take care in the destructor to not invalidate the memory layout of the Mutex.  Using a kConstInit-constructed global Mutex after it is destroyed happens to work on the toolchains we use.  Google relies heavily on this behavior internally.

Code sanitizers that detect undefined behavior are able to notice use-after-free of globals, and might complain about this pattern.

PiperOrigin-RevId: 225389447

--
7b553a54bc6460cc7008b028552e66799475ca64 by Abseil Team <absl-team@google.com>:

Internal change.

PiperOrigin-RevId: 225373389

--
fd0c722d217b3b509102274765ccb1a0b596cf46 by Abseil Team <absl-team@google.com>:

Update absl/time/CMakeLists.txt to use new functions
i.e. absl_cc_(library|test)

PiperOrigin-RevId: 225246853

--
9f8f3ba3b67a6d1ac4ecdc529c8b8eb0f02576d9 by Abseil Team <absl-team@google.com>:

Update absl/synchronisation/CMakeLists.txt to use new functions
i.e. absl_cc_(library|test)

PiperOrigin-RevId: 225237980

--
a3fdd67dad2e596f804f5e100c8d3a74d8064faa by Abseil Team <absl-team@google.com>:

Internal cleanup

PiperOrigin-RevId: 225226813

--
48fab23fb8cdca45e95da14fce0de56614d09c25 by Jon Cohen <cohenjon@google.com>:

Use a shim #define for wchar_t in msvc in int128.

On ancient versions of msvc and with some compatibility flags on wchar_t is a typedef for unsigned short, whereas on standards-conforming versions wchar_t is a typedef for __wchar_t.  The first situation causes int128 to not compile as you can't define both `operator wchar_t()` and `operator unsigned short()` because they are the same type.

This CL introduces a wrapper #define in order to abstract over the different typedefs for wchar_t.  We do a define instead of a typedef so that we can #undef at the end and not leak the symbol, since we need it in a header.

https://docs.microsoft.com/en-us/previous-versions/dh8che7s(v=vs.140) has more detail about the underlying problem.

PiperOrigin-RevId: 225223756
GitOrigin-RevId: 636137f6f0de910691a3950387fefacfa4909fb8
Change-Id: Iad94e52e9484c5acec115a2f09ef2d5ec22c2074
2018-12-13 14:40:49 -05:00

175 lines
6.2 KiB
C++

// Copyright 2017 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <cstdlib>
#include <thread> // NOLINT(build/c++11), Abseil test
#include <type_traits>
#include "absl/base/attributes.h"
#include "absl/base/const_init.h"
#include "absl/base/internal/raw_logging.h"
#include "absl/base/thread_annotations.h"
#include "absl/synchronization/mutex.h"
#include "absl/synchronization/notification.h"
namespace {
// A two-threaded test which checks that Mutex, CondVar, and Notification have
// correct basic functionality. The intent is to establish that they
// function correctly in various phases of construction and destruction.
//
// Thread one acquires a lock on 'mutex', wakes thread two via 'notification',
// then waits for 'state' to be set, as signalled by 'condvar'.
//
// Thread two waits on 'notification', then sets 'state' inside the 'mutex',
// signalling the change via 'condvar'.
//
// These tests use ABSL_RAW_CHECK to validate invariants, rather than EXPECT or
// ASSERT from gUnit, because we need to invoke them during global destructors,
// when gUnit teardown would have already begun.
void ThreadOne(absl::Mutex* mutex, absl::CondVar* condvar,
absl::Notification* notification, bool* state) {
// Test that the notification is in a valid initial state.
ABSL_RAW_CHECK(!notification->HasBeenNotified(), "invalid Notification");
ABSL_RAW_CHECK(*state == false, "*state not initialized");
{
absl::MutexLock lock(mutex);
notification->Notify();
ABSL_RAW_CHECK(notification->HasBeenNotified(), "invalid Notification");
while (*state == false) {
condvar->Wait(mutex);
}
}
}
void ThreadTwo(absl::Mutex* mutex, absl::CondVar* condvar,
absl::Notification* notification, bool* state) {
ABSL_RAW_CHECK(*state == false, "*state not initialized");
// Wake thread one
notification->WaitForNotification();
ABSL_RAW_CHECK(notification->HasBeenNotified(), "invalid Notification");
{
absl::MutexLock lock(mutex);
*state = true;
condvar->Signal();
}
}
// Launch thread 1 and thread 2, and block on their completion.
// If any of 'mutex', 'condvar', or 'notification' is nullptr, use a locally
// constructed instance instead.
void RunTests(absl::Mutex* mutex, absl::CondVar* condvar) {
absl::Mutex default_mutex;
absl::CondVar default_condvar;
absl::Notification notification;
if (!mutex) {
mutex = &default_mutex;
}
if (!condvar) {
condvar = &default_condvar;
}
bool state = false;
std::thread thread_one(ThreadOne, mutex, condvar, &notification, &state);
std::thread thread_two(ThreadTwo, mutex, condvar, &notification, &state);
thread_one.join();
thread_two.join();
}
void TestLocals() {
absl::Mutex mutex;
absl::CondVar condvar;
RunTests(&mutex, &condvar);
}
// Normal kConstInit usage
ABSL_CONST_INIT absl::Mutex const_init_mutex(absl::kConstInit);
void TestConstInitGlobal() { RunTests(&const_init_mutex, nullptr); }
// Global variables during start and termination
//
// In a translation unit, static storage duration variables are initialized in
// the order of their definitions, and destroyed in the reverse order of their
// definitions. We can use this to arrange for tests to be run on these objects
// before they are created, and after they are destroyed.
using Function = void (*)();
class OnConstruction {
public:
explicit OnConstruction(Function fn) { fn(); }
};
class OnDestruction {
public:
explicit OnDestruction(Function fn) : fn_(fn) {}
~OnDestruction() { fn_(); }
private:
Function fn_;
};
// kConstInit
// Test early usage. (Declaration comes first; definitions must appear after
// the test runner.)
extern absl::Mutex early_const_init_mutex;
// (Normally I'd write this +[], to make the cast-to-function-pointer explicit,
// but in some MSVC setups we support, lambdas provide conversion operators to
// different flavors of function pointers, making this trick ambiguous.)
OnConstruction test_early_const_init([] {
RunTests(&early_const_init_mutex, nullptr);
});
// This definition appears before test_early_const_init, but it should be
// initialized first (due to constant initialization). Test that the object
// actually works when constructed this way.
ABSL_CONST_INIT absl::Mutex early_const_init_mutex(absl::kConstInit);
// Furthermore, test that the const-init c'tor doesn't stomp over the state of
// a Mutex. Really, this is a test that the platform under test correctly
// supports C++11 constant initialization. (The constant-initialization
// constructors of globals "happen at link time"; memory is pre-initialized,
// before the constructors of either grab_lock or check_still_locked are run.)
extern absl::Mutex const_init_sanity_mutex;
OnConstruction grab_lock([]() NO_THREAD_SAFETY_ANALYSIS {
const_init_sanity_mutex.Lock();
});
ABSL_CONST_INIT absl::Mutex const_init_sanity_mutex(absl::kConstInit);
OnConstruction check_still_locked([]() NO_THREAD_SAFETY_ANALYSIS {
const_init_sanity_mutex.AssertHeld();
const_init_sanity_mutex.Unlock();
});
// Test shutdown usage. (Declarations come first; definitions must appear after
// the test runner.)
extern absl::Mutex late_const_init_mutex;
// OnDestruction is being used here as a global variable, even though it has a
// non-trivial destructor. This is against the style guide. We're violating
// that rule here to check that the exception we allow for kConstInit is safe.
// NOLINTNEXTLINE
OnDestruction test_late_const_init([] {
RunTests(&late_const_init_mutex, nullptr);
});
ABSL_CONST_INIT absl::Mutex late_const_init_mutex(absl::kConstInit);
} // namespace
int main() {
TestLocals();
TestConstInitGlobal();
// Explicitly call exit(0) here, to make it clear that we intend for the
// above global object destructors to run.
std::exit(0);
}