| // Copyright 2020 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 GARNET_BIN_HWSTRESS_PROFILE_MANAGER_H_ |
| #define GARNET_BIN_HWSTRESS_PROFILE_MANAGER_H_ |
| |
| #include <fuchsia/scheduler/cpp/fidl.h> |
| #include <lib/sys/cpp/service_directory.h> |
| #include <lib/zx/profile.h> |
| #include <lib/zx/status.h> |
| #include <lib/zx/thread.h> |
| #include <zircon/compiler.h> |
| #include <zircon/status.h> |
| |
| #include <memory> |
| #include <unordered_map> |
| #include <utility> |
| |
| namespace hwstress { |
| |
| // A ProfileManager creates, caches, and applies Zircon scheduling profilings to |
| // threads. |
| // |
| // Thread safe. |
| class ProfileManager { |
| public: |
| // Create a new profile manager from services in the environment. |
| static std::unique_ptr<ProfileManager> CreateFromEnvironment(); |
| |
| // Create a new profile manager. |
| explicit ProfileManager(fuchsia::scheduler::ProfileProviderSyncPtr profile_provider); |
| |
| // Apply a given affinity mask to the given thread. |
| // |
| // Bit |i| in the mask being set corresponds to the thread being allowed to run on CPU |i|. |
| zx_status_t SetThreadAffinity(std::thread* thread, uint32_t mask); |
| zx_status_t SetThreadAffinity(const zx::thread& thread, uint32_t mask); |
| |
| // Apply a given priority mask to the given thread. |
| zx_status_t SetThreadPriority(std::thread* thread, uint32_t priority); |
| zx_status_t SetThreadPriority(const zx::thread& thread, uint32_t priority); |
| |
| private: |
| // Disallow copy and move. |
| ProfileManager(const ProfileManager&) = delete; |
| ProfileManager& operator=(const ProfileManager&) = delete; |
| |
| // Apply a profile to the given thread. |
| // |
| // If the profile already exists in |cache|, we apply that. Otherwise we create a |
| // new profile using |create_fn|. |
| template <typename T> |
| zx_status_t CreateAndApplyProfile(std::unordered_map<T, zx::profile>* cache, T key, |
| std::function<zx::status<zx::profile>(T)> create_fn, |
| const zx::thread& thread); |
| |
| fuchsia::scheduler::ProfileProviderSyncPtr profile_provider_; |
| std::mutex mutex_; |
| std::unordered_map<uint32_t, zx::profile> affinity_profiles_ __TA_GUARDED(mutex_); |
| std::unordered_map<uint32_t, zx::profile> priority_profiles_ __TA_GUARDED(mutex_); |
| }; |
| |
| // Return the Zircon handle from a given C++ thread. |
| zx::unowned<zx::thread> HandleFromThread(std::thread* thread); |
| |
| // |
| // Implementation details. |
| // |
| |
| template <typename T> |
| zx_status_t ProfileManager::CreateAndApplyProfile( |
| std::unordered_map<T, zx::profile>* cache, T key, |
| std::function<zx::status<zx::profile>(T)> create_fn, const zx::thread& thread) { |
| std::lock_guard<std::mutex> guard(mutex_); |
| |
| // If we already have a profile for this priority, just use that. |
| auto it = cache->find(key); |
| if (it != cache->end()) { |
| return thread.set_profile(it->second, /*options=*/0); |
| } |
| |
| // Create a profile. |
| zx::status<zx::profile> profile = create_fn(key); |
| if (profile.is_error()) { |
| return profile.error_value(); |
| } |
| |
| // Save it and apply it. |
| zx_status_t status = thread.set_profile(profile.value(), /*options=*/0); |
| (*cache)[key] = std::move(profile.value()); |
| return status; |
| } |
| |
| } // namespace hwstress |
| |
| #endif // GARNET_BIN_HWSTRESS_PROFILE_MANAGER_H_ |