Commit 7414a33e authored by Maged Michael's avatar Maged Michael Committed by Facebook Github Bot

Add indicators of process phases

Summary:
Add function `get_process_phase` that returns indicators of process phases:
- `ProcessPhase::Init`: Not all global variables may have been initialized.
- `ProcessPhase::Regular`: All globals have been initialized and have not been destroyed.
- `ProcessPhase::Exit`: Some globals may have been destroyed.

Add function `set_process_phases` and calls to it from `folly::init` and `common/init/Init.cpp` wherever `SingletonVault::singleton()->registrationComplete` is called.

Will add calls to `set_process_phases` in separate diffs to other places that call `registrationComplete ` directly.

See Section 6.9.3 Start and termination [basic.start] of the C++ Standard Working Draft [N4849](https://github.com/cplusplus/draft/releases/download/n4849/n4849.pdf).

In general this addition is useful for simplifying cases of dependence on the existence of certain globals during main, where it is safe to ignore such globals at shutdown.
For example, the destructor of `hazptr_obj_cohort` normally needs to access the default `hazptr_domain`, but it is safe to skip that access at shutdown.

Reviewed By: yfeldblum

Differential Revision: D19567170

fbshipit-source-id: a44cbd3301aa9dc77a1e992922403635a3ff6552
parent 75b2e2f4
......@@ -19,6 +19,7 @@
#include <glog/logging.h>
#include <folly/Singleton.h>
#include <folly/init/Phase.h>
#include <folly/logging/Init.h>
#include <folly/portability/Config.h>
#include <folly/synchronization/HazptrThreadPoolExecutor.h>
......@@ -41,6 +42,10 @@ void init(int* argc, char*** argv, bool removeFlags) {
google::InstallFailureSignalHandler();
#endif
// Indicate ProcessPhase::Regular and register handler to
// indicate ProcessPhase::Exit.
folly::set_process_phases();
// Move from the registration phase to the "you can actually instantiate
// things now" phase.
folly::SingletonVault::singleton()->registrationComplete();
......
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* 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 <folly/init/Phase.h>
#include <atomic>
#include <stdexcept>
namespace folly {
namespace {
static std::atomic<ProcessPhase> process_phase{ProcessPhase::Init};
void set_process_phase(ProcessPhase newphase) {
ProcessPhase curphase = get_process_phase();
if (curphase == ProcessPhase::Exit || int(newphase) - int(curphase) != 1) {
throw std::logic_error("folly-init: unexpected process-phase transition");
}
process_phase.store(newphase, std::memory_order_relaxed);
}
} // namespace
void set_process_phases() {
set_process_phase(ProcessPhase::Regular);
std::atexit([]() { set_process_phase(ProcessPhase::Exit); });
}
ProcessPhase get_process_phase() noexcept {
return process_phase.load(std::memory_order_relaxed);
}
} // namespace folly
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* 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.
*/
#pragma once
#include <atomic>
namespace folly {
/// Process phases for programs that use Folly:
/// - Init: Not all globals may have been initialized.
/// - Regular: All globals have been initialized and have not
/// been destroyed.
/// - Exit: Some globals may have been destroyed.
/// Process phases
enum class ProcessPhase {
Init = 0,
Regular = 1,
Exit = 2,
};
/// Start Regular phase and register handler to set Exit phase.
/// To be called exactly once in each program that uses Folly.
/// Ideally, it is to be called from folly::init(), which in turn
/// is to be called by every program that uses Folly.
void set_process_phases();
/// Get the current process phase.
ProcessPhase get_process_phase() noexcept;
} // namespace folly
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* 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 <folly/init/Phase.h>
#include <folly/Singleton.h>
#include <folly/portability/GTest.h>
#include <glog/logging.h>
#include <thread>
/// Types
struct Global {
Global() {
CHECK(folly::get_process_phase() == folly::ProcessPhase::Init);
}
~Global() {
CHECK(folly::get_process_phase() >= folly::ProcessPhase::Exit);
}
};
/// Variables
static Global global;
/// Tests
TEST(InitTest, basic) {
ASSERT_EQ(folly::get_process_phase(), folly::ProcessPhase::Regular);
}
TEST(InitTest, fork) {
std::thread t1([] {
ASSERT_EQ(folly::get_process_phase(), folly::ProcessPhase::Regular);
});
t1.join();
folly::SingletonVault::singleton()->destroyInstances();
auto pid = fork();
folly::SingletonVault::singleton()->reenableInstances();
if (pid > 0) {
// parent
int status = -1;
auto pid2 = waitpid(pid, &status, 0);
EXPECT_EQ(status, 0);
EXPECT_EQ(pid, pid2);
ASSERT_EQ(folly::get_process_phase(), folly::ProcessPhase::Regular);
} else if (pid == 0) {
// child
std::thread t2([] {
ASSERT_EQ(folly::get_process_phase(), folly::ProcessPhase::Regular);
});
t2.join();
std::exit(0); // Do not print gtest results
} else {
PLOG(FATAL) << "Failed to fork()";
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment