Skip to content

Commit 1fbba25

Browse files
committed
WIP Async writing
1 parent 8180678 commit 1fbba25

6 files changed

Lines changed: 133 additions & 11 deletions

File tree

include/openPMD/toolkit/Aws.hpp

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,47 @@
44

55
#include <aws/s3/S3Client.h>
66

7+
#include <condition_variable>
8+
79
namespace openPMD::internal
810
{
11+
struct AwsAsyncHandler
12+
{
13+
std::mutex mutex;
14+
std::condition_variable event;
15+
std::size_t request_counter = 0;
16+
// Upon C++20, we can use a std::atomic for this and ditch the
17+
// condition_variable + mutex approach
18+
std::size_t completion_counter = 0;
19+
20+
void wait();
21+
void add_task();
22+
void add_and_notify_result();
23+
24+
~AwsAsyncHandler();
25+
};
26+
927
struct ExternalBlockStorageAws : ExternalBlockStorageBackend
1028
{
1129
private:
1230
Aws::S3::S3Client m_client;
1331
std::string m_bucketName;
1432
std::optional<std::string> m_endpoint;
33+
std::optional<AwsAsyncHandler> m_async;
1534

1635
public:
1736
ExternalBlockStorageAws(
1837
Aws::S3::S3Client,
1938
std::string bucketName,
20-
std::optional<std::string> endpoint);
39+
std::optional<std::string> endpoint,
40+
bool async);
2141
auto put(std::string const &identifier, void const *data, size_t len)
2242
-> std::string override;
2343
void get(std::string const &external_ref, void *data, size_t len) override;
2444
[[nodiscard]] auto externalStorageLocation() const
2545
-> nlohmann::json override;
46+
void sync() override;
47+
2648
~ExternalBlockStorageAws() override;
2749
};
2850
} // namespace openPMD::internal

include/openPMD/toolkit/ExternalBlockStorage.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ struct ExternalBlockStorageBackend
2929
[[nodiscard]] virtual auto externalStorageLocation() const
3030
-> nlohmann::json = 0;
3131

32+
virtual void sync();
33+
3234
virtual ~ExternalBlockStorageBackend();
3335
};
3436
} // namespace openPMD::internal
@@ -105,6 +107,8 @@ class ExternalBlockStorage
105107
nlohmann::json::json_pointer const &path,
106108
T *data);
107109

110+
void sync();
111+
108112
[[nodiscard]] auto externalStorageLocation() const -> nlohmann::json;
109113

110114
static void sanitizeString(std::string &s);

src/IO/JSON/JSONIOHandlerImpl.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -689,6 +689,13 @@ std::future<void> JSONIOHandlerImpl::flush()
689689
putJsonContents(file, false);
690690
}
691691
m_dirty.clear();
692+
std::visit(
693+
auxiliary::overloaded{
694+
[](DatasetMode::External_t &externalStorage) {
695+
externalStorage->sync();
696+
},
697+
[](auto &&) {}},
698+
this->m_datasetMode.m_mode.as_base());
692699
return std::future<void>();
693700
}
694701

src/toolkit/Aws.cpp

Lines changed: 82 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
#include "openPMD/toolkit/Aws.hpp"
22

3+
#include <aws/s3/S3Client.h>
34
#include <aws/s3/model/CreateBucketRequest.h>
45
#include <aws/s3/model/GetObjectRequest.h>
56
#include <aws/s3/model/PutObjectRequest.h>
67

78
#include <iostream>
9+
#include <mutex>
810
#include <stdexcept>
911

1012
namespace
@@ -31,13 +33,44 @@ struct imemstream : std::iostream
3133

3234
namespace openPMD::internal
3335
{
36+
void AwsAsyncHandler::wait()
37+
{
38+
std::cerr << "Waiting for remaining tasks. Have " << completion_counter
39+
<< " of " << request_counter << std::endl;
40+
size_t target = this->request_counter;
41+
std::unique_lock lk(this->mutex);
42+
this->event.wait(
43+
lk, [this, target]() { return this->completion_counter >= target; });
44+
std::cerr << "Finished waiting for remaining tasks" << std::endl;
45+
}
46+
47+
void AwsAsyncHandler::add_task()
48+
{
49+
this->request_counter++;
50+
}
51+
52+
void AwsAsyncHandler::add_and_notify_result()
53+
{
54+
std::unique_lock lk(this->mutex);
55+
this->completion_counter++;
56+
lk.unlock();
57+
this->event.notify_all();
58+
}
59+
60+
AwsAsyncHandler::~AwsAsyncHandler()
61+
{
62+
this->wait();
63+
}
64+
3465
ExternalBlockStorageAws::ExternalBlockStorageAws(
3566
Aws::S3::S3Client client,
3667
std::string bucketName,
37-
std::optional<std::string> endpoint)
68+
std::optional<std::string> endpoint,
69+
bool async)
3870
: m_client{std::move(client)}
3971
, m_bucketName(std::move(bucketName))
4072
, m_endpoint(std::move(endpoint))
73+
, m_async(async ? std::make_optional<AwsAsyncHandler>() : std::nullopt)
4174
{
4275
Aws::S3::Model::CreateBucketRequest create_request;
4376
create_request.SetBucket(m_bucketName);
@@ -71,16 +104,49 @@ auto ExternalBlockStorageAws::put(
71104
put_request.SetBody(input_data);
72105
put_request.SetContentLength(static_cast<long long>(len));
73106

74-
auto put_outcome = m_client.PutObject(put_request);
75-
76-
if (put_outcome.IsSuccess())
107+
if (!m_async.has_value())
77108
{
78-
std::cout << "File uploaded successfully to S3!" << std::endl;
109+
auto put_outcome = m_client.PutObject(put_request);
110+
111+
if (put_outcome.IsSuccess())
112+
{
113+
std::cout << "File synchronously uploaded successfully to S3!"
114+
<< std::endl;
115+
}
116+
else
117+
{
118+
std::cerr << "Synchronous upload failed: "
119+
<< put_outcome.GetError().GetMessage() << std::endl;
120+
}
79121
}
80122
else
81123
{
82-
std::cerr << "Upload failed: " << put_outcome.GetError().GetMessage()
83-
<< std::endl;
124+
auto &async_handler = *m_async;
125+
auto responseReceivedHandler =
126+
[&async_handler](
127+
const Aws::S3::S3Client *,
128+
const Aws::S3::Model::PutObjectRequest &,
129+
const Aws::S3::Model::PutObjectOutcome &put_outcome,
130+
const std::shared_ptr<const Aws::Client::AsyncCallerContext>
131+
&) {
132+
if (put_outcome.IsSuccess())
133+
{
134+
std::cout
135+
<< "File asynchronously uploaded successfully to S3!"
136+
<< std::endl;
137+
}
138+
else
139+
{
140+
std::cerr << "Asynchronous upload failed: "
141+
<< put_outcome.GetError().GetMessage()
142+
<< std::endl;
143+
}
144+
async_handler.add_and_notify_result();
145+
};
146+
async_handler.add_task();
147+
m_client.PutObjectAsync(put_request, responseReceivedHandler);
148+
// todo replace this
149+
async_handler.wait();
84150
}
85151
return sanitized;
86152
}
@@ -117,6 +183,15 @@ void ExternalBlockStorageAws::get(
117183
}
118184
}
119185

186+
void ExternalBlockStorageAws::sync()
187+
{
188+
if (!this->m_async.has_value())
189+
{
190+
return;
191+
}
192+
this->m_async->wait();
193+
}
194+
120195
[[nodiscard]] auto ExternalBlockStorageAws::externalStorageLocation() const
121196
-> nlohmann::json
122197
{

src/toolkit/AwsBuilder.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,9 @@ AwsBuilder::operator ExternalBlockStorage()
119119
return ExternalBlockStorage{std::make_unique<ExternalBlockStorageAws>(
120120
std::move(s3_client),
121121
std::move(m_bucketName),
122-
std::move(m_endpointOverride))};
122+
std::move(m_endpointOverride),
123+
// TODO: Add config option for this
124+
/* async = */ true)};
123125
}
124126

125127
auto AwsBuilder::build() -> ExternalBlockStorage

src/toolkit/ExternalBlockStorage.cpp

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,14 @@
1212

1313
namespace openPMD::internal
1414
{
15-
ExternalBlockStorageBackend::~ExternalBlockStorageBackend() = default;
15+
void ExternalBlockStorageBackend::sync()
16+
{
17+
// default for non-async backends: no-op
1618
}
1719

20+
ExternalBlockStorageBackend::~ExternalBlockStorageBackend() = default;
21+
} // namespace openPMD::internal
22+
1823
namespace openPMD
1924
{
2025

@@ -161,7 +166,9 @@ void ExternalBlockStorage::read(
161166
[[maybe_unused]] nlohmann::json const &fullJsonDataset,
162167
[[maybe_unused]] nlohmann::json::json_pointer const &path,
163168
[[maybe_unused]] T *data)
164-
{}
169+
{
170+
throw std::runtime_error("Unimplemented!");
171+
}
165172

166173
template <typename DatatypeHandling, typename T>
167174
void ExternalBlockStorage::read(
@@ -211,6 +218,11 @@ void ExternalBlockStorage::read(
211218
}
212219
}
213220

221+
void ExternalBlockStorage::sync()
222+
{
223+
this->m_worker->sync();
224+
}
225+
214226
[[nodiscard]] auto ExternalBlockStorage::externalStorageLocation() const
215227
-> nlohmann::json
216228
{

0 commit comments

Comments
 (0)