QGIS API Documentation  2.99.0-Master (6a61179)
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 
29 #define CONN_POOL_MAX_CONCURRENT_CONNS 4
30 #define CONN_POOL_EXPIRATION_TIME 60 // in seconds
31 
32 
53 template <typename T>
55 {
56  public:
57 
58  static const int maxConcurrentConnections;
59 
60  struct Item
61  {
62  T c;
63  QTime lastUsedTime;
64  };
65 
66  QgsConnectionPoolGroup( const QString& ci )
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;
139  i.lastUsedTime = QTime::currentTime();
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 );
172  expirationTimer->setInterval( CONN_POOL_EXPIRATION_TIME * 1000 );
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() )
203  expirationTimer->stop();
204 
205  connMutex.unlock();
206  }
207 
208  protected:
209 
210  QString connInfo;
211  QStack<Item> conns;
212  QList<T> acquiredConns;
213  QMutex connMutex;
214  QSemaphore sem;
216 
217  private:
218 
220  QgsConnectionPoolGroup& operator=( const QgsConnectionPoolGroup& other );
221 
222 };
223 
224 
240 template <typename T, typename T_Group>
242 {
243  public:
244 
245  typedef QMap<QString, T_Group*> T_Groups;
246 
248  {
249  mMutex.lock();
250  Q_FOREACH ( T_Group* group, mGroups )
251  {
252  delete group;
253  }
254  mGroups.clear();
255  mMutex.unlock();
256  }
257 
260  T acquireConnection( const QString& connInfo )
261  {
262  mMutex.lock();
263  typename T_Groups::iterator it = mGroups.find( connInfo );
264  if ( it == mGroups.end() )
265  {
266  it = mGroups.insert( connInfo, new T_Group( connInfo ) );
267  }
268  T_Group* group = *it;
269  mMutex.unlock();
270 
271  return group->acquire();
272  }
273 
275  void releaseConnection( T conn )
276  {
277  mMutex.lock();
278  typename T_Groups::iterator it = mGroups.find( qgsConnectionPool_ConnectionToName( conn ) );
279  Q_ASSERT( it != mGroups.end() );
280  T_Group* group = *it;
281  mMutex.unlock();
282 
283  group->release( conn );
284  }
285 
291  void invalidateConnections( const QString& connInfo )
292  {
293  mMutex.lock();
294  if ( mGroups.contains( connInfo ) )
295  mGroups[connInfo]->invalidateConnections();
296  mMutex.unlock();
297  }
298 
299 
300  protected:
301  T_Groups mGroups;
302  QMutex mMutex;
303 };
304 
305 
306 #endif // QGSCONNECTIONPOOL_H
static unsigned index
#define CONN_POOL_MAX_CONCURRENT_CONNS
virtual ~QgsConnectionPool()
#define CONN_POOL_EXPIRATION_TIME
void invalidateConnections(const QString &connInfo)
Invalidates all connections to the specified resource.
QMap< QString, T_Group * > T_Groups
void initTimer(QObject *parent)
void releaseConnection(T conn)
Release an existing connection so it will get back into the pool and can be reused.
static const int maxConcurrentConnections
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.
Template that stores data related to one server.
QgsConnectionPoolGroup(const QString &ci)