#include "dynlib.h"
#include "guard.h"
#include "mutex.h"
#include <util/generic/singleton.h>
#include <util/generic/yexception.h>
#ifdef _win32_
#include "winint.h"
#define DLLOPEN(path, flags) LoadLibrary(path)
#define DLLCLOSE(hndl) FreeLibrary(hndl)
#define DLLSYM(hndl, name) GetProcAddress(hndl, name)
#else
#include <dlfcn.h>
#ifndef RTLD_GLOBAL
#define RTLD_GLOBAL (0)
#endif
using HINSTANCE = void*;
#define DLLOPEN(path, flags) dlopen(path, flags)
#define DLLCLOSE(hndl) dlclose(hndl)
#define DLLSYM(hndl, name) dlsym(hndl, name)
#endif
inline TString DLLERR() {
#ifdef _unix_
return dlerror();
#endif
#ifdef _win32_
char* msg = 0;
DWORD cnt = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char*)&msg, 0, nullptr);
if (!msg)
return "DLLERR() unknown error";
while (cnt && isspace(msg[cnt - 1]))
--cnt;
TString err(msg, 0, cnt);
LocalFree(msg);
return err;
#endif
}
class TDynamicLibrary::TImpl {
private:
inline TImpl(const char* path, int flags)
: Module(DLLOPEN(path, flags))
, Unloadable(true)
{
(void)flags;
if (!Module) {
ythrow yexception() << DLLERR().data();
}
}
class TCreateMutex: public TMutex {
};
public:
static inline TImpl* SafeCreate(const char* path, int flags) {
auto guard = Guard(*Singleton<TCreateMutex>());
return new TImpl(path, flags);
}
inline ~TImpl() {
if (Module && Unloadable) {
DLLCLOSE(Module);
}
}
inline void* SymOptional(const char* name) noexcept {
return (void*)DLLSYM(Module, name);
}
inline void* Sym(const char* name) {
void* symbol = SymOptional(name);
if (symbol == nullptr) {
ythrow yexception() << DLLERR().data();
}
return symbol;
}
inline void SetUnloadable(bool unloadable) {
Unloadable = unloadable;
}
private:
HINSTANCE Module;
bool Unloadable;
};
TDynamicLibrary::TDynamicLibrary() noexcept {
}
TDynamicLibrary::TDynamicLibrary(const TString& path, int flags) {
Open(path.data(), flags);
}
TDynamicLibrary::~TDynamicLibrary() = default;
void TDynamicLibrary::Open(const char* path, int flags) {
Impl_.Reset(TImpl::SafeCreate(path, flags));
}
void TDynamicLibrary::Close() noexcept {
Impl_.Destroy();
}
void* TDynamicLibrary::SymOptional(const char* name) noexcept {
if (!IsLoaded()) {
return nullptr;
}
return Impl_->SymOptional(name);
}
void* TDynamicLibrary::Sym(const char* name) {
if (!IsLoaded()) {
ythrow yexception() << "library not loaded";
}
return Impl_->Sym(name);
}
bool TDynamicLibrary::IsLoaded() const noexcept {
return (bool)Impl_.Get();
}
void TDynamicLibrary::SetUnloadable(bool unloadable) {
Impl_->SetUnloadable(unloadable);
}