diff --git a/google/cloud/google_cloud_cpp_common.bzl b/google/cloud/google_cloud_cpp_common.bzl index 450a070542af9..421c9c00a4160 100644 --- a/google/cloud/google_cloud_cpp_common.bzl +++ b/google/cloud/google_cloud_cpp_common.bzl @@ -74,6 +74,7 @@ google_cloud_cpp_common_hdrs = [ "internal/retry_info.h", "internal/retry_loop_helpers.h", "internal/retry_policy_impl.h", + "internal/run_async_base.h", "internal/service_endpoint.h", "internal/sha256_hash.h", "internal/sha256_hmac.h", diff --git a/google/cloud/google_cloud_cpp_common.cmake b/google/cloud/google_cloud_cpp_common.cmake index a3f11bb04ac9c..8b3601db39f01 100644 --- a/google/cloud/google_cloud_cpp_common.cmake +++ b/google/cloud/google_cloud_cpp_common.cmake @@ -117,6 +117,7 @@ add_library( internal/retry_loop_helpers.h internal/retry_policy_impl.cc internal/retry_policy_impl.h + internal/run_async_base.h internal/service_endpoint.cc internal/service_endpoint.h internal/sha256_hash.cc diff --git a/google/cloud/google_cloud_cpp_rest_internal.bzl b/google/cloud/google_cloud_cpp_rest_internal.bzl index 1506bb22a18cd..6fcc004b1de88 100644 --- a/google/cloud/google_cloud_cpp_rest_internal.bzl +++ b/google/cloud/google_cloud_cpp_rest_internal.bzl @@ -65,6 +65,8 @@ google_cloud_cpp_rest_internal_hdrs = [ "internal/rest_opentelemetry.h", "internal/rest_options.h", "internal/rest_parse_json_error.h", + "internal/rest_pure_background_threads_impl.h", + "internal/rest_pure_completion_queue_impl.h", "internal/rest_request.h", "internal/rest_response.h", "internal/rest_retry_loop.h", @@ -121,6 +123,8 @@ google_cloud_cpp_rest_internal_srcs = [ "internal/rest_lro_helpers.cc", "internal/rest_opentelemetry.cc", "internal/rest_parse_json_error.cc", + "internal/rest_pure_background_threads_impl.cc", + "internal/rest_pure_completion_queue_impl.cc", "internal/rest_request.cc", "internal/rest_response.cc", "internal/rest_set_metadata.cc", diff --git a/google/cloud/google_cloud_cpp_rest_internal.cmake b/google/cloud/google_cloud_cpp_rest_internal.cmake index a6d611cbb8ca7..2906820d25289 100644 --- a/google/cloud/google_cloud_cpp_rest_internal.cmake +++ b/google/cloud/google_cloud_cpp_rest_internal.cmake @@ -113,6 +113,10 @@ add_library( internal/rest_options.h internal/rest_parse_json_error.cc internal/rest_parse_json_error.h + internal/rest_pure_background_threads_impl.cc + internal/rest_pure_background_threads_impl.h + internal/rest_pure_completion_queue_impl.cc + internal/rest_pure_completion_queue_impl.h internal/rest_request.cc internal/rest_request.h internal/rest_response.cc diff --git a/google/cloud/internal/completion_queue_impl.h b/google/cloud/internal/completion_queue_impl.h index 31afec8e3c005..3f623668aa408 100644 --- a/google/cloud/internal/completion_queue_impl.h +++ b/google/cloud/internal/completion_queue_impl.h @@ -18,6 +18,7 @@ #include "google/cloud/async_operation.h" #include "google/cloud/future.h" #include "google/cloud/internal/invoke_result.h" +#include "google/cloud/internal/run_async_base.h" #include "google/cloud/internal/throw_delegate.h" #include "google/cloud/status_or.h" #include "google/cloud/version.h" @@ -30,12 +31,6 @@ namespace cloud { GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN namespace internal { -// Type erase the callables in RunAsync() -struct RunAsyncBase { - virtual ~RunAsyncBase() = default; - virtual void exec() = 0; -}; - /** * The implementation details for `CompletionQueue`. * diff --git a/google/cloud/internal/rest_completion_queue_impl.cc b/google/cloud/internal/rest_completion_queue_impl.cc index b9c6e9c6ff32a..0791c471a1daf 100644 --- a/google/cloud/internal/rest_completion_queue_impl.cc +++ b/google/cloud/internal/rest_completion_queue_impl.cc @@ -13,7 +13,6 @@ // limitations under the License. #include "google/cloud/internal/rest_completion_queue_impl.h" -#include "google/cloud/internal/timer_queue.h" namespace google { namespace cloud { @@ -21,42 +20,28 @@ namespace rest_internal { GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN RestCompletionQueueImpl::RestCompletionQueueImpl() - : tq_(internal::TimerQueue::Create()) {} + : impl_(std::make_shared()) {} -void RestCompletionQueueImpl::Run() { tq_->Service(); } +void RestCompletionQueueImpl::Run() { impl_->Run(); } -void RestCompletionQueueImpl::Shutdown() { tq_->Shutdown(); } +void RestCompletionQueueImpl::Shutdown() { impl_->Shutdown(); } void RestCompletionQueueImpl::CancelAll() {} future> RestCompletionQueueImpl::MakeDeadlineTimer( std::chrono::system_clock::time_point deadline) { - return tq_->Schedule(deadline); + return impl_->MakeDeadlineTimer(deadline); } future> RestCompletionQueueImpl::MakeRelativeTimer(std::chrono::nanoseconds duration) { - using std::chrono::system_clock; - auto d = std::chrono::duration_cast(duration); - if (d < duration) d += system_clock::duration{1}; - return MakeDeadlineTimer(system_clock::now() + d); + return impl_->MakeRelativeTimer(duration); } -// Use an "immediately" expiring timer in order to get the thread(s) servicing -// the TimerQueue to execute the function. However, if the timer -// expires before .then() is invoked, the lambda will be immediately called and -// the passing of execution to the queue servicing thread will not occur. void RestCompletionQueueImpl::RunAsync( std::unique_ptr function) { - ++run_async_counter_; - tq_->Schedule([f = std::move(function)](auto) { f->exec(); }); -} - -void RestCompletionQueueImpl::StartOperation( - std::shared_ptr, - absl::FunctionRef) { - GCP_LOG(FATAL) << " function not supported.\n"; + impl_->RunAsync(std::move(function)); } GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END diff --git a/google/cloud/internal/rest_completion_queue_impl.h b/google/cloud/internal/rest_completion_queue_impl.h index b90df4cde65eb..44ccea0528fe2 100644 --- a/google/cloud/internal/rest_completion_queue_impl.h +++ b/google/cloud/internal/rest_completion_queue_impl.h @@ -17,6 +17,7 @@ #include "google/cloud/future.h" #include "google/cloud/internal/completion_queue_impl.h" +#include "google/cloud/internal/rest_pure_completion_queue_impl.h" #include "google/cloud/internal/timer_queue.h" #include "google/cloud/log.h" #include "google/cloud/status_or.h" @@ -69,19 +70,18 @@ class RestCompletionQueueImpl final /// This function is not supported by RestCompletionQueueImpl, but as the /// function is pure virtual, it must be overridden. void StartOperation(std::shared_ptr, - absl::FunctionRef) override; + absl::FunctionRef) override { + GCP_LOG(FATAL) << " function not supported.\n"; + } /// The underlying gRPC completion queue, which does not exist. grpc::CompletionQueue* cq() override { return nullptr; } /// Some counters for testing and debugging. - std::int64_t run_async_counter() const { return run_async_counter_.load(); } + std::int64_t run_async_counter() const { return impl_->run_async_counter(); } private: - std::shared_ptr tq_; - - // These are metrics used in testing. - std::atomic run_async_counter_{0}; + std::shared_ptr impl_; }; GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END diff --git a/google/cloud/internal/rest_pure_background_threads_impl.cc b/google/cloud/internal/rest_pure_background_threads_impl.cc new file mode 100644 index 0000000000000..525b92c0adb79 --- /dev/null +++ b/google/cloud/internal/rest_pure_background_threads_impl.cc @@ -0,0 +1,73 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "google/cloud/internal/rest_pure_background_threads_impl.h" +#include "google/cloud/future.h" +#include "google/cloud/internal/call_context.h" +#include "google/cloud/internal/rest_pure_completion_queue_impl.h" +#include "google/cloud/log.h" +#include +#include + +namespace google { +namespace cloud { +namespace rest_internal { +GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN + +AutomaticallyCreatedRestPureBackgroundThreads:: + AutomaticallyCreatedRestPureBackgroundThreads(std::size_t thread_count) + : cq_(std::make_shared()), + pool_(thread_count == 0 ? 1 : thread_count) { + std::generate_n(pool_.begin(), pool_.size(), [this] { + promise started; + auto thread = std::thread( + [](RestPureCompletionQueue cq, promise& started, + internal::CallContext c) { + internal::ScopedCallContext scope(std::move(c)); + started.set_value(); + cq.Run(); + }, + cq_, std::ref(started), internal::CallContext{}); + started.get_future().wait(); + return thread; + }); +} + +AutomaticallyCreatedRestPureBackgroundThreads:: + ~AutomaticallyCreatedRestPureBackgroundThreads() { + Shutdown(); +} + +void AutomaticallyCreatedRestPureBackgroundThreads::Shutdown() { + cq_.Shutdown(); + for (auto& t : pool_) { +#if GOOGLE_CLOUD_CPP_HAVE_EXCEPTIONS + try { +#endif + t.join(); +#if GOOGLE_CLOUD_CPP_HAVE_EXCEPTIONS + } catch (std::system_error const& e) { + GCP_LOG(FATAL) + << "AutomaticallyCreatedRestPureBackgroundThreads::Shutdown: " + << e.what(); + } +#endif + } + pool_.clear(); +} + +GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END +} // namespace rest_internal +} // namespace cloud +} // namespace google diff --git a/google/cloud/internal/rest_pure_background_threads_impl.h b/google/cloud/internal/rest_pure_background_threads_impl.h new file mode 100644 index 0000000000000..4061729dd7f63 --- /dev/null +++ b/google/cloud/internal/rest_pure_background_threads_impl.h @@ -0,0 +1,62 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_INTERNAL_REST_PURE_BACKGROUND_THREADS_IMPL_H +#define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_INTERNAL_REST_PURE_BACKGROUND_THREADS_IMPL_H + +#include "google/cloud/internal/rest_pure_completion_queue_impl.h" +#include "google/cloud/version.h" +#include +#include + +namespace google { +namespace cloud { +namespace rest_internal { +GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN + +/// This interface mimics `google::cloud::BackgroundThreads` except it returns +/// a `RestPureCompletionQueue` instead of a `CompletionQueue`. This is +/// important as `CompletionQueue` has gRPC dependencies we need to avoid in a +/// pure REST library. +class RestPureBackgroundThreads { + public: + virtual ~RestPureBackgroundThreads() = default; + + /// The completion queue used for the background operations. + virtual RestPureCompletionQueue cq() const = 0; +}; + +/// Background threads that run on a RestPureCompletionQueue. +class AutomaticallyCreatedRestPureBackgroundThreads + : public RestPureBackgroundThreads { + public: + explicit AutomaticallyCreatedRestPureBackgroundThreads( + std::size_t thread_count = 1U); + ~AutomaticallyCreatedRestPureBackgroundThreads() override; + + RestPureCompletionQueue cq() const override { return cq_; } + void Shutdown(); + std::size_t pool_size() const { return pool_.size(); } + + private: + RestPureCompletionQueue cq_; + std::vector pool_; +}; + +GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END +} // namespace rest_internal +} // namespace cloud +} // namespace google + +#endif // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_INTERNAL_REST_PURE_BACKGROUND_THREADS_IMPL_H diff --git a/google/cloud/internal/rest_pure_completion_queue_impl.cc b/google/cloud/internal/rest_pure_completion_queue_impl.cc new file mode 100644 index 0000000000000..5eb70581d1358 --- /dev/null +++ b/google/cloud/internal/rest_pure_completion_queue_impl.cc @@ -0,0 +1,63 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "google/cloud/internal/rest_pure_completion_queue_impl.h" +#include "google/cloud/internal/timer_queue.h" + +namespace google { +namespace cloud { +namespace rest_internal { +GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN + +RestPureCompletionQueue::RestPureCompletionQueue() + : impl_(std::make_shared()) {} + +RestPureCompletionQueueImpl::RestPureCompletionQueueImpl() + : tq_(internal::TimerQueue::Create()) {} + +void RestPureCompletionQueueImpl::Run() { tq_->Service(); } + +void RestPureCompletionQueueImpl::Shutdown() { tq_->Shutdown(); } + +void RestPureCompletionQueueImpl::CancelAll() {} + +future> +RestPureCompletionQueueImpl::MakeDeadlineTimer( + std::chrono::system_clock::time_point deadline) { + return tq_->Schedule(deadline); +} + +future> +RestPureCompletionQueueImpl::MakeRelativeTimer( + std::chrono::nanoseconds duration) { + using std::chrono::system_clock; + auto d = std::chrono::duration_cast(duration); + if (d < duration) d += system_clock::duration{1}; + return MakeDeadlineTimer(system_clock::now() + d); +} + +// Use an "immediately" expiring timer in order to get the thread(s) servicing +// the TimerQueue to execute the function. However, if the timer +// expires before .then() is invoked, the lambda will be immediately called and +// the passing of execution to the queue servicing thread will not occur. +void RestPureCompletionQueueImpl::RunAsync( + std::unique_ptr function) { + ++run_async_counter_; + tq_->Schedule([f = std::move(function)](auto) { f->exec(); }); +} + +GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END +} // namespace rest_internal +} // namespace cloud +} // namespace google diff --git a/google/cloud/internal/rest_pure_completion_queue_impl.h b/google/cloud/internal/rest_pure_completion_queue_impl.h new file mode 100644 index 0000000000000..d1e8d77bcb365 --- /dev/null +++ b/google/cloud/internal/rest_pure_completion_queue_impl.h @@ -0,0 +1,266 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_INTERNAL_REST_PURE_COMPLETION_QUEUE_IMPL_H +#define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_INTERNAL_REST_PURE_COMPLETION_QUEUE_IMPL_H + +#include "google/cloud/future.h" +#include "google/cloud/internal/run_async_base.h" +#include "google/cloud/internal/timer_queue.h" +#include "google/cloud/log.h" +#include "google/cloud/status_or.h" +#include "google/cloud/version.h" +#include +#include +#include + +namespace google { +namespace cloud { +namespace rest_internal { +GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN + +/** + * Interface for CompletionQueue that does NOT use a grpc::CompletionQueue. + * + * Due to the lack of a completion queue that can manage multiple, simultaneous + * REST requests, asynchronous calls should be launched on a thread of their own + * and RunAsync should only be called with a function to join that thread after + * it completes its work. + * + * This class differs from RestCompletionQueueImpl as it does not contain a + * method that returns a `grpc::CompletionQueue*` nor a `StartOperation` method + * as that method takes an `internal::AsyncGrpcOperation` as a parameter. + */ +class RestPureCompletionQueueInterface { + public: + virtual ~RestPureCompletionQueueInterface() = default; + + /// Run the event loop until Shutdown() is called. + virtual void Run() = 0; + + /// Terminate the event loop. + virtual void Shutdown() = 0; + + /// Cancel all existing operations. + virtual void CancelAll() = 0; + + /// Create a new timer. + virtual future> + MakeDeadlineTimer(std::chrono::system_clock::time_point deadline) = 0; + + /// Create a new timer. + virtual future> + MakeRelativeTimer(std::chrono::nanoseconds duration) = 0; + + /// Enqueue a new asynchronous function. + virtual void RunAsync(std::unique_ptr function) = 0; +}; + +class RestPureCompletionQueue; + +std::shared_ptr +GetRestPureCompletionQueueImpl(RestPureCompletionQueue const& cq); +std::shared_ptr +GetRestPureCompletionQueueImpl(RestPureCompletionQueue&& cq); + +template +using CheckRunAsyncCallback = + google::cloud::internal::is_invocable; + +/// A pure REST version of `google::cloud::CompletionQueue` that wraps an +/// implementation of `RestPureCompletionQueueInterface`. +class RestPureCompletionQueue { + public: + RestPureCompletionQueue(); + explicit RestPureCompletionQueue( + std::shared_ptr impl) + : impl_(std::move(impl)) {} + + /** + * Run the completion queue event loop. + * + * Note that more than one thread can call this member function, to create a + * pool of threads completing asynchronous operations. + */ + void Run() { impl_->Run(); } + + /// Terminate the completion queue event loop. + void Shutdown() { impl_->Shutdown(); } + + /// Cancel all pending operations. + void CancelAll() { impl_->CancelAll(); } + + /** + * Create a timer that fires at @p deadline. + * + * @param deadline when should the timer expire. + * + * @return a future that becomes satisfied after @p deadline. + * The result of the future is the time at which it expired, or an error + * Status if the timer did not run to expiration (e.g. it was cancelled). + */ + google::cloud::future> + MakeDeadlineTimer(std::chrono::system_clock::time_point deadline) { + return impl_->MakeDeadlineTimer(deadline); + } + + /** + * Create a timer that fires after the @p duration. + * + * @tparam Rep a placeholder to match the Rep tparam for @p duration type, + * the semantics of this template parameter are documented in + * `std::chrono::duration<>` (in brief, the underlying arithmetic type + * used to store the number of ticks), for our purposes it is simply a + * formal parameter. + * @tparam Period a placeholder to match the Period tparam for @p duration + * type, the semantics of this template parameter are documented in + * `std::chrono::duration<>` (in brief, the length of the tick in seconds, + * expressed as a `std::ratio<>`), for our purposes it is simply a formal + * parameter. + * + * @param duration when should the timer expire relative to the current time. + * + * @return a future that becomes satisfied after @p duration time has elapsed. + * The result of the future is the time at which it expired, or an error + * Status if the timer did not run to expiration (e.g. it was cancelled). + */ + template + future> MakeRelativeTimer( + std::chrono::duration duration) { + return impl_->MakeRelativeTimer( + std::chrono::duration_cast(duration)); + } + + /** + * Asynchronously run a functor on a thread `Run()`ning the `CompletionQueue`. + * + * @param functor the functor to invoke in one of the CompletionQueue's + * threads. + * + * @tparam Functor the type of @p functor. It must satisfy + * `std::is_invocable` + */ + template ::value, int> = 0 + /// @endcond + > + void RunAsync(Functor&& functor) { + class Wrapper : public internal::RunAsyncBase { + public: + Wrapper(std::weak_ptr impl, Functor&& f) + : impl_(std::move(impl)), fun_(std::forward(f)) {} + ~Wrapper() override = default; + void exec() override { + auto impl = impl_.lock(); + if (!impl) return; + RestPureCompletionQueue cq(std::move(impl)); + fun_(cq); + } + + private: + std::weak_ptr impl_; + std::decay_t fun_; + }; + impl_->RunAsync( + std::make_unique(impl_, std::forward(functor))); + } + + /** + * Asynchronously run a functor on a thread `Run()`ning the `CompletionQueue`. + * + * @param functor the functor to call in one of the CompletionQueue's threads. + * @tparam Functor the type of @p functor. It must satisfy + * `std::is_invocable`. + */ + template ::value, int> = 0 + /// @endcond + > + void RunAsync(Functor&& functor) { + class Wrapper : public internal::RunAsyncBase { + public: + explicit Wrapper(Functor&& f) : fun_(std::forward(f)) {} + ~Wrapper() override = default; + void exec() override { fun_(); } + + private: + std::decay_t fun_; + }; + impl_->RunAsync(std::make_unique(std::forward(functor))); + } + + private: + friend std::shared_ptr + GetRestPureCompletionQueueImpl(RestPureCompletionQueue const& cq); + friend std::shared_ptr + GetRestPureCompletionQueueImpl(RestPureCompletionQueue&& cq); + std::shared_ptr impl_; +}; + +inline std::shared_ptr +GetRestPureCompletionQueueImpl(RestPureCompletionQueue const& cq) { + return cq.impl_; +} + +inline std::shared_ptr +GetRestPureCompletionQueueImpl(RestPureCompletionQueue&& cq) { + return std::move(cq.impl_); +} + +/// Default implementation of `RestPureCompletionQueueInterface` that uses a +/// `TimerQueue` under the hood. +class RestPureCompletionQueueImpl final + : public RestPureCompletionQueueInterface, + public std::enable_shared_from_this { + public: + ~RestPureCompletionQueueImpl() override = default; + RestPureCompletionQueueImpl(); + + /// Run the event loop until Shutdown() is called. + void Run() override; + + /// Terminate the event loop. + void Shutdown() override; + + /// Cancel all existing operations. + void CancelAll() override; + + /// Create a new timer. + future> MakeDeadlineTimer( + std::chrono::system_clock::time_point deadline) override; + + /// Create a new timer. + future> MakeRelativeTimer( + std::chrono::nanoseconds duration) override; + + /// Enqueue a new asynchronous function. + void RunAsync(std::unique_ptr function) override; + + /// Some counters for testing and debugging. + std::int64_t run_async_counter() const { return run_async_counter_.load(); } + + private: + std::shared_ptr tq_; + // These are metrics used in testing. + std::atomic run_async_counter_{0}; +}; + +GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END +} // namespace rest_internal +} // namespace cloud +} // namespace google + +#endif // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_INTERNAL_REST_PURE_COMPLETION_QUEUE_IMPL_H diff --git a/google/cloud/internal/run_async_base.h b/google/cloud/internal/run_async_base.h new file mode 100644 index 0000000000000..e01e99be6f65d --- /dev/null +++ b/google/cloud/internal/run_async_base.h @@ -0,0 +1,36 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_INTERNAL_RUN_ASYNC_BASE_H +#define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_INTERNAL_RUN_ASYNC_BASE_H + +#include "google/cloud/version.h" + +namespace google { +namespace cloud { +GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN +namespace internal { + +// Type erase the callables in RunAsync() +struct RunAsyncBase { + virtual ~RunAsyncBase() = default; + virtual void exec() = 0; +}; + +} // namespace internal +GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END +} // namespace cloud +} // namespace google + +#endif // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_INTERNAL_RUN_ASYNC_BASE_H