Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[POC RFC] header only API for singletons (Windows) #1595

Closed
9 changes: 6 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,12 @@ endif()

if(WITH_STL)
# These definitions are needed for test projects that do not link against
# opentelemetry-api library directly. We ensure that variant implementation
# (absl::variant or std::variant) in variant unit test code is consistent with
# the global project build definitions. Optimize for speed to reduce the hops
# opentelemetry-api library directly. We ensure that STL implementation
# (nostd:: or std::) in unit test code is consistent with the global project
# build definitions.
add_definitions(-DHAVE_CPP_STDLIB)

# Optimize for speed to reduce the hops
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
if(CMAKE_BUILD_TYPE MATCHES Debug)
# Turn off optimizations for DEBUG
Expand Down
19 changes: 17 additions & 2 deletions api/include/opentelemetry/_metrics/provider.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

# include "opentelemetry/_metrics/meter_provider.h"
# include "opentelemetry/_metrics/noop.h"
# include "opentelemetry/common/macros.h"
# include "opentelemetry/common/spin_lock_mutex.h"
# include "opentelemetry/nostd/shared_ptr.h"

Expand Down Expand Up @@ -41,19 +42,33 @@ class Provider
}

private:
static nostd::shared_ptr<MeterProvider> &GetProvider() noexcept
OPENTELEMETRY_API_SINGLETON static nostd::shared_ptr<MeterProvider> &GetProvider() noexcept
{
# ifdef OPENTELEMETRY_SINGLETON_IN_METHOD
static nostd::shared_ptr<MeterProvider> provider(new NoopMeterProvider);
# endif
return provider;
}

static common::SpinLockMutex &GetLock() noexcept
OPENTELEMETRY_API_SINGLETON static common::SpinLockMutex &GetLock() noexcept
{
# ifdef OPENTELEMETRY_SINGLETON_IN_METHOD
static common::SpinLockMutex lock;
# endif
return lock;
}

# ifdef OPENTELEMETRY_SINGLETON_IN_MEMBER
static nostd::shared_ptr<MeterProvider> provider;
static common::SpinLockMutex lock;
# endif
};

# ifdef OPENTELEMETRY_SINGLETON_IN_MEMBER
static nostd::shared_ptr<MeterProvider> Provider::provider(new NoopMeterProvider);
static common::SpinLockMutex Provider::lock;
# endif

} // namespace metrics
OPENTELEMETRY_END_NAMESPACE
#endif
13 changes: 12 additions & 1 deletion api/include/opentelemetry/baggage/baggage.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <cctype>

#include "opentelemetry/common/kv_properties.h"
#include "opentelemetry/common/macros.h"
#include "opentelemetry/nostd/shared_ptr.h"
#include "opentelemetry/nostd/string_view.h"
#include "opentelemetry/version.h"
Expand Down Expand Up @@ -34,9 +35,11 @@ class Baggage
: kv_properties_(new opentelemetry::common::KeyValueProperties(keys_and_values))
{}

static nostd::shared_ptr<Baggage> GetDefault()
OPENTELEMETRY_API_SINGLETON static nostd::shared_ptr<Baggage> GetDefault()
{
#ifdef OPENTELEMETRY_SINGLETON_IN_METHOD
static nostd::shared_ptr<Baggage> baggage{new Baggage()};
#endif
return baggage;
}

Expand Down Expand Up @@ -292,8 +295,16 @@ class Baggage
private:
// Store entries in a C-style array to avoid using std::array or std::vector.
nostd::unique_ptr<opentelemetry::common::KeyValueProperties> kv_properties_;

#ifdef OPENTELEMETRY_SINGLETON_IN_MEMBER
static nostd::shared_ptr<Baggage> baggage;
#endif
};

#ifdef OPENTELEMETRY_SINGLETON_IN_MEMBER
OPENTELEMETRY_MEMBER_SINGLETON nostd::shared_ptr<Baggage> Baggage::baggage{new Baggage()};
#endif

} // namespace baggage

OPENTELEMETRY_END_NAMESPACE
139 changes: 139 additions & 0 deletions api/include/opentelemetry/common/macros.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,142 @@
#else
# define OPENTELEMETRY_DEPRECATED_MESSAGE(msg)
#endif

/* clang-format off */

/**
@page HEADER_ONLY_SINGLETON Header only singleton.

@section ELF_SINGLETON

For clang and gcc, the desired coding pattern is as follows.

@verbatim
class Foo
{
// (a)
__attribute__((visibility("default")))
// (b)
T& get_singleton()
{
// (c)
static T singleton;
return singleton;
}
};
@endverbatim

(a) is needed when the code is build with
@code -fvisibility="hidden" @endcode
to ensure that all instances of (b) are visible to the linker.

What is duplicated in the binary is @em code, in (b).

The linker will make sure only one instance
of all the (b) methods is used.

(c) is a singleton implemented inside a method.

This is very desirable, because:

- the C++ compiler guarantees the variable (c) is thread safe,
starting with C++ XX (TODO: link needed)

- constructors for (c) singletons are executed in code path order,
or not at all if the singleton is never used.

@section WINDOWS_SINGLETON

For Visual Studio, the desired coding pattern is as follows.

@verbatim
class Foo
{
// (d)
T& get_singleton()
{
return singleton;
}

// (e)
static T singleton;
};

// (f)
__declspec(selectany) T Foo::singleton;
@endverbatim

(d) is just used to expose the singleton,
so that other methods do not see implementation details.

(e) is a declaration

(f) is a definition, that breaks ODR (One Definition Rule),
corrected with the
@code __declspec(selectany) @endcode
annotation.

(f) has to be done separately from (e), in an "out of class definition",
because combining (e) and (f) in an "in class definition" is not a valid
syntax with @c selectany.

What is duplicated in the binary is @em data, in (f).

In this pattern, initialization of (f) does not happen in code path order,
initialization depends on global C++ constructors.

@section CODING_PATTERN

As a result, to support all platforms,
the coding pattern to use is as follows

@verbatim
class Foo
{
OPENTELEMETRY_API_SINGLETON
T& get_singleton()
{
#ifdef OPENTELEMETRY_SINGLETON_IN_METHOD
static T singleton;
#endif
return singleton;
}

#ifdef OPENTELEMETRY_SINGLETON_IN_MEMBER
static T singleton;
#endif
};


#ifdef OPENTELEMETRY_SINGLETON_IN_MEMBER
OPENTELEMETRY_MEMBER_SINGLETON T Foo::singleton;
#endif
@endverbatim
*/

/* clang-format on */

#if defined(__clang__)

# define OPENTELEMETRY_SINGLETON_IN_METHOD
# define OPENTELEMETRY_API_SINGLETON __attribute__((visibility("default")))

#elif defined(__GNUC__)

# define OPENTELEMETRY_SINGLETON_IN_METHOD
# define OPENTELEMETRY_API_SINGLETON __attribute__((visibility("default")))

#elif defined(_MSC_VER)

# define OPENTELEMETRY_SINGLETON_IN_MEMBER
# define OPENTELEMETRY_API_SINGLETON
# define OPENTELEMETRY_MEMBER_SINGLETON __declspec(selectany)

#else

/* Add support for another compiler here. */

# define OPENTELEMETRY_SINGLETON_IN_METHOD
# define OPENTELEMETRY_API_SINGLETON

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "opentelemetry/context/propagation/noop_propagator.h"
#include "opentelemetry/context/propagation/text_map_propagator.h"

#include "opentelemetry/common/macros.h"
#include "opentelemetry/common/spin_lock_mutex.h"
#include "opentelemetry/nostd/shared_ptr.h"

Expand Down Expand Up @@ -37,19 +38,34 @@ class GlobalTextMapPropagator
}

private:
static nostd::shared_ptr<TextMapPropagator> &GetPropagator() noexcept
OPENTELEMETRY_API_SINGLETON static nostd::shared_ptr<TextMapPropagator> &GetPropagator() noexcept
{
#ifdef OPENTELEMETRY_SINGLETON_IN_METHOD
static nostd::shared_ptr<TextMapPropagator> propagator(new NoOpPropagator());
#endif
return propagator;
}

static common::SpinLockMutex &GetLock() noexcept
OPENTELEMETRY_API_SINGLETON static common::SpinLockMutex &GetLock() noexcept
{
#ifdef OPENTELEMETRY_SINGLETON_IN_METHOD
static common::SpinLockMutex lock;
#endif
return lock;
}

#ifdef OPENTELEMETRY_SINGLETON_IN_MEMBER
static nostd::shared_ptr<TextMapPropagator> propagator;
static common::SpinLockMutex lock;
#endif
};

#ifdef OPENTELEMETRY_SINGLETON_IN_MEMBER
OPENTELEMETRY_MEMBER_SINGLETON nostd::shared_ptr<TextMapPropagator>
GlobalTextMapPropagator::propagator(new NoOpPropagator());
OPENTELEMETRY_MEMBER_SINGLETON common::SpinLockMutex GlobalTextMapPropagator::lock;
#endif

} // namespace propagation
} // namespace context
OPENTELEMETRY_END_NAMESPACE
OPENTELEMETRY_END_NAMESPACE
19 changes: 17 additions & 2 deletions api/include/opentelemetry/context/runtime_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#pragma once

#include "opentelemetry/common/macros.h"
#include "opentelemetry/context/context.h"

OPENTELEMETRY_BEGIN_NAMESPACE
Expand Down Expand Up @@ -166,13 +167,24 @@ class RuntimeContext
return GetStorage();
}

static nostd::shared_ptr<RuntimeContextStorage> &GetStorage() noexcept
OPENTELEMETRY_API_SINGLETON static nostd::shared_ptr<RuntimeContextStorage> &GetStorage() noexcept
{
#ifdef OPENTELEMETRY_SINGLETON_IN_METHOD
static nostd::shared_ptr<RuntimeContextStorage> context(GetDefaultStorage());
#endif
return context;
}

#ifdef OPENTELEMETRY_SINGLETON_IN_MEMBER
static nostd::shared_ptr<RuntimeContextStorage> context;
#endif
};

#ifdef OPENTELEMETRY_SINGLETON_IN_MEMBER
OPENTELEMETRY_MEMBER_SINGLETON nostd::shared_ptr<RuntimeContextStorage> RuntimeContext::context(
GetDefaultStorage());
#endif

inline Token::~Token() noexcept
{
context::RuntimeContext::Detach(*this);
Expand Down Expand Up @@ -315,8 +327,11 @@ class ThreadLocalContextStorage : public RuntimeContextStorage
Context *base_;
};

Stack &GetStack()
OPENTELEMETRY_API_SINGLETON Stack &GetStack()
{
// Safe with OPENTELEMETRY_SINGLETON_IN_METHOD.

// TODO: Investigate if this is safe with windows
static thread_local Stack stack_ = Stack();
return stack_;
}
Expand Down
20 changes: 18 additions & 2 deletions api/include/opentelemetry/logs/provider.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

# include <mutex>

# include "opentelemetry/common/macros.h"
# include "opentelemetry/common/spin_lock_mutex.h"
# include "opentelemetry/logs/logger_provider.h"
# include "opentelemetry/logs/noop.h"
Expand Down Expand Up @@ -42,19 +43,34 @@ class Provider
}

private:
static nostd::shared_ptr<LoggerProvider> &GetProvider() noexcept
OPENTELEMETRY_API_SINGLETON static nostd::shared_ptr<LoggerProvider> &GetProvider() noexcept
{
# ifdef OPENTELEMETRY_SINGLETON_IN_METHOD
static nostd::shared_ptr<LoggerProvider> provider(new NoopLoggerProvider);
# endif
return provider;
}

static common::SpinLockMutex &GetLock() noexcept
OPENTELEMETRY_API_SINGLETON static common::SpinLockMutex &GetLock() noexcept
{
# ifdef OPENTELEMETRY_SINGLETON_IN_METHOD
static common::SpinLockMutex lock;
# endif
return lock;
}

# ifdef OPENTELEMETRY_SINGLETON_IN_MEMBER
static nostd::shared_ptr<LoggerProvider> provider;
static common::SpinLockMutex lock;
# endif
};

# ifdef OPENTELEMETRY_SINGLETON_IN_MEMBER
OPENTELEMETRY_MEMBER_SINGLETON nostd::shared_ptr<LoggerProvider> Provider::provider(
new NoopLoggerProvider);
OPENTELEMETRY_MEMBER_SINGLETON common::SpinLockMutex Provider::lock;
# endif

} // namespace logs
OPENTELEMETRY_END_NAMESPACE
#endif
Loading