reSIProcate
SIP stack, Dialog Usage Manager and related libraries
Loading...
Searching...
No Matches
ConnectionManager.cxx
Go to the documentation of this file.
1#if defined(HAVE_CONFIG_H)
2#include "config.h"
3#endif
4
5#ifndef WIN32
6#include <sys/time.h>
7#include <sys/resource.h>
8#endif
9
12#include "rutil/Logger.hxx"
13#include "rutil/Inserter.hxx"
14
15#include <vector>
16
17using namespace resip;
18using namespace std;
19
20#define RESIPROCATE_SUBSYSTEM Subsystem::TRANSPORT
21
22uint64_t ConnectionManager::MinimumGcAge = 1; // in milliseconds
25
27 mHead(0,Tuple(),0,Compression::Disabled, false),
28 mWriteHead(ConnectionWriteList::makeList(&mHead)),
29 mReadHead(ConnectionReadList::makeList(&mHead)),
30 mLRUHead(ConnectionLruList::makeList(&mHead)),
31 mFlowTimerLRUHead(FlowTimerLruList::makeList(&mHead)),
32 mPollGrp(0)
33{
34 DebugLog(<<"ConnectionManager::ConnectionManager() called ");
35}
36
38{
39 closeConnections();
40 resip_assert(mReadHead->empty());
41 resip_assert(mWriteHead->empty());
42 resip_assert(mLRUHead->empty());
43 resip_assert(mFlowTimerLRUHead->empty());
44}
45
46void
47ConnectionManager::closeConnections()
48{
49 while (!mAddrMap.empty())
50 {
51 delete mAddrMap.begin()->second;
52 }
53}
54
57{
58 if (addr.mFlowKey != 0)
59 {
60 IdMap::iterator i = mIdMap.find(addr.mFlowKey);
61 if (i != mIdMap.end())
62 {
63 if(i->second->who() == addr)
64 {
65 DebugLog(<<"Found fd " << addr.mFlowKey);
66 return i->second;
67 }
68 else
69 {
70 DebugLog(<<"fd " << addr.mFlowKey
71 << " exists, but does not match the destination. FD -> "
72 << i->second->who() << ", tuple -> " << addr);
73 }
74 }
75 else
76 {
77 DebugLog(<<"fd " << addr.mFlowKey << " does not exist.");
78 }
79
81 {
82 return 0;
83 }
84 }
85
86 AddrMap::iterator i = mAddrMap.find(addr);
87 if (i != mAddrMap.end())
88 {
89 DebugLog(<<"Found connection for tuple "<< addr );
90 return i->second;
91 }
92
93 DebugLog(<<"Could not find a connection for " << addr);
94 return 0;
95}
96
97const Connection*
99{
100 if (addr.mFlowKey != 0)
101 {
102 IdMap::const_iterator i = mIdMap.find(addr.mFlowKey);
103 if (i != mIdMap.end())
104 {
105 if(i->second->who()==addr)
106 {
107 DebugLog(<<"Found fd " << addr.mFlowKey);
108 return i->second;
109 }
110 else
111 {
112 DebugLog(<<"fd " << addr.mFlowKey
113 << " exists, but does not match the destination. FD -> "
114 << i->second->who() << ", tuple -> " << addr);
115 }
116 }
117 else
118 {
119 DebugLog(<<"fd " << addr.mFlowKey << " does not exist.");
120 }
121 }
122
123 AddrMap::const_iterator i = mAddrMap.find(addr);
124 if (i != mAddrMap.end())
125 {
126 DebugLog(<<"Found connection for tuple "<< addr );
127 return i->second;
128 }
129
130 DebugLog(<<"Could not find a connection for " << addr);
131 return 0;
132}
133
134void
136{
137 // If using PollGrp, caller shouldn't call this
138 resip_assert( mPollGrp==0 );
139
140 for (ConnectionReadList::iterator i = mReadHead->begin();
141 i != mReadHead->end(); ++i)
142 {
143 fdset.setRead((*i)->getSocket());
144 fdset.setExcept((*i)->getSocket());
145 }
146
147 for (ConnectionWriteList::iterator i = mWriteHead->begin();
148 i != mWriteHead->end(); ++i)
149 {
150 fdset.setWrite((*i)->getSocket());
151 fdset.setExcept((*i)->getSocket());
152 }
153}
154
155void
156ConnectionManager::addToWritable(Connection* conn)
157{
158 if ( mPollGrp )
159 {
160 mPollGrp->modPollItem(conn->mPollItemHandle, FPEM_Read|FPEM_Write|FPEM_Error);
161 }
162 else
163 {
164 mWriteHead->push_back(conn);
165 }
166}
167
168void
169ConnectionManager::removeFromWritable(Connection* conn)
170{
171 if ( mPollGrp )
172 {
173 mPollGrp->modPollItem(conn->mPollItemHandle, FPEM_Read|FPEM_Error);
174 }
175 else
176 {
177 resip_assert(!mWriteHead->empty());
178 conn->ConnectionWriteList::remove();
179 }
180}
181
182void
183ConnectionManager::addConnection(Connection* connection)
184{
185 resip_assert(mAddrMap.find(connection->who())==mAddrMap.end());
186
187 DebugLog (<< "ConnectionManager::addConnection() " << connection->mWho.mFlowKey << ":" << connection->who() << ", totalConnections=" << mIdMap.size());
188
189 mAddrMap[connection->who()] = connection;
190 mIdMap[connection->who().mFlowKey] = connection;
191
192 if ( mPollGrp )
193 {
194 connection->mPollItemHandle = mPollGrp->addPollItem(
195 connection->getSocket(), FPEM_Read|FPEM_Error, connection);
196 }
197 else
198 {
199 mReadHead->push_back(connection);
200 }
201 mLRUHead->push_back(connection);
202
203 // Garbage collect old connections if agressive is enabled
205 {
206 gc(MinimumGcAge, 0); // cleanup all connections that haven't seen data in last x ms
207 }
208
209 //DebugLog (<< "count=" << mAddrMap.count(connection->who()) << "who=" << connection->who() << " mAddrMap=" << Inserter(mAddrMap));
210 //assert(mAddrMap.begin()->first == connection->who());
211 resip_assert(mAddrMap.count(connection->who()) == 1);
212}
213
214void
215ConnectionManager::removeConnection(Connection* connection)
216{
217 DebugLog (<< "ConnectionManager::removeConnection()");
218
219 mIdMap.erase(connection->mWho.mFlowKey);
220 mAddrMap.erase(connection->mWho);
221
222 if ( mPollGrp )
223 {
224 mPollGrp->delPollItem(connection->mPollItemHandle);
225 }
226 else
227 {
228 resip_assert(!mReadHead->empty());
229 connection->ConnectionReadList::remove();
230 connection->ConnectionWriteList::remove();
231 if(connection->isFlowTimerEnabled())
232 {
233 connection->FlowTimerLruList::remove();
234 }
235 else
236 {
237 connection->ConnectionLruList::remove();
238 }
239 }
240}
241
242// release excessively old connections (free up file descriptors)
243unsigned int
244ConnectionManager::gc(uint64_t relThreshold, unsigned int maxToRemove)
245{
246 uint64_t curTimeMs = Timer::getTimeMs();
247 uint64_t threshold = curTimeMs > relThreshold ? curTimeMs - relThreshold : 0; // Ensure we don't underflow
248
249 DebugLog(<< "recycling connections not used in last " << relThreshold/1000.0 << " seconds");
250
251 // Look through non-flow-timer connections and close those using the passed in relThreshold
252 unsigned int numRemoved = 0;
253 for (ConnectionLruList::iterator i = mLRUHead->begin();
254 i != mLRUHead->end() &&
255 (maxToRemove == 0 || numRemoved != maxToRemove);)
256 {
257 if ((*i)->whenLastUsed() < threshold)
258 {
259 Connection* discard = *i;
260 InfoLog(<< "recycling connection: " << discard << " " << discard->getSocket());
261 // iterate before removing
262 ++i;
263 delete discard;
264 numRemoved++;
265 }
266 else
267 {
268 break;
269 }
270 }
271
272 // Look through flow-timer connections and close those using the configured FlowTimer
273 // value + the configured grace period as a threshold
274 if(mFlowTimerLRUHead->begin() != mFlowTimerLRUHead->end())
275 {
277 threshold = curTimeMs > flowTimerPeriodMs ? curTimeMs - flowTimerPeriodMs : 0;
278 for (FlowTimerLruList::iterator i2 = mFlowTimerLRUHead->begin();
279 i2 != mFlowTimerLRUHead->end() &&
280 (maxToRemove == 0 || numRemoved != maxToRemove);)
281 {
282 if ((*i2)->whenLastUsed() < threshold)
283 {
284 Connection* discard = *i2;
285 InfoLog(<< "recycling flow-timer enabled connection: " << discard << " " << discard->getSocket());
286 // iterate before removing
287 ++i2;
288 delete discard;
289 numRemoved++;
290 }
291 else
292 {
293 break;
294 }
295 }
296 }
297
298 if(MinimumGcHeadroom > 0)
299 {
300#ifdef WIN32
301 DebugLog(<<"MinimumGcHeadroom not yet implemented on Windows (requires getrlimit())");
302#else
303 struct rlimit rlim;
304 if(getrlimit(RLIMIT_NOFILE, &rlim) != 0)
305 {
306 ErrLog(<<"Call to getrlimit() for RLIMIT_NOFILE failed: " << strError(errno));
307 }
308 else
309 {
310 rlim_t& soft_limit = rlim.rlim_cur;
311 AddrMap::size_type conn_count = mAddrMap.size();
312 AddrMap::size_type headroom = soft_limit - conn_count;
313 DebugLog(<< "GC headroom check: soft_limit = " << soft_limit << ", managed connection count = " << conn_count << ", headroom = " << headroom << ", minimum headroom = " << MinimumGcHeadroom);
314 if(headroom < MinimumGcHeadroom)
315 {
316 WarningLog(<< "actual headroom = " << headroom << ", MinimumGcHeadroom = " << MinimumGcHeadroom << ", garbage collector making extra effort to reclaim file descriptors");
317 AddrMap::size_type mustRemove = MinimumGcHeadroom - headroom;
318 unsigned int remainder = gcWithTarget(mustRemove);
319 numRemoved += (mustRemove - remainder);
320 if(remainder > 0)
321 {
322 ErrLog(<< "No more stream connections to close, something else must be eating file descriptors, limit too low or MinimumGcHeadroom too high");
323 }
324 }
325 }
326#endif
327 }
328 return numRemoved;
329}
330
331unsigned int
332ConnectionManager::gcWithTarget(unsigned int target)
333{
334 ConnectionLruList::iterator i = mLRUHead->begin();
335 FlowTimerLruList::iterator i2 = mFlowTimerLRUHead->begin();
336 while(target > 0)
337 {
338 Connection* discard = 0;
339 if(i == mLRUHead->end())
340 {
341 if(i2 == mFlowTimerLRUHead->end())
342 {
343 WarningLog(<< "No more stream connections to close, remaining target = " << target);
344 return target;
345 }
346 discard = *i2;
347 ++i2;
348 }
349 else
350 {
351 if (i2 == mFlowTimerLRUHead->end() ||
352 (*i)->whenLastUsed() < (*i2)->whenLastUsed())
353 {
354 discard = *i;
355 ++i;
356 }
357 else
358 {
359 discard = *i2;
360 ++i2;
361 }
362 }
363 resip_assert(discard);
364 WarningLog(<< "recycling LRU connection: " << discard << " " << discard->getSocket());
365 delete discard;
366 target--;
367 }
368 return target;
369}
370
371// move to youngest
372void
373ConnectionManager::touch(Connection* connection)
374{
375 connection->resetLastUsed();
376 if(connection->isFlowTimerEnabled())
377 {
378 connection->FlowTimerLruList::remove();
379 mFlowTimerLRUHead->push_back(connection);
380 }
381 else
382 {
383 connection->ConnectionLruList::remove();
384 mLRUHead->push_back(connection);
385 }
386}
387
388void
389ConnectionManager::moveToFlowTimerLru(Connection *connection)
390{
391 connection->ConnectionLruList::remove();
392 mFlowTimerLRUHead->push_back(connection);
393}
394
395void
397{
398 resip_assert( mPollGrp==NULL ); // owner shouldn't call this if polling
399
400 // process the write list
401 for (ConnectionWriteList::iterator writeIter = mWriteHead->begin();
402 writeIter != mWriteHead->end(); )
403 {
404 Connection* currConnection = *writeIter;
405
406 // update iterator to next first so that it can traverse safely
407 // even if current one is removed from the list later
408 ++writeIter;
409
410 if (!currConnection)
411 continue;
412
413 if (fdset.readyToWrite(currConnection->getSocket()))
414 {
415 if (!currConnection->performWrites())
416 {
417 delete currConnection;
418 }
419 }
420 else if (fdset.hasException(currConnection->getSocket()))
421 {
422 int errNum = 0;
423 int errNumSize = sizeof(errNum);
424 getsockopt(currConnection->getSocket(), SOL_SOCKET, SO_ERROR, (char *)&errNum, (socklen_t *)&errNumSize);
425 InfoLog(<< "Exception writing to socket " << currConnection->getSocket() << " code: " << errNum << "; closing connection");
426 delete currConnection;
427 }
428 }
429
430 // process the read list
431 for (ConnectionReadList::iterator readIter = mReadHead->begin();
432 readIter != mReadHead->end(); )
433 {
434 Connection* currConnection = *readIter;
435
436 // update iterator to next first so that it can traverse safely
437 // even if current one is removed from the list later
438 ++readIter;
439
440 if (!currConnection)
441 continue;
442
443 if ( fdset.readyToRead(currConnection->getSocket()) ||
444 currConnection->hasDataToRead() )
445 {
446 fdset.clear(currConnection->getSocket());
447 currConnection->performReads();
448 }
449 else if (fdset.hasException(currConnection->getSocket()))
450 {
451 int errNum = 0;
452 int errNumSize = sizeof(errNum);
453 getsockopt(currConnection->getSocket(), SOL_SOCKET, SO_ERROR, (char *)&errNum, (socklen_t *)&errNumSize);
454 InfoLog(<< "Exception reading from socket " << currConnection->getSocket() << " code: " << errNum << "; closing connection");
455 delete currConnection;
456 }
457 }
458}
459
460void
462{
463 // .bwc. We could have all the connections detach and re-attach, but the
464 // only place we call this when connections exist is when tearing down
465 // anyway.
466 closeConnections();
467 mPollGrp = grp;
468}
469
470void
472{
473 for (AddrMap::const_iterator it = mAddrMap.begin(); it != mAddrMap.end(); it++)
474 {
475 it->second->invokeAfterSocketCreationFunc();
476 }
477}
478
479/* ====================================================================
480 * The Vovida Software License, Version 1.0
481 *
482 * Copyright (c)
483 *
484 * Redistribution and use in source and binary forms, with or without
485 * modification, are permitted provided that the following conditions
486 * are met:
487 *
488 * 1. Redistributions of source code must retain the above copyright
489 * notice, this list of conditions and the following disclaimer.
490 *
491 * 2. Redistributions in binary form must reproduce the above copyright
492 * notice, this list of conditions and the following disclaimer in
493 * the documentation and/or other materials provided with the
494 * distribution.
495 *
496 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
497 * and "Vovida Open Communication Application Library (VOCAL)" must
498 * not be used to endorse or promote products derived from this
499 * software without prior written permission. For written
500 * permission, please contact vocal@vovida.org.
501 *
502 * 4. Products derived from this software may not be called "VOCAL", nor
503 * may "VOCAL" appear in their name, without prior written
504 * permission of Vovida Networks, Inc.
505 *
506 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
507 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
508 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
509 * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA
510 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
511 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
512 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
513 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
514 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
515 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
516 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
517 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
518 * DAMAGE.
519 *
520 * ====================================================================
521 *
522 * This software consists of voluntary contributions made by Vovida
523 * Networks, Inc. and many individuals on behalf of Vovida Networks,
524 * Inc. For more information on Vovida Networks, Inc., please see
525 * <http://www.vovida.org/>.
526 *
527 */
#define FPEM_Write
Definition FdPoll.hxx:30
#define FPEM_Read
Definition FdPoll.hxx:29
#define FPEM_Error
Definition FdPoll.hxx:31
#define DebugLog(args_)
Definition Logger.hxx:128
#define InfoLog(args_)
Definition Logger.hxx:131
#define WarningLog(args_)
Definition Logger.hxx:134
#define ErrLog(args_)
Definition Logger.hxx:137
#define resip_assert(x)
This file needs to work for both C and C++ code.
static bool EnableAgressiveGc
Enable Agressive Connection Garbage Collection to have resip perform garbage collection on every new ...
Connection * findConnection(const Tuple &tuple)
may return 0
void setPollGrp(FdPollGrp *grp)
populate the fdset againt the read and write lists
static uint64_t MinimumGcAge
connection must have no inbound traffic for greater than this time (in ms) before it is garbage colle...
virtual void invokeAfterSocketCreationFunc() const
static uint64_t MinimumGcHeadroom
If the difference between the number of permitted FDs (reported by periodic calls to getrlimit()) and...
Connection implements, via sockets, ConnectionBase for managed connections.
Socket getSocket() const
bool performWrites(unsigned int max=0)
Call performWrite() repeatedly, until either the send queue is exhausted, the write() call fails (pro...
virtual bool hasDataToRead()
always true – always add to fdset as read ready
bool performReads(unsigned int max=0)
Call read() repeatedly, until an error occurs (because the fd is not ready to read,...
virtual FdPollItemHandle addPollItem(Socket sock, FdPollEventMask newMask, FdPollItemIf *item)=0
virtual void delPollItem(FdPollItemHandle handle)=0
virtual void modPollItem(FdPollItemHandle handle, FdPollEventMask newMask)=0
Object-oriented wrapper for your platform's file-descriptor set.
Definition Socket.hxx:162
void clear(Socket fd)
Definition Socket.hxx:239
bool readyToWrite(Socket fd)
Definition Socket.hxx:194
bool readyToRead(Socket fd)
Definition Socket.hxx:189
void setRead(Socket fd)
Definition Socket.hxx:204
void setExcept(Socket fd)
Definition Socket.hxx:227
bool hasException(Socket fd)
Definition Socket.hxx:199
void setWrite(Socket fd)
Definition Socket.hxx:216
static unsigned int getFlowTimerSeconds()
static unsigned int getFlowTimerGracePeriodSeconds()
static uint64_t getTimeMs()
Returns the current clock time in milliseconds.
Definition Timer.hxx:72
Represents a network address.
Definition Tuple.hxx:59
bool onlyUseExistingConnection
Definition Tuple.hxx:192
FlowKey mFlowKey
This is a (largely) opaque key that subclasses of Transport will use to help record/find flows.
Definition Tuple.hxx:189
dcm! – add UnusedChecking(_enum) below;
std::string strError(int err)
Definition compat.hxx:274
unsigned __int64 uint64_t
Definition stdint.h:95