Skip to content

Commit 832c839

Browse files
author
Marco Magdy
committed
Add copy and move constructors to AWSClient
addresses the issue raised in #815
1 parent 1840466 commit 832c839

File tree

7 files changed

+139
-56
lines changed

7 files changed

+139
-56
lines changed

aws-cpp-sdk-core-tests/aws/client/AWSClientTest.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include <aws/core/utils/memory/stl/AWSStringStream.h>
3030
#include <aws/core/utils/HashingUtils.h>
3131
#include <aws/core/utils/Outcome.h>
32+
#include <aws/core/Globals.h>
3233
#include <aws/testing/mocks/http/MockHttpClient.h>
3334

3435
using namespace Aws::Client;
@@ -408,3 +409,41 @@ TEST(AWSClientTest, TestHostHeaderWithStandardHttpPort)
408409
host = r4.GetHeaderValue(Aws::Http::HOST_HEADER);
409410
ASSERT_STREQ("example.amazonaws.com:80", host.c_str());
410411
}
412+
413+
TEST(AWSClientTest, TestOverflowContainerLife)
414+
{
415+
{
416+
ClientConfiguration config;
417+
MockAWSClient client(config);
418+
auto container = Aws::GetEnumOverflowContainer();
419+
ASSERT_NE(container, nullptr);
420+
}
421+
422+
auto container = Aws::GetEnumOverflowContainer();
423+
ASSERT_EQ(container, nullptr);
424+
}
425+
426+
TEST(AWSClientTest, TestCopyingClientExtendsOverflowContainerLife)
427+
{
428+
ClientConfiguration config;
429+
auto client1 = Aws::MakeUnique<MockAWSClient>(ALLOCATION_TAG, config);
430+
auto container = Aws::GetEnumOverflowContainer();
431+
ASSERT_NE(container, nullptr);
432+
433+
// copy clien1 to client2
434+
auto client2 = Aws::MakeUnique<MockAWSClient>(ALLOCATION_TAG, *client1);
435+
client1 = nullptr;
436+
container = Aws::GetEnumOverflowContainer();
437+
ASSERT_NE(container, nullptr);
438+
439+
// move client2 to client3
440+
auto client3 = Aws::MakeUnique<MockAWSClient>(ALLOCATION_TAG, std::move(*client2));
441+
client2 = nullptr;
442+
container = Aws::GetEnumOverflowContainer();
443+
ASSERT_NE(container, nullptr);
444+
445+
// clear client3
446+
client3 = nullptr;
447+
container = Aws::GetEnumOverflowContainer();
448+
ASSERT_EQ(container, nullptr);
449+
}

aws-cpp-sdk-core/include/aws/core/Globals.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ namespace Aws
3232
AWS_CORE_API Utils::EnumParseOverflowContainer* GetEnumOverflowContainer();
3333

3434
/**
35-
* Atomically set the underlying container to newValue, if it's current value is expectedValue.
35+
* Sets the underlying container to newValue.
3636
*/
37-
AWS_CORE_API bool CheckAndSwapEnumOverflowContainer(Utils::EnumParseOverflowContainer* expectedValue, Utils::EnumParseOverflowContainer* newValue);
38-
}
37+
void SetEnumOverflowContainer(Utils::EnumParseOverflowContainer* newValue);
38+
}

aws-cpp-sdk-core/include/aws/core/client/AWSClient.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,11 @@ namespace Aws
111111
const std::shared_ptr<Aws::Auth::AWSAuthSignerProvider>& signerProvider,
112112
const std::shared_ptr<AWSErrorMarshaller>& errorMarshaller);
113113

114+
AWSClient(const AWSClient&);
115+
AWSClient(AWSClient&&);
116+
AWSClient& operator = (const AWSClient&);
117+
AWSClient& operator = (AWSClient&&);
118+
114119
virtual ~AWSClient();
115120

116121
/**
@@ -244,7 +249,6 @@ namespace Aws
244249
const std::shared_ptr<Aws::IOStream>& body, bool needsContentMd5 = false) const;
245250
void AddCommonHeaders(Aws::Http::HttpRequest& httpRequest) const;
246251
void InitializeGlobalStatics();
247-
void CleanupGlobalStatics();
248252
std::shared_ptr<Aws::Http::HttpRequest> ConvertToRequestForPresigning(const Aws::AmazonWebServiceRequest& request, Aws::Http::URI& uri,
249253
Aws::Http::HttpMethod method, const Aws::Http::QueryStringParameterCollection& extraParams) const;
250254

aws-cpp-sdk-core/include/aws/core/utils/EnumParseOverflowContainer.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
#include <aws/core/utils/memory/stl/AWSMap.h>
1919
#include <aws/core/utils/memory/stl/AWSString.h>
2020
#include <aws/core/Core_EXPORTS.h>
21-
#include <mutex>
21+
#include <aws/core/utils/threading/ReaderWriterLock.h>
2222

2323
namespace Aws
2424
{
@@ -36,9 +36,9 @@ namespace Aws
3636
void StoreOverflow(int hashCode, const Aws::String& value);
3737

3838
private:
39-
mutable std::mutex m_overflowLock;
39+
mutable Aws::Utils::Threading::ReaderWriterLock m_overflowLock;
4040
Aws::Map<int, Aws::String> m_overflowMap;
41-
Aws::String m_emptyString;
41+
static const Aws::String m_emptyString;
4242
};
4343
}
4444
}

aws-cpp-sdk-core/source/Globals.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,15 @@
1818

1919
namespace Aws
2020
{
21-
static std::atomic<Utils::EnumParseOverflowContainer*> g_enumOverflow(nullptr);
21+
static Utils::EnumParseOverflowContainer* g_enumOverflow = nullptr;
2222

2323
Utils::EnumParseOverflowContainer* GetEnumOverflowContainer()
2424
{
25-
return g_enumOverflow.load();
25+
return g_enumOverflow;
2626
}
2727

28-
bool CheckAndSwapEnumOverflowContainer(Utils::EnumParseOverflowContainer* expectedValue, Utils::EnumParseOverflowContainer* newValue)
28+
void SetEnumOverflowContainer(Utils::EnumParseOverflowContainer* newValue)
2929
{
30-
return g_enumOverflow.compare_exchange_strong(expectedValue, newValue);
30+
g_enumOverflow = newValue;
3131
}
32-
}
32+
}

aws-cpp-sdk-core/source/client/AWSClient.cpp

Lines changed: 72 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -74,40 +74,11 @@ static CoreErrors GuessBodylessErrorType(Aws::Http::HttpResponseCode responseCod
7474

7575
void AWSClient::InitializeGlobalStatics()
7676
{
77-
int currentRefCount = s_refCount.load();
78-
if (!currentRefCount)
79-
{
80-
int expectedRefCount = 0;
81-
Utils::EnumParseOverflowContainer* expectedPtrValue = nullptr;
82-
Utils::EnumParseOverflowContainer* container = Aws::New<Utils::EnumParseOverflowContainer>(AWS_CLIENT_LOG_TAG);
83-
if (!s_refCount.compare_exchange_strong(expectedRefCount, 1) ||
84-
!Aws::CheckAndSwapEnumOverflowContainer(expectedPtrValue, container))
85-
{
86-
Aws::Delete(container);
87-
}
88-
}
89-
else
77+
if (s_refCount++ == 0)
9078
{
91-
++s_refCount;
92-
}
93-
}
94-
95-
void AWSClient::CleanupGlobalStatics()
96-
{
97-
int currentRefCount = s_refCount.load();
98-
Utils::EnumParseOverflowContainer* expectedPtrValue = Aws::GetEnumOverflowContainer();
99-
100-
if (currentRefCount == 1)
101-
{
102-
if (s_refCount.compare_exchange_strong(currentRefCount, 0) &&
103-
Aws::CheckAndSwapEnumOverflowContainer(expectedPtrValue, nullptr))
104-
{
105-
Aws::Delete(expectedPtrValue);
106-
return;
107-
}
79+
Utils::EnumParseOverflowContainer* container = Aws::New<Utils::EnumParseOverflowContainer>(AWS_CLIENT_LOG_TAG);
80+
Aws::SetEnumOverflowContainer(container);
10881
}
109-
110-
--s_refCount;
11182
}
11283

11384
AWSClient::AWSClient(const Aws::Client::ClientConfiguration& configuration,
@@ -143,9 +114,77 @@ AWSClient::AWSClient(const Aws::Client::ClientConfiguration& configuration,
143114
}
144115

145116

117+
AWSClient::AWSClient(const AWSClient& other) :
118+
m_httpClient(other.m_httpClient),
119+
m_signerProvider(other.m_signerProvider),
120+
m_errorMarshaller(other.m_errorMarshaller),
121+
m_retryStrategy(other.m_retryStrategy),
122+
m_writeRateLimiter(other.m_writeRateLimiter),
123+
m_readRateLimiter(other.m_readRateLimiter),
124+
m_userAgent(other.m_userAgent),
125+
m_hash(other.m_hash),
126+
m_enableClockSkewAdjustment(other.m_enableClockSkewAdjustment)
127+
{
128+
s_refCount++;
129+
}
130+
131+
AWSClient::AWSClient(AWSClient&& other) :
132+
m_httpClient(std::move(other.m_httpClient)),
133+
m_signerProvider(std::move(other.m_signerProvider)),
134+
m_errorMarshaller(std::move(other.m_errorMarshaller)),
135+
m_retryStrategy(std::move(other.m_retryStrategy)),
136+
m_writeRateLimiter(std::move(other.m_writeRateLimiter)),
137+
m_readRateLimiter(std::move(other.m_readRateLimiter)),
138+
m_userAgent(std::move(other.m_userAgent)),
139+
m_hash(std::move(other.m_hash)),
140+
m_enableClockSkewAdjustment(other.m_enableClockSkewAdjustment)
141+
{
142+
s_refCount++;
143+
}
144+
145+
AWSClient& AWSClient::operator = (const AWSClient& other)
146+
{
147+
if(this == &other)
148+
{
149+
return *this;
150+
}
151+
152+
m_httpClient = other.m_httpClient;
153+
m_signerProvider = other.m_signerProvider;
154+
m_errorMarshaller = other.m_errorMarshaller;
155+
m_retryStrategy = other.m_retryStrategy;
156+
m_writeRateLimiter = other.m_writeRateLimiter;
157+
m_readRateLimiter = other.m_readRateLimiter;
158+
m_userAgent = other.m_userAgent;
159+
m_hash = other.m_hash;
160+
m_enableClockSkewAdjustment = other.m_enableClockSkewAdjustment;
161+
s_refCount++;
162+
return *this;
163+
}
164+
165+
AWSClient& AWSClient::operator = (AWSClient&& other)
166+
{
167+
m_httpClient = std::move(other.m_httpClient);
168+
m_signerProvider = std::move(other.m_signerProvider);
169+
m_errorMarshaller = std::move(other.m_errorMarshaller);
170+
m_retryStrategy = std::move(other.m_retryStrategy);
171+
m_writeRateLimiter = std::move(other.m_writeRateLimiter);
172+
m_readRateLimiter = std::move(other.m_readRateLimiter);
173+
m_userAgent = std::move(other.m_userAgent);
174+
m_hash = std::move(other.m_hash);
175+
m_enableClockSkewAdjustment = other.m_enableClockSkewAdjustment;
176+
s_refCount++;
177+
return *this;
178+
}
179+
146180
AWSClient::~AWSClient()
147181
{
148-
CleanupGlobalStatics();
182+
if (--s_refCount == 0)
183+
{
184+
Utils::EnumParseOverflowContainer* expectedPtrValue = Aws::GetEnumOverflowContainer();
185+
Aws::SetEnumOverflowContainer(nullptr);
186+
Aws::Delete(expectedPtrValue);
187+
}
149188
}
150189

151190
void AWSClient::DisableRequestProcessing()

aws-cpp-sdk-core/source/utils/EnumParseOverflowContainer.cpp

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,28 +17,29 @@
1717
#include <aws/core/utils/logging/LogMacros.h>
1818

1919
using namespace Aws::Utils;
20+
using namespace Aws::Utils::Threading;
2021

21-
static const char* LOG_TAG = "EnumParseOverflowContainer";
22+
static const char LOG_TAG[] = "EnumParseOverflowContainer";
23+
24+
const Aws::String EnumParseOverflowContainer::m_emptyString("");
2225

2326
const Aws::String& EnumParseOverflowContainer::RetrieveOverflow(int hashCode) const
2427
{
28+
ReaderLockGuard guard(m_overflowLock);
29+
auto foundIter = m_overflowMap.find(hashCode);
30+
if (foundIter != m_overflowMap.end())
2531
{
26-
std::lock_guard<std::mutex> locker(m_overflowLock);
27-
auto foundIter = m_overflowMap.find(hashCode);
28-
if (foundIter != m_overflowMap.end())
29-
{
30-
AWS_LOGSTREAM_DEBUG(LOG_TAG, "Found value " << foundIter->second << " for hash " << hashCode << " from enum overflow container.");
31-
return foundIter->second;
32-
}
33-
}
32+
AWS_LOGSTREAM_DEBUG(LOG_TAG, "Found value " << foundIter->second << " for hash " << hashCode << " from enum overflow container.");
33+
return foundIter->second;
34+
}
3435

3536
AWS_LOGSTREAM_ERROR(LOG_TAG, "Could not find a previously stored overflow value for hash " << hashCode << ". This will likely break some requests.");
3637
return m_emptyString;
3738
}
3839

3940
void EnumParseOverflowContainer::StoreOverflow(int hashCode, const Aws::String& value)
4041
{
42+
WriterLockGuard guard(m_overflowLock);
4143
AWS_LOGSTREAM_WARN(LOG_TAG, "Encountered enum member " << value << " which is not modeled in your clients. You should update your clients when you get a chance.");
42-
std::lock_guard<std::mutex> locker(m_overflowLock);
4344
m_overflowMap[hashCode] = value;
44-
}
45+
}

0 commit comments

Comments
 (0)