Warning, file /sdk/codevis/thirdparty/soci/src/core/connection-pool.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
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/connection-pool.h"
0010 #include "soci/error.h"
0011 #include "soci/session.h"
0012 #include <vector>
0013 #include <utility>
0014 
0015 #ifndef _WIN32
0016 // POSIX implementation
0017 
0018 #include <pthread.h>
0019 #include <sys/time.h>
0020 #include <errno.h>
0021 
0022 using namespace soci;
0023 
0024 struct connection_pool::connection_pool_impl
0025 {
0026     bool find_free(std::size_t & pos)
0027     {
0028         for (std::size_t i = 0; i != sessions_.size(); ++i)
0029         {
0030             if (sessions_[i].first)
0031             {
0032                 pos = i;
0033                 return true;
0034             }
0035         }
0036 
0037         return false;
0038     }
0039 
0040     // by convention, first == true means the entry is free (not used)
0041     std::vector<std::pair<bool, session *> > sessions_;
0042     pthread_mutex_t mtx_;
0043     pthread_cond_t cond_;
0044 };
0045 
0046 connection_pool::connection_pool(std::size_t size)
0047 {
0048     if (size == 0)
0049     {
0050         throw soci_error("Invalid pool size");
0051     }
0052 
0053     pimpl_ = new connection_pool_impl();
0054     pimpl_->sessions_.resize(size);
0055     for (std::size_t i = 0; i != size; ++i)
0056     {
0057         pimpl_->sessions_[i] = std::make_pair(true, new session());
0058     }
0059 
0060     int cc = pthread_mutex_init(&(pimpl_->mtx_), NULL);
0061     if (cc != 0)
0062     {
0063         throw soci_error("Synchronization error");
0064     }
0065 
0066     cc = pthread_cond_init(&(pimpl_->cond_), NULL);
0067     if (cc != 0)
0068     {
0069         throw soci_error("Synchronization error");
0070     }
0071 }
0072 
0073 connection_pool::~connection_pool()
0074 {
0075     for (std::size_t i = 0; i != pimpl_->sessions_.size(); ++i)
0076     {
0077         delete pimpl_->sessions_[i].second;
0078     }
0079 
0080     pthread_mutex_destroy(&(pimpl_->mtx_));
0081     pthread_cond_destroy(&(pimpl_->cond_));
0082 
0083     delete pimpl_;
0084 }
0085 
0086 bool connection_pool::try_lease(std::size_t & pos, int timeout)
0087 {
0088     struct timespec tm;
0089     if (timeout >= 0)
0090     {
0091         // timeout is relative in milliseconds
0092 
0093         struct timeval tmv;
0094         gettimeofday(&tmv, NULL);
0095 
0096         tm.tv_sec = tmv.tv_sec + timeout / 1000;
0097         tm.tv_nsec = tmv.tv_usec * 1000 + (timeout % 1000) * 1000 * 1000;
0098 
0099         if (tm.tv_nsec >= 1000 * 1000 * 1000)
0100         {
0101             ++tm.tv_sec;
0102             tm.tv_nsec -= 1000 * 1000 * 1000;
0103         }
0104     }
0105 
0106     int cc = pthread_mutex_lock(&(pimpl_->mtx_));
0107     if (cc != 0)
0108     {
0109         throw soci_error("Synchronization error");
0110     }
0111 
0112     while (pimpl_->find_free(pos) == false)
0113     {
0114         if (timeout < 0)
0115         {
0116             // no timeout, allow unlimited blocking
0117             cc = pthread_cond_wait(&(pimpl_->cond_), &(pimpl_->mtx_));
0118         }
0119         else
0120         {
0121             // wait with timeout
0122             cc = pthread_cond_timedwait(
0123                 &(pimpl_->cond_), &(pimpl_->mtx_), &tm);
0124         }
0125 
0126         if (cc == ETIMEDOUT)
0127         {
0128             break;
0129         }
0130 
0131         // pthread_cond_timedwait() can apparently return these errors too,
0132         // even if POSIX doesn't document them for the scenario in which we
0133         // call it.
0134         if (cc == EINVAL || cc == EPERM)
0135         {
0136             // We should perhaps throw an exception here, but at the very least
0137             // exit the loop to avoid being stuck in it forever.
0138             break;
0139         }
0140     }
0141 
0142     if (cc == 0)
0143     {
0144         pimpl_->sessions_[pos].first = false;
0145     }
0146 
0147     pthread_mutex_unlock(&(pimpl_->mtx_));
0148 
0149     if (cc != 0)
0150     {
0151         // we can only fail if timeout expired
0152         if (timeout < 0)
0153         {
0154             throw soci_error("Getting connection from the pool unexpectedly failed");
0155         }
0156 
0157         return false;
0158     }
0159 
0160     return true;
0161 }
0162 
0163 void connection_pool::give_back(std::size_t pos)
0164 {
0165     if (pos >= pimpl_->sessions_.size())
0166     {
0167         throw soci_error("Invalid pool position");
0168     }
0169 
0170     int cc = pthread_mutex_lock(&(pimpl_->mtx_));
0171     if (cc != 0)
0172     {
0173         throw soci_error("Synchronization error");
0174     }
0175 
0176     if (pimpl_->sessions_[pos].first)
0177     {
0178         pthread_mutex_unlock(&(pimpl_->mtx_));
0179         throw soci_error("Cannot release pool entry (already free)");
0180     }
0181 
0182     pimpl_->sessions_[pos].first = true;
0183 
0184     pthread_mutex_unlock(&(pimpl_->mtx_));
0185 
0186     pthread_cond_signal(&(pimpl_->cond_));
0187 }
0188 
0189 #else
0190 // Windows implementation
0191 
0192 #include <windows.h>
0193 
0194 using namespace soci;
0195 
0196 struct connection_pool::connection_pool_impl
0197 {
0198     bool find_free(std::size_t & pos)
0199     {
0200         for (std::size_t i = 0; i != sessions_.size(); ++i)
0201         {
0202             if (sessions_[i].first)
0203             {
0204                 pos = i;
0205                 return true;
0206             }
0207         }
0208 
0209         return false;
0210     }
0211 
0212     // by convention, first == true means the entry is free (not used)
0213     std::vector<std::pair<bool, session *> > sessions_;
0214 
0215     CRITICAL_SECTION mtx_;
0216     HANDLE sem_;
0217 };
0218 
0219 connection_pool::connection_pool(std::size_t size)
0220 {
0221     if (size == 0)
0222     {
0223         throw soci_error("Invalid pool size");
0224     }
0225 
0226     pimpl_ = new connection_pool_impl();
0227     pimpl_->sessions_.resize(size);
0228     for (std::size_t i = 0; i != size; ++i)
0229     {
0230         pimpl_->sessions_[i] = std::make_pair(true, new session());
0231     }
0232 
0233     InitializeCriticalSection(&(pimpl_->mtx_));
0234 
0235     // initially all entries are available
0236     HANDLE s = CreateSemaphore(NULL,
0237         static_cast<LONG>(size), static_cast<LONG>(size), NULL);
0238     if (s == NULL)
0239     {
0240         throw soci_error("Synchronization error");
0241     }
0242 
0243     pimpl_->sem_ = s;
0244 }
0245 
0246 connection_pool::~connection_pool()
0247 {
0248     for (std::size_t i = 0; i != pimpl_->sessions_.size(); ++i)
0249     {
0250         delete pimpl_->sessions_[i].second;
0251     }
0252 
0253     DeleteCriticalSection(&(pimpl_->mtx_));
0254     CloseHandle(pimpl_->sem_);
0255 
0256     delete pimpl_;
0257 }
0258 
0259 bool connection_pool::try_lease(std::size_t & pos, int timeout)
0260 {
0261     DWORD cc = WaitForSingleObject(pimpl_->sem_,
0262         timeout >= 0 ? static_cast<DWORD>(timeout) : INFINITE);
0263     if (cc == WAIT_OBJECT_0)
0264     {
0265         // semaphore acquired, there is (at least) one free entry
0266 
0267         EnterCriticalSection(&(pimpl_->mtx_));
0268 
0269         if (!pimpl_->find_free(pos))
0270         {
0271             // this should be impossible
0272             throw soci_error("Getting connection from the pool unexpectedly failed");
0273         }
0274 
0275         pimpl_->sessions_[pos].first = false;
0276 
0277         LeaveCriticalSection(&(pimpl_->mtx_));
0278 
0279         return true;
0280     }
0281     else if (cc == WAIT_TIMEOUT)
0282     {
0283         return false;
0284     }
0285     else
0286     {
0287         throw soci_error("Synchronization error");
0288     }
0289 }
0290 
0291 void connection_pool::give_back(std::size_t pos)
0292 {
0293     if (pos >= pimpl_->sessions_.size())
0294     {
0295         throw soci_error("Invalid pool position");
0296     }
0297 
0298     EnterCriticalSection(&(pimpl_->mtx_));
0299 
0300     if (pimpl_->sessions_[pos].first)
0301     {
0302         LeaveCriticalSection(&(pimpl_->mtx_));
0303         throw soci_error("Cannot release pool entry (already free)");
0304     }
0305 
0306     pimpl_->sessions_[pos].first = true;
0307 
0308     LeaveCriticalSection(&(pimpl_->mtx_));
0309 
0310     ReleaseSemaphore(pimpl_->sem_, 1, NULL);
0311 }
0312 
0313 #endif // _WIN32
0314 
0315 session & connection_pool::at(std::size_t pos)
0316 {
0317     if (pos >= pimpl_->sessions_.size())
0318     {
0319         throw soci_error("Invalid pool position");
0320     }
0321 
0322     return *(pimpl_->sessions_[pos].second);
0323 }
0324 
0325 std::size_t connection_pool::lease()
0326 {
0327     std::size_t pos SOCI_DUMMY_INIT(0);
0328 
0329     // no timeout, so can't fail
0330     try_lease(pos, -1);
0331 
0332     return pos;
0333 }
0334 
0335