blob: 1ec141f6a04e9705e6334041dc2ef471547df865 [file] [log] [blame]
// Copyright 2019 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 IREE_HAL_HOST_HOST_SUBMISSION_QUEUE_H_
#define IREE_HAL_HOST_HOST_SUBMISSION_QUEUE_H_
#include "absl/base/thread_annotations.h"
#include "absl/container/inlined_vector.h"
#include "absl/synchronization/mutex.h"
#include "base/intrusive_list.h"
#include "base/status.h"
#include "hal/command_queue.h"
#include "hal/host/host_fence.h"
#include "hal/semaphore.h"
namespace iree {
namespace hal {
class HostSubmissionQueue;
// Simple host-only binary semaphore implemented with a mutex.
// To match the expected HAL behavior (mostly dictated by Vulkan) we can only
// have a single waiter and waits can only occur once a signal has been
// enqueued.
//
// Thread-safe (as instances may be imported and used by others).
class HostBinarySemaphore final : public BinarySemaphore {
public:
explicit HostBinarySemaphore(bool initial_value);
// Returns true if the semaphore has been signaled.
bool is_signaled() const;
private:
friend class HostSubmissionQueue;
// Begins a signal operation and ensures no other signal operation is pending.
Status BeginSignaling();
// Ends a signal operation by setting the semaphore to the signaled state.
Status EndSignaling();
// Begins a wait operation and ensures no other wait operation is pending.
Status BeginWaiting();
// Ends a wait operation by resetting the semaphore to the unsignaled state.
Status EndWaiting();
// A single 32-bit int for lock-free semaphore behavior. We need to do this
// extra tracking so that we get consistent behavior across HAL
// implementations that have strict semaphore semantics.
struct State {
uint32_t signal_pending : 1;
uint32_t wait_pending : 1;
uint32_t signaled : 1;
};
std::atomic<State> state_{{0, 0, 0}};
};
// Simple host-only timeline semaphore implemented with a mutex.
//
// Thread-safe (as instances may be imported and used by others).
class HostTimelineSemaphore final : public TimelineSemaphore {
public:
// TODO(b/140141417): implement timeline semaphores.
};
// A queue managing CommandQueue submissions that uses host-local
// synchronization primitives. Evaluates submission order by respecting the
// wait and signal semaphores defined per batch and notifies fences upon
// submission completion.
//
// Note that it's possible for HAL users to deadlock themselves; we don't try to
// avoid that as in device backends it may not be possible and we want to have
// some kind of warning in the host implementation that TSAN can catch.
//
// Thread-compatible. Const methods may be called from any thread.
class HostSubmissionQueue {
public:
using ExecuteFn =
std::function<Status(absl::Span<CommandBuffer* const> command_buffers)>;
HostSubmissionQueue();
~HostSubmissionQueue();
// Returns true if the queue is currently empty.
bool empty() const { return list_.empty(); }
// Returns true if SignalShutdown has been called.
bool has_shutdown() const { return has_shutdown_; }
// The sticky error status, if an error has occurred.
Status permanent_error() const { return permanent_error_; }
// Enqueues a new submission.
// No work will be performed until Process is called.
Status Enqueue(absl::Span<const SubmissionBatch> batches, FenceValue fence);
// Processes all ready batches using the provided |execute_fn|.
// The function may be called several times if new batches become ready due to
// prior batches in the sequence completing during processing.
//
// Returns any errors returned by |execute_fn| (which will be the same as
// permanent_error()). When an error occurs all in-flight submissions are
// aborted, the permanent_error() is set, and the queue is shutdown.
Status ProcessBatches(ExecuteFn execute_fn);
// Marks the queue as having shutdown. All pending submissions will be allowed
// to complete but future enqueues will fail.
void SignalShutdown();
private:
// A submitted command buffer batch and its synchronization information.
struct PendingBatch {
absl::InlinedVector<SemaphoreValue, 4> wait_semaphores;
absl::InlinedVector<CommandBuffer*, 4> command_buffers;
absl::InlinedVector<SemaphoreValue, 4> signal_semaphores;
};
struct Submission : public IntrusiveLinkBase<void> {
absl::InlinedVector<PendingBatch, 4> pending_batches;
FenceValue fence;
};
// Returns true if all wait semaphores in the |batch| are signaled.
bool IsBatchReady(const PendingBatch& batch) const;
// Processes a batch by resetting semaphores, dispatching the command buffers
// to the specified |execute_fn|, and signaling semaphores.
//
// Preconditions: IsBatchReady(batch) == true
Status ProcessBatch(const PendingBatch& batch, const ExecuteFn& execute_fn);
// Completes a submission by signaling the fence with the given |status|.
Status CompleteSubmission(Submission* submission, Status status);
// Fails all pending submissions with the given status.
// Errors that occur during this process are silently ignored.
void FailAllPending(Status status);
// True to exit the thread after all submissions complete.
bool has_shutdown_ = false;
// A sticky error that is set on the first failed submit. All future
// submissions will be skipped except for fences, which will receive this
// error.
Status permanent_error_;
// Pending submissions in submission order.
// Note that we may evaluate batches within the list out of order.
IntrusiveList<std::unique_ptr<Submission>> list_;
};
} // namespace hal
} // namespace iree
#endif // IREE_HAL_HOST_HOST_SUBMISSION_QUEUE_H_