QGIS API Documentation  2.11.0-Master
qgsconnectionpool.h
Go to the documentation of this file.
1 /***************************************************************************
2  qgsconnectionpool.h
3  ---------------------
4  begin : February 2014
5  copyright : (C) 2014 by Martin Dobias
6  email : wonder dot sk at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #ifndef QGSCONNECTIONPOOL_H
17 #define QGSCONNECTIONPOOL_H
18 
19 #include <QCoreApplication>
20 #include <QMap>
21 #include <QMutex>
22 #include <QSemaphore>
23 #include <QStack>
24 #include <QTime>
25 #include <QTimer>
26 #include <QThread>
27 
28 #include "qgslogger.h"
29 
30 #define CONN_POOL_MAX_CONCURRENT_CONNS 4
31 #define CONN_POOL_EXPIRATION_TIME 60 // in seconds
32 
33 
52 template <typename T>
54 {
55  public:
56 
57  static const int maxConcurrentConnections;
58 
59  struct Item
60  {
61  T c;
63  };
64 
66  : connInfo( ci )
68  , expirationTimer( 0 )
69  {
70  }
71 
73  {
74  foreach ( Item item, conns )
75  {
76  qgsConnectionPool_ConnectionDestroy( item.c );
77  }
78  }
79 
80  T acquire()
81  {
82  // we are going to acquire a resource - if no resource is available, we will block here
83  sem.acquire();
84 
85  // quick (preferred) way - use cached connection
86  {
87  QMutexLocker locker( &connMutex );
88 
89  if ( !conns.isEmpty() )
90  {
91  Item i = conns.pop();
92  if ( !qgsConnectionPool_ConnectionIsValid( i.c ) )
93  {
94  qgsConnectionPool_ConnectionDestroy( i.c );
95  qgsConnectionPool_ConnectionCreate( connInfo, i.c );
96  }
97 
98  // no need to run if nothing can expire
99  if ( conns.isEmpty() )
100  {
101  // will call the slot directly or queue the call (if the object lives in a different thread)
102  QMetaObject::invokeMethod( expirationTimer->parent(), "stopExpirationTimer" );
103  }
104 
105  acquiredConns.append( i.c );
106 
107  return i.c;
108  }
109  }
110 
111  T c;
112  qgsConnectionPool_ConnectionCreate( connInfo, c );
113  if ( !c )
114  {
115  // we didn't get connection for some reason, so release the lock
116  sem.release();
117  return 0;
118  }
119 
120  connMutex.lock();
121  acquiredConns.append( c );
122  connMutex.unlock();
123  return c;
124  }
125 
126  void release( T conn )
127  {
128  connMutex.lock();
129  acquiredConns.removeAll( conn );
130  Item i;
131  i.c = conn;
133  conns.push( i );
134 
135  if ( !expirationTimer->isActive() )
136  {
137  // will call the slot directly or queue the call (if the object lives in a different thread)
138  QMetaObject::invokeMethod( expirationTimer->parent(), "startExpirationTimer" );
139  }
140 
141  connMutex.unlock();
142 
143  sem.release(); // this can unlock a thread waiting in acquire()
144  }
145 
147  {
148  connMutex.lock();
149  foreach ( Item i, conns )
150  {
151  qgsConnectionPool_InvalidateConnection( i.c );
152  }
153  foreach ( T c, acquiredConns )
154  qgsConnectionPool_InvalidateConnection( c );
155  connMutex.unlock();
156  }
157 
158  protected:
159 
160  void initTimer( QObject* parent )
161  {
162  expirationTimer = new QTimer( parent );
164  QObject::connect( expirationTimer, SIGNAL( timeout() ), parent, SLOT( handleConnectionExpired() ) );
165 
166  // just to make sure the object belongs to main thread and thus will get events
167  if ( qApp )
168  parent->moveToThread( qApp->thread() );
169  }
170 
172  {
173  connMutex.lock();
174 
175  QTime now = QTime::currentTime();
176 
177  // what connections have expired?
178  QList<int> toDelete;
179  for ( int i = 0; i < conns.count(); ++i )
180  {
181  if ( conns.at( i ).lastUsedTime.secsTo( now ) >= CONN_POOL_EXPIRATION_TIME )
182  toDelete.append( i );
183  }
184 
185  // delete expired connections
186  for ( int j = toDelete.count() - 1; j >= 0; --j )
187  {
188  int index = toDelete[j];
189  qgsConnectionPool_ConnectionDestroy( conns[index].c );
190  conns.remove( index );
191  }
192 
193  if ( conns.isEmpty() )
195 
196  connMutex.unlock();
197  }
198 
199  protected:
200 
207 };
208 
209 
225 template <typename T, typename T_Group>
227 {
228  public:
229 
231 
233  {
234  mMutex.lock();
235  foreach ( T_Group* group, mGroups )
236  {
237  delete group;
238  }
239  mGroups.clear();
240  mMutex.unlock();
241  }
242 
245  T acquireConnection( const QString& connInfo )
246  {
247  mMutex.lock();
248  typename T_Groups::iterator it = mGroups.find( connInfo );
249  if ( it == mGroups.end() )
250  {
251  it = mGroups.insert( connInfo, new T_Group( connInfo ) );
252  }
253  T_Group* group = *it;
254  mMutex.unlock();
255 
256  return group->acquire();
257  }
258 
260  void releaseConnection( T conn )
261  {
262  mMutex.lock();
263  typename T_Groups::iterator it = mGroups.find( qgsConnectionPool_ConnectionToName( conn ) );
264  Q_ASSERT( it != mGroups.end() );
265  T_Group* group = *it;
266  mMutex.unlock();
267 
268  group->release( conn );
269  }
270 
276  void invalidateConnections( const QString& connInfo )
277  {
278  mMutex.lock();
279  if ( mGroups.contains( connInfo ) )
280  mGroups[connInfo]->invalidateConnections();
281  mMutex.unlock();
282  }
283 
284 
285  protected:
286  T_Groups mGroups;
288 };
289 
290 
291 #endif // QGSCONNECTIONPOOL_H
void setInterval(int msec)
static unsigned index
#define CONN_POOL_MAX_CONCURRENT_CONNS
virtual ~QgsConnectionPool()
bool contains(const Key &key) const
#define CONN_POOL_EXPIRATION_TIME
void moveToThread(QThread *targetThread)
void invalidateConnections(const QString &connInfo)
Invalidates all connections to the specified resource.
void unlock()
QMap< QString, T_Group * > T_Groups
void clear()
void initTimer(QObject *parent)
int count(const T &value) const
void append(const T &value)
void releaseConnection(T conn)
Release an existing connection so it will get back into the pool and can be reused.
int removeAll(const T &value)
iterator end()
void lock()
void stop()
bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType type, QGenericReturnArgument ret, QGenericArgument val0, QGenericArgument val1, QGenericArgument val2, QGenericArgument val3, QGenericArgument val4, QGenericArgument val5, QGenericArgument val6, QGenericArgument val7, QGenericArgument val8, QGenericArgument val9)
QTime currentTime()
static const int maxConcurrentConnections
iterator insert(const Key &key, const T &value)
void release(int n)
T acquireConnection(const QString &connInfo)
Try to acquire a connection: if no connections are available, the thread will get blocked...
Template class responsible for keeping a pool of open connections.
bool isActive() const
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject * parent() const
Template that stores data related to one server.
QgsConnectionPoolGroup(const QString &ci)
iterator find(const Key &key)
void acquire(int n)