diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a0a688e38..26195e04bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ Increment the: * [METRICS EXPORTER] Add `OtlpGrpcMetricExporterFactory` and `OtlpHttpMetricExporterFactory`. [#1606](https://github.com/open-telemetry/opentelemetry-cpp/pull/1606) * [METRICS EXPORTER] Add `OtlpGrpcClient` [#1606](https://github.com/open-telemetry/opentelemetry-cpp/pull/1606) +* [BUILD] Fix header only api singletons [#1604](https://github.com/open-telemetry/opentelemetry-cpp/pull/1604) ## [1.6.0] 2022-08-15 diff --git a/api/include/opentelemetry/_metrics/provider.h b/api/include/opentelemetry/_metrics/provider.h index e079b34d5f..397aa64c16 100644 --- a/api/include/opentelemetry/_metrics/provider.h +++ b/api/include/opentelemetry/_metrics/provider.h @@ -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" @@ -41,13 +42,13 @@ class Provider } private: - static nostd::shared_ptr &GetProvider() noexcept + OPENTELEMETRY_API_SINGLETON static nostd::shared_ptr &GetProvider() noexcept { static nostd::shared_ptr provider(new NoopMeterProvider); return provider; } - static common::SpinLockMutex &GetLock() noexcept + OPENTELEMETRY_API_SINGLETON static common::SpinLockMutex &GetLock() noexcept { static common::SpinLockMutex lock; return lock; diff --git a/api/include/opentelemetry/baggage/baggage.h b/api/include/opentelemetry/baggage/baggage.h index eb5e4dcc7c..2bf0fda0d4 100644 --- a/api/include/opentelemetry/baggage/baggage.h +++ b/api/include/opentelemetry/baggage/baggage.h @@ -6,6 +6,7 @@ #include #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" @@ -34,7 +35,7 @@ class Baggage : kv_properties_(new opentelemetry::common::KeyValueProperties(keys_and_values)) {} - static nostd::shared_ptr GetDefault() + OPENTELEMETRY_API_SINGLETON static nostd::shared_ptr GetDefault() { static nostd::shared_ptr baggage{new Baggage()}; return baggage; diff --git a/api/include/opentelemetry/common/macros.h b/api/include/opentelemetry/common/macros.h index 204c1ed04e..8f88fc8f85 100644 --- a/api/include/opentelemetry/common/macros.h +++ b/api/include/opentelemetry/common/macros.h @@ -89,3 +89,86 @@ #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 that construction + of the variable (c) is thread safe. + + - constructors for (c) singletons are executed in code path order, + or not at all if the singleton is never used. + + @section OTHER_SINGLETON + + For other platforms, header only singletons are not supported at this +point. + + @section CODING_PATTERN + + The coding pattern to use in the source code is as follows + + @verbatim + class Foo + { + OPENTELEMETRY_API_SINGLETON + T& get_singleton() + { + static T singleton; + return singleton; + } + }; + @endverbatim +*/ + +/* clang-format on */ + +#if defined(__clang__) + +# define OPENTELEMETRY_API_SINGLETON __attribute__((visibility("default"))) + +#elif defined(__GNUC__) + +# define OPENTELEMETRY_API_SINGLETON __attribute__((visibility("default"))) + +#else + +/* Add support for other compilers here. */ + +# define OPENTELEMETRY_API_SINGLETON + +#endif diff --git a/api/include/opentelemetry/context/propagation/global_propagator.h b/api/include/opentelemetry/context/propagation/global_propagator.h index b4e49325cd..6d26b84019 100644 --- a/api/include/opentelemetry/context/propagation/global_propagator.h +++ b/api/include/opentelemetry/context/propagation/global_propagator.h @@ -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" @@ -37,13 +38,13 @@ class GlobalTextMapPropagator } private: - static nostd::shared_ptr &GetPropagator() noexcept + OPENTELEMETRY_API_SINGLETON static nostd::shared_ptr &GetPropagator() noexcept { static nostd::shared_ptr propagator(new NoOpPropagator()); return propagator; } - static common::SpinLockMutex &GetLock() noexcept + OPENTELEMETRY_API_SINGLETON static common::SpinLockMutex &GetLock() noexcept { static common::SpinLockMutex lock; return lock; @@ -52,4 +53,4 @@ class GlobalTextMapPropagator } // namespace propagation } // namespace context -OPENTELEMETRY_END_NAMESPACE \ No newline at end of file +OPENTELEMETRY_END_NAMESPACE diff --git a/api/include/opentelemetry/context/runtime_context.h b/api/include/opentelemetry/context/runtime_context.h index 167a928f10..a92dcdf05f 100644 --- a/api/include/opentelemetry/context/runtime_context.h +++ b/api/include/opentelemetry/context/runtime_context.h @@ -3,6 +3,7 @@ #pragma once +#include "opentelemetry/common/macros.h" #include "opentelemetry/context/context.h" OPENTELEMETRY_BEGIN_NAMESPACE @@ -166,7 +167,7 @@ class RuntimeContext return GetStorage(); } - static nostd::shared_ptr &GetStorage() noexcept + OPENTELEMETRY_API_SINGLETON static nostd::shared_ptr &GetStorage() noexcept { static nostd::shared_ptr context(GetDefaultStorage()); return context; @@ -315,7 +316,7 @@ class ThreadLocalContextStorage : public RuntimeContextStorage Context *base_; }; - Stack &GetStack() + OPENTELEMETRY_API_SINGLETON Stack &GetStack() { static thread_local Stack stack_ = Stack(); return stack_; diff --git a/api/include/opentelemetry/logs/provider.h b/api/include/opentelemetry/logs/provider.h index 38186bbe75..6f4b76fbd7 100644 --- a/api/include/opentelemetry/logs/provider.h +++ b/api/include/opentelemetry/logs/provider.h @@ -6,6 +6,7 @@ # include +# include "opentelemetry/common/macros.h" # include "opentelemetry/common/spin_lock_mutex.h" # include "opentelemetry/logs/logger_provider.h" # include "opentelemetry/logs/noop.h" @@ -42,13 +43,13 @@ class Provider } private: - static nostd::shared_ptr &GetProvider() noexcept + OPENTELEMETRY_API_SINGLETON static nostd::shared_ptr &GetProvider() noexcept { static nostd::shared_ptr provider(new NoopLoggerProvider); return provider; } - static common::SpinLockMutex &GetLock() noexcept + OPENTELEMETRY_API_SINGLETON static common::SpinLockMutex &GetLock() noexcept { static common::SpinLockMutex lock; return lock; diff --git a/api/include/opentelemetry/metrics/provider.h b/api/include/opentelemetry/metrics/provider.h index 3bacac9760..0eddc61142 100644 --- a/api/include/opentelemetry/metrics/provider.h +++ b/api/include/opentelemetry/metrics/provider.h @@ -6,6 +6,7 @@ # include +# include "opentelemetry/common/macros.h" # include "opentelemetry/common/spin_lock_mutex.h" # include "opentelemetry/metrics/meter_provider.h" # include "opentelemetry/metrics/noop.h" @@ -42,13 +43,13 @@ class Provider } private: - static nostd::shared_ptr &GetProvider() noexcept + OPENTELEMETRY_API_SINGLETON static nostd::shared_ptr &GetProvider() noexcept { static nostd::shared_ptr provider(new NoopMeterProvider); return provider; } - static common::SpinLockMutex &GetLock() noexcept + OPENTELEMETRY_API_SINGLETON static common::SpinLockMutex &GetLock() noexcept { static common::SpinLockMutex lock; return lock; @@ -57,4 +58,4 @@ class Provider } // namespace metrics OPENTELEMETRY_END_NAMESPACE -#endif \ No newline at end of file +#endif diff --git a/api/include/opentelemetry/trace/provider.h b/api/include/opentelemetry/trace/provider.h index 6267cbd4d2..e59c5f9512 100644 --- a/api/include/opentelemetry/trace/provider.h +++ b/api/include/opentelemetry/trace/provider.h @@ -5,6 +5,7 @@ #include +#include "opentelemetry/common/macros.h" #include "opentelemetry/common/spin_lock_mutex.h" #include "opentelemetry/nostd/shared_ptr.h" #include "opentelemetry/trace/noop.h" @@ -41,13 +42,13 @@ class Provider } private: - static nostd::shared_ptr &GetProvider() noexcept + OPENTELEMETRY_API_SINGLETON static nostd::shared_ptr &GetProvider() noexcept { static nostd::shared_ptr provider(new NoopTracerProvider); return provider; } - static common::SpinLockMutex &GetLock() noexcept + OPENTELEMETRY_API_SINGLETON static common::SpinLockMutex &GetLock() noexcept { static common::SpinLockMutex lock; return lock; diff --git a/api/include/opentelemetry/trace/trace_state.h b/api/include/opentelemetry/trace/trace_state.h index 0343637cfa..ce88dc91db 100644 --- a/api/include/opentelemetry/trace/trace_state.h +++ b/api/include/opentelemetry/trace/trace_state.h @@ -15,6 +15,7 @@ #endif #include "opentelemetry/common/kv_properties.h" +#include "opentelemetry/common/macros.h" #include "opentelemetry/nostd/shared_ptr.h" #include "opentelemetry/nostd/span.h" #include "opentelemetry/nostd/string_view.h" @@ -41,7 +42,7 @@ class TraceState static constexpr auto kKeyValueSeparator = '='; static constexpr auto kMembersSeparator = ','; - static nostd::shared_ptr GetDefault() + OPENTELEMETRY_API_SINGLETON static nostd::shared_ptr GetDefault() { static nostd::shared_ptr ts{new TraceState()}; return ts; @@ -316,5 +317,6 @@ class TraceState // Store entries in a C-style array to avoid using std::array or std::vector. nostd::unique_ptr kv_properties_; }; + } // namespace trace OPENTELEMETRY_END_NAMESPACE diff --git a/api/test/CMakeLists.txt b/api/test/CMakeLists.txt index 41125005ee..06f29d7365 100644 --- a/api/test/CMakeLists.txt +++ b/api/test/CMakeLists.txt @@ -13,3 +13,4 @@ if(WITH_LOGS_PREVIEW) endif() add_subdirectory(common) add_subdirectory(baggage) +add_subdirectory(singleton) diff --git a/api/test/singleton/BUILD b/api/test/singleton/BUILD new file mode 100644 index 0000000000..5522284509 --- /dev/null +++ b/api/test/singleton/BUILD @@ -0,0 +1,140 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +DEFAULT_WIN_COPTS = [ +] + +# gcc and clang, assumed to be used on this platform +DEFAULT_NOWIN_COPTS = [ + "-fvisibility=default", +] + +HIDDEN_WIN_COPTS = [ +] + +# gcc and clang, assumed to be used on this platform +HIDDEN_NOWIN_COPTS = [ + "-fvisibility=hidden", +] + +cc_library( + name = "component_a", + srcs = [ + "component_a.cc", + ], + hdrs = [ + "component_a.h", + ], + linkstatic = True, + deps = [ + "//api", + ], +) + +cc_library( + name = "component_b", + srcs = [ + "component_b.cc", + ], + hdrs = [ + "component_b.h", + ], + linkstatic = True, + deps = [ + "//api", + ], +) + +cc_library( + name = "component_c", + srcs = [ + "component_c.cc", + ], + hdrs = [ + "component_c.h", + ], + copts = select({ + "//bazel:windows": DEFAULT_WIN_COPTS, + "//conditions:default": DEFAULT_NOWIN_COPTS, + }), + linkstatic = False, + deps = [ + "//api", + ], +) + +cc_library( + name = "component_d", + srcs = [ + "component_d.cc", + ], + hdrs = [ + "component_d.h", + ], + copts = select({ + "//bazel:windows": HIDDEN_WIN_COPTS, + "//conditions:default": HIDDEN_NOWIN_COPTS, + }), + linkstatic = False, + deps = [ + "//api", + ], +) + +cc_library( + name = "component_e", + srcs = [ + "component_e.cc", + ], + hdrs = [ + "component_e.h", + ], + copts = select({ + "//bazel:windows": DEFAULT_WIN_COPTS, + "//conditions:default": DEFAULT_NOWIN_COPTS, + }), + linkstatic = False, + deps = [ + "//api", + ], +) + +cc_library( + name = "component_f", + srcs = [ + "component_f.cc", + ], + hdrs = [ + "component_f.h", + ], + copts = select({ + "//bazel:windows": HIDDEN_WIN_COPTS, + "//conditions:default": HIDDEN_NOWIN_COPTS, + }), + linkstatic = False, + deps = [ + "//api", + ], +) + +cc_test( + name = "singleton_test", + srcs = [ + "singleton_test.cc", + ], + linkstatic = False, + tags = [ + "api", + "test", + ], + deps = [ + "component_a", + "component_b", + "component_c", + "component_d", + "component_e", + "component_f", + "//api", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/api/test/singleton/CMakeLists.txt b/api/test/singleton/CMakeLists.txt new file mode 100644 index 0000000000..a92233fca3 --- /dev/null +++ b/api/test/singleton/CMakeLists.txt @@ -0,0 +1,51 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +include(GoogleTest) + +# Header only singletons are not available in windows yet. + +if(NOT WIN32) + + add_library(component_a STATIC component_a.cc) + target_link_libraries(component_a opentelemetry_api) + + add_library(component_b STATIC component_b.cc) + target_link_libraries(component_b opentelemetry_api) + + add_library(component_c SHARED component_c.cc) + set_target_properties(component_c PROPERTIES CXX_VISIBILITY_PRESET default) + target_link_libraries(component_c opentelemetry_api) + + add_library(component_d SHARED component_d.cc) + set_target_properties(component_d PROPERTIES CXX_VISIBILITY_PRESET hidden) + target_link_libraries(component_d opentelemetry_api) + + add_library(component_e SHARED component_e.cc) + set_target_properties(component_e PROPERTIES CXX_VISIBILITY_PRESET default) + target_link_libraries(component_e opentelemetry_api) + + add_library(component_f SHARED component_f.cc) + set_target_properties(component_f PROPERTIES CXX_VISIBILITY_PRESET hidden) + target_link_libraries(component_f opentelemetry_api) + + add_executable(singleton_test singleton_test.cc) + + target_link_libraries( + singleton_test + component_a + component_b + component_c + component_d + component_e + component_f + ${GTEST_BOTH_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} + opentelemetry_api) + + gtest_add_tests( + TARGET singleton_test + TEST_PREFIX singleton. + TEST_LIST singleton_test) + +endif() diff --git a/api/test/singleton/component_a.cc b/api/test/singleton/component_a.cc new file mode 100644 index 0000000000..bdf03cb699 --- /dev/null +++ b/api/test/singleton/component_a.cc @@ -0,0 +1,37 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "opentelemetry/nostd/shared_ptr.h" +#include "opentelemetry/trace/provider.h" +#include "opentelemetry/version.h" + +#include "component_a.h" + +namespace trace = opentelemetry::trace; +namespace nostd = opentelemetry::nostd; + +static nostd::shared_ptr get_tracer() +{ + auto provider = trace::Provider::GetTracerProvider(); + return provider->GetTracer("A", "10.1"); +} + +static void f1() +{ + auto scoped_span = trace::Scope(get_tracer()->StartSpan("A::f1")); +} + +static void f2() +{ + auto scoped_span = trace::Scope(get_tracer()->StartSpan("A::f2")); + + f1(); + f1(); +} + +void do_something_in_a() +{ + auto scoped_span = trace::Scope(get_tracer()->StartSpan("A::library")); + + f2(); +} diff --git a/api/test/singleton/component_a.h b/api/test/singleton/component_a.h new file mode 100644 index 0000000000..2e06edf9ca --- /dev/null +++ b/api/test/singleton/component_a.h @@ -0,0 +1,4 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +void do_something_in_a(); diff --git a/api/test/singleton/component_b.cc b/api/test/singleton/component_b.cc new file mode 100644 index 0000000000..966d9c461a --- /dev/null +++ b/api/test/singleton/component_b.cc @@ -0,0 +1,37 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "opentelemetry/nostd/shared_ptr.h" +#include "opentelemetry/trace/provider.h" +#include "opentelemetry/version.h" + +#include "component_b.h" + +namespace trace = opentelemetry::trace; +namespace nostd = opentelemetry::nostd; + +static nostd::shared_ptr get_tracer() +{ + auto provider = trace::Provider::GetTracerProvider(); + return provider->GetTracer("B", "20.2"); +} + +static void f1() +{ + auto scoped_span = trace::Scope(get_tracer()->StartSpan("B::f1")); +} + +static void f2() +{ + auto scoped_span = trace::Scope(get_tracer()->StartSpan("B::f2")); + + f1(); + f1(); +} + +void do_something_in_b() +{ + auto scoped_span = trace::Scope(get_tracer()->StartSpan("B::library")); + + f2(); +} diff --git a/api/test/singleton/component_b.h b/api/test/singleton/component_b.h new file mode 100644 index 0000000000..2e526e21d5 --- /dev/null +++ b/api/test/singleton/component_b.h @@ -0,0 +1,4 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +void do_something_in_b(); diff --git a/api/test/singleton/component_c.cc b/api/test/singleton/component_c.cc new file mode 100644 index 0000000000..cd87ceb727 --- /dev/null +++ b/api/test/singleton/component_c.cc @@ -0,0 +1,39 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "opentelemetry/nostd/shared_ptr.h" +#include "opentelemetry/trace/provider.h" +#include "opentelemetry/version.h" + +#define BUILD_COMPONENT_C + +#include "component_c.h" + +namespace trace = opentelemetry::trace; +namespace nostd = opentelemetry::nostd; + +static nostd::shared_ptr get_tracer() +{ + auto provider = trace::Provider::GetTracerProvider(); + return provider->GetTracer("C", "30.3"); +} + +static void f1() +{ + auto scoped_span = trace::Scope(get_tracer()->StartSpan("C::f1")); +} + +static void f2() +{ + auto scoped_span = trace::Scope(get_tracer()->StartSpan("C::f2")); + + f1(); + f1(); +} + +void do_something_in_c() +{ + auto scoped_span = trace::Scope(get_tracer()->StartSpan("C::library")); + + f2(); +} diff --git a/api/test/singleton/component_c.h b/api/test/singleton/component_c.h new file mode 100644 index 0000000000..31193da2df --- /dev/null +++ b/api/test/singleton/component_c.h @@ -0,0 +1,15 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#if defined(_MSC_VER) +// component_c is a DDL + +# ifdef BUILD_COMPONENT_C +__declspec(dllexport) +# else +__declspec(dllimport) +# endif + +#endif + + void do_something_in_c(); diff --git a/api/test/singleton/component_d.cc b/api/test/singleton/component_d.cc new file mode 100644 index 0000000000..313fd1d23a --- /dev/null +++ b/api/test/singleton/component_d.cc @@ -0,0 +1,39 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "opentelemetry/nostd/shared_ptr.h" +#include "opentelemetry/trace/provider.h" +#include "opentelemetry/version.h" + +#define BUILD_COMPONENT_D + +#include "component_d.h" + +namespace trace = opentelemetry::trace; +namespace nostd = opentelemetry::nostd; + +static nostd::shared_ptr get_tracer() +{ + auto provider = trace::Provider::GetTracerProvider(); + return provider->GetTracer("D", "40.4"); +} + +static void f1() +{ + auto scoped_span = trace::Scope(get_tracer()->StartSpan("D::f1")); +} + +static void f2() +{ + auto scoped_span = trace::Scope(get_tracer()->StartSpan("D::f2")); + + f1(); + f1(); +} + +void do_something_in_d() +{ + auto scoped_span = trace::Scope(get_tracer()->StartSpan("D::library")); + + f2(); +} diff --git a/api/test/singleton/component_d.h b/api/test/singleton/component_d.h new file mode 100644 index 0000000000..be65f4b5ac --- /dev/null +++ b/api/test/singleton/component_d.h @@ -0,0 +1,21 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +// Make the entry point visible, loaded dynamically + +#if defined(_MSC_VER) +// component_d is a DDL + +# ifdef BUILD_COMPONENT_D +__declspec(dllexport) +# else +__declspec(dllimport) +# endif + +#else +// component_d is a shared library (*.so) +// component_d is compiled with visibility("hidden"), +__attribute__((visibility("default"))) +#endif + + void do_something_in_d(); diff --git a/api/test/singleton/component_e.cc b/api/test/singleton/component_e.cc new file mode 100644 index 0000000000..9c88d27732 --- /dev/null +++ b/api/test/singleton/component_e.cc @@ -0,0 +1,39 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "opentelemetry/nostd/shared_ptr.h" +#include "opentelemetry/trace/provider.h" +#include "opentelemetry/version.h" + +#define BUILD_COMPONENT_E + +#include "component_e.h" + +namespace trace = opentelemetry::trace; +namespace nostd = opentelemetry::nostd; + +static nostd::shared_ptr get_tracer() +{ + auto provider = trace::Provider::GetTracerProvider(); + return provider->GetTracer("E", "50.5"); +} + +static void f1() +{ + auto scoped_span = trace::Scope(get_tracer()->StartSpan("E::f1")); +} + +static void f2() +{ + auto scoped_span = trace::Scope(get_tracer()->StartSpan("E::f2")); + + f1(); + f1(); +} + +void do_something_in_e() +{ + auto scoped_span = trace::Scope(get_tracer()->StartSpan("E::library")); + + f2(); +} diff --git a/api/test/singleton/component_e.h b/api/test/singleton/component_e.h new file mode 100644 index 0000000000..53782d76f7 --- /dev/null +++ b/api/test/singleton/component_e.h @@ -0,0 +1,15 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#if defined(_MSC_VER) +// component_e is a DDL + +# ifdef BUILD_COMPONENT_E +__declspec(dllexport) +# else +__declspec(dllimport) +# endif + +#endif + + void do_something_in_e(); diff --git a/api/test/singleton/component_f.cc b/api/test/singleton/component_f.cc new file mode 100644 index 0000000000..33b0af2fbd --- /dev/null +++ b/api/test/singleton/component_f.cc @@ -0,0 +1,39 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "opentelemetry/nostd/shared_ptr.h" +#include "opentelemetry/trace/provider.h" +#include "opentelemetry/version.h" + +#define BUILD_COMPONENT_F + +#include "component_f.h" + +namespace trace = opentelemetry::trace; +namespace nostd = opentelemetry::nostd; + +static nostd::shared_ptr get_tracer() +{ + auto provider = trace::Provider::GetTracerProvider(); + return provider->GetTracer("F", "60.6"); +} + +static void f1() +{ + auto scoped_span = trace::Scope(get_tracer()->StartSpan("F::f1")); +} + +static void f2() +{ + auto scoped_span = trace::Scope(get_tracer()->StartSpan("F::f2")); + + f1(); + f1(); +} + +void do_something_in_f() +{ + auto scoped_span = trace::Scope(get_tracer()->StartSpan("F::library")); + + f2(); +} diff --git a/api/test/singleton/component_f.h b/api/test/singleton/component_f.h new file mode 100644 index 0000000000..e042ba5bb3 --- /dev/null +++ b/api/test/singleton/component_f.h @@ -0,0 +1,21 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +// Make the entry point visible, loaded dynamically + +#if defined(_MSC_VER) +// component_f is a DDL + +# ifdef BUILD_COMPONENT_F +__declspec(dllexport) +# else +__declspec(dllimport) +# endif + +#else +// component_f is a shared library (*.so) +// component_f is compiled with visibility("hidden"), +__attribute__((visibility("default"))) +#endif + + void do_something_in_f(); diff --git a/api/test/singleton/singleton_test.cc b/api/test/singleton/singleton_test.cc new file mode 100644 index 0000000000..f9942c2f65 --- /dev/null +++ b/api/test/singleton/singleton_test.cc @@ -0,0 +1,289 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include + +#include +#include + +#include "component_a.h" +#include "component_b.h" +#include "component_c.h" +#include "component_d.h" +#include "component_e.h" +#include "component_f.h" + +#include "opentelemetry/trace/default_span.h" +#include "opentelemetry/trace/provider.h" +#include "opentelemetry/trace/span.h" +#include "opentelemetry/trace/span_context_kv_iterable.h" +#include "opentelemetry/trace/span_startoptions.h" +#include "opentelemetry/trace/tracer_provider.h" + +using namespace opentelemetry; + +void do_something() +{ + do_something_in_a(); + do_something_in_b(); + do_something_in_c(); + do_something_in_d(); + do_something_in_e(); + do_something_in_f(); +} + +int span_a_lib_count = 0; +int span_a_f1_count = 0; +int span_a_f2_count = 0; +int span_b_lib_count = 0; +int span_b_f1_count = 0; +int span_b_f2_count = 0; +int span_c_lib_count = 0; +int span_c_f1_count = 0; +int span_c_f2_count = 0; +int span_d_lib_count = 0; +int span_d_f1_count = 0; +int span_d_f2_count = 0; +int span_e_lib_count = 0; +int span_e_f1_count = 0; +int span_e_f2_count = 0; +int span_f_lib_count = 0; +int span_f_f1_count = 0; +int span_f_f2_count = 0; +int unknown_span_count = 0; + +void reset_counts() +{ + span_a_lib_count = 0; + span_a_f1_count = 0; + span_a_f2_count = 0; + span_b_lib_count = 0; + span_b_f1_count = 0; + span_b_f2_count = 0; + span_c_lib_count = 0; + span_c_f1_count = 0; + span_c_f2_count = 0; + span_d_lib_count = 0; + span_d_f1_count = 0; + span_d_f2_count = 0; + span_e_lib_count = 0; + span_e_f1_count = 0; + span_e_f2_count = 0; + span_f_lib_count = 0; + span_f_f1_count = 0; + span_f_f2_count = 0; + unknown_span_count = 0; +} + +class MyTracer : public trace::Tracer +{ +public: + nostd::shared_ptr StartSpan(nostd::string_view name, + const common::KeyValueIterable &attributes, + const trace::SpanContextKeyValueIterable &links, + const trace::StartSpanOptions &options) noexcept override + { + nostd::shared_ptr result(new trace::DefaultSpan(trace::SpanContext::GetInvalid())); + + /* + Unit test code, no need to be fancy. + */ + + if (name == "A::library") + { + span_a_lib_count++; + } + else if (name == "A::f1") + { + span_a_f1_count++; + } + else if (name == "A::f2") + { + span_a_f2_count++; + } + else if (name == "B::library") + { + span_b_lib_count++; + } + else if (name == "B::f1") + { + span_b_f1_count++; + } + else if (name == "B::f2") + { + span_b_f2_count++; + } + else if (name == "C::library") + { + span_c_lib_count++; + } + else if (name == "C::f1") + { + span_c_f1_count++; + } + else if (name == "C::f2") + { + span_c_f2_count++; + } + else if (name == "D::library") + { + span_d_lib_count++; + } + else if (name == "D::f1") + { + span_d_f1_count++; + } + else if (name == "D::f2") + { + span_d_f2_count++; + } + else if (name == "E::library") + { + span_e_lib_count++; + } + else if (name == "E::f1") + { + span_e_f1_count++; + } + else if (name == "E::f2") + { + span_e_f2_count++; + } + else if (name == "F::library") + { + span_f_lib_count++; + } + else if (name == "F::f1") + { + span_f_f1_count++; + } + else if (name == "F::f2") + { + span_f_f2_count++; + } + else + { + unknown_span_count++; + } + + return result; + } + + void ForceFlushWithMicroseconds(uint64_t timeout) noexcept override {} + + void CloseWithMicroseconds(uint64_t timeout) noexcept override {} +}; + +class MyTracerProvider : public trace::TracerProvider +{ +public: + static std::shared_ptr Create() + { + std::shared_ptr result(new MyTracerProvider()); + return result; + } + + nostd::shared_ptr GetTracer(nostd::string_view library_name, + nostd::string_view library_version, + nostd::string_view schema_url) noexcept override + { + nostd::shared_ptr result(new MyTracer()); + return result; + } +}; + +void setup_otel() +{ + std::shared_ptr provider = MyTracerProvider::Create(); + + // The whole point of this test is to make sure + // that the API singleton behind SetTracerProvider() + // works for all components, static and dynamic. + + // Set the global tracer provider + trace_api::Provider::SetTracerProvider(provider); +} + +void cleanup_otel() +{ + std::shared_ptr provider( + new opentelemetry::trace::NoopTracerProvider()); + + // Set the global tracer provider + trace_api::Provider::SetTracerProvider(provider); +} + +TEST(SingletonTest, Uniqueness) +{ + do_something(); + + EXPECT_EQ(span_a_lib_count, 0); + EXPECT_EQ(span_a_f1_count, 0); + EXPECT_EQ(span_a_f2_count, 0); + EXPECT_EQ(span_b_lib_count, 0); + EXPECT_EQ(span_b_f1_count, 0); + EXPECT_EQ(span_b_f2_count, 0); + EXPECT_EQ(span_c_lib_count, 0); + EXPECT_EQ(span_c_f1_count, 0); + EXPECT_EQ(span_c_f2_count, 0); + EXPECT_EQ(span_d_lib_count, 0); + EXPECT_EQ(span_d_f1_count, 0); + EXPECT_EQ(span_d_f2_count, 0); + EXPECT_EQ(span_e_lib_count, 0); + EXPECT_EQ(span_e_f1_count, 0); + EXPECT_EQ(span_e_f2_count, 0); + EXPECT_EQ(span_f_lib_count, 0); + EXPECT_EQ(span_f_f1_count, 0); + EXPECT_EQ(span_f_f2_count, 0); + EXPECT_EQ(unknown_span_count, 0); + + reset_counts(); + setup_otel(); + + do_something(); + + EXPECT_EQ(span_a_lib_count, 1); + EXPECT_EQ(span_a_f1_count, 2); + EXPECT_EQ(span_a_f2_count, 1); + EXPECT_EQ(span_b_lib_count, 1); + EXPECT_EQ(span_b_f1_count, 2); + EXPECT_EQ(span_b_f2_count, 1); + EXPECT_EQ(span_c_lib_count, 1); + EXPECT_EQ(span_c_f1_count, 2); + EXPECT_EQ(span_c_f2_count, 1); + EXPECT_EQ(span_d_lib_count, 1); + EXPECT_EQ(span_d_f1_count, 2); + EXPECT_EQ(span_d_f2_count, 1); + EXPECT_EQ(span_e_lib_count, 1); + EXPECT_EQ(span_e_f1_count, 2); + EXPECT_EQ(span_e_f2_count, 1); + EXPECT_EQ(span_f_lib_count, 1); + EXPECT_EQ(span_f_f1_count, 2); + EXPECT_EQ(span_f_f2_count, 1); + EXPECT_EQ(unknown_span_count, 0); + + reset_counts(); + cleanup_otel(); + + do_something(); + + EXPECT_EQ(span_a_lib_count, 0); + EXPECT_EQ(span_a_f1_count, 0); + EXPECT_EQ(span_a_f2_count, 0); + EXPECT_EQ(span_b_lib_count, 0); + EXPECT_EQ(span_b_f1_count, 0); + EXPECT_EQ(span_b_f2_count, 0); + EXPECT_EQ(span_c_lib_count, 0); + EXPECT_EQ(span_c_f1_count, 0); + EXPECT_EQ(span_c_f2_count, 0); + EXPECT_EQ(span_d_lib_count, 0); + EXPECT_EQ(span_d_f1_count, 0); + EXPECT_EQ(span_d_f2_count, 0); + EXPECT_EQ(span_e_lib_count, 0); + EXPECT_EQ(span_e_f1_count, 0); + EXPECT_EQ(span_e_f2_count, 0); + EXPECT_EQ(span_f_lib_count, 0); + EXPECT_EQ(span_f_f1_count, 0); + EXPECT_EQ(span_f_f2_count, 0); + EXPECT_EQ(unknown_span_count, 0); +}