Warning, file /sdk/codevis/thirdparty/soci/src/core/backend-loader.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 // 0002 // Copyright (C) 2008 Maciej Sobczak with contributions from Artyom Tonkikh 0003 // Distributed under the Boost Software License, Version 1.0. 0004 // (See accompanying file LICENSE_1_0.txt or copy at 0005 // http://www.boost.org/LICENSE_1_0.txt) 0006 // 0007 0008 #define SOCI_SOURCE 0009 #include "soci/soci-platform.h" 0010 #include "soci/backend-loader.h" 0011 #include "soci/error.h" 0012 #include <cstdlib> 0013 #include <map> 0014 #include <string> 0015 #include <vector> 0016 #ifndef _MSC_VER 0017 #include <stdint.h> 0018 #endif 0019 0020 #include "soci_backends_config.h" 0021 0022 using namespace soci; 0023 using namespace soci::dynamic_backends; 0024 0025 #ifdef _WIN32 0026 0027 #include <windows.h> 0028 0029 typedef CRITICAL_SECTION soci_mutex_t; 0030 typedef HMODULE soci_handler_t; 0031 0032 #define LOCK(x) EnterCriticalSection(x) 0033 #define UNLOCK(x) LeaveCriticalSection(x) 0034 #define MUTEX_INIT(x) InitializeCriticalSection(x) 0035 #define MUTEX_DEST(x) DeleteCriticalSection(x) 0036 #ifdef _UNICODE 0037 #define DLOPEN(x) LoadLibraryA(x) 0038 #else 0039 #define DLOPEN(x) LoadLibrary(x) 0040 #endif 0041 #define DLCLOSE(x) FreeLibrary(x) 0042 #define DLSYM(x, y) GetProcAddress(x, y) 0043 0044 #ifdef SOCI_ABI_VERSION 0045 #ifndef NDEBUG 0046 #define LIBNAME(x) (SOCI_LIB_PREFIX + x + "_" SOCI_ABI_VERSION SOCI_DEBUG_POSTFIX SOCI_LIB_SUFFIX) 0047 #else 0048 #define LIBNAME(x) (SOCI_LIB_PREFIX + x + "_" SOCI_ABI_VERSION SOCI_LIB_SUFFIX) 0049 #endif 0050 #else 0051 #define LIBNAME(x) (SOCI_LIB_PREFIX + x + SOCI_LIB_SUFFIX) 0052 #endif // SOCI_ABI_VERSION 0053 0054 #else 0055 0056 #include <pthread.h> 0057 #include <dlfcn.h> 0058 0059 typedef pthread_mutex_t soci_mutex_t; 0060 typedef void * soci_handler_t; 0061 0062 #define LOCK(x) pthread_mutex_lock(x) 0063 #define UNLOCK(x) pthread_mutex_unlock(x) 0064 #define MUTEX_INIT(x) pthread_mutex_init(x, NULL) 0065 #define MUTEX_DEST(x) pthread_mutex_destroy(x) 0066 #define DLOPEN(x) dlopen(x, RTLD_LAZY) 0067 #define DLCLOSE(x) dlclose(x) 0068 #define DLSYM(x, y) dlsym(x, y) 0069 0070 #ifdef SOCI_ABI_VERSION 0071 0072 #ifdef __APPLE__ 0073 #define LIBNAME(x) (SOCI_LIB_PREFIX + x + "." SOCI_ABI_VERSION SOCI_LIB_SUFFIX) 0074 #else 0075 #define LIBNAME(x) (SOCI_LIB_PREFIX + x + SOCI_LIB_SUFFIX "." SOCI_ABI_VERSION) 0076 #endif 0077 0078 #else 0079 #define LIBNAME(x) (SOCI_LIB_PREFIX + x + SOCI_LIB_SUFFIX) 0080 #endif // SOCI_ABI_VERSION 0081 0082 #endif // _WIN32 0083 0084 0085 namespace // unnamed 0086 { 0087 0088 struct info 0089 { 0090 soci_handler_t handler_; 0091 backend_factory const * factory_; 0092 0093 // The use count is the number of existing sessions using this backend (in 0094 // fact it's the count of connection_parameters objects, but as these 0095 // objects are part of the session, it's roughly the same thing). 0096 // 0097 // While use count is non-zero, the corresponding backend can't be unloaded 0098 // as this would leave the code using it with dangling (code) pointers. If 0099 // it reaches 0, the backend is _not_ unloaded automatically because we 0100 // don't want to unload/reload it all the time when recreating sessions, 0101 // but it can be unloaded manually, if necessary, by calling unload() or 0102 // unload_all() functions. 0103 int use_count_; 0104 0105 // If unloading this backend is requested while its use count is non-zero, 0106 // this flag is set to true and the backend is actually unloaded when the 0107 // use count drops to 0. 0108 bool unload_requested_; 0109 0110 info() : handler_(0), factory_(0), use_count_(0), unload_requested_(false) {} 0111 }; 0112 0113 typedef std::map<std::string, info> factory_map; 0114 factory_map factories_; 0115 0116 std::vector<std::string> search_paths_; 0117 0118 soci_mutex_t mutex_; 0119 0120 std::vector<std::string> get_default_paths() 0121 { 0122 std::vector<std::string> paths; 0123 0124 char const* const penv = std::getenv("SOCI_BACKENDS_PATH"); 0125 std::string const env(penv ? penv : ""); 0126 if (env.empty()) 0127 { 0128 paths.push_back("."); 0129 #ifdef DEFAULT_BACKENDS_PATH 0130 paths.push_back(DEFAULT_BACKENDS_PATH); 0131 #endif // DEFAULT_BACKENDS_PATH 0132 return paths; 0133 } 0134 0135 std::string::size_type searchFrom = 0; 0136 while (searchFrom != env.size()) 0137 { 0138 std::string::size_type const found = env.find(":", searchFrom); 0139 if (found == searchFrom) 0140 { 0141 ++searchFrom; 0142 } 0143 else if (std::string::npos != found) 0144 { 0145 std::string const path(env.substr(searchFrom, found - searchFrom)); 0146 paths.push_back(path); 0147 0148 searchFrom = found + 1; 0149 } 0150 else // found == npos 0151 { 0152 std::string const path = env.substr(searchFrom); 0153 paths.push_back(path); 0154 0155 searchFrom = env.size(); 0156 } 0157 } 0158 0159 return paths; 0160 } 0161 0162 // used to automatically initialize the global state 0163 struct static_state_mgr 0164 { 0165 static_state_mgr() 0166 { 0167 MUTEX_INIT(&mutex_); 0168 0169 search_paths_ = get_default_paths(); 0170 } 0171 0172 ~static_state_mgr() 0173 { 0174 unload_all(); 0175 0176 MUTEX_DEST(&mutex_); 0177 } 0178 } static_state_mgr_; 0179 0180 class scoped_lock 0181 { 0182 public: 0183 scoped_lock(soci_mutex_t * m) : mptr(m) { LOCK(m); }; 0184 ~scoped_lock() { UNLOCK(mptr); }; 0185 private: 0186 soci_mutex_t * mptr; 0187 }; 0188 0189 // non-synchronized helpers for the other functions 0190 factory_map::iterator do_unload(factory_map::iterator i) 0191 { 0192 soci_handler_t h = i->second.handler_; 0193 if (h != NULL) 0194 { 0195 DLCLOSE(h); 0196 } 0197 0198 // TODO-C++11: Use erase() return value. 0199 factory_map::iterator const to_erase = i; 0200 ++i; 0201 factories_.erase(to_erase); 0202 return i; 0203 } 0204 0205 void do_unload_or_throw_if_in_use(std::string const & name) 0206 { 0207 factory_map::iterator i = factories_.find(name); 0208 0209 if (i != factories_.end()) 0210 { 0211 if (i->second.use_count_) 0212 { 0213 throw soci_error("Backend " + name + " is used and can't be unloaded"); 0214 } 0215 0216 do_unload(i); 0217 } 0218 } 0219 0220 // non-synchronized helper 0221 void do_register_backend(std::string const & name, std::string const & shared_object) 0222 { 0223 do_unload_or_throw_if_in_use(name); 0224 0225 // The rules for backend search are as follows: 0226 // - if the shared_object is given, 0227 // it names the library file and the search paths are not used 0228 // - otherwise (shared_object not provided or empty): 0229 // - file named libsoci_NAME.so.SOVERSION is searched in the list of search paths 0230 0231 soci_handler_t h = 0; 0232 if (shared_object.empty() == false) 0233 { 0234 h = DLOPEN(shared_object.c_str()); 0235 } 0236 else 0237 { 0238 // try system paths 0239 h = DLOPEN(LIBNAME(name).c_str()); 0240 if (0 == h) 0241 { 0242 // try all search paths 0243 for (std::size_t i = 0; i != search_paths_.size(); ++i) 0244 { 0245 std::string const fullFileName(search_paths_[i] + "/" + LIBNAME(name)); 0246 h = DLOPEN(fullFileName.c_str()); 0247 if (0 != h) 0248 { 0249 // already found 0250 break; 0251 } 0252 } 0253 } 0254 } 0255 0256 if (0 == h) 0257 { 0258 throw soci_error("Failed to find shared library for backend " + name); 0259 } 0260 0261 std::string symbol = "factory_" + name; 0262 0263 typedef backend_factory const * bfc_ptr; 0264 typedef bfc_ptr (*get_t)(void); 0265 get_t entry; 0266 entry = reinterpret_cast<get_t>( 0267 reinterpret_cast<uintptr_t>(DLSYM(h, symbol.c_str()))); 0268 0269 if (0 == entry) 0270 { 0271 DLCLOSE(h); 0272 throw soci_error("Failed to resolve dynamic symbol: " + symbol); 0273 } 0274 0275 backend_factory const* f = entry(); 0276 0277 info new_entry; 0278 new_entry.factory_ = f; 0279 new_entry.handler_ = h; 0280 0281 factories_[name] = new_entry; 0282 } 0283 0284 } // unnamed namespace 0285 0286 backend_factory const& dynamic_backends::get(std::string const& name) 0287 { 0288 scoped_lock lock(&mutex_); 0289 0290 factory_map::iterator i = factories_.find(name); 0291 0292 if (i == factories_.end()) 0293 { 0294 // no backend found with this name, try to register it first 0295 0296 do_register_backend(name, std::string()); 0297 0298 // second attempt, must succeed (the backend is already loaded) 0299 0300 i = factories_.find(name); 0301 } 0302 0303 i->second.use_count_++; 0304 0305 return *(i->second.factory_); 0306 } 0307 0308 void dynamic_backends::unget(std::string const& name) 0309 { 0310 scoped_lock lock(&mutex_); 0311 0312 factory_map::iterator i = factories_.find(name); 0313 0314 if (i == factories_.end()) 0315 { 0316 // We don't throw here as this is often called from destructors, and so 0317 // this would result in a call to std::terminate(), even if this is 0318 // totally unexpected -- but, unfortunately, we have no way to report 0319 // it to the application without possibly killing it. 0320 return; 0321 } 0322 0323 info& backend_info = i->second; 0324 0325 --backend_info.use_count_; 0326 0327 // Check if this backend should be unloaded if unloading it had been 0328 // previously requested. 0329 if (backend_info.use_count_ == 0 && backend_info.unload_requested_) 0330 { 0331 do_unload(i); 0332 } 0333 } 0334 0335 SOCI_DECL std::vector<std::string>& dynamic_backends::search_paths() 0336 { 0337 return search_paths_; 0338 } 0339 0340 SOCI_DECL void dynamic_backends::register_backend( 0341 std::string const& name, std::string const& shared_object) 0342 { 0343 scoped_lock lock(&mutex_); 0344 0345 do_register_backend(name, shared_object); 0346 } 0347 0348 SOCI_DECL void dynamic_backends::register_backend( 0349 std::string const& name, backend_factory const& factory) 0350 { 0351 scoped_lock lock(&mutex_); 0352 0353 do_unload_or_throw_if_in_use(name); 0354 0355 info new_entry; 0356 new_entry.factory_ = &factory; 0357 0358 factories_[name] = new_entry; 0359 } 0360 0361 SOCI_DECL std::vector<std::string> dynamic_backends::list_all() 0362 { 0363 scoped_lock lock(&mutex_); 0364 0365 std::vector<std::string> ret; 0366 ret.reserve(factories_.size()); 0367 0368 for (factory_map::iterator i = factories_.begin(); i != factories_.end(); ++i) 0369 { 0370 std::string const& name = i->first; 0371 ret.push_back(name); 0372 } 0373 0374 return ret; 0375 } 0376 0377 SOCI_DECL void dynamic_backends::unload(std::string const& name) 0378 { 0379 scoped_lock lock(&mutex_); 0380 0381 factory_map::iterator i = factories_.find(name); 0382 0383 if (i != factories_.end()) 0384 { 0385 info& backend_info = i->second; 0386 if (backend_info.use_count_) 0387 { 0388 // We can't unload the backend while it's in use, so do it later 0389 // when it's not used any longer. 0390 backend_info.unload_requested_ = true; 0391 return; 0392 } 0393 0394 do_unload(i); 0395 } 0396 } 0397 0398 SOCI_DECL void dynamic_backends::unload_all() 0399 { 0400 scoped_lock lock(&mutex_); 0401 0402 for (factory_map::iterator i = factories_.begin(); i != factories_.end(); ) 0403 { 0404 info& backend_info = i->second; 0405 0406 // Same logic as in unload() above. 0407 if (backend_info.use_count_) 0408 { 0409 backend_info.unload_requested_ = true; 0410 ++i; 0411 continue; 0412 } 0413 0414 i = do_unload(i); 0415 } 0416 }