* Copyright (c) 2025 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef OHOS_DM_LIBRARY_MANAGER_H
#define OHOS_DM_LIBRARY_MANAGER_H
#include <atomic>
#include <chrono>
#include <dlfcn.h>
#include <memory>
#include <mutex>
#include <shared_mutex>
#include <string>
#include <unordered_map>
#include <vector>
#include "dm_log.h"
#include "dm_timer.h"
namespace OHOS {
namespace DistributedHardware {
constexpr int32_t LIB_UNLOAD_TRIGGER_FREE_TIMESPAN = 60;
class DMLibraryManager {
public:
DMLibraryManager();
static DMLibraryManager& GetInstance();
template<typename FuncType>
FuncType GetFunction(const std::string& libraryPath, const std::string& functionName);
void Release(const std::string& libraryPath);
private:
struct LibraryInfo {
void* handle = nullptr;
std::atomic<int32_t> refCount{0};
std::atomic<bool> isLibTimerBegin{false};
std::atomic<uint64_t> lastUsed;
mutable std::shared_mutex mutex;
std::unique_ptr<DmTimer> libTimer;
LibraryInfo()
{
auto now = std::chrono::steady_clock::now();
lastUsed = static_cast<uint64_t>(
std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count());
libTimer = std::make_unique<DmTimer>();
}
};
static const size_t NUM_SEGMENTS = 8;
std::vector<std::unordered_map<std::string, std::shared_ptr<LibraryInfo>>> segments_;
std::vector<std::shared_mutex> segmentMutexes_;
size_t GetSegmentIndex(const std::string& libraryPath) const;
static std::unique_ptr<DMLibraryManager> instance_;
static std::once_flag initFlag_;
private:
std::shared_ptr<LibraryInfo> GetOrCreateLibrary(const std::string& libraryPath);
std::shared_ptr<LibraryInfo> GetLibraryInfo(const std::string& libraryPath);
void DoUnloadLib(const std::string& libraryPath);
};
template<typename FuncType>
FuncType DMLibraryManager::GetFunction(const std::string& libraryPath, const std::string& functionName)
{
auto libInfo = GetOrCreateLibrary(libraryPath);
{
std::shared_lock<std::shared_mutex> readLock(libInfo->mutex);
libInfo->refCount++;
if (libInfo->handle == nullptr) {
readLock.unlock();
std::unique_lock<std::shared_mutex> writeLock(libInfo->mutex);
if (libInfo->handle == nullptr) {
libInfo->handle = dlopen(libraryPath.c_str(), RTLD_LAZY);
if (libInfo->handle == nullptr) {
libInfo->refCount--;
LOGE("dlopen failed for %{public}s: %{public}s", libraryPath.c_str(), dlerror());
return nullptr;
}
}
}
}
void* sym = dlsym(libInfo->handle, functionName.c_str());
if (sym == nullptr) {
libInfo->refCount--;
LOGE("dlsym failed for %{public}s: %{public}s, refCount: %{public}d",
libraryPath.c_str(), dlerror(), libInfo->refCount.load());
return nullptr;
}
auto now = std::chrono::steady_clock::now();
libInfo->lastUsed = static_cast<uint64_t>(
std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count());
bool expected = false;
if (libInfo->isLibTimerBegin.compare_exchange_strong(expected, true,
std::memory_order_acq_rel, std::memory_order_acquire)) {
libInfo->libTimer->StartTimer(libraryPath, LIB_UNLOAD_TRIGGER_FREE_TIMESPAN, [this] (std::string libPath) {
DMLibraryManager::DoUnloadLib(libPath);
});
}
LOGI("Do load lib: %{public}s", libraryPath.c_str());
return reinterpret_cast<FuncType>(sym);
}
inline DMLibraryManager& GetLibraryManager()
{
return DMLibraryManager::GetInstance();
}
}
}
#endif