AWS SDK for C++をマルチスレッドで利用する際の注意点

AWS SDK for C++をマルチスレッドで利用する場合、`Aws::InitAPI` と`Aws::ShutdownAPI` の呼び出しタイミングでハマったのでメモ。


## 環境準備

DockerとMinioを利用して開発環境を用意しています。

下記記事の設定や実装をベースにしています。

DockerとMinioでAWS SDK for C++の開発環境を構築する - Qiita
https://qiita.com/kai_kou/items/d160948553504848d380


## 検証

### シングルスレッドで実行

シングルスレッドで実行してみます。`main.cpp` の`main` メソッドを以下のように変更します。

```cpp:main.cpp
(略)

int main()
{
    // シングルスレッドで実行
    put_object("hoge1.txt");
    put_object("hoge2.txt");

    return 0;
}
```

```console:コンテナ内
# cd /cpp-dev/build
# cmake ..
# cmake --build .
# ./main
hoge1.txt Done!
hoge2.txt Done!
```

問題なく実行できました。
Minioが利用するディレクトリにファイルが保存されたのも確認できます。

```console：コンテナ外
> tree data/hoge
data/hoge/
├── hoge1.txt
└── hoge2.txt
> cat data/hoge/hoge1.txt
hoge!

```

### マルチスレッドで実行

`main.cpp` の`main` メソッドでマルチスレッド実行するように変更して実行してみます。

```cpp:main.cpp
(略)

int main()
{
  // マルチスレッドで実行
  std::thread th_1(put_object, "hoge3.txt");
  std::thread th_2(put_object, "hoge4.txt");
  th_1.join();
  th_2.join();

  return 0;
}
```

```console:コンテナ内
# cmake --build .
# ./main
hoge3.txt Done!
main: /aws-sdk-cpp/aws-cpp-sdk-core/source/monitoring/MonitoringManager.cpp:55: 
void Aws::Monitoring::OnRequestSucceeded(
  const String&, const String&,
  const std::shared_ptr<const Aws::Http::HttpRequest>&,
  const HttpResponseOutcome&,
  const Aws::Monitoring::CoreMetricsCollection&,
  Aws::Vector<void*>&
): Assertion `s_monitors' failed.
Aborted
```

1つめは実行されて、2つめがエラーになりました。
`Assertion `s_monitors' failed.` あたりで調べてみると、`Aws::InitAPI` と`Aws::ShutdownAPI` の実行タイミングに問題がありそうでした。

How to handle Aws::ShutdownAPI with multiple threads on Windows? · Issue #1050 · aws/aws-sdk-cpp
https://github.com/aws/aws-sdk-cpp/issues/1050

c++ - Why does an assert(s_monitors) in MonitoringManager::OnRequestSucceeded() fail? - Stack Overflow
https://stackoverflow.com/questions/54290073/why-does-an-asserts-monitors-in-monitoringmanageronrequestsucceeded-fail


`InitAPI` 、`PutObject` 、`ShutdownAPI` の実行後にログを仕込んてみると`PutObject` 実行時に落ちてるみたいです。マルチスレッドなので、`hoge3.txt` と`hoge4.txt` の実行タイミングが入れ替わることもあります。

```console
# ./main
hoge3.txt InitAPI
hoge4.txt InitAPI
hoge3.txt PutObject
hoge3.txt Done!
hoge3.txt ShutdownAPI
main: /aws-sdk-cpp/aws-cpp-sdk-core/source/monitoring/MonitoringManager.cpp:55:
void Aws::Monitoring::OnRequestSucceeded(
  const String&, const String&,
  const std::shared_ptr<const Aws::Http::HttpRequest>&,
  const HttpResponseOutcome&,
  const Aws::Monitoring::CoreMetricsCollection&,
  Aws::Vector<void*>&
): Assertion `s_monitors' failed.
Aborted
```

## 原因と解決策

おそらくですが、`Aws::InitAPI` と`Aws::ShutdownAPI` がグローバルな初期化関数とクリーンアップ機能なので、マルチスレッドスレッド中に呼び出すとダメっぽいです。

なので、`put_object` メソッド内で呼び出している`Aws::InitAPI` と`Aws::ShutdownAPI` を`main` メソッドに移動してやればひとまず解決です。

```cpp:main.cpp
(略)

// put_objectメソッド内のInitAPIとShutdownAPI呼び出しをなくす

int main()
{
    Aws::SDKOptions options;
    Aws::InitAPI(options);

    // マルチスレッドで実行
    std::thread th_1(put_object, "hoge3.txt");
    std::thread th_2(put_object, "hoge4.txt");
    th_1.join();
    th_2.join();

    Aws::ShutdownAPI(options);

    return 0;
}
```

`Aws::ShutdownAPI` についてはAWS公式のGitHubリポジトリにあるサンプルだと複数回実行しても問題なさそうな実装になっているのですが、シングルスレッドで実行するので問題が発生しないのだと思います。
マルチスレッドで実行する場合は`Aws::InitAPI` と同様にメインスレッドで呼び出すのが無難みたいです。

aws-doc-sdk-examples/s3-demo.cpp at master · awsdocs/aws-doc-sdk-examples
https://github.com/awsdocs/aws-doc-sdk-examples/blob/master/cpp/example_code/s3/s3-demo.cpp


## 参考

How to handle Aws::ShutdownAPI with multiple threads on Windows? · Issue #1050 · aws/aws-sdk-cpp
https://github.com/aws/aws-sdk-cpp/issues/1050

c++ - Why does an assert(s_monitors) in MonitoringManager::OnRequestSucceeded() fail? - Stack Overflow
https://stackoverflow.com/questions/54290073/why-does-an-asserts-monitors-in-monitoringmanageronrequestsucceeded-fail

aws-doc-sdk-examples/s3-demo.cpp at master · awsdocs/aws-doc-sdk-examples
https://github.com/awsdocs/aws-doc-sdk-examples/blob/master/cpp/example_code/s3/s3-demo.cpp

DockerとMinioでAWS SDK for C++の開発環境を構築する - Qiita
https://qiita.com/kai_kou/items/d160948553504848d380

C/C++によるマルチスレッドプログラミング入門 - Qiita
https://qiita.com/nsnonsugar/items/be8a066c6627ab5b052a