QGIS API Documentation  2.14.0-Essen
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 
53 template <typename T>
55 {
56  public:
57 
58  static const int maxConcurrentConnections;
59 
60  struct Item
61  {
62  T c;
64  };
65 
67  : connInfo( ci )
69  , expirationTimer( nullptr )
70  {
71  }
72 
74  {
75  Q_FOREACH ( Item item, conns )
76  {
77  qgsConnectionPool_ConnectionDestroy( item.c );
78  }
79  }
80 
81  T acquire()
82  {
83  // we are going to acquire a resource - if no resource is available, we will block here
84  sem.acquire();
85 
86  // quick (preferred) way - use cached connection
87  {
88  QMutexLocker locker( &connMutex );
89 
90  if ( !conns.isEmpty() )
91  {
92  Item i = conns.pop();
93  if ( !qgsConnectionPool_ConnectionIsValid( i.c ) )
94  {
95  qgsConnectionPool_ConnectionDestroy( i.c );
96  qgsConnectionPool_ConnectionCreate( connInfo, i.c );
97  }
98 
99  // no need to run if nothing can expire
100  if ( conns.isEmpty() )
101  {
102  // will call the slot directly or queue the call (if the object lives in a different thread)
103  QMetaObject::invokeMethod( expirationTimer->parent(), "stopExpirationTimer" );
104  }
105 
106  acquiredConns.append( i.c );
107 
108  return i.c;
109  }
110  }
111 
112  T c;
113  qgsConnectionPool_ConnectionCreate( connInfo, c );
114  if ( !c )
115  {
116  // we didn't get connection for some reason, so release the lock
117  sem.release();
118  return nullptr;
119  }
120 
121  connMutex.lock();
122  acquiredConns.append( c );
123  connMutex.unlock();
124  return c;
125  }
126 
127  void release( T conn )
128  {
129  connMutex.lock();
130  acquiredConns.removeAll( conn );
131  if ( !qgsConnectionPool_ConnectionIsValid( conn ) )
132  {
133  qgsConnectionPool_ConnectionDestroy( conn );
134  }
135  else
136  {
137  Item i;
138  i.c = conn;
140  conns.push( i );
141 
142  if ( !expirationTimer->isActive() )
143  {
144  // will call the slot directly or queue the call (if the object lives in a different thread)
145  QMetaObject::invokeMethod( expirationTimer->parent(), "startExpirationTimer" );
146  }
147  }
148 
149  connMutex.unlock();
150 
151  sem.release(); // this can unlock a thread waiting in acquire()
152  }
153 
155  {
156  connMutex.lock();
157  Q_FOREACH ( Item i, conns )
158  {
159  qgsConnectionPool_ConnectionDestroy( i.c );
160  }
161  conns.clear();
162  Q_FOREACH ( T c, acquiredConns )
163  qgsConnectionPool_InvalidateConnection( c );
164  connMutex.unlock();
165  }
166 
167  protected:
168 
169  void initTimer( QObject* parent )
170  {
171  expirationTimer = new QTimer( parent );
173  QObject::connect( expirationTimer, SIGNAL( timeout() ), parent, SLOT( handleConnectionExpired() ) );
174 
175  // just to make sure the object belongs to main thread and thus will get events
176  if ( qApp )
177  parent->moveToThread( qApp->thread() );
178  }
179 
181  {
182  connMutex.lock();
183 
184  QTime now = QTime::currentTime();
185 
186  // what connections have expired?
187  QList<int> toDelete;
188  for ( int i = 0; i < conns.count(); ++i )
189  {
190  if ( conns.at( i ).lastUsedTime.secsTo( now ) >= CONN_POOL_EXPIRATION_TIME )
191  toDelete.append( i );
192  }
193 
194  // delete expired connections
195  for ( int j = toDelete.count() - 1; j >= 0; --j )
196  {
197  int index = toDelete[j];
198  qgsConnectionPool_ConnectionDestroy( conns[index].c );
199  conns.remove( index );
200  }
201 
202  if ( conns.isEmpty() )
204 
205  connMutex.unlock();
206  }
207 
208  protected:
209 
216 };
217 
218 
234 template <typename T, typename T_Group>
236 {
237  public:
238 
240 
242  {
243  mMutex.lock();
244  Q_FOREACH ( T_Group* group, mGroups )
245  {
246  delete group;
247  }
248  mGroups.clear();
249  mMutex.unlock();
250  }
251 
255  {
256  mMutex.lock();
257  typename T_Groups::iterator it = mGroups.find( connInfo );
258  if ( it == mGroups.end() )
259  {
260  it = mGroups.insert( connInfo, new T_Group( connInfo ) );
261  }
262  T_Group* group = *it;
263  mMutex.unlock();
264 
265  return group->acquire();
266  }
267 
269  void releaseConnection( T conn )
270  {
271  mMutex.lock();
272  typename T_Groups::iterator it = mGroups.find( qgsConnectionPool_ConnectionToName( conn ) );
273  Q_ASSERT( it != mGroups.end() );
274  T_Group* group = *it;
275  mMutex.unlock();
276 
277  group->release( conn );
278  }
279 
286  {
287  mMutex.lock();
288  if ( mGroups.contains( connInfo ) )
289  mGroups[connInfo]->invalidateConnections();
290  mMutex.unlock();
291  }
292 
293 
294  protected:
295  T_Groups mGroups;
297 };
298 
299 
300 #endif // QGSCONNECTIONPOOL_H
void setInterval(int msec)
static unsigned index
#define CONN_POOL_MAX_CONCURRENT_CONNS
virtual ~QgsConnectionPool()
#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 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)
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
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)
void acquire(int n)