Repository navigation
Fix singletons across DLL boundaries #4642
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
Open
Desp4
wants to merge
19
commits into
open-telemetry:main
Choose a base branch
from
Desp4:win-dll-fix
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+454
−35
Open
Changes from all commits
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
a004844
attempt to resolve the dll singleton issue
Desp4 d270e52
run all singleton tests on windows as well
Desp4 5034832
fix a typo in compiler options
Desp4 c6fd389
add missing licenses
Desp4 ab9c20c
Merge branch 'main' into win-dll-fix
ThomsonTan 7f9e4b7
Merge branch 'main' into win-dll-fix
marcalff eb84855
fix formatting, remove static api lib, update ci test script
Desp4 883db3a
Merge branch 'win-dll-fix' of https://github.com/Desp4/opentelemetry-…
Desp4 b367b69
Merge branch 'main' into win-dll-fix
marcalff 874cc2d
bring win pipeline env up to date
Desp4 dc8bd42
Merge branch 'win-dll-fix' of https://github.com/Desp4/opentelemetry-…
Desp4 da62861
update formatting
Desp4 b398f20
update formatting again, add missing path to win ci script
Desp4 e8f3a23
update formatting
Desp4 e8eb23d
update ci script
Desp4 a377093
Merge branch 'main' into win-dll-fix
marcalff 6e67e75
fix PeriodicExportingMetricReader hanging in ExtDllInstallTest
Desp4 c447399
Merge branch 'win-dll-fix' of https://github.com/Desp4/opentelemetry-…
Desp4 5678bed
Merge branch 'main' into win-dll-fix
marcalff File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
99 changes: 99 additions & 0 deletions
99
api/include/opentelemetry/common/detail/symbol_bridge_windows.h
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,99 @@ | ||
| // Copyright The OpenTelemetry Authors | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| #pragma once | ||
|
|
||
| #include "opentelemetry/version.h" | ||
|
|
||
| #ifndef OPENTELEMETRY_SYMBOL_BRIDGE_NOINLINE | ||
| # ifndef WIN32_LEAN_AND_MEAN | ||
| # define WIN32_LEAN_AND_MEAN | ||
| # endif | ||
| # ifndef NOMINMAX | ||
| # define NOMINMAX | ||
| # endif | ||
| # include <Windows.h> | ||
| #endif | ||
|
|
||
| OPENTELEMETRY_BEGIN_NAMESPACE | ||
| namespace common | ||
| { | ||
| namespace detail | ||
| { | ||
|
|
||
| struct LoadedBridgeSymbol | ||
| { | ||
| void *address; | ||
| bool try_again; | ||
| }; | ||
|
|
||
| LoadedBridgeSymbol LoadSymbolBridgeSymbol(const char *symbol_name) noexcept; | ||
|
|
||
| template <typename Signature, typename Disambiguate = void> | ||
| Signature LoadSymbolBridgeSymbol(const char *symbol_name) noexcept | ||
| { | ||
| static LoadedBridgeSymbol symbol{nullptr, true}; | ||
| if (!symbol.address && symbol.try_again) | ||
| { | ||
| symbol = LoadSymbolBridgeSymbol(symbol_name); | ||
| } | ||
| return reinterpret_cast<Signature>(symbol.address); | ||
| } | ||
|
|
||
| #ifndef OPENTELEMETRY_SYMBOL_BRIDGE_NOINLINE | ||
| inline | ||
| #endif | ||
|
|
||
| #if !defined(OPENTELEMETRY_SYMBOL_BRIDGE_NOINLINE) || defined(OPENTELEMETRY_SYMBOL_BRIDGE_IMPL) | ||
| LoadedBridgeSymbol | ||
| LoadSymbolBridgeSymbol(const char *symbol_name) noexcept | ||
| { | ||
| struct ModuleLifetime | ||
| { | ||
| ::HMODULE module{nullptr}; | ||
| bool load_failed{false}; | ||
|
|
||
| ~ModuleLifetime() | ||
| { | ||
| if (module) | ||
| { | ||
| ::FreeLibrary(module); | ||
| } | ||
| } | ||
| }; | ||
|
|
||
| static ::ATOM bridge_present_atom{0}; | ||
| if (bridge_present_atom == 0) | ||
| { | ||
| bridge_present_atom = ::FindAtomW(L"otelcpp-abi-bridge"); | ||
| if (bridge_present_atom == 0) | ||
| { | ||
| return {nullptr, true}; | ||
| } | ||
| } | ||
|
|
||
| static ModuleLifetime bridge_module{}; | ||
| if (!bridge_module.module) | ||
| { | ||
| if (bridge_module.load_failed) | ||
| { | ||
| return {nullptr, false}; | ||
| } | ||
|
|
||
| ::GetModuleHandleExW(0x0, L"opentelemetry_api_symbol_bridge", &bridge_module.module); | ||
| bridge_module.load_failed = !bridge_module.module; | ||
|
|
||
| if (bridge_module.load_failed) | ||
| { | ||
| return {nullptr, false}; | ||
| } | ||
| } | ||
|
|
||
| const auto address = ::GetProcAddress(bridge_module.module, symbol_name); | ||
| return {reinterpret_cast<void *>(address), false}; | ||
| } | ||
| #endif | ||
|
|
||
| } // namespace detail | ||
| } // namespace common | ||
| OPENTELEMETRY_END_NAMESPACE | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just wondersing, Should this return
{nullptr, false}here?Two concerns about the lazy-loading path:
LoadLibrary, the cached address instatic LoadedBridgeSymbol symbol{nullptr, true}; becomes invalid afterFreeLibrary, which forces us to track the DLL lifetime and reset all the singletons — that adds real complexity.Given that, I'd lean toward requiring the bridge to be linked rather than dynamically loaded. Then its availability is fixed when this function is called (always present or always absent), and we can drop the reload logic for
otelcpp-abi-bridge. WDYT?Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FindAtomWcall to be around 30ns on my machine, plus this should lend itself well for branch prediction in a repeated noop case when the atom is not found. I personally don't think that the added cost is critical enough, and I also don't know of a cheaper process-wide method to check for a loaded abi bridge(though this is of course irrelevant if we drop the flexibility of the bridge being loaded after the first singleton access).FreeLibrarycall is tied to the lifetime ofModuleLifetime bridge_module, that is we're only freeing the library at the global destruction phase when the last binary using the bridge is getting unloaded(sinceFreeLibraryonly decrements the refcount).My thoughts on dropping the dynamic check for the bridge availability. I personally don't find the dynamic check to be a critical feature and wouldn't oppose dropping it, though the benefit I see is only cutting the cost of repeated atom check for a noop case.
I originally wanted to support the dynamic bridge load specifically for two cases:
I find this case niche enough to not be something the SDK ought to support(given that using the API at global init is very much guaranteed to only give you the noop singletons even on linux, this usage pattern is not really productive to begin with).
This too I don't find to be that critical since most plugin-loading software loads them near startup before any API consuming code is supposedly ran.
So overall I would not oppose dropping the atom check and just resorting to a hard requirement of a bridge being there on first singleton access, given that the cases I could come up with above are not that common or worth supporting.