Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions analytics/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,14 @@ target_compile_definitions(firebase_analytics
-DINTERNAL_EXPERIMENTAL=1
)

# Reject static/global C++ objects with non-trivial destructors so SDK state
# cannot be destroyed before user-owned static Firebase objects at process exit.
# Restricted to CXX so C (`analytics_desktop_dynamic.c`) and Swift sources do
# not receive a C++-only warning flag.
target_compile_options(
firebase_analytics PRIVATE
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CXX_COMPILER_ID:Clang,AppleClang>>:-Werror=exit-time-destructors>)



if(ANDROID)
Expand Down
42 changes: 30 additions & 12 deletions analytics/src/analytics_desktop.cc
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "app/src/include/firebase/future.h"
#include "app/src/include/firebase/variant.h"
#include "app/src/log.h"
#include "app/src/no_destructor.h"
#include "firebase/log.h"

#if defined(_WIN32)
Expand All @@ -48,12 +49,29 @@ static HMODULE g_analytics_module = 0;
// Future data for analytics.
// This is initialized in `Initialize()` and cleaned up in `Terminate()`.
static bool g_initialized = false;
static LogCallback g_log_callback;
static std::mutex g_log_callback_mutex;

static LogCallback& GetLogCallback() {
static NoDestructor<LogCallback> log_callback;
return *log_callback;
}

static std::mutex& LogCallbackMutex() {
static NoDestructor<std::mutex> log_callback_mutex;
return *log_callback_mutex;
}

static int g_fake_instance_id = 0;
static bool g_analytics_collection_enabled = true;
static std::string g_app_id;
static std::string g_package_name;

static std::string& AppId() {
static NoDestructor<std::string> app_id;
return *app_id;
}

static std::string& PackageName() {
static NoDestructor<std::string> package_name;
return *package_name;
}

// Initializes the Analytics desktop API.
// This function must be called before any other Analytics methods.
Expand Down Expand Up @@ -91,15 +109,15 @@ void Initialize(const App& app) {
LogInfo("Analytics: Loaded Google Analytics module.");

// Initialize Google Analytics C API
g_app_id = app.options().app_id();
g_package_name = app.options().package_name();
AppId() = app.options().app_id();
PackageName() = app.options().package_name();

GoogleAnalytics_Options* c_options = GoogleAnalytics_Options_Create();
if (!c_options) {
LogError("Analytics: Failed to create GoogleAnalytics_Options.");
} else {
c_options->app_id = g_app_id.c_str();
c_options->package_name = g_package_name.c_str();
c_options->app_id = AppId().c_str();
c_options->package_name = PackageName().c_str();
c_options->analytics_collection_enabled_at_first_launch =
g_analytics_collection_enabled;

Expand Down Expand Up @@ -416,8 +434,8 @@ void GoogleAnalyticsWrapperLogCallback(int32_t log_level, const char* message) {
LogCallback callback_to_call;

{
std::lock_guard<std::mutex> lock(g_log_callback_mutex);
callback_to_call = g_log_callback;
std::lock_guard<std::mutex> lock(LogCallbackMutex());
callback_to_call = GetLogCallback();
}

if (callback_to_call) {
Expand All @@ -433,8 +451,8 @@ void SetLogCallback(const LogCallback& callback) {
FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized());
// The C API does not support user data, so we must use a global variable.
{
std::lock_guard<std::mutex> lock(g_log_callback_mutex);
g_log_callback = callback;
std::lock_guard<std::mutex> lock(LogCallbackMutex());
GetLogCallback() = callback;
}
if (callback) {
GoogleAnalytics_SetLogCallback(GoogleAnalyticsWrapperLogCallback);
Expand Down
30 changes: 17 additions & 13 deletions analytics/src/analytics_ios.mm
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include "app/src/include/firebase/internal/mutex.h"
#include "app/src/include/firebase/version.h"
#include "app/src/log.h"
#include "app/src/no_destructor.h"
#include "app/src/thread.h"
#include "app/src/time.h"
#include "app/src/util.h"
Expand Down Expand Up @@ -120,13 +121,16 @@ uint64_t GetResetTimeElapsedMilliseconds() const {
const uint64_t AnalyticsDataResetter::kResetTimeoutMilliseconds = 5000;

static const double kMillisecondsPerSecond = 1000.0;
static Mutex g_mutex; // NOLINT
static Mutex& AnalyticsMutex() {
static NoDestructor<Mutex> mutex;
return *mutex;
}
static bool g_initialized = false;
static AnalyticsDataResetter* g_resetter = nullptr;

// Initialize the API.
void Initialize(const ::firebase::App& app) {
MutexLock lock(g_mutex);
MutexLock lock(AnalyticsMutex());
g_initialized = true;
g_resetter = new AnalyticsDataResetter();
internal::RegisterTerminateOnDefaultAppDestroy();
Expand All @@ -142,7 +146,7 @@ void Initialize(const ::firebase::App& app) {

// Terminate the API.
void Terminate() {
MutexLock lock(g_mutex);
MutexLock lock(AnalyticsMutex());
internal::FutureData::Destroy();
internal::UnregisterTerminateOnDefaultAppDestroy();
delete g_resetter;
Expand Down Expand Up @@ -237,7 +241,7 @@ void LogEvent(const char* name) {
}

Future<void> LogAppleTransaction(const char* transaction_id) {
MutexLock lock(g_mutex);
MutexLock lock(AnalyticsMutex());
FIREBASE_ASSERT_RETURN(Future<void>(), internal::IsInitialized());

auto* api = internal::FutureData::Get()->api();
Expand All @@ -251,7 +255,7 @@ void LogEvent(const char* name) {
[AppleTransactionVerifier
verifyWithTransactionId:SafeString(transaction_id)
completion:^(BOOL isFound) {
MutexLock lock(g_mutex);
MutexLock lock(AnalyticsMutex());
if (!internal::IsInitialized()) return;

auto* api = internal::FutureData::Get()->api();
Expand All @@ -266,7 +270,7 @@ void LogEvent(const char* name) {
}

Future<void> LogAppleTransactionLastResult() {
MutexLock lock(g_mutex);
MutexLock lock(AnalyticsMutex());
FIREBASE_ASSERT_RETURN(Future<void>(), internal::IsInitialized());
return static_cast<const Future<void>&>(
internal::FutureData::Get()->api()->LastResult(internal::kAnalyticsFnLogAppleTransaction));
Expand Down Expand Up @@ -449,7 +453,7 @@ void SetSessionTimeoutDuration(int64_t milliseconds) {
}

void ResetAnalyticsData() {
MutexLock lock(g_mutex);
MutexLock lock(AnalyticsMutex());
FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized());
g_resetter->Reset();
}
Expand All @@ -467,7 +471,7 @@ void NotifyAppLifecycleChange(AppLifecycleState) {}
void SetDesktopDebugMode(bool) {}

Future<std::string> GetAnalyticsInstanceId() {
MutexLock lock(g_mutex);
MutexLock lock(AnalyticsMutex());
FIREBASE_ASSERT_RETURN(Future<std::string>(), internal::IsInitialized());
auto* api = internal::FutureData::Get()->api();
const auto future_handle =
Expand All @@ -477,7 +481,7 @@ Thread get_id_thread(
[](SafeFutureHandle<std::string>* handle) {
for (;;) {
{
MutexLock lock(g_mutex);
MutexLock lock(AnalyticsMutex());
if (!internal::IsInitialized()) break;
std::string instance_id = g_resetter->GetInstanceId();
if (!instance_id.empty()) {
Expand All @@ -495,19 +499,19 @@ Thread get_id_thread(
}

Future<std::string> GetAnalyticsInstanceIdLastResult() {
MutexLock lock(g_mutex);
MutexLock lock(AnalyticsMutex());
FIREBASE_ASSERT_RETURN(Future<std::string>(), internal::IsInitialized());
return static_cast<const Future<std::string>&>(
internal::FutureData::Get()->api()->LastResult(internal::kAnalyticsFnGetAnalyticsInstanceId));
}

Future<int64_t> GetSessionId() {
MutexLock lock(g_mutex);
MutexLock lock(AnalyticsMutex());
FIREBASE_ASSERT_RETURN(Future<int64_t>(), internal::IsInitialized());
auto* api = internal::FutureData::Get()->api();
const auto future_handle = api->SafeAlloc<int64_t>(internal::kAnalyticsFnGetSessionId);
[FIRAnalytics sessionIDWithCompletion:^(int64_t session_id, NSError* _Nullable error) {
MutexLock lock(g_mutex);
MutexLock lock(AnalyticsMutex());
if (!internal::IsInitialized()) return;
if (error) {
api->Complete(future_handle, -1, util::NSStringToString(error.localizedDescription).c_str());
Expand All @@ -519,7 +523,7 @@ Thread get_id_thread(
}

Future<int64_t> GetSessionIdLastResult() {
MutexLock lock(g_mutex);
MutexLock lock(AnalyticsMutex());
FIREBASE_ASSERT_RETURN(Future<int64_t>(), internal::IsInitialized());
return static_cast<const Future<int64_t>&>(
internal::FutureData::Get()->api()->LastResult(internal::kAnalyticsFnGetSessionId));
Expand Down
13 changes: 13 additions & 0 deletions app/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ set(utility_common_HDRS
src/future_manager.h
src/intrusive_list.h
src/log.h
src/no_destructor.h
src/optional.h
src/path.h
src/pthread_condvar.h
Expand Down Expand Up @@ -302,6 +303,12 @@ if(ANDROID OR IOS)
${FLATBUFFERS_SOURCE_DIR}/src/idl_gen_text.cpp
${FLATBUFFERS_SOURCE_DIR}/src/reflection.cpp
${FLATBUFFERS_SOURCE_DIR}/src/util.cpp)
# Third-party FlatBuffers sources define static globals with non-trivial
# destructors; exempt them from the SDK's exit-time destructor check below.
set_source_files_properties(
${app_flatbuffers_srcs}
PROPERTIES COMPILE_OPTIONS
"$<$<CXX_COMPILER_ID:Clang,AppleClang>:-Wno-exit-time-destructors>")
set(app_flatbuffers_lib)
else()
set(app_flatbuffers_srcs)
Expand Down Expand Up @@ -332,6 +339,12 @@ else()
target_compile_options(firebase_app PRIVATE -fno-exceptions)
endif()

# Reject static/global C++ objects with non-trivial destructors so SDK state
# cannot be destroyed before user-owned static Firebase objects at process exit.
target_compile_options(
firebase_app PRIVATE
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CXX_COMPILER_ID:Clang,AppleClang>>:-Werror=exit-time-destructors>)

target_include_directories(firebase_app
PUBLIC
${CMAKE_CURRENT_LIST_DIR}/src/include
Expand Down
6 changes: 6 additions & 0 deletions app/rest/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ set(rest_SRCS
add_library(firebase_rest_lib STATIC
${rest_SRCS})

# Reject static/global C++ objects with non-trivial destructors so REST
# transport locks outlive user-owned static Firebase objects at process exit.
target_compile_options(
firebase_rest_lib PRIVATE
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CXX_COMPILER_ID:Clang,AppleClang>>:-Werror=exit-time-destructors>)

target_include_directories(firebase_rest_lib
PUBLIC
${FIREBASE_CPP_SDK_ROOT_DIR}
Expand Down
12 changes: 8 additions & 4 deletions app/rest/transport_curl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "app/src/assert.h"
#include "app/src/include/firebase/internal/mutex.h"
#include "app/src/include/firebase/internal/platform.h"
#include "app/src/no_destructor.h"
#include "app/src/semaphore.h"
#include "app/src/thread.h"
#include "app/src/util.h"
Expand Down Expand Up @@ -312,13 +313,16 @@ CurlThread* g_curl_thread = nullptr;
// Count initializations that multiple libraries can use this simultaneously.
int g_initialize_count = 0;

// Mutex for Curl initialization.
Mutex* g_initialize_mutex = new Mutex();
// Mutex for Curl initialization and teardown.
Mutex& InitializeMutex() {
static NoDestructor<Mutex> initialize_mutex;
return *initialize_mutex;
}

} // namespace

void InitTransportCurl() {
MutexLock lock(*g_initialize_mutex);
MutexLock lock(InitializeMutex());
if (g_initialize_count == 0) {
// Initialize curl.
CURLcode global_init_code = curl_global_init(CURL_GLOBAL_ALL);
Expand All @@ -334,7 +338,7 @@ void InitTransportCurl() {
}

void CleanupTransportCurl() {
MutexLock lock(*g_initialize_mutex);
MutexLock lock(InitializeMutex());
assert(g_initialize_count > 0);
g_initialize_count--;
if (g_initialize_count == 0) {
Expand Down
15 changes: 10 additions & 5 deletions app/rest/util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
#include <cctype>
#include <string>

#include "app/src/include/firebase/internal/mutex.h"
#include "app/src/no_destructor.h"
#include "app/src/variant_util.h"
#include "curl/curl.h"
#include "flatbuffers/flatbuffers.h"
Expand All @@ -45,23 +47,26 @@ const char kGet[] = "GET";
const char kPost[] = "POST";

// Mutex for utils initialization and distribution of CURL pointers.
static ::firebase::Mutex* g_util_curl_mutex = new Mutex();
static ::firebase::Mutex& UtilCurlMutex() {
static NoDestructor<::firebase::Mutex> util_curl_mutex;
return *util_curl_mutex;
}

CURL* g_curl_instance = nullptr;
int g_init_ref_count = 0;

void* CreateCurlPtr() { // NOLINT
MutexLock lock(*g_util_curl_mutex);
MutexLock lock(UtilCurlMutex());
return curl_easy_init();
}

void DestroyCurlPtr(void* curl_ptr) {
MutexLock lock(*g_util_curl_mutex);
MutexLock lock(UtilCurlMutex());
curl_easy_cleanup(static_cast<CURL*>(curl_ptr));
}

void Initialize() {
MutexLock curl_lock(*g_util_curl_mutex);
MutexLock curl_lock(UtilCurlMutex());
assert(g_init_ref_count >= 0);
if (g_init_ref_count == 0) {
g_curl_instance = reinterpret_cast<CURL*>(CreateCurlPtr());
Expand All @@ -70,7 +75,7 @@ void Initialize() {
}

void Terminate() {
MutexLock curl_lock(*g_util_curl_mutex);
MutexLock curl_lock(UtilCurlMutex());
--g_init_ref_count;
assert(g_init_ref_count >= 0);
if (g_init_ref_count == 0) {
Expand Down
Loading
Loading