This is Unofficial EPICS BASE Doxygen Site
fdManager.cpp
Go to the documentation of this file.
1 /*************************************************************************\
2 * Copyright (c) 2002 The University of Chicago, as Operator of Argonne
3 * National Laboratory.
4 * Copyright (c) 2002 The Regents of the University of California, as
5 * Operator of Los Alamos National Laboratory.
6 * EPICS BASE Versions 3.13.7
7 * and higher are distributed subject to a Software License Agreement found
8 * in file LICENSE that is included with this distribution.
9 \*************************************************************************/
10 //
11 // File descriptor management C++ class library
12 // (for multiplexing IO in a single threaded environment)
13 //
14 // Author Jeffrey O. Hill
15 // johill@lanl.gov
16 // 505 665 1831
17 //
18 // NOTES:
19 // 1) This library is not thread safe
20 //
21 
22 #include <algorithm>
23 
24 #define instantiateRecourceLib
25 #include "epicsAssert.h"
26 #include "epicsThread.h"
27 #include "fdManager.h"
28 #include "locationException.h"
29 
30 using std :: max;
31 
33 
34 const unsigned mSecPerSec = 1000u;
35 const unsigned uSecPerSec = 1000u * mSecPerSec;
36 
37 //
38 // fdManager::fdManager()
39 //
40 // hopefully its a reasonable guess that select() and epicsThreadSleep()
41 // will have the same sleep quantum
42 //
43 LIBCOM_API fdManager::fdManager () :
44  sleepQuantum ( epicsThreadSleepQuantum () ),
45  fdSetsPtr ( new fd_set [fdrNEnums] ),
46  pTimerQueue ( 0 ), maxFD ( 0 ), processInProg ( false ),
47  pCBReg ( 0 )
48 {
49  int status = osiSockAttach ();
50  assert (status);
51 
52  for ( size_t i = 0u; i < fdrNEnums; i++ ) {
53  FD_ZERO ( &fdSetsPtr[i] );
54  }
55 }
56 
57 //
58 // fdManager::~fdManager()
59 //
61 {
62  fdReg *pReg;
63 
64  while ( (pReg = this->regList.get()) ) {
65  pReg->state = fdReg::limbo;
66  pReg->destroy();
67  }
68  while ( (pReg = this->activeList.get()) ) {
69  pReg->state = fdReg::limbo;
70  pReg->destroy();
71  }
72  delete this->pTimerQueue;
73  delete [] this->fdSetsPtr;
75 }
76 
77 //
78 // fdManager::process()
79 //
80 LIBCOM_API void fdManager::process (double delay)
81 {
82  this->lazyInitTimerQueue ();
83 
84  //
85  // no recursion
86  //
87  if (this->processInProg) {
88  return;
89  }
90  this->processInProg = true;
91 
92  //
93  // One shot at expired timers prior to going into
94  // select. This allows zero delay timers to arm
95  // fd writes. We will never process the timer queue
96  // more than once here so that fd activity get serviced
97  // in a reasonable length of time.
98  //
99  double minDelay = this->pTimerQueue->process(epicsTime::getCurrent());
100 
101  if ( minDelay >= delay ) {
102  minDelay = delay;
103  }
104 
105  bool ioPending = false;
106  tsDLIter < fdReg > iter = this->regList.firstIter ();
107  while ( iter.valid () ) {
108  FD_SET(iter->getFD(), &this->fdSetsPtr[iter->getType()]);
109  ioPending = true;
110  ++iter;
111  }
112 
113  if ( ioPending ) {
114  struct timeval tv;
115  tv.tv_sec = static_cast<time_t> ( minDelay );
116  tv.tv_usec = static_cast<long> ( (minDelay-tv.tv_sec) * uSecPerSec );
117 
118  fd_set * pReadSet = & this->fdSetsPtr[fdrRead];
119  fd_set * pWriteSet = & this->fdSetsPtr[fdrWrite];
120  fd_set * pExceptSet = & this->fdSetsPtr[fdrException];
121  int status = select (this->maxFD, pReadSet, pWriteSet, pExceptSet, &tv);
122 
123  this->pTimerQueue->process(epicsTime::getCurrent());
124 
125  if ( status > 0 ) {
126 
127  //
128  // Look for activity
129  //
130  iter=this->regList.firstIter ();
131  while ( iter.valid () && status > 0 ) {
132  tsDLIter < fdReg > tmp = iter;
133  tmp++;
134  if (FD_ISSET(iter->getFD(), &this->fdSetsPtr[iter->getType()])) {
135  FD_CLR(iter->getFD(), &this->fdSetsPtr[iter->getType()]);
136  this->regList.remove(*iter);
137  this->activeList.add(*iter);
138  iter->state = fdReg::active;
139  status--;
140  }
141  iter = tmp;
142  }
143 
144  //
145  // I am careful to prevent problems if they access the
146  // above list while in a "callBack()" routine
147  //
148  fdReg * pReg;
149  while ( (pReg = this->activeList.get()) ) {
150  pReg->state = fdReg::limbo;
151 
152  //
153  // Tag current fdReg so that we
154  // can detect if it was deleted
155  // during the call back
156  //
157  this->pCBReg = pReg;
158  pReg->callBack();
159  if (this->pCBReg != NULL) {
160  //
161  // check only after we see that it is non-null so
162  // that we dont trigger bounds-checker dangling pointer
163  // error
164  //
165  assert (this->pCBReg==pReg);
166  this->pCBReg = 0;
167  if (pReg->onceOnly) {
168  pReg->destroy();
169  }
170  else {
171  this->regList.add(*pReg);
172  pReg->state = fdReg::pending;
173  }
174  }
175  }
176  }
177  else if ( status < 0 ) {
178  int errnoCpy = SOCKERRNO;
179 
180  // dont depend on flags being properly set if
181  // an error is retuned from select
182  for ( size_t i = 0u; i < fdrNEnums; i++ ) {
183  FD_ZERO ( &fdSetsPtr[i] );
184  }
185 
186  //
187  // print a message if its an unexpected error
188  //
189  if ( errnoCpy != SOCK_EINTR ) {
190  char sockErrBuf[64];
192  sockErrBuf, sizeof ( sockErrBuf ) );
193  fprintf ( stderr,
194  "fdManager: select failed because \"%s\"\n",
195  sockErrBuf );
196  }
197  }
198  }
199  else {
200  /*
201  * recover from subtle differences between
202  * windows sockets and UNIX sockets implementation
203  * of select()
204  */
205  epicsThreadSleep(minDelay);
206  this->pTimerQueue->process(epicsTime::getCurrent());
207  }
208  this->processInProg = false;
209  return;
210 }
211 
212 //
213 // fdReg::destroy()
214 // (default destroy method)
215 //
217 {
218  delete this;
219 }
220 
221 //
222 // fdReg::~fdReg()
223 //
225 {
226  this->manager.removeReg(*this);
227 }
228 
229 //
230 // fdReg::show()
231 //
232 void fdReg::show(unsigned level) const
233 {
234  printf ("fdReg at %p\n", (void *) this);
235  if (level>1u) {
236  printf ("\tstate = %d, onceOnly = %d\n",
237  this->state, this->onceOnly);
238  }
239  this->fdRegId::show(level);
240 }
241 
242 //
243 // fdRegId::show()
244 //
245 void fdRegId::show ( unsigned level ) const
246 {
247  printf ( "fdRegId at %p\n",
248  static_cast <const void *> ( this ) );
249  if ( level > 1u ) {
250  printf ( "\tfd = %d, type = %d\n",
251  int(this->fd), this->type );
252  }
253 }
254 
255 //
256 // fdManager::installReg ()
257 //
258 void fdManager::installReg (fdReg &reg)
259 {
260  this->maxFD = max ( this->maxFD, reg.getFD()+1 );
261  // Most applications will find that its important to push here to
262  // the front of the list so that transient writes get executed
263  // first allowing incoming read protocol to find that outgoing
264  // buffer space is newly available.
265  this->regList.push ( reg );
266  reg.state = fdReg::pending;
267 
268  int status = this->fdTbl.add ( reg );
269  if ( status != 0 ) {
270  throwWithLocation ( fdInterestSubscriptionAlreadyExits () );
271  }
272 }
273 
274 //
275 // fdManager::removeReg ()
276 //
277 void fdManager::removeReg (fdReg &regIn)
278 {
279  fdReg *pItemFound;
280 
281  pItemFound = this->fdTbl.remove (regIn);
282  if (pItemFound!=&regIn) {
283  fprintf(stderr,
284  "fdManager::removeReg() bad fd registration object\n");
285  return;
286  }
287 
288  //
289  // signal fdManager that the fdReg was deleted
290  // during the call back
291  //
292  if (this->pCBReg == &regIn) {
293  this->pCBReg = 0;
294  }
295 
296  switch (regIn.state) {
297  case fdReg::active:
298  this->activeList.remove (regIn);
299  break;
300  case fdReg::pending:
301  this->regList.remove (regIn);
302  break;
303  case fdReg::limbo:
304  break;
305  default:
306  //
307  // here if memory corrupted
308  //
309  assert(0);
310  }
311  regIn.state = fdReg::limbo;
312 
313  FD_CLR(regIn.getFD(), &this->fdSetsPtr[regIn.getType()]);
314 }
315 
316 //
317 // fdManager::reschedule ()
318 // NOOP - this only runs single threaded, and therefore they can only
319 // add a new timer from places that will always end up in a reschedule
320 //
321 void fdManager::reschedule ()
322 {
323 }
324 
325 double fdManager::quantum ()
326 {
327  return this->sleepQuantum;
328 }
329 
330 //
331 // lookUpFD()
332 //
333 LIBCOM_API fdReg *fdManager::lookUpFD (const SOCKET fd, const fdRegType type)
334 {
335  if (fd<0) {
336  return NULL;
337  }
338  fdRegId id (fd,type);
339  return this->fdTbl.lookup(id);
340 }
341 
342 //
343 // fdReg::fdReg()
344 //
345 fdReg::fdReg (const SOCKET fdIn, const fdRegType typIn,
346  const bool onceOnlyIn, fdManager &managerIn) :
347  fdRegId (fdIn,typIn), state (limbo),
348  onceOnly (onceOnlyIn), manager (managerIn)
349 {
350  if (!FD_IN_FDSET(fdIn)) {
351  fprintf (stderr, "%s: fd > FD_SETSIZE ignored\n",
352  __FILE__);
353  return;
354  }
355  this->manager.installReg (*this);
356 }
357 
358 template class resTable<fdReg, fdRegId>;
virtual void destroy()
Definition: fdManager.cpp:216
#define max(x, y)
Definition: flexdef.h:81
#define assert(exp)
Declare that a condition should be true.
Definition: epicsAssert.h:70
void add(T &item)
Definition: tsDLList.h:313
fdRegType getType() const
Definition: fdManager.h:48
pvd::Status status
An EPICS-specific replacement for ANSI C&#39;s assert.
fdManager fileDescriptorManager
Definition: fdManager.cpp:32
int i
Definition: scan.c:967
void osiSockRelease()
Definition: osdSock.c:62
int add(T &res)
Definition: resourceLib.h:643
virtual ~fdReg()
Definition: fdManager.cpp:224
LIBCOM_API double epicsStdCall epicsThreadSleepQuantum(void)
Query a value approximating the OS timer/scheduler resolution.
Definition: osdThread.c:981
tsDLIterConst< T > firstIter() const
Definition: tsDLList.h:459
#define printf
Definition: epicsStdio.h:41
pvd::StructureConstPtr type
fdRegType
Definition: fdManager.h:29
#define NULL
Definition: catime.c:38
const unsigned uSecPerSec
Definition: fdManager.cpp:35
bool valid() const
Definition: tsDLList.h:607
T * lookup(const ID &idIn) const
Definition: resourceLib.h:342
void epicsSocketConvertErrnoToString(char *pBuf, unsigned bufSize)
LIBCOM_API class fdReg * lookUpFD(const SOCKET fd, const fdRegType type)
Definition: fdManager.cpp:333
SOCKET getFD() const
Definition: fdManager.h:43
virtual void show(unsigned level) const
Definition: fdManager.cpp:245
BSD and SRV5 Unix timestamp.
Definition: epicsTime.h:52
int SOCKET
Definition: osdSock.h:31
int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)
LIBCOM_API void process(double delay)
Definition: fdManager.cpp:80
#define SOCKERRNO
Definition: osdSock.h:33
T * remove(const ID &idIn)
Definition: resourceLib.h:297
T * get()
Definition: tsDLList.h:261
fdReg(const SOCKET fdIn, const fdRegType type, const bool onceOnly=false, fdManager &manager=fileDescriptorManager)
Definition: fdManager.cpp:345
LIBCOM_API fdManager()
Definition: fdManager.cpp:43
void push(T &item)
Definition: tsDLList.h:416
#define SOCK_EINTR
Definition: osdSock.h:64
LIBCOM_API void epicsStdCall epicsThreadSleep(double seconds)
Block the calling thread for at least the specified time.
Definition: osdThread.c:790
int osiSockAttach()
Definition: osdSock.c:54
void remove(T &item)
Definition: tsDLList.h:219
#define stderr
Definition: epicsStdio.h:32
#define FD_IN_FDSET(FD)
Definition: osdSock.h:40
const unsigned mSecPerSec
Definition: fdManager.cpp:34
C++ and C descriptions for a thread.
virtual LIBCOM_API ~fdManager()
Definition: fdManager.cpp:60
#define throwWithLocation(parm)
virtual void show(unsigned level) const
Definition: fdManager.cpp:232
#define false
Definition: flexdef.h:85