QGIS API Documentation  2.6.0-Brighton
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
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 
50 template <typename T>
52 {
53  public:
54 
55  static const int maxConcurrentConnections;
56 
57  struct Item
58  {
59  T c;
60  QTime lastUsedTime;
61  };
62 
63  QgsConnectionPoolGroup( const QString& ci )
64  : connInfo( ci )
66  , expirationTimer( 0 )
67  {
68  }
69 
71  {
72  foreach ( Item item, conns )
73  {
74  qgsConnectionPool_ConnectionDestroy( item.c );
75  }
76  }
77 
78  T acquire()
79  {
80  // we are going to acquire a resource - if no resource is available, we will block here
81  sem.acquire();
82 
83  // quick (preferred) way - use cached connection
84  {
85  QMutexLocker locker( &connMutex );
86 
87  if ( !conns.isEmpty() )
88  {
89  Item i = conns.pop();
90 
91  // no need to run if nothing can expire
92  if ( conns.isEmpty() )
93  {
94  // will call the slot directly or queue the call (if the object lives in a different thread)
95  QMetaObject::invokeMethod( expirationTimer->parent(), "stopExpirationTimer" );
96  }
97 
98  return i.c;
99  }
100  }
101 
102  T c;
103  qgsConnectionPool_ConnectionCreate( connInfo, c );
104  if ( !c )
105  {
106  // we didn't get connection for some reason, so release the lock
107  sem.release();
108  return 0;
109  }
110 
111  return c;
112  }
113 
114  void release( T conn )
115  {
116  connMutex.lock();
117  Item i;
118  i.c = conn;
119  i.lastUsedTime = QTime::currentTime();
120  conns.push( i );
121 
122  if ( !expirationTimer->isActive() )
123  {
124  // will call the slot directly or queue the call (if the object lives in a different thread)
125  QMetaObject::invokeMethod( expirationTimer->parent(), "startExpirationTimer" );
126  }
127 
128  connMutex.unlock();
129 
130  sem.release(); // this can unlock a thread waiting in acquire()
131  }
132 
133  protected:
134 
135  void initTimer( QObject* parent )
136  {
137  expirationTimer = new QTimer( parent );
138  expirationTimer->setInterval( CONN_POOL_EXPIRATION_TIME * 1000 );
139  QObject::connect( expirationTimer, SIGNAL( timeout() ), parent, SLOT( handleConnectionExpired() ) );
140 
141  // just to make sure the object belongs to main thread and thus will get events
142  parent->moveToThread( qApp->thread() );
143  }
144 
146  {
147  connMutex.lock();
148 
149  QTime now = QTime::currentTime();
150 
151  // what connections have expired?
152  QList<int> toDelete;
153  for ( int i = 0; i < conns.count(); ++i )
154  {
155  if ( conns.at( i ).lastUsedTime.secsTo( now ) >= CONN_POOL_EXPIRATION_TIME )
156  toDelete.append( i );
157  }
158 
159  // delete expired connections
160  for ( int j = toDelete.count() - 1; j >= 0; --j )
161  {
162  int index = toDelete[j];
163  qgsConnectionPool_ConnectionDestroy( conns[index].c );
164  conns.remove( index );
165  }
166 
167  if ( conns.isEmpty() )
168  expirationTimer->stop();
169 
170  connMutex.unlock();
171  }
172 
173  protected:
174 
175  QString connInfo;
176  QStack<Item> conns;
177  QMutex connMutex;
178  QSemaphore sem;
180 };
181 
182 
198 template <typename T, typename T_Group>
200 {
201  public:
202 
203  typedef QMap<QString, T_Group*> T_Groups;
204 
207  T acquireConnection( const QString& connInfo )
208  {
209  mMutex.lock();
210  typename T_Groups::iterator it = mGroups.find( connInfo );
211  if ( it == mGroups.end() )
212  {
213  it = mGroups.insert( connInfo, new T_Group( connInfo ) );
214  }
215  T_Group* group = *it;
216  mMutex.unlock();
217 
218  return group->acquire();
219  }
220 
222  void releaseConnection( T conn )
223  {
224  mMutex.lock();
225  typename T_Groups::iterator it = mGroups.find( qgsConnectionPool_ConnectionToName( conn ) );
226  Q_ASSERT( it != mGroups.end() );
227  T_Group* group = *it;
228  mMutex.unlock();
229 
230  group->release( conn );
231  }
232 
233  protected:
235 
236  private:
237  QMutex mMutex;
238 };
239 
240 
241 #endif // QGSCONNECTIONPOOL_H