Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
reload_library.hpp
Go to the documentation of this file.
1#pragma once
2
31
32#include <string>
33#include <utility>
34
35#if defined(_WIN32)
36#ifndef WIN32_LEAN_AND_MEAN
37#define WIN32_LEAN_AND_MEAN
38#endif
39#ifndef NOMINMAX
40#define NOMINMAX
41#endif
42#include <windows.h>
43#else
44#include <dlfcn.h>
45#endif
46
47namespace pulp::format::reload {
48
49#if !defined(_WIN32)
75 static const bool promoted = [] {
76 ::Dl_info info{};
77 if (::dladdr(reinterpret_cast<const void*>(&promote_host_symbols_once), &info) &&
78 info.dli_fname) {
79 // RTLD_NOLOAD: do not map a new copy — just look up the already-loaded
80 // host image and (via RTLD_GLOBAL) promote its symbols' scope. glibc
81 // rejects a mode without RTLD_LAZY or RTLD_NOW ("invalid mode"), so
82 // on Linux the promotion silently did nothing and thin logic failed
83 // to bind in a plug-in loaded RTLD_LOCAL.
84#if defined(__linux__)
85 constexpr int kPromoteMode = RTLD_LAZY | RTLD_GLOBAL | RTLD_NOLOAD;
86#else
87 constexpr int kPromoteMode = RTLD_GLOBAL | RTLD_NOLOAD;
88#endif
89 if (void* self = ::dlopen(info.dli_fname, kPromoteMode)) {
90 // Intentionally leak this handle: we only wanted the scope change;
91 // the image is the running host and is never unloaded regardless.
92 (void)self;
93 }
94 }
95 return true;
96 }();
97 (void)promoted;
98}
99#else
100inline void promote_host_symbols_once() {}
101#endif
102
108
110public:
111 ReloadLibrary() = default;
112
115 explicit ReloadLibrary(const std::string& path,
117 : path_(path), policy_(policy) {
118 open(path);
119 }
120
121 // Move-only: a handle has unique ownership of the (un)load decision.
122 ReloadLibrary(ReloadLibrary&& other) noexcept { move_from(other); }
124 if (this != &other) {
125 destroy();
126 move_from(other);
127 }
128 return *this;
129 }
130 ReloadLibrary(const ReloadLibrary&) = delete;
132
133 ~ReloadLibrary() { destroy(); }
134
135 bool valid() const noexcept { return handle_ != nullptr; }
136 explicit operator bool() const noexcept { return valid(); }
137
138 const std::string& error() const noexcept { return error_; }
139 const std::string& path() const noexcept { return path_; }
140 void* native_handle() const noexcept { return handle_; }
141
142 LeakPolicy leak_policy() const noexcept { return policy_; }
145 void set_leak_policy(LeakPolicy policy) noexcept { policy_ = policy; }
146
148 void* raw_symbol(const char* name) const noexcept {
149 if (!handle_ || !name) return nullptr;
150#if defined(_WIN32)
151 return reinterpret_cast<void*>(
152 ::GetProcAddress(reinterpret_cast<HMODULE>(handle_), name));
153#else
154 return ::dlsym(handle_, name);
155#endif
156 }
157
159 template <typename T>
160 T symbol(const char* name) const noexcept {
161 return reinterpret_cast<T>(raw_symbol(name));
162 }
163
167 bool close() noexcept {
168 if (!handle_) return false;
169 const bool ok = native_close(handle_);
170 handle_ = nullptr;
171 return ok;
172 }
173
174private:
175 void open(const std::string& path) {
176#if defined(_WIN32)
177 DWORD old_mode = 0;
178 const BOOL changed = ::SetThreadErrorMode(
179 SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX, &old_mode);
180 handle_ = reinterpret_cast<void*>(::LoadLibraryA(path.c_str()));
181 if (changed) ::SetThreadErrorMode(old_mode, nullptr);
182 if (!handle_) error_ = "LoadLibraryA failed for '" + path + "'";
183#else
184 // Ensure the host bundle's pulp::* SDK symbols are globally visible
185 // BEFORE loading (possibly thin) logic that resolves them at dlopen via
186 // flat-namespace lookup — the in-DAW RTLD_LOCAL fix. Once per
187 // process; a no-op after the first load and for the static-link model.
189 handle_ = ::dlopen(path.c_str(), RTLD_NOW | RTLD_LOCAL);
190 if (!handle_) {
191 const char* e = ::dlerror();
192 error_ = e ? e : ("dlopen failed for '" + path + "'");
193 }
194#endif
195 }
196
197 static bool native_close(void* handle) noexcept {
198#if defined(_WIN32)
199 return ::FreeLibrary(reinterpret_cast<HMODULE>(handle)) != 0;
200#else
201 return ::dlclose(handle) == 0;
202#endif
203 }
204
205 void destroy() noexcept {
206 if (handle_ && policy_ == LeakPolicy::CloseOnDestroy) {
207 native_close(handle_);
208 }
209 // LeakPolicy::Retain: intentionally leak the image for the process
210 // lifetime (see file header). Drop our reference without unloading.
211 handle_ = nullptr;
212 }
213
214 void move_from(ReloadLibrary& other) noexcept {
215 handle_ = other.handle_;
216 path_ = std::move(other.path_);
217 error_ = std::move(other.error_);
218 policy_ = other.policy_;
219 other.handle_ = nullptr; // moved-from never unloads
220 }
221
222 void* handle_ = nullptr;
223 std::string path_;
224 std::string error_;
226};
227
228} // namespace pulp::format::reload
ReloadLibrary(ReloadLibrary &&other) noexcept
Definition reload_library.hpp:122
ReloadLibrary & operator=(const ReloadLibrary &)=delete
bool close() noexcept
Explicitly unload now, regardless of policy.
Definition reload_library.hpp:167
ReloadLibrary & operator=(ReloadLibrary &&other) noexcept
Definition reload_library.hpp:123
ReloadLibrary(const std::string &path, LeakPolicy policy=LeakPolicy::Retain)
Load path.
Definition reload_library.hpp:115
const std::string & error() const noexcept
Definition reload_library.hpp:138
ReloadLibrary(const ReloadLibrary &)=delete
void * raw_symbol(const char *name) const noexcept
Resolve a symbol. Returns nullptr if absent or the image isn't loaded.
Definition reload_library.hpp:148
LeakPolicy leak_policy() const noexcept
Definition reload_library.hpp:142
const std::string & path() const noexcept
Definition reload_library.hpp:139
void * native_handle() const noexcept
Definition reload_library.hpp:140
void set_leak_policy(LeakPolicy policy) noexcept
Promote/demote the unload decision after construction (e.g.
Definition reload_library.hpp:145
T symbol(const char *name) const noexcept
Typed convenience over raw_symbol (typically a factory function pointer).
Definition reload_library.hpp:160
~ReloadLibrary()
Definition reload_library.hpp:133
bool valid() const noexcept
Definition reload_library.hpp:135
Definition build_fingerprint.hpp:23
void promote_host_symbols_once()
Promote the HOST image's symbols to global loader scope, once per process
Definition reload_library.hpp:74
LeakPolicy
What the handle does with the loaded image when it is destroyed.
Definition reload_library.hpp:104
@ CloseOnDestroy
dlclose on destruction. Only when the image is provably quiesced.
Definition reload_library.hpp:106
@ Retain
Never unload (default). Safe for live reload — see file header.
Definition reload_library.hpp:105
@ ok
Definition offline_sample_bounce.hpp:20
@ other
Definition udev_monitor.hpp:26