| 889 | }; |
| 890 | |
| 891 | Result<S3ClientLock> S3ClientHolder::Lock() { |
| 892 | std::shared_ptr<S3ClientFinalizer> finalizer; |
| 893 | std::shared_ptr<S3Client> client; |
| 894 | { |
| 895 | std::unique_lock lock(mutex_); |
| 896 | finalizer = finalizer_.lock(); |
| 897 | client = client_; |
| 898 | } |
| 899 | // Do not hold mutex while taking finalizer lock below. |
| 900 | // |
| 901 | // Acquiring a shared_mutex in shared mode may block even if not already |
| 902 | // acquired in exclusive mode, because of pending writers: |
| 903 | // https://github.com/google/sanitizers/issues/1668#issuecomment-1624985664 |
| 904 | // """It is implementation-defined whether the calling thread acquires |
| 905 | // the lock when a writer does not hold the lock and there are writers |
| 906 | // blocked on the lock""". |
| 907 | // |
| 908 | // Therefore, we want to avoid potential lock ordering issues |
| 909 | // even when a shared lock is involved (GH-36523). |
| 910 | if (!finalizer) { |
| 911 | return ErrorS3Finalized(); |
| 912 | } |
| 913 | |
| 914 | S3ClientLock client_lock; |
| 915 | // Lock the finalizer before examining it |
| 916 | client_lock.lock_ = finalizer->LockShared(); |
| 917 | if (finalizer->finalized_) { |
| 918 | return ErrorS3Finalized(); |
| 919 | } |
| 920 | // (the client can be cleared only if finalizer->finalized_ is true) |
| 921 | DCHECK(client) << "inconsistent S3ClientHolder"; |
| 922 | client_lock.client_ = std::move(client); |
| 923 | return client_lock; |
| 924 | } |
| 925 | |
| 926 | void S3ClientHolder::Finalize() { |
| 927 | std::shared_ptr<S3Client> client; |
no test coverage detected