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