Theme Catppuccin
Make yourself at homeCatppuccin

A softer light for long learning sessions.

Appearance

Loading your preference…

Dark palette

Choose a dark palette to try it.

Light uses Latte. Your choices stay in this browser.

Back to your source shelf

Inside the companion

Renderer Engineering

Explore bounded image work, allocator lifetimes, cancellation, and Asio coroutines.

Project files 15

Own an image request through completion

Admission, results, image generations, and the pipeline and gallery interfaces.

102 lines asset_pipeline.hpp
#pragma once
#include "prism_engineering/worker_pool.hpp"
#include <array>
#include <atomic>
#include <condition_variable>
#include <cstdint>
#include <expected>
#include <memory>
#include <mutex>
#include <optional>
#include <thread>
#include <vector>

namespace prism_engineering {
struct Rgb { std::uint8_t r{}, g{}, b{}; bool operator==(const Rgb&) const = default; };
struct PreparedImage {
  std::uint32_t width{}, height{};
  std::vector<Rgb> pixels;
};
// The descriptor is copied by value. No borrowed pixel input escapes the caller.
struct ImageRequest {
  std::uint64_t generation{};
  std::uint32_t width{8}, height{8};
  std::uint8_t blue{64};
  std::shared_ptr<WorkGate> gate; // Optional deterministic test seam.
  bool inject_failure{};
};
struct Completion {
  std::uint64_t generation{};
  Status status{Status::pending};
  PreparedImage image;
};
enum class Admission { closed, full, invalid_dimensions, invalid_generation };
struct PipelineOptions {
  std::size_t workers{2};
  std::optional<std::size_t> fail_launch_after{};
};

// One owning application thread calls the public methods. Workers only use the
// private loop. Destruction/shutdown must not race with public calls.
class AssetPipeline {
public:
  static constexpr std::size_t capacity = 8;
  static constexpr std::size_t worker_limit = 4;
  static constexpr std::uint32_t extent_limit = 64;
  [[nodiscard]] static std::expected<std::unique_ptr<AssetPipeline>, Error>
  create(PipelineOptions options = {});
  ~AssetPipeline();
  AssetPipeline(const AssetPipeline&) = delete;
  AssetPipeline& operator=(const AssetPipeline&) = delete;

  // Brief mutex acquisition, but no wait for rendering or queue capacity.
  [[nodiscard]] std::expected<void, Admission> try_submit(ImageRequest request);
  [[nodiscard]] std::optional<Completion> try_take();
  // CLI/test convenience ONLY. A UI uses try_take from its regular event loop.
  // While admission is open this waits for a completion, even if no job exists.
  // After request_stop(), call shutdown() before draining: shutdown finalizes
  // queued slots. request_stop() alone does not guarantee a terminal result.
  [[nodiscard]] std::optional<Completion> wait_take();
  void request_stop();
  void shutdown(); // Stop admission, cancel, join, preserve terminal results.
  [[nodiscard]] bool runtime_failed() const noexcept { return failed_.load(); }

private:
  AssetPipeline() = default;
  enum class Phase { empty, queued, running, complete };
  struct Slot { Phase phase{Phase::empty}; ImageRequest request; Completion result; };
  [[nodiscard]] std::optional<Completion> take_locked();
  [[nodiscard]] bool has_completed_locked() const;
  void run_worker();

  std::mutex mutex_;
  std::condition_variable_any changed_;
  std::array<Slot, capacity> slots_{};
  std::stop_source stop_;
  std::atomic<bool> failed_{false};
  std::uint64_t last_generation_{};
  bool accepting_{true};
  bool joined_{};
  // Last member, and explicitly joined: every borrowed member above survives.
  std::array<std::jthread, worker_limit> workers_{};
};

// Single-owner publication policy. Request identity and published identity differ.
class Gallery {
public:
  [[nodiscard]] std::expected<void, Admission>
  request(AssetPipeline& pipeline, ImageRequest request);
  [[nodiscard]] bool apply(Completion completion);
  [[nodiscard]] std::uint64_t requested_generation() const noexcept { return requested_; }
  [[nodiscard]] std::uint64_t published_generation() const noexcept { return published_; }
  [[nodiscard]] Status latest_status() const noexcept { return latest_; }
  [[nodiscard]] const PreparedImage& image() const noexcept { return image_; }
private:
  std::uint64_t requested_{};
  std::uint64_t published_{};
  bool settled_{};
  Status latest_{Status::pending};
  PreparedImage image_;
};
} // namespace prism_engineering

This is the file included with the course. Copy and download give you its complete source.

Taking your work offline? Download all course companions