Asynchronous Methods - AWS SDK for C++

Asynchronous Methods

Asynchronous SDK Methods

For many methods, the AWS SDK for C++ provides both synchronous and asynchronous versions. A method is asynchronous if it includes the Async suffix in its name. For example, the Amazon S3 method PutObject is synchronous, while PutObjectAsync is asynchronous.

Like all asynchronous operations, an asynchronous SDK method returns before its main task is finished. For example, the PutObjectAsync method returns before it finishes uploading the file to the Amazon S3 bucket. While the upload operation continues, the application can perform other operations, including calling other asynchronous methods. The application is notified that an asynchronous operation has finished when an associated callback function is invoked.

The following sections describe a code example that demonstrates calling an SDK asynchronous method. Each section focuses on individual portions from the example’s entire source file.

Calling SDK Asynchronous Methods

In general, the asynchronous version of an SDK method accepts the following arguments.

  • A reference to the same Request-type object as its synchronous counterpart.

  • A reference to a response handler callback function. This callback function is invoked when the asynchronous operation finishes. One of the arguments contains the operation’s outcome.

  • An optional shared_ptr to an AsyncCallerContext object. The object is passed to the response handler callback. It includes a UUID property that can be used to pass text information to the callback.

The put_s3_object_async method shown below sets up and calls the SDK’s Amazon S3 PutObjectAsync method to asynchronously upload a file to an S3 bucket.

The method initializes a PutObjectRequest object in the same manner as its synchronous counterpart. In addition, a shared_ptr to an AsyncCallerContext object is allocated. Its UUID property is set to the Amazon S3 object name. For demonstration purposes, the response handler callback will access the property and output its value.

The call to PutObjectAsync includes a reference argument to the response handler callback function put_object_async_finished. This callback function is examined in more detail in the next section.

bool put_s3_object_async(const Aws::S3::S3Client& s3_client, const Aws::String& s3_bucket_name, const Aws::String& s3_object_name, const std::string& file_name) { // Verify file_name exists if (!file_exists(file_name)) { std::cout << "ERROR: NoSuchFile: The specified file does not exist" << std::endl; return false; } // Set up request Aws::S3::Model::PutObjectRequest object_request; object_request.SetBucket(s3_bucket_name); object_request.SetKey(s3_object_name); const std::shared_ptr<Aws::IOStream> input_data = Aws::MakeShared<Aws::FStream>("SampleAllocationTag", file_name.c_str(), std::ios_base::in | std::ios_base::binary); object_request.SetBody(input_data); // Set up AsyncCallerContext. Pass the S3 object name to the callback. auto context = Aws::MakeShared<Aws::Client::AsyncCallerContext>("PutObjectAllocationTag"); context->SetUUID(s3_object_name); // Put the object asynchronously s3_client.PutObjectAsync(object_request, put_object_async_finished, context); return true;

The resources directly associated with an asynchronous operation must continue to exist until the operation finishes. For example, the client object used to invoke an asynchronous SDK method must exist until the application receives notification that the operation has finished. Similarly, the application itself cannot terminate until the asynchronous operation completes.

For this reason, the put_s3_object_async method accepts a reference to an S3Client object instead of creating the client in a local variable. In the example, the method returns to the caller immediately after beginning the asynchronous operation, enabling the caller to perform additional tasks while the upload operation is in progress. If the client is stored in a local variable, it would go out of scope when the method returned. However, the client object must continue to exist until the asynchronous operation finishes.

Notification of the Completion of an Asynchronous Operation

When an asynchronous operation finishes, an application response handler callback function is invoked. This notification includes the outcome of the operation. The outcome is contained in the same Outcome-type class returned by the method’s synchronous counterpart. In the code example, the outcome is in a PutObjectOutcome object.

The example’s response handler callback function put_object_async_finished is shown below. It checks whether the asynchronous operation succeeded or failed. It uses a std::condition_variable to notify the application thread that the async operation has finished.

std::mutex upload_mutex; std::condition_variable upload_variable;
void put_object_async_finished(const Aws::S3::S3Client* client, const Aws::S3::Model::PutObjectRequest& request, const Aws::S3::Model::PutObjectOutcome& outcome, const std::shared_ptr<const Aws::Client::AsyncCallerContext>& context) { // Output operation status if (outcome.IsSuccess()) { std::cout << "put_object_async_finished: Finished uploading " << context->GetUUID() << std::endl; } else { auto error = outcome.GetError(); std::cout << "ERROR: " << error.GetExceptionName() << ": " << error.GetMessage() << std::endl; } // Notify the thread that started the operation upload_variable.notify_one(); }

With the asynchronous operation finished, the resources associated with it can be released. The application can also terminate if it wishes.

The following code demonstrates how the put_object_async and put_object_async_finished methods are used by an application.

The S3Client object is allocated so it continues to exist until the asynchronous operation finishes. After calling put_object_async, the application can perform whatever operations it wishes. For simplicity, the example uses a std::mutex and std::condition_variable to wait until the response handler callback notifies it that the upload operation has finished.

// NOTE: The S3Client object that starts the async operation must // continue to exist until the async operation completes. Aws::S3::S3Client s3Client(clientConfig); // Put the file into the S3 bucket asynchronously std::unique_lock<std::mutex> lock(upload_mutex); if (put_s3_object_async(s3Client, bucket_name, object_name, file_name)) { // While the upload is in progress, we can perform other tasks. // For this example, we just wait for the upload to finish. std::cout << "main: Waiting for file upload to complete..." << std::endl; upload_variable.wait(lock); // The upload has finished. The S3Client object can be cleaned up // now. We can also terminate the program if we wish. std::cout << "main: File upload completed" << std::endl; }