Lua: Don't lua_error() out of context with pending dtors
[lsnes.git] / src / library / loadlib.cpp
blob6075ea5b4ca695bf5292cdc6c7a481c6d3a8c925
1 #include "loadlib.hpp"
2 #include <sstream>
3 #include <list>
4 #include <set>
6 #if !defined(NO_DLFCN) && !defined(_WIN32) && !defined(_WIN64)
7 #include <dlfcn.h>
8 #include <unistd.h>
9 #endif
11 namespace loadlib
13 threads::lock& global_mutex()
15 static threads::lock m;
16 return m;
19 namespace
21 thread_local library* currently_loading;
22 #if defined(_WIN32) || defined(_WIN64)
23 std::string callsign = "dynamic link library";
24 std::string callsign_ext = "dll";
25 #elif !defined(NO_DLFCN)
26 #if defined(__APPLE__)
27 std::string callsign = "dynamic library";
28 std::string callsign_ext = "bundle";
29 #else
30 std::string callsign = "shared object";
31 std::string callsign_ext = "so";
32 #endif
33 #else
34 std::string callsign = "";
35 std::string callsign_ext = "";
36 #endif
39 library::internal::internal(const std::string& filename) throw(std::bad_alloc, std::runtime_error)
41 libname = filename;
42 refs = 1;
43 #if !defined(NO_DLFCN) && !defined(_WIN32) && !defined(_WIN64)
44 char buffer[16384];
45 getcwd(buffer, 16383);
46 std::string _filename = filename;
47 if(filename.find_first_of("/") >= filename.length())
48 _filename = buffer + std::string("/") + filename;
49 handle = dlopen(_filename.c_str(), RTLD_LOCAL | RTLD_NOW);
50 if(!handle)
51 throw std::runtime_error(dlerror());
52 #elif defined(_WIN32) || defined(_WIN64)
53 char buffer[16384];
54 GetCurrentDirectory(16383, buffer);
55 std::string _filename = filename;
56 if(filename.find_first_of("/\\") >= filename.length())
57 _filename = buffer + std::string("/") + filename;
58 handle = LoadLibraryA(_filename.c_str());
59 if(!handle) {
60 int errcode = GetLastError();
61 char errorbuffer[1024];
62 if(FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM, NULL, errcode, 0,
63 errorbuffer, sizeof(errorbuffer), NULL))
64 throw std::runtime_error(errorbuffer);
65 else {
66 std::ostringstream str;
67 str << "Unknown system error (code " << errcode << ")";
68 throw std::runtime_error(str.str());
71 #else
72 throw std::runtime_error("Loading libraries is not supported");
73 #endif
76 library::internal::~internal() throw()
78 #if !defined(NO_DLFCN) && !defined(_WIN32) && !defined(_WIN64)
79 dlclose(handle);
80 #elif defined(_WIN32) || defined(_WIN64)
81 FreeLibrary(handle);
82 #endif
85 void* library::internal::operator[](const std::string& symbol) const throw(std::bad_alloc, std::runtime_error)
87 #if !defined(NO_DLFCN) && !defined(_WIN32) && !defined(_WIN64)
88 dlerror();
89 void* s = dlsym(handle, symbol.c_str());
90 if(s)
91 return s;
92 char* e = dlerror();
93 if(e)
94 throw std::runtime_error(e);
95 return NULL; //Yes, real NULL symbol.
96 #elif defined(_WIN32) || defined(_WIN64)
97 void* s = (void*)GetProcAddress(handle, symbol.c_str());
98 if(s)
99 return s;
100 int errcode = GetLastError();
101 char errorbuffer[1024];
102 if(FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM, NULL, errcode, 0,
103 errorbuffer, sizeof(errorbuffer), NULL))
104 throw std::runtime_error(errorbuffer);
105 else {
106 std::ostringstream str;
107 str << "Unknown system error (code " << errcode << ")";
108 throw std::runtime_error(str.str());
110 #else
111 throw std::runtime_error("Library loading not supported");
112 #endif
115 const std::string& library::name() throw()
117 return callsign;
120 const std::string& library::extension() throw()
122 return callsign_ext;
125 library* library::loading() throw() { return currently_loading; }
126 void library::set_loading(library* lib) throw(std::bad_alloc) { currently_loading = lib; }
128 namespace
130 std::list<loadlib::module*>& module_queue()
132 static std::list<module*> x;
133 return x;
137 module::module(std::initializer_list<symbol> _symbols, std::function<void(const module&)> init_fn)
139 dynamic = false;
140 for(auto i : _symbols)
141 symbols[i.name] = i.address;
142 init = init_fn;
143 if(init) {
144 threads::alock h(global_mutex());
145 module_queue().push_back(this);
147 libname = "<anonymous module inside executable>";
150 module::module(library _lib)
152 dynamic = true;
153 lib = _lib;
154 libname = _lib.get_libname();
157 module::~module()
159 threads::alock h(global_mutex());
160 for(auto i = module_queue().begin(); i != module_queue().end(); i++) {
161 if(*i == this) {
162 module_queue().erase(i);
163 break;
168 module::module(const module& mod)
170 dynamic = mod.dynamic;
171 lib = mod.lib;
172 symbols = mod.symbols;
173 init = mod.init;
174 libname = mod.libname;
175 if(init) {
176 threads::alock h(global_mutex());
177 module_queue().push_back(this);
181 void* module::operator[](const std::string& symbol) const throw(std::bad_alloc, std::runtime_error)
183 if(dynamic)
184 return lib[symbol];
185 else if(symbols.count(symbol))
186 return symbols.find(symbol)->second;
187 else
188 throw std::runtime_error("Symbol '" + symbol + "' not found");
191 void module::run_initializers()
193 for(auto i : module_queue())
194 if(i->init) {
195 i->init(*i);
196 i->init = std::function<void(const module&)>();
198 module_queue().clear();