blob: 6cc648603561d823621b560858603112a4a8bdb1 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SRC_GRAPHICS_DISPLAY_DRIVERS_COORDINATOR_FENCE_H_
#define SRC_GRAPHICS_DISPLAY_DRIVERS_COORDINATOR_FENCE_H_
#ifndef _ALL_SOURCE
#define _ALL_SOURCE // To get MTX_INIT
#endif
#include <lib/async/cpp/wait.h>
#include <lib/fit/function.h>
#include <lib/fit/thread_checker.h>
#include <lib/zx/event.h>
#include <threads.h>
#include <zircon/compiler.h>
#include <fbl/intrusive_double_list.h>
#include <fbl/intrusive_single_list.h>
#include <fbl/macros.h>
#include <fbl/ref_counted.h>
#include <fbl/ref_ptr.h>
#include "src/graphics/display/drivers/coordinator/id-map.h"
#include "src/graphics/display/lib/api-types-cpp/event-id.h"
namespace display {
class FenceReference;
class Fence;
class FenceCallback {
public:
virtual void OnFenceFired(FenceReference* ref) = 0;
virtual void OnRefForFenceDead(Fence* fence) = 0;
};
// Class which wraps an event into a fence. A single Fence can have multiple FenceReference
// objects, which allows an event to be treated as a semaphore independently of it being
// imported/released (i.e. can be released while still in use).
//
// Fence is not thread-safe (but thread-compatible). For the sake of simplicity,
// in order to avoid data races, we require `Fence`s and its `FenceReference`s
// be created and destroyed on the same thread where the Fence is created.
class Fence : public fbl::RefCounted<Fence>,
public IdMappable<fbl::RefPtr<Fence>, EventId>,
public fbl::SinglyLinkedListable<fbl::RefPtr<Fence>> {
public:
Fence(FenceCallback* cb, async_dispatcher_t* dispatcher, EventId id, zx::event&& event);
~Fence();
// Creates a new FenceReference when an event is imported.
bool CreateRef();
// Clears a FenceReference when an event is released. Note that references to the cleared
// FenceReference might still exist within the driver.
void ClearRef();
// Decrements the reference count and returns true if the last ref died.
bool OnRefDead();
// Gets the fence reference for the current import. An individual fence reference cannot
// be used for multiple things simultaneously.
fbl::RefPtr<FenceReference> GetReference();
// The raw event underlying this fence. Only used for validation.
zx_handle_t event() const { return event_.get(); }
private:
void Signal() const;
void OnRefDied();
zx_status_t OnRefArmed(fbl::RefPtr<FenceReference>&& ref);
void OnRefDisarmed(FenceReference* ref);
// The fence reference corresponding to the current event import.
fbl::RefPtr<FenceReference> cur_ref_;
// A queue of fence references which are being waited upon. When the event is
// signaled, the signal will be cleared and the first fence ref will be marked ready.
fbl::DoublyLinkedList<fbl::RefPtr<FenceReference>> armed_refs_;
void OnReady(async_dispatcher_t* dispatcher, async::WaitBase* self, zx_status_t status,
const zx_packet_signal_t* signal);
async::WaitMethod<Fence, &Fence::OnReady> ready_wait_{this};
FenceCallback* cb_;
async_dispatcher_t* dispatcher_;
zx::event event_;
int ref_count_ = 0;
zx_koid_t koid_ = 0;
FIT_DECLARE_THREAD_CHECKER(thread_checker_)
friend FenceReference;
DISALLOW_COPY_ASSIGN_AND_MOVE(Fence);
};
// Each FenceReference represents a pending / active wait or signaling of the
// Fence it refers to, regardless of the Fence it refers to being imported
// or released by the Client.
//
// FenceReference is not thread-safe (but thread-compatible). For the sake of
// simplicity, we require `FenceReference`s be created and destroyed on the same
// thread where the Fence is created.
class FenceReference : public fbl::RefCounted<FenceReference>,
public fbl::DoublyLinkedListable<fbl::RefPtr<FenceReference>> {
public:
explicit FenceReference(fbl::RefPtr<Fence> fence, std::thread::id fence_thread_id);
~FenceReference();
void Signal() const;
zx_status_t StartReadyWait();
void ResetReadyWait();
// Sets the fence which will be signaled immediately when this fence is ready.
void SetImmediateRelease(fbl::RefPtr<FenceReference>&& fence);
void OnReady();
private:
fbl::RefPtr<Fence> fence_;
fbl::RefPtr<FenceReference> release_fence_;
const fit::thread_checker thread_checker_;
DISALLOW_COPY_ASSIGN_AND_MOVE(FenceReference);
};
// FenceCollection controls the access and lifecycles for several display::Fences.
class FenceCollection : private FenceCallback {
public:
// Creates an empty collection.
//
// `dispatcher` must outlive the newly created instance. `on_fence_fired` must
// be callable while the newly created instance is alive.
//
// `on_fence_fired` will be called when one of the fences fires. The call
// will be done from an async task processed using `dispatcher`.
FenceCollection(async_dispatcher_t* dispatcher,
fit::function<void(FenceReference*)> on_fence_fired);
FenceCollection(const FenceCollection&) = delete;
FenceCollection(FenceCollection&&) = delete;
FenceCollection& operator=(const FenceCollection&) = delete;
FenceCollection& operator=(FenceCollection&&) = delete;
virtual ~FenceCollection() = default;
// Explicit destruction step. Use this to control when fences are destroyed.
void Clear() __TA_EXCLUDES(mtx_);
zx_status_t ImportEvent(zx::event event, EventId id) __TA_EXCLUDES(mtx_);
void ReleaseEvent(EventId id) __TA_EXCLUDES(mtx_);
fbl::RefPtr<FenceReference> GetFence(EventId id) __TA_EXCLUDES(mtx_);
private:
// |FenceCallback|
void OnFenceFired(FenceReference* fence) override;
// |FenceCallback|
void OnRefForFenceDead(Fence* fence) __TA_EXCLUDES(mtx_) override;
mtx_t mtx_ = MTX_INIT;
Fence::Map fences_ __TA_GUARDED(mtx_);
async_dispatcher_t* const dispatcher_;
fit::function<void(FenceReference*)> on_fence_fired_;
};
} // namespace display
#endif // SRC_GRAPHICS_DISPLAY_DRIVERS_COORDINATOR_FENCE_H_