00001 // 00002 // Copyright (C) 2008 Maciej Sobczak with contributions from Artyom Tonkikh 00003 // Distributed under the Boost Software License, Version 1.0. 00004 // (See accompanying file LICENSE_1_0.txt or copy at 00005 // http://www.boost.org/LICENSE_1_0.txt) 00006 // 00007 00008 #define SOCI_SOURCE 00009 #include "backend-loader.h" 00010 #include "error.h" 00011 #include <map> 00012 #include <cassert> 00013 #include <cstdlib> 00014 //#include <cstddef> 00015 //#include <cstdio> 00016 #ifndef _MSC_VER 00017 #include <stdint.h> 00018 #endif 00019 00020 using namespace soci; 00021 using namespace soci::dynamic_backends; 00022 00023 #ifdef _WIN32 00024 00025 #include <windows.h> 00026 00027 typedef CRITICAL_SECTION soci_mutex_t; 00028 typedef HMODULE soci_handler_t; 00029 00030 #define LOCK(x) EnterCriticalSection(x) 00031 #define UNLOCK(x) LeaveCriticalSection(x) 00032 #define MUTEX_INIT(x) InitializeCriticalSection(x) 00033 #define MUTEX_DEST(x) DeleteCriticalSection(x) 00034 #define DLOPEN(x) LoadLibrary(x) 00035 #define DLCLOSE(x) FreeLibrary(x) 00036 #define DLSYM(x, y) GetProcAddress(x, y) 00037 #define LIBNAME(x) ("libsoci_" + x + ".dll") 00038 00039 #else 00040 00041 #include <pthread.h> 00042 #include <dlfcn.h> 00043 00044 typedef pthread_mutex_t soci_mutex_t; 00045 typedef void * soci_handler_t; 00046 00047 #define LOCK(x) pthread_mutex_lock(x) 00048 #define UNLOCK(x) pthread_mutex_unlock(x) 00049 #define MUTEX_INIT(x) pthread_mutex_init(x, NULL) 00050 #define MUTEX_DEST(x) pthread_mutex_destroy(x) 00051 #define DLOPEN(x) dlopen(x, RTLD_LAZY) 00052 #define DLCLOSE(x) dlclose(x) 00053 #define DLSYM(x, y) dlsym(x, y) 00054 #define LIBNAME(x) ("libsoci_" + x + ".so") 00055 00056 #endif // _WIN32 00057 00058 00059 namespace // unnamed 00060 { 00061 00062 struct info 00063 { 00064 soci_handler_t handler_; 00065 backend_factory const * factory_; 00066 info() : handler_(NULL), factory_(NULL) {} 00067 }; 00068 00069 typedef std::map<std::string, info> factory_map; 00070 factory_map factories_; 00071 00072 std::vector<std::string> search_paths_; 00073 00074 soci_mutex_t mutex_; 00075 00076 std::vector<std::string> get_default_paths() 00077 { 00078 std::vector<std::string> paths; 00079 00080 char const * const penv = std::getenv("SOCI_BACKENDS_PATH"); 00081 if (penv == NULL) 00082 { 00083 paths.push_back("."); 00084 return paths; 00085 } 00086 00087 std::string const env = penv; 00088 if (env.empty()) 00089 { 00090 paths.push_back("."); 00091 return paths; 00092 } 00093 00094 std::string::size_type searchFrom = 0; 00095 while (searchFrom != env.size()) 00096 { 00097 std::string::size_type const found = env.find(":", searchFrom); 00098 if (found == searchFrom) 00099 { 00100 ++searchFrom; 00101 } 00102 else if (found != std::string::npos) 00103 { 00104 std::string const path = env.substr(searchFrom, found - searchFrom); 00105 paths.push_back(path); 00106 00107 searchFrom = found + 1; 00108 } 00109 else // found == npos 00110 { 00111 std::string const path = env.substr(searchFrom); 00112 paths.push_back(path); 00113 00114 searchFrom = env.size(); 00115 } 00116 } 00117 00118 return paths; 00119 } 00120 00121 // used to automatically initialize the global state 00122 struct static_state_mgr 00123 { 00124 static_state_mgr() 00125 { 00126 MUTEX_INIT(&mutex_); 00127 00128 search_paths_ = get_default_paths(); 00129 } 00130 00131 ~static_state_mgr() 00132 { 00133 unload_all(); 00134 00135 MUTEX_DEST(&mutex_); 00136 } 00137 } static_state_mgr_; 00138 00139 class scoped_lock 00140 { 00141 public: 00142 scoped_lock(soci_mutex_t * m) : mptr(m) { LOCK(m); }; 00143 ~scoped_lock() { UNLOCK(mptr); }; 00144 private: 00145 soci_mutex_t * mptr; 00146 }; 00147 00148 // non-synchronized helper for the other functions 00149 void do_unload(std::string const & name) 00150 { 00151 factory_map::iterator i = factories_.find(name); 00152 00153 if (i != factories_.end()) 00154 { 00155 soci_handler_t h = i->second.handler_; 00156 if (h != NULL) 00157 { 00158 DLCLOSE(h); 00159 } 00160 00161 factories_.erase(i); 00162 } 00163 } 00164 00165 // non-synchronized helper 00166 void do_register_backend( 00167 std::string const & name, std::string const & shared_object) 00168 { 00169 // The rules for backend search are as follows: 00170 // - if the shared_object is given, 00171 // it names the library file and the search paths are not used 00172 // - otherwise (shared_object not provided or empty): 00173 // - file named libsoci_NAME.so is searched in the list of search paths 00174 00175 soci_handler_t h = NULL; 00176 if (shared_object.empty() == false) 00177 { 00178 h = DLOPEN(shared_object.c_str()); 00179 } 00180 else 00181 { 00182 // try all search paths 00183 for (std::size_t i = 0; i != search_paths_.size(); ++i) 00184 { 00185 std::string const fullFileName = search_paths_[i] + "/" + LIBNAME(name); 00186 h = DLOPEN(fullFileName.c_str()); 00187 if (h != NULL) 00188 { 00189 // already found 00190 break; 00191 } 00192 } 00193 } 00194 00195 if (h == NULL) 00196 { 00197 throw soci_error("Failed to find shared library for backend " + name); 00198 } 00199 00200 std::string symbol = "factory_" + name; 00201 00202 typedef backend_factory const * bfc_ptr; 00203 typedef bfc_ptr (*get_t)(void); 00204 get_t entry; 00205 entry = reinterpret_cast<get_t>(reinterpret_cast<uintptr_t>(DLSYM(h, symbol.c_str()))); 00206 00207 if (entry == NULL) 00208 { 00209 DLCLOSE(h); 00210 throw soci_error("Failed to resolve dynamic symbol: " + symbol); 00211 } 00212 00213 // unload the existing handler if it's already loaded 00214 00215 do_unload(name); 00216 00217 backend_factory const * f = entry(); 00218 00219 info new_entry; 00220 new_entry.factory_ = f; 00221 new_entry.handler_ = h; 00222 00223 factories_[name] = new_entry; 00224 } 00225 00226 } // unnamed namespace 00227 00228 backend_factory const & dynamic_backends::get(std::string const & name) 00229 { 00230 scoped_lock lock(&mutex_); 00231 00232 factory_map::iterator i = factories_.find(name); 00233 00234 if (i != factories_.end()) 00235 { 00236 return *(i->second.factory_); 00237 } 00238 00239 // no backend found with this name, try to register it first 00240 00241 do_register_backend(name, std::string()); 00242 00243 // second attempt, must succeed (the backend is already loaded) 00244 00245 i = factories_.find(name); 00246 00247 assert(i != factories_.end()); 00248 00249 return *(i->second.factory_); 00250 } 00251 00252 std::vector<std::string> & search_paths() 00253 { 00254 return search_paths_; 00255 } 00256 00257 void dynamic_backends::register_backend( 00258 std::string const & name, std::string const & shared_object) 00259 { 00260 scoped_lock lock(&mutex_); 00261 00262 do_register_backend(name, shared_object); 00263 } 00264 00265 void dynamic_backends::register_backend( 00266 std::string const & name, backend_factory const & factory) 00267 { 00268 scoped_lock lock(&mutex_); 00269 00270 // unload the existing handler if it's already loaded 00271 00272 do_unload(name); 00273 00274 info new_entry; 00275 new_entry.factory_ = &factory; 00276 00277 factories_[name] = new_entry; 00278 } 00279 00280 std::vector<std::string> dynamic_backends::list_all() 00281 { 00282 scoped_lock lock(&mutex_); 00283 00284 std::vector<std::string> ret; 00285 ret.reserve(factories_.size()); 00286 00287 factory_map::iterator i; 00288 for (i = factories_.begin(); i != factories_.end(); ++i) 00289 { 00290 std::string const & name = i->first; 00291 ret.push_back(name); 00292 } 00293 00294 return ret; 00295 } 00296 00297 void dynamic_backends::unload(std::string const & name) 00298 { 00299 scoped_lock lock(&mutex_); 00300 00301 do_unload(name); 00302 } 00303 00304 void dynamic_backends::unload_all() 00305 { 00306 scoped_lock lock(&mutex_); 00307 00308 factory_map::iterator i; 00309 for (i = factories_.begin(); i != factories_.end(); ++i) 00310 { 00311 soci_handler_t h = i->second.handler_; 00312 if (h != NULL) 00313 { 00314 DLCLOSE(h); 00315 } 00316 } 00317 00318 factories_.clear(); 00319 }
Generated on Sun Oct 3 2010 17:42:17 for EXTRAS-SOCI by Doxygen 1.7.1