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 }