|  | // 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. | 
|  |  | 
|  | #include "gpu/command_buffer/service/sync_point_manager.h" | 
|  |  | 
|  | #include <limits.h> | 
|  | #include <stddef.h> | 
|  | #include <stdint.h> | 
|  |  | 
|  | #include <climits> | 
|  |  | 
|  | #include "base/bind.h" | 
|  | #include "base/containers/hash_tables.h" | 
|  | #include "base/location.h" | 
|  | #include "base/logging.h" | 
|  | #include "base/memory/ptr_util.h" | 
|  | #include "base/rand_util.h" | 
|  | #include "base/sequence_checker.h" | 
|  | #include "base/single_thread_task_runner.h" | 
|  |  | 
|  | namespace gpu { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | void RunOnThread(scoped_refptr<base::SingleThreadTaskRunner> task_runner, | 
|  | const base::Closure& callback) { | 
|  | if (task_runner->BelongsToCurrentThread()) { | 
|  | callback.Run(); | 
|  | } else { | 
|  | task_runner->PostTask(FROM_HERE, callback); | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | scoped_refptr<SyncPointOrderData> SyncPointOrderData::Create() { | 
|  | return new SyncPointOrderData; | 
|  | } | 
|  |  | 
|  | void SyncPointOrderData::Destroy() { | 
|  | // Because of circular references between the SyncPointOrderData and | 
|  | // SyncPointClientState, we must remove the references on destroy. Releasing | 
|  | // the fence syncs in the order fence queue would be redundant at this point | 
|  | // because they are assumed to be released on the destruction of the | 
|  | // SyncPointClient. | 
|  | base::AutoLock auto_lock(lock_); | 
|  | destroyed_ = true; | 
|  | while (!order_fence_queue_.empty()) { | 
|  | order_fence_queue_.pop(); | 
|  | } | 
|  | } | 
|  |  | 
|  | uint32_t SyncPointOrderData::GenerateUnprocessedOrderNumber( | 
|  | SyncPointManager* sync_point_manager) { | 
|  | const uint32_t order_num = sync_point_manager->GenerateOrderNumber(); | 
|  | base::AutoLock auto_lock(lock_); | 
|  | unprocessed_order_num_ = order_num; | 
|  | return order_num; | 
|  | } | 
|  |  | 
|  | void SyncPointOrderData::BeginProcessingOrderNumber(uint32_t order_num) { | 
|  | DCHECK(processing_thread_checker_.CalledOnValidThread()); | 
|  | DCHECK_GE(order_num, current_order_num_); | 
|  | // Use thread-safe accessors here because |processed_order_num_| and | 
|  | // |unprocessed_order_num_| are protected by a lock. | 
|  | DCHECK_GT(order_num, processed_order_num()); | 
|  | DCHECK_LE(order_num, unprocessed_order_num()); | 
|  | current_order_num_ = order_num; | 
|  | paused_ = false; | 
|  |  | 
|  | // Catch invalid waits which were waiting on fence syncs that do not exist. | 
|  | // When we begin processing an order number, we should release any fence | 
|  | // syncs which were enqueued but the order number never existed. | 
|  | // Release without the lock to avoid possible deadlocks. | 
|  | std::vector<OrderFence> ensure_releases; | 
|  | { | 
|  | base::AutoLock auto_lock(lock_); | 
|  | while (!order_fence_queue_.empty()) { | 
|  | const OrderFence& order_fence = order_fence_queue_.top(); | 
|  | if (order_fence_queue_.top().order_num < order_num) { | 
|  | ensure_releases.push_back(order_fence); | 
|  | order_fence_queue_.pop(); | 
|  | continue; | 
|  | } | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | for (OrderFence& order_fence : ensure_releases) { | 
|  | order_fence.client_state->EnsureWaitReleased(order_fence.fence_release, | 
|  | order_fence.release_callback); | 
|  | } | 
|  | } | 
|  |  | 
|  | void SyncPointOrderData::PauseProcessingOrderNumber(uint32_t order_num) { | 
|  | DCHECK(processing_thread_checker_.CalledOnValidThread()); | 
|  | DCHECK_EQ(current_order_num_, order_num); | 
|  | DCHECK(!paused_); | 
|  | paused_ = true; | 
|  | } | 
|  |  | 
|  | void SyncPointOrderData::FinishProcessingOrderNumber(uint32_t order_num) { | 
|  | DCHECK(processing_thread_checker_.CalledOnValidThread()); | 
|  | DCHECK_EQ(current_order_num_, order_num); | 
|  | DCHECK(!paused_); | 
|  |  | 
|  | // Catch invalid waits which were waiting on fence syncs that do not exist. | 
|  | // When we end processing an order number, we should release any fence syncs | 
|  | // which were suppose to be released during this order number. | 
|  | // Release without the lock to avoid possible deadlocks. | 
|  | std::vector<OrderFence> ensure_releases; | 
|  | { | 
|  | base::AutoLock auto_lock(lock_); | 
|  | DCHECK_GT(order_num, processed_order_num_); | 
|  | processed_order_num_ = order_num; | 
|  |  | 
|  | while (!order_fence_queue_.empty()) { | 
|  | const OrderFence& order_fence = order_fence_queue_.top(); | 
|  | if (order_fence_queue_.top().order_num <= order_num) { | 
|  | ensure_releases.push_back(order_fence); | 
|  | order_fence_queue_.pop(); | 
|  | continue; | 
|  | } | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | for (OrderFence& order_fence : ensure_releases) { | 
|  | order_fence.client_state->EnsureWaitReleased(order_fence.fence_release, | 
|  | order_fence.release_callback); | 
|  | } | 
|  | } | 
|  |  | 
|  | SyncPointOrderData::OrderFence::OrderFence( | 
|  | uint32_t order, | 
|  | uint64_t release, | 
|  | const base::Closure& callback, | 
|  | scoped_refptr<SyncPointClientState> state) | 
|  | : order_num(order), | 
|  | fence_release(release), | 
|  | release_callback(callback), | 
|  | client_state(state) {} | 
|  |  | 
|  | SyncPointOrderData::OrderFence::OrderFence(const OrderFence& other) = default; | 
|  |  | 
|  | SyncPointOrderData::OrderFence::~OrderFence() {} | 
|  |  | 
|  | SyncPointOrderData::SyncPointOrderData() | 
|  | : current_order_num_(0), | 
|  | paused_(false), | 
|  | destroyed_(false), | 
|  | processed_order_num_(0), | 
|  | unprocessed_order_num_(0) {} | 
|  |  | 
|  | SyncPointOrderData::~SyncPointOrderData() {} | 
|  |  | 
|  | bool SyncPointOrderData::ValidateReleaseOrderNumber( | 
|  | scoped_refptr<SyncPointClientState> client_state, | 
|  | uint32_t wait_order_num, | 
|  | uint64_t fence_release, | 
|  | const base::Closure& release_callback) { | 
|  | base::AutoLock auto_lock(lock_); | 
|  | if (destroyed_) | 
|  | return false; | 
|  |  | 
|  | // Release should have a possible unprocessed order number lower | 
|  | // than the wait order number. | 
|  | if ((processed_order_num_ + 1) >= wait_order_num) | 
|  | return false; | 
|  |  | 
|  | // Release should have more unprocessed numbers if we are waiting. | 
|  | if (unprocessed_order_num_ <= processed_order_num_) | 
|  | return false; | 
|  |  | 
|  | // So far it could be valid, but add an order fence guard to be sure it | 
|  | // gets released eventually. | 
|  | const uint32_t expected_order_num = | 
|  | std::min(unprocessed_order_num_, wait_order_num); | 
|  | order_fence_queue_.push(OrderFence(expected_order_num, fence_release, | 
|  | release_callback, client_state)); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | SyncPointClientState::ReleaseCallback::ReleaseCallback( | 
|  | uint64_t release, | 
|  | const base::Closure& callback) | 
|  | : release_count(release), callback_closure(callback) {} | 
|  |  | 
|  | SyncPointClientState::ReleaseCallback::ReleaseCallback( | 
|  | const ReleaseCallback& other) = default; | 
|  |  | 
|  | SyncPointClientState::ReleaseCallback::~ReleaseCallback() {} | 
|  |  | 
|  | SyncPointClientState::SyncPointClientState( | 
|  | scoped_refptr<SyncPointOrderData> order_data) | 
|  | : order_data_(order_data), fence_sync_release_(0) {} | 
|  |  | 
|  | SyncPointClientState::~SyncPointClientState() { | 
|  | } | 
|  |  | 
|  | bool SyncPointClientState::WaitForRelease(CommandBufferNamespace namespace_id, | 
|  | CommandBufferId client_id, | 
|  | uint32_t wait_order_num, | 
|  | uint64_t release, | 
|  | const base::Closure& callback) { | 
|  | // Lock must be held the whole time while we validate otherwise it could be | 
|  | // released while we are checking. | 
|  | { | 
|  | base::AutoLock auto_lock(fence_sync_lock_); | 
|  | if (release > fence_sync_release_) { | 
|  | if (!order_data_->ValidateReleaseOrderNumber(this, wait_order_num, | 
|  | release, callback)) { | 
|  | return false; | 
|  | } else { | 
|  | // Add the callback which will be called upon release. | 
|  | release_callback_queue_.push(ReleaseCallback(release, callback)); | 
|  | if (!on_wait_callback_.is_null()) | 
|  | on_wait_callback_.Run(namespace_id, client_id); | 
|  | return true; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Already released, run the callback now. | 
|  | callback.Run(); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void SyncPointClientState::ReleaseFenceSync(uint64_t release) { | 
|  | // Call callbacks without the lock to avoid possible deadlocks. | 
|  | std::vector<base::Closure> callback_list; | 
|  | { | 
|  | base::AutoLock auto_lock(fence_sync_lock_); | 
|  | DLOG_IF(ERROR, release <= fence_sync_release_) | 
|  | << "Client submitted fence releases out of order."; | 
|  |  | 
|  | fence_sync_release_ = release; | 
|  | while (!release_callback_queue_.empty() && | 
|  | release_callback_queue_.top().release_count <= release) { | 
|  | callback_list.push_back(release_callback_queue_.top().callback_closure); | 
|  | release_callback_queue_.pop(); | 
|  | } | 
|  | } | 
|  |  | 
|  | for (const base::Closure& closure : callback_list) { | 
|  | closure.Run(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void SyncPointClientState::EnsureWaitReleased(uint64_t release, | 
|  | const base::Closure& callback) { | 
|  | // Call callbacks without the lock to avoid possible deadlocks. | 
|  | bool call_callback = false; | 
|  | { | 
|  | base::AutoLock auto_lock(fence_sync_lock_); | 
|  | if (release <= fence_sync_release_) | 
|  | return; | 
|  |  | 
|  | std::vector<ReleaseCallback> popped_callbacks; | 
|  | popped_callbacks.reserve(release_callback_queue_.size()); | 
|  |  | 
|  | while (!release_callback_queue_.empty() && | 
|  | release_callback_queue_.top().release_count <= release) { | 
|  | const ReleaseCallback& top_item = release_callback_queue_.top(); | 
|  | if (top_item.release_count == release && | 
|  | top_item.callback_closure.Equals(callback)) { | 
|  | // Call the callback, and discard this item from the callback queue. | 
|  | call_callback = true; | 
|  | } else { | 
|  | // Store the item to be placed back into the callback queue later. | 
|  | popped_callbacks.push_back(top_item); | 
|  | } | 
|  | release_callback_queue_.pop(); | 
|  | } | 
|  |  | 
|  | // Add back in popped items. | 
|  | for (const ReleaseCallback& popped_callback : popped_callbacks) { | 
|  | release_callback_queue_.push(popped_callback); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (call_callback) { | 
|  | // This effectively releases the wait without releasing the fence. | 
|  | callback.Run(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void SyncPointClientState::SetOnWaitCallback(const OnWaitCallback& callback) { | 
|  | on_wait_callback_ = callback; | 
|  | } | 
|  |  | 
|  | SyncPointClient::~SyncPointClient() { | 
|  | if (namespace_id_ != gpu::CommandBufferNamespace::INVALID) { | 
|  | // Release all fences on destruction. | 
|  | client_state_->ReleaseFenceSync(UINT64_MAX); | 
|  |  | 
|  | sync_point_manager_->DestroySyncPointClient(namespace_id_, client_id_); | 
|  | } | 
|  | } | 
|  |  | 
|  | bool SyncPointClient::Wait(SyncPointClientState* release_state, | 
|  | uint64_t release_count, | 
|  | const base::Closure& wait_complete_callback) { | 
|  | // Validate that this Wait call is between BeginProcessingOrderNumber() and | 
|  | // FinishProcessingOrderNumber(), or else we may deadlock. | 
|  | DCHECK(client_state_->order_data()->IsProcessingOrderNumber()); | 
|  |  | 
|  | const uint32_t wait_order_number = | 
|  | client_state_->order_data()->current_order_num(); | 
|  |  | 
|  | // If waiting on self or wait was invalid, call the callback and return false. | 
|  | if (client_state_ == release_state || | 
|  | !release_state->WaitForRelease(namespace_id_, client_id_, | 
|  | wait_order_number, release_count, | 
|  | wait_complete_callback)) { | 
|  | wait_complete_callback.Run(); | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool SyncPointClient::WaitNonThreadSafe( | 
|  | SyncPointClientState* release_state, | 
|  | uint64_t release_count, | 
|  | scoped_refptr<base::SingleThreadTaskRunner> runner, | 
|  | const base::Closure& wait_complete_callback) { | 
|  | return Wait(release_state, release_count, | 
|  | base::Bind(&RunOnThread, runner, wait_complete_callback)); | 
|  | } | 
|  |  | 
|  | bool SyncPointClient::WaitOutOfOrder( | 
|  | SyncPointClientState* release_state, | 
|  | uint64_t release_count, | 
|  | const base::Closure& wait_complete_callback) { | 
|  | // Validate that this Wait call is not between BeginProcessingOrderNumber() | 
|  | // and FinishProcessingOrderNumber(), or else we may deadlock. | 
|  | DCHECK(!client_state_ || | 
|  | !client_state_->order_data()->IsProcessingOrderNumber()); | 
|  |  | 
|  | // No order number associated with the current execution context, using | 
|  | // UINT32_MAX will just assume the release is in the SyncPointClientState's | 
|  | // order numbers to be executed. | 
|  | if (!release_state->WaitForRelease(namespace_id_, client_id_, UINT32_MAX, | 
|  | release_count, wait_complete_callback)) { | 
|  | wait_complete_callback.Run(); | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool SyncPointClient::WaitOutOfOrderNonThreadSafe( | 
|  | SyncPointClientState* release_state, | 
|  | uint64_t release_count, | 
|  | scoped_refptr<base::SingleThreadTaskRunner> runner, | 
|  | const base::Closure& wait_complete_callback) { | 
|  | return WaitOutOfOrder( | 
|  | release_state, release_count, | 
|  | base::Bind(&RunOnThread, runner, wait_complete_callback)); | 
|  | } | 
|  |  | 
|  | void SyncPointClient::ReleaseFenceSync(uint64_t release) { | 
|  | // Validate that this Release call is between BeginProcessingOrderNumber() and | 
|  | // FinishProcessingOrderNumber(), or else we may deadlock. | 
|  | DCHECK(client_state_->order_data()->IsProcessingOrderNumber()); | 
|  | client_state_->ReleaseFenceSync(release); | 
|  | } | 
|  |  | 
|  | void SyncPointClient::SetOnWaitCallback(const OnWaitCallback& callback) { | 
|  | client_state_->SetOnWaitCallback(callback); | 
|  | } | 
|  |  | 
|  | SyncPointClient::SyncPointClient() | 
|  | : sync_point_manager_(nullptr), | 
|  | namespace_id_(gpu::CommandBufferNamespace::INVALID), | 
|  | client_id_() {} | 
|  |  | 
|  | SyncPointClient::SyncPointClient(SyncPointManager* sync_point_manager, | 
|  | scoped_refptr<SyncPointOrderData> order_data, | 
|  | CommandBufferNamespace namespace_id, | 
|  | CommandBufferId client_id) | 
|  | : sync_point_manager_(sync_point_manager), | 
|  | client_state_(new SyncPointClientState(order_data)), | 
|  | namespace_id_(namespace_id), | 
|  | client_id_(client_id) {} | 
|  |  | 
|  | SyncPointManager::SyncPointManager(bool allow_threaded_wait) { | 
|  | global_order_num_.GetNext(); | 
|  | } | 
|  |  | 
|  | SyncPointManager::~SyncPointManager() { | 
|  | for (const ClientMap& client_map : client_maps_) { | 
|  | DCHECK(client_map.empty()); | 
|  | } | 
|  | } | 
|  |  | 
|  | std::unique_ptr<SyncPointClient> SyncPointManager::CreateSyncPointClient( | 
|  | scoped_refptr<SyncPointOrderData> order_data, | 
|  | CommandBufferNamespace namespace_id, | 
|  | CommandBufferId client_id) { | 
|  | DCHECK_GE(namespace_id, 0); | 
|  | DCHECK_LT(static_cast<size_t>(namespace_id), arraysize(client_maps_)); | 
|  | base::AutoLock auto_lock(client_maps_lock_); | 
|  |  | 
|  | ClientMap& client_map = client_maps_[namespace_id]; | 
|  | std::pair<ClientMap::iterator, bool> result = client_map.insert( | 
|  | std::make_pair(client_id, new SyncPointClient(this, order_data, | 
|  | namespace_id, client_id))); | 
|  | DCHECK(result.second); | 
|  |  | 
|  | return base::WrapUnique(result.first->second); | 
|  | } | 
|  |  | 
|  | std::unique_ptr<SyncPointClient> | 
|  | SyncPointManager::CreateSyncPointClientWaiter() { | 
|  | return base::WrapUnique(new SyncPointClient); | 
|  | } | 
|  |  | 
|  | scoped_refptr<SyncPointClientState> SyncPointManager::GetSyncPointClientState( | 
|  | CommandBufferNamespace namespace_id, | 
|  | CommandBufferId client_id) { | 
|  | if (namespace_id >= 0) { | 
|  | DCHECK_LT(static_cast<size_t>(namespace_id), arraysize(client_maps_)); | 
|  | base::AutoLock auto_lock(client_maps_lock_); | 
|  | ClientMap& client_map = client_maps_[namespace_id]; | 
|  | ClientMap::iterator it = client_map.find(client_id); | 
|  | if (it != client_map.end()) { | 
|  | return it->second->client_state(); | 
|  | } | 
|  | } | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | uint32_t SyncPointManager::GenerateOrderNumber() { | 
|  | return global_order_num_.GetNext(); | 
|  | } | 
|  |  | 
|  | void SyncPointManager::DestroySyncPointClient( | 
|  | CommandBufferNamespace namespace_id, | 
|  | CommandBufferId client_id) { | 
|  | DCHECK_GE(namespace_id, 0); | 
|  | DCHECK_LT(static_cast<size_t>(namespace_id), arraysize(client_maps_)); | 
|  |  | 
|  | base::AutoLock auto_lock(client_maps_lock_); | 
|  | ClientMap& client_map = client_maps_[namespace_id]; | 
|  | ClientMap::iterator it = client_map.find(client_id); | 
|  | DCHECK(it != client_map.end()); | 
|  | client_map.erase(it); | 
|  | } | 
|  |  | 
|  | }  // namespace gpu |