// Copyright (c) 2012 The Chromium 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 GPU_COMMAND_BUFFER_SERVICE_SYNC_POINT_MANAGER_H_ #define GPU_COMMAND_BUFFER_SERVICE_SYNC_POINT_MANAGER_H_ #include #include #include #include "base/atomic_sequence_num.h" #include "base/callback.h" #include "base/containers/hash_tables.h" #include "base/logging.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/synchronization/condition_variable.h" #include "base/synchronization/lock.h" #include "base/threading/thread_checker.h" #include "gpu/command_buffer/common/constants.h" #include "gpu/gpu_export.h" namespace base { class SingleThreadTaskRunner; } // namespace base namespace gpu { class SyncPointClient; class SyncPointClientState; class SyncPointManager; class GPU_EXPORT SyncPointOrderData : public base::RefCountedThreadSafe { public: static scoped_refptr Create(); void Destroy(); uint32_t GenerateUnprocessedOrderNumber(SyncPointManager* sync_point_manager); void BeginProcessingOrderNumber(uint32_t order_num); void FinishProcessingOrderNumber(uint32_t order_num); uint32_t processed_order_num() const { base::AutoLock auto_lock(lock_); return processed_order_num_; } uint32_t unprocessed_order_num() const { base::AutoLock auto_lock(lock_); return unprocessed_order_num_; } uint32_t current_order_num() const { DCHECK(processing_thread_checker_.CalledOnValidThread()); return current_order_num_; } private: friend class base::RefCountedThreadSafe; friend class SyncPointClientState; struct OrderFence { uint32_t order_num; uint64_t fence_release; scoped_refptr client_state; OrderFence(uint32_t order, uint64_t release, scoped_refptr state); ~OrderFence(); bool operator>(const OrderFence& rhs) const { return (order_num > rhs.order_num) || ((order_num == rhs.order_num) && (fence_release > rhs.fence_release)); } }; typedef std::priority_queue, std::greater> OrderFenceQueue; SyncPointOrderData(); ~SyncPointOrderData(); bool ValidateReleaseOrderNumber( scoped_refptr client_state, uint32_t wait_order_num, uint64_t fence_release); // Non thread-safe functions need to be called from a single thread. base::ThreadChecker processing_thread_checker_; // Current IPC order number being processed (only used on processing thread). uint32_t current_order_num_; // This lock protects destroyed_, processed_order_num_, // unprocessed_order_num_, and order_fence_queue_. All order numbers (n) in // order_fence_queue_ must follow the invariant: // processed_order_num_ < n <= unprocessed_order_num_. mutable base::Lock lock_; bool destroyed_; // Last finished IPC order number. uint32_t processed_order_num_; // Unprocessed order number expected to be processed under normal execution. uint32_t unprocessed_order_num_; // In situations where we are waiting on fence syncs that do not exist, we // validate by making sure the order number does not pass the order number // which the wait command was issued. If the order number reaches the // wait command's, we should automatically release up to the expected // release count. Note that this also releases other lower release counts, // so a single misbehaved fence sync is enough to invalidate/signal all // previous fence syncs. OrderFenceQueue order_fence_queue_; DISALLOW_COPY_AND_ASSIGN(SyncPointOrderData); }; class GPU_EXPORT SyncPointClientState : public base::RefCountedThreadSafe { public: scoped_refptr order_data() { return order_data_; } bool IsFenceSyncReleased(uint64_t release) { return release <= fence_sync_release(); } uint64_t fence_sync_release() { base::AutoLock auto_lock(fence_sync_lock_); return fence_sync_release_; } private: friend class base::RefCountedThreadSafe; friend class SyncPointClient; friend class SyncPointOrderData; struct ReleaseCallback { uint64_t release_count; base::Closure callback_closure; ReleaseCallback(uint64_t release, const base::Closure& callback); ~ReleaseCallback(); bool operator>(const ReleaseCallback& rhs) const { return release_count > rhs.release_count; } }; typedef std::priority_queue, std::greater> ReleaseCallbackQueue; SyncPointClientState(scoped_refptr order_data); ~SyncPointClientState(); // Queues the callback to be called if the release is valid. If the release // is invalid this function will return False and the callback will never // be called. bool WaitForRelease(uint32_t wait_order_num, uint64_t release, const base::Closure& callback); void ReleaseFenceSync(uint64_t release); void EnsureReleased(uint64_t release); void ReleaseFenceSyncLocked(uint64_t release, std::vector* callback_list); // Global order data where releases will originate from. scoped_refptr order_data_; // Protects fence_sync_release_, fence_callback_queue_. base::Lock fence_sync_lock_; // Current fence sync release that has been signaled. uint64_t fence_sync_release_; // In well defined fence sync operations, fence syncs are released in order // so simply having a priority queue for callbacks is enough. ReleaseCallbackQueue release_callback_queue_; DISALLOW_COPY_AND_ASSIGN(SyncPointClientState); }; class GPU_EXPORT SyncPointClient { public: ~SyncPointClient(); scoped_refptr client_state() { return client_state_; } // Wait for a release count to be reached on a SyncPointClientState. If this // function returns false, that means the wait was invalid. Otherwise if it // returns True it means the release was valid. In the case where the release // is valid but has happened already, it will still return true. In all cases // wait_complete_callback will be called eventually. The callback function // may be called on another thread so it should be thread-safe. For // convenience, another non-threadsafe version is defined below where you // can supply a task runner. bool Wait(SyncPointClientState* release_state, uint64_t release_count, const base::Closure& wait_complete_callback); bool WaitNonThreadSafe(SyncPointClientState* release_state, uint64_t release_count, scoped_refptr runner, const base::Closure& wait_complete_callback); void ReleaseFenceSync(uint64_t release); private: friend class SyncPointManager; SyncPointClient(SyncPointManager* sync_point_manager, scoped_refptr order_data, CommandBufferNamespace namespace_id, uint64_t client_id); // Sync point manager is guaranteed to exist in the lifetime of the client. SyncPointManager* sync_point_manager_; // Keep the state that is sharable across multiple threads. scoped_refptr client_state_; // Unique namespace/client id pair for this sync point client. const CommandBufferNamespace namespace_id_; const uint64_t client_id_; DISALLOW_COPY_AND_ASSIGN(SyncPointClient); }; // This class manages the sync points, which allow cross-channel // synchronization. class GPU_EXPORT SyncPointManager { public: explicit SyncPointManager(bool allow_threaded_wait); ~SyncPointManager(); // Creates/Destroy a sync point client which message processors should hold. scoped_ptr CreateSyncPointClient( scoped_refptr order_data, CommandBufferNamespace namespace_id, uint64_t client_id); // Finds the state of an already created sync point client. scoped_refptr GetSyncPointClientState( CommandBufferNamespace namespace_id, uint64_t client_id); // Generates a sync point, returning its ID. This can me called on any thread. // IDs start at a random number. Never return 0. uint32 GenerateSyncPoint(); // Retires a sync point. This will call all the registered callbacks for this // sync point. This can only be called on the main thread. void RetireSyncPoint(uint32 sync_point); // Adds a callback to the sync point. The callback will be called when the // sync point is retired, or immediately (from within that function) if the // sync point was already retired (or not created yet). This can only be // called on the main thread. void AddSyncPointCallback(uint32 sync_point, const base::Closure& callback); bool IsSyncPointRetired(uint32 sync_point); // Block and wait until a sync point is signaled. This is only useful when // the sync point is signaled on another thread. void WaitSyncPoint(uint32 sync_point); private: friend class SyncPointClient; friend class SyncPointOrderData; typedef std::vector ClosureList; typedef base::hash_map SyncPointMap; typedef base::hash_map ClientMap; bool IsSyncPointRetiredLocked(uint32 sync_point); uint32_t GenerateOrderNumber(); void DestroySyncPointClient(CommandBufferNamespace namespace_id, uint64_t client_id); const bool allow_threaded_wait_; // Order number is global for all clients. base::AtomicSequenceNumber global_order_num_; // Client map holds a map of clients id to client for each namespace. base::Lock client_maps_lock_; ClientMap client_maps_[NUM_COMMAND_BUFFER_NAMESPACES]; // Protects the 2 fields below. Note: callbacks shouldn't be called with this // held. base::Lock lock_; SyncPointMap sync_point_map_; uint32 next_sync_point_; base::ConditionVariable retire_cond_var_; DISALLOW_COPY_AND_ASSIGN(SyncPointManager); }; } // namespace gpu #endif // GPU_COMMAND_BUFFER_SERVICE_SYNC_POINT_MANAGER_H_