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  parent->moveToThread( qApp->thread() );
168  }
169 
171  {
172  connMutex.lock();
173 
174  QTime now = QTime::currentTime();
175 
176  // what connections have expired?
177  QList<int> toDelete;
178  for ( int i = 0; i < conns.count(); ++i )
179  {
180  if ( conns.at( i ).lastUsedTime.secsTo( now ) >= CONN_POOL_EXPIRATION_TIME )
181  toDelete.append( i );
182  }
183 
184  // delete expired connections
185  for ( int j = toDelete.count() - 1; j >= 0; --j )
186  {
187  int index = toDelete[j];
188  qgsConnectionPool_ConnectionDestroy( conns[index].c );
189  conns.remove( index );
190  }
191 
192  if ( conns.isEmpty() )
194 
195  connMutex.unlock();
196  }
197 
198  protected:
199 
206 };
207 
208 
224 template <typename T, typename T_Group>
226 {
227  public:
228 
230 
233  T acquireConnection( const QString& connInfo )
234  {
235  mMutex.lock();
236  typename T_Groups::iterator it = mGroups.find( connInfo );
237  if ( it == mGroups.end() )
238  {
239  it = mGroups.insert( connInfo, new T_Group( connInfo ) );
240  }
241  T_Group* group = *it;
242  mMutex.unlock();
243 
244  return group->acquire();
245  }
246 
248  void releaseConnection( T conn )
249  {
250  mMutex.lock();
251  typename T_Groups::iterator it = mGroups.find( qgsConnectionPool_ConnectionToName( conn ) );
252  Q_ASSERT( it != mGroups.end() );
253  T_Group* group = *it;
254  mMutex.unlock();
255 
256  group->release( conn );
257  }
258 
264  void invalidateConnections( const QString& connInfo )
265  {
266  mMutex.lock();
267  if ( mGroups.contains( connInfo ) )
268  mGroups[connInfo]->invalidateConnections();
269  mMutex.unlock();
270  }
271 
272 
273  protected:
274  T_Groups mGroups;
276 };
277 
278 
279 #endif // QGSCONNECTIONPOOL_H
void setInterval(int msec)
static unsigned index
#define CONN_POOL_MAX_CONCURRENT_CONNS
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 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
QgsConnectionPoolGroup(const QString &ci)
iterator find(const Key &key)
void acquire(int n)