/* * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. */ #pragma once #include #include #include #if __cplusplus >= 201402L && !defined(__APPLE__) // For C++14, use shared_timed_mutex. // some macOS C++14 compilers don't support shared_timed_mutex. #define FBGEMM_USE_SHARED_TIMED_MUTEX #endif #ifdef FBGEMM_USE_SHARED_TIMED_MUTEX #include #else #include #endif #ifdef FBCODE_CAFFE2 #include #endif namespace fbgemm { /** * @brief Thread safe cache for microkernels, ensures single creation per key. * @tparam KEY Type of unique key (typically a tuple) * @tparam VALUE Type of the microkernel function (Typically a function pointer) * @tparam THREAD_LOCAL use thread local and avoid locking (default false) */ template class CodeCache { private: #ifdef FBCODE_CAFFE2 folly::F14FastMap> values_; #else std::map> values_; #endif #ifdef FBGEMM_USE_SHARED_TIMED_MUTEX std::shared_timed_mutex mutex_; #else std::mutex mutex_; #endif public: CodeCache(const CodeCache&) = delete; CodeCache& operator=(const CodeCache&) = delete; CodeCache() {} template VALUE getOrCreate(const KEY& key, GENFUNC generatorFunction) { #ifdef FBGEMM_USE_SHARED_TIMED_MUTEX std::shared_lock sharedLock(mutex_); #else std::unique_lock uniqueLock(mutex_); #endif // Check for existence of the key auto it = values_.find(key); if (it != values_.end()) { return it->second.get(); } else { #ifdef FBGEMM_USE_SHARED_TIMED_MUTEX sharedLock.unlock(); std::unique_lock uniqueLock(mutex_); // Need to look up again because there could be race condition from // the time gap between sharedLock.unlock() and creating uniqueLock. it = values_.find(key); if (it == values_.end()) { #endif std::promise returnPromise; values_[key] = returnPromise.get_future().share(); uniqueLock.unlock(); // The value (code) generation is not happening under a lock VALUE val = generatorFunction(); returnPromise.set_value(val); return val; #ifdef FBGEMM_USE_SHARED_TIMED_MUTEX } else { return it->second.get(); } #endif } } }; // This class must be used as a static variable. template class CodeCache { private: #ifdef FBCODE_CAFFE2 static folly::F14FastMap& getValues_() { static thread_local folly::F14FastMap values_; /* library-local */ return values_; } #else static std::map& getValues_() { static thread_local std::map values_; return values_; } #endif public: CodeCache(const CodeCache&) = delete; CodeCache& operator=(const CodeCache&) = delete; CodeCache() {} template VALUE getOrCreate(const KEY& key, GENFUNC generatorFunction) { // Check for existence of the key auto it = getValues_().find(key); if (it != getValues_().end()) { return it->second; } else { VALUE val = generatorFunction(); getValues_()[key] = val; return val; } } }; } // namespace fbgemm