backend-loader.cpp 7.77 KB
Newer Older
msobczak's avatar
msobczak committed
1 2 3 4 5 6 7
//
// Copyright (C) 2008 Maciej Sobczak with contributions from Artyom Tonkikh
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//

8 9 10
#define SOCI_SOURCE
#include "backend-loader.h"
#include "error.h"
msobczak's avatar
msobczak committed
11
#include <cassert>
msobczak's avatar
msobczak committed
12
#include <cstdlib>
13 14 15
#include <map>
#include <string>
#include <vector>
16
#ifndef _MSC_VER
Maciej Sobczak's avatar
Maciej Sobczak committed
17
#include <stdint.h>
18
#endif
19

20 21
#include "soci_backends_config.h"

22 23 24 25 26 27 28 29 30 31
using namespace soci;
using namespace soci::dynamic_backends;

#ifdef _WIN32

#include <windows.h>

typedef CRITICAL_SECTION soci_mutex_t;
typedef HMODULE soci_handler_t;

msobczak's avatar
msobczak committed
32
#define LOCK(x) EnterCriticalSection(x)
33 34 35
#define UNLOCK(x) LeaveCriticalSection(x)
#define MUTEX_INIT(x) InitializeCriticalSection(x)
#define MUTEX_DEST(x) DeleteCriticalSection(x)
36 37 38
#ifdef _UNICODE
#define DLOPEN(x) LoadLibraryA(x)
#else
39
#define DLOPEN(x) LoadLibrary(x)
40
#endif
41
#define DLCLOSE(x) FreeLibrary(x)
msobczak's avatar
msobczak committed
42
#define DLSYM(x, y) GetProcAddress(x, y)
43 44

#ifdef SOCI_ABI_VERSION
45
#define LIBNAME(x) (SOCI_LIB_PREFIX + x + "_" SOCI_ABI_VERSION SOCI_LIB_SUFFIX)
46 47 48
#else
#define LIBNAME(x) (SOCI_LIB_PREFIX + x + SOCI_LIB_SUFFIX)
#endif // SOCI_ABI_VERSION
49 50 51 52 53 54 55 56 57

#else

#include <pthread.h>
#include <dlfcn.h>

typedef pthread_mutex_t soci_mutex_t;
typedef void * soci_handler_t;

msobczak's avatar
msobczak committed
58
#define LOCK(x) pthread_mutex_lock(x)
59 60 61 62 63
#define UNLOCK(x) pthread_mutex_unlock(x)
#define MUTEX_INIT(x) pthread_mutex_init(x, NULL)
#define MUTEX_DEST(x) pthread_mutex_destroy(x)
#define DLOPEN(x) dlopen(x, RTLD_LAZY)
#define DLCLOSE(x) dlclose(x)
msobczak's avatar
msobczak committed
64
#define DLSYM(x, y) dlsym(x, y)
65 66 67

#ifdef SOCI_ABI_VERSION

68 69 70 71 72
#ifdef __APPLE__
#define LIBNAME(x) (SOCI_LIB_PREFIX + x + "." SOCI_ABI_VERSION SOCI_LIB_SUFFIX)
#else
#define LIBNAME(x) (SOCI_LIB_PREFIX + x + SOCI_LIB_SUFFIX "." SOCI_ABI_VERSION)
#endif
73

74 75 76 77
#else
#define LIBNAME(x) (SOCI_LIB_PREFIX + x + SOCI_LIB_SUFFIX)
#endif // SOCI_ABI_VERSION

78 79 80 81 82 83
#endif // _WIN32


namespace // unnamed
{

msobczak's avatar
msobczak committed
84 85 86 87
struct info
{
    soci_handler_t handler_;
    backend_factory const * factory_;
88
    info() : handler_(0), factory_(0) {}
msobczak's avatar
msobczak committed
89 90
};

msobczak's avatar
msobczak committed
91
typedef std::map<std::string, info> factory_map;
msobczak's avatar
msobczak committed
92 93
factory_map factories_;

msobczak's avatar
msobczak committed
94 95
std::vector<std::string> search_paths_;

96 97
soci_mutex_t mutex_;

msobczak's avatar
msobczak committed
98 99 100 101
std::vector<std::string> get_default_paths()
{
    std::vector<std::string> paths;

102 103 104
    // TODO: may be problem with finding getenv in std namespace in Visual C++ --mloskot
    char const* const penv = std::getenv("SOCI_BACKENDS_PATH");
    if (0 == penv)
msobczak's avatar
msobczak committed
105
    {
106
        paths.push_back(".");
107
        paths.push_back(DEFAULT_BACKENDS_PATH);
msobczak's avatar
msobczak committed
108 109 110 111 112 113
        return paths;
    }

    std::string const env = penv;
    if (env.empty())
    {
114
        paths.push_back(".");
115
        paths.push_back(DEFAULT_BACKENDS_PATH);
msobczak's avatar
msobczak committed
116 117 118 119 120 121 122 123 124 125 126
        return paths;
    }

    std::string::size_type searchFrom = 0;
    while (searchFrom != env.size())
    {
        std::string::size_type const found = env.find(":", searchFrom);
        if (found == searchFrom)
        {
            ++searchFrom;
        }
127
        else if (std::string::npos != found)
msobczak's avatar
msobczak committed
128
        {
129
            std::string const path(env.substr(searchFrom, found - searchFrom));
msobczak's avatar
msobczak committed
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
            paths.push_back(path);

            searchFrom = found + 1;
        }
        else // found == npos
        {
            std::string const path = env.substr(searchFrom);
            paths.push_back(path);

            searchFrom = env.size();
        }
    }

    return paths;
}

// used to automatically initialize the global state
struct static_state_mgr
148
{
msobczak's avatar
msobczak committed
149
    static_state_mgr()
150
    {
msobczak's avatar
msobczak committed
151
        MUTEX_INIT(&mutex_);
msobczak's avatar
msobczak committed
152 153

        search_paths_ = get_default_paths();
154 155
    }

msobczak's avatar
msobczak committed
156
    ~static_state_mgr()
157
    {
msobczak's avatar
msobczak committed
158 159
        unload_all();

msobczak's avatar
msobczak committed
160
        MUTEX_DEST(&mutex_);
161
    }
msobczak's avatar
msobczak committed
162
} static_state_mgr_;
163 164 165 166 167 168 169 170 171 172 173

class scoped_lock
{
public:
    scoped_lock(soci_mutex_t * m) : mptr(m) { LOCK(m); };
    ~scoped_lock() { UNLOCK(mptr); };
private:
    soci_mutex_t * mptr;
};

// non-synchronized helper for the other functions
msobczak's avatar
msobczak committed
174
void do_unload(std::string const & name)
175 176 177 178 179
{
    factory_map::iterator i = factories_.find(name);

    if (i != factories_.end())
    {
msobczak's avatar
msobczak committed
180 181 182 183 184
        soci_handler_t h = i->second.handler_;
        if (h != NULL)
        {
            DLCLOSE(h);
        }
185

msobczak's avatar
msobczak committed
186
        factories_.erase(i);
187 188 189 190
    }
}

// non-synchronized helper
191
void do_register_backend(std::string const & name, std::string const & shared_object)
192
{
msobczak's avatar
msobczak committed
193 194 195 196
    // The rules for backend search are as follows:
    // - if the shared_object is given,
    //   it names the library file and the search paths are not used
    // - otherwise (shared_object not provided or empty):
197
    //   - file named libsoci_NAME.so.SOVERSION is searched in the list of search paths
msobczak's avatar
msobczak committed
198

199
    soci_handler_t h = 0;
msobczak's avatar
msobczak committed
200 201 202 203 204 205
    if (shared_object.empty() == false)
    {
        h = DLOPEN(shared_object.c_str());
    }
    else
    {
206 207 208
        // try system paths
        h = DLOPEN(LIBNAME(name).c_str());
        if (0 == h)
msobczak's avatar
msobczak committed
209
        {
210 211
            // try all search paths
            for (std::size_t i = 0; i != search_paths_.size(); ++i)
msobczak's avatar
msobczak committed
212
            {
213 214 215 216 217 218 219 220 221
                std::string const fullFileName(search_paths_[i] + "/" + LIBNAME(name));
                h = DLOPEN(fullFileName.c_str());
                if (0 != h)
                {
                    // already found
                    break;
                }
             }
         }
msobczak's avatar
msobczak committed
222
    }
223

224
    if (0 == h)
225
    {
msobczak's avatar
msobczak committed
226
        throw soci_error("Failed to find shared library for backend " + name);
227 228 229 230 231 232 233
    }

    std::string symbol = "factory_" + name;

    typedef backend_factory const * bfc_ptr;
    typedef bfc_ptr (*get_t)(void);
    get_t entry;
234 235
    entry = reinterpret_cast<get_t>(
            reinterpret_cast<uintptr_t>(DLSYM(h, symbol.c_str())));
236

237
    if (0 == entry)
238
    {
msobczak's avatar
msobczak committed
239 240
        DLCLOSE(h);
        throw soci_error("Failed to resolve dynamic symbol: " + symbol);
241 242 243 244 245 246
    }

    // unload the existing handler if it's already loaded

    do_unload(name);
    
247
    backend_factory const* f = entry();
248 249 250 251 252 253 254 255 256 257

    info new_entry;
    new_entry.factory_ = f;
    new_entry.handler_ = h;

    factories_[name] = new_entry;
}

} // unnamed namespace

258
backend_factory const& dynamic_backends::get(std::string const& name)
259 260 261 262 263 264 265
{
    scoped_lock lock(&mutex_);

    factory_map::iterator i = factories_.find(name);

    if (i != factories_.end())
    {
msobczak's avatar
msobczak committed
266
        return *(i->second.factory_);
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
    }

    // no backend found with this name, try to register it first

    do_register_backend(name, std::string());

    // second attempt, must succeed (the backend is already loaded)

    i = factories_.find(name);

    assert(i != factories_.end());

    return *(i->second.factory_);
}

282
SOCI_DECL std::vector<std::string>& search_paths()
msobczak's avatar
msobczak committed
283 284 285 286
{
    return search_paths_;
}

287
SOCI_DECL void dynamic_backends::register_backend(
288
    std::string const& name, std::string const& shared_object)
289 290 291 292 293 294
{
    scoped_lock lock(&mutex_);

    do_register_backend(name, shared_object);
}

295
SOCI_DECL void dynamic_backends::register_backend(
296
    std::string const& name, backend_factory const& factory)
297 298 299 300 301 302 303 304 305 306 307 308 309
{
    scoped_lock lock(&mutex_);

    // unload the existing handler if it's already loaded

    do_unload(name);
    
    info new_entry;
    new_entry.factory_ = &factory;

    factories_[name] = new_entry;
}

310
SOCI_DECL std::vector<std::string> dynamic_backends::list_all()
311 312 313 314 315 316
{
    scoped_lock lock(&mutex_);

    std::vector<std::string> ret;
    ret.reserve(factories_.size());

317
    for (factory_map::iterator i = factories_.begin(); i != factories_.end(); ++i)
318
    {
319
        std::string const& name = i->first;
msobczak's avatar
msobczak committed
320
        ret.push_back(name);
321 322 323 324 325
    }

    return ret;
}

326
SOCI_DECL void dynamic_backends::unload(std::string const& name)
327 328 329 330 331 332
{
    scoped_lock lock(&mutex_);

    do_unload(name);
}

333
SOCI_DECL void dynamic_backends::unload_all()
334 335 336
{
    scoped_lock lock(&mutex_);

337
    for (factory_map::iterator i = factories_.begin(); i != factories_.end(); ++i)
338
    {
msobczak's avatar
msobczak committed
339
        soci_handler_t h = i->second.handler_;
340
        if (0 != h)
msobczak's avatar
msobczak committed
341 342 343
        {
            DLCLOSE(h);
        }
344 345 346 347
    }

    factories_.clear();
}