blob: 9b592b82037009cebb35471f7cda8f0bb326ba57 [file] [log] [blame]
// Copyright 2021 The Pigweed Authors
// 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
// 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.
#pragma once
#include <array>
#include <cstdint>
#include <span>
#include "pw_function/function.h"
#include "pw_metric/metric.h"
#include "pw_status/status.h"
#include "pw_sync/interrupt_spin_lock.h"
#include "pw_sync/lock_annotations.h"
#include "pw_sync/thread_notification.h"
#include "pw_thread/thread_core.h"
#include "pw_work_queue/internal/circular_buffer.h"
namespace pw::work_queue {
using WorkItem = Function<void()>;
// The WorkQueue class enables threads and interrupts to enqueue work as a
// pw::work_queue::WorkItem for execution by the work queue.
// The entire API is thread and interrupt safe.
class WorkQueue : public thread::ThreadCore {
// Note: the ThreadNotification prevents this from being constexpr.
explicit WorkQueue(std::span<WorkItem> queue_storage)
: stop_requested_(false), circular_buffer_(queue_storage) {}
// Enqueues a work_item for execution by the work queue thread.
// Returns:
// Ok - Success, entry was enqueued for execution.
// FailedPrecondition - the work queue is shutting down, entries are no
// longer permitted.
// ResourceExhausted - internal work queue is full, entry was not enqueued.
Status PushWork(WorkItem&& work_item) PW_LOCKS_EXCLUDED(lock_) {
return InternalPushWork(std::move(work_item));
// Queue work for execution. Crash if the work cannot be queued due to a
// full queue or a stopped worker thread.
// This call is recommended where possible since it saves error handling code
// at the callsite; and in many practical cases, it is a bug if the work
// queue is full (and so a crash is useful to detect the problem).
// Precondition: The queue must not overflow, i.e. be full.
// Precondition: The queue must not have been requested to stop, i.e. it must
// not be in the process of shutting down.
void CheckPushWork(WorkItem&& work_item) PW_LOCKS_EXCLUDED(lock_);
// Locks the queue to prevent further work enqueing, finishes outstanding
// work, then shuts down the worker thread.
// The WorkQueue cannot be resumed after stopping as the ThreadCore thread
// returns and may be joined. It must be reconstructed for re-use after
// the thread has been joined.
void RequestStop() PW_LOCKS_EXCLUDED(lock_);
void Run() override PW_LOCKS_EXCLUDED(lock_);
Status InternalPushWork(WorkItem&& work_item) PW_LOCKS_EXCLUDED(lock_);
sync::InterruptSpinLock lock_;
bool stop_requested_ PW_GUARDED_BY(lock_);
internal::CircularBuffer<WorkItem> circular_buffer_ PW_GUARDED_BY(lock_);
sync::ThreadNotification work_notification_;
// TODO(ewout): The group and/or its name token should be passed as a ctor
// arg instead. Depending on the approach here the group should be exposed
// While doing this evaluate whether perhaps we should instead construct
// TypedMetric<uint32_t>s directly, avoiding the macro usage given the
// min_queue_remaining_ initial value requires dependency injection.
// And lastly when the restructure is finalized add unit tests to ensure these
// metrics work as intended.
PW_METRIC_GROUP(metrics_, "pw::work_queue::WorkQueue");
PW_METRIC(metrics_, max_queue_used_, "max_queue_used", 0u);
template <size_t kWorkQueueEntries>
class WorkQueueWithBuffer : public WorkQueue {
constexpr WorkQueueWithBuffer() : WorkQueue(queue_storage_) {}
std::array<WorkItem, kWorkQueueEntries> queue_storage_;
} // namespace pw::work_queue