1818 */
1919
2020#include " iceberg/catalog/rest/auth/auth_manager_internal.h"
21+ #include " iceberg/catalog/rest/auth/aws_sdk.h"
2122#include " iceberg/catalog/rest/auth/sigv4_auth_manager_internal.h"
2223
2324#ifdef ICEBERG_SIGV4
2425
26+ # include < atomic>
27+ # include < mutex>
2528# include < sstream>
2629
2730# include < aws/core/Aws.h>
@@ -41,23 +44,17 @@ namespace iceberg::rest::auth {
4144
4245namespace {
4346
44- // / \brief Ensures the AWS SDK is initialized exactly once per process.
45- // /
46- // / Aws::InitAPI / ShutdownAPI must bracket the process lifetime, which a
47- // / library cannot enforce, so we never call ShutdownAPI (leak by design).
48- class AwsSdkGuard {
49- public:
50- static void EnsureInitialized () {
51- static AwsSdkGuard instance;
52- (void )instance;
53- }
47+ enum class LifecycleState : uint8_t { kUninitialized , kInitialized , kFinalized };
5448
55- private:
56- AwsSdkGuard () {
57- Aws::SDKOptions options;
58- Aws::InitAPI (options);
59- }
60- };
49+ std::atomic<LifecycleState> g_state{LifecycleState::kUninitialized };
50+ std::mutex g_lifecycle_mutex;
51+ Aws::SDKOptions g_sdk_options;
52+ std::atomic<size_t > g_active_session_count{0 };
53+
54+ Status EnsureSdkInitialized () {
55+ if (g_state.load () == LifecycleState::kInitialized ) return {};
56+ return InitializeAwsSdk ();
57+ }
6158
6259Aws::Http::HttpMethod ToAwsMethod (HttpMethod method) {
6360 switch (method) {
@@ -114,9 +111,14 @@ SigV4AuthSession::SigV4AuthSession(
114111 signing_name_(std::move(signing_name)),
115112 credentials_provider_(std::move(credentials_provider)),
116113 signer_(std::make_unique<RestSigV4Signer>(
117- credentials_provider_, signing_name_.c_str(), signing_region_.c_str())) {}
114+ credentials_provider_, signing_name_.c_str(), signing_region_.c_str())) {
115+ // Counted so FinalizeAwsSdk() refuses to ShutdownAPI while sessions exist.
116+ g_active_session_count.fetch_add (1 , std::memory_order_relaxed);
117+ }
118118
119- SigV4AuthSession::~SigV4AuthSession () = default ;
119+ SigV4AuthSession::~SigV4AuthSession () {
120+ g_active_session_count.fetch_sub (1 , std::memory_order_relaxed);
121+ }
120122
121123Result<HttpRequest> SigV4AuthSession::Authenticate (const HttpRequest& request) {
122124 ICEBERG_ASSIGN_OR_RAISE (auto delegate_request, delegate_->Authenticate (request));
@@ -189,7 +191,7 @@ SigV4AuthManager::~SigV4AuthManager() = default;
189191Result<std::shared_ptr<AuthSession>> SigV4AuthManager::InitSession (
190192 HttpClient& init_client,
191193 const std::unordered_map<std::string, std::string>& properties) {
192- AwsSdkGuard::EnsureInitialized ( );
194+ ICEBERG_RETURN_UNEXPECTED ( EnsureSdkInitialized () );
193195 ICEBERG_ASSIGN_OR_RAISE (auto delegate_session,
194196 delegate_->InitSession (init_client, properties));
195197 return WrapSession (std::move (delegate_session), properties);
@@ -198,7 +200,7 @@ Result<std::shared_ptr<AuthSession>> SigV4AuthManager::InitSession(
198200Result<std::shared_ptr<AuthSession>> SigV4AuthManager::CatalogSession (
199201 HttpClient& shared_client,
200202 const std::unordered_map<std::string, std::string>& properties) {
201- AwsSdkGuard::EnsureInitialized ( );
203+ ICEBERG_RETURN_UNEXPECTED ( EnsureSdkInitialized () );
202204 catalog_properties_ = properties;
203205 ICEBERG_ASSIGN_OR_RAISE (auto delegate_session,
204206 delegate_->CatalogSession (shared_client, properties));
@@ -323,6 +325,35 @@ Result<std::unique_ptr<AuthManager>> MakeSigV4AuthManager(
323325 return std::make_unique<SigV4AuthManager>(std::move (delegate));
324326}
325327
328+ Status InitializeAwsSdk () {
329+ std::lock_guard<std::mutex> lock (g_lifecycle_mutex);
330+ auto state = g_state.load ();
331+ if (state == LifecycleState::kInitialized ) return {};
332+ if (state == LifecycleState::kFinalized ) {
333+ return InvalidArgument (" AWS SDK has already been finalized; cannot reinitialize" );
334+ }
335+ Aws::InitAPI (g_sdk_options);
336+ g_state.store (LifecycleState::kInitialized );
337+ return {};
338+ }
339+
340+ Status FinalizeAwsSdk () {
341+ std::lock_guard<std::mutex> lock (g_lifecycle_mutex);
342+ if (g_state.load () != LifecycleState::kInitialized ) return {};
343+ auto live = g_active_session_count.load ();
344+ if (live != 0 ) {
345+ return Invalid (
346+ " Cannot finalize AWS SDK while {} SigV4 auth session(s) are still alive" , live);
347+ }
348+ Aws::ShutdownAPI (g_sdk_options);
349+ g_state.store (LifecycleState::kFinalized );
350+ return {};
351+ }
352+
353+ bool IsAwsSdkInitialized () { return g_state.load () == LifecycleState::kInitialized ; }
354+
355+ bool IsAwsSdkFinalized () { return g_state.load () == LifecycleState::kFinalized ; }
356+
326357} // namespace iceberg::rest::auth
327358
328359#else // !ICEBERG_SIGV4
@@ -336,6 +367,17 @@ Result<std::unique_ptr<AuthManager>> MakeSigV4AuthManager(
336367 " SigV4 authentication is not built; configure with -DICEBERG_SIGV4=ON" );
337368}
338369
370+ Status InitializeAwsSdk () {
371+ return NotSupported (
372+ " SigV4 authentication is not built; configure with -DICEBERG_SIGV4=ON" );
373+ }
374+
375+ Status FinalizeAwsSdk () { return {}; }
376+
377+ bool IsAwsSdkInitialized () { return false ; }
378+
379+ bool IsAwsSdkFinalized () { return false ; }
380+
339381} // namespace iceberg::rest::auth
340382
341383#endif // ICEBERG_SIGV4
0 commit comments