This is Unofficial EPICS BASE Doxygen Site
ipAddrToAsciiAsynchronous.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 /*
12  * Author Jeffrey O. Hill
13  * johill@lanl.gov
14  */
15 
16 #include <string>
17 #include <climits>
18 #include <stdexcept>
19 #include <cstdio>
20 
21 //#define EPICS_FREELIST_DEBUG
22 #define EPICS_PRIVATE_API
23 
25 #include "epicsThread.h"
26 #include "epicsMutex.h"
27 #include "epicsEvent.h"
28 #include "epicsGuard.h"
29 #include "epicsExit.h"
30 #include "tsDLList.h"
31 #include "tsFreeList.h"
32 #include "errlog.h"
33 
34 // - this class implements the asynchronous DNS query
35 // - it completes early with the host name in dotted IP address form
36 // if the ipAddrToAsciiEngine is destroyed before IO completion
37 // or if there are too many items already in the engine's queue.
40  public tsDLNode < ipAddrToAsciiTransactionPrivate > {
41 public:
44  osiSockAddr address () const;
45  void show ( unsigned level ) const;
46  void * operator new ( size_t size, tsFreeList
47  < ipAddrToAsciiTransactionPrivate, 0x80 > & );
49  < ipAddrToAsciiTransactionPrivate, 0x80 > & ))
50  osiSockAddr addr;
53  bool pending;
55  void release ();
56  void operator delete ( void * );
57 private:
58  ipAddrToAsciiTransactionPrivate & operator = ( const ipAddrToAsciiTransactionPrivate & );
59  ipAddrToAsciiTransactionPrivate ( const ipAddrToAsciiTransactionPrivate & );
60 };
61 
62 #ifdef _MSC_VER
63 # pragma warning ( push )
64 # pragma warning ( disable:4660 )
65 #endif
66 
67 template class tsFreeList
69 
70 #ifdef _MSC_VER
71 # pragma warning ( pop )
72 #endif
73 
74 extern "C" {
75 static void ipAddrToAsciiEngineGlobalMutexConstruct ( void * );
76 }
77 
78 namespace {
79 struct ipAddrToAsciiGlobal : public epicsThreadRunable {
80  ipAddrToAsciiGlobal();
81  virtual ~ipAddrToAsciiGlobal() {}
82 
83  virtual void run ();
84 
85  char nameTmp [1024];
88  transactionFreeList;
90  mutable epicsMutex mutex;
91  epicsEvent laborEvent;
92  epicsEvent destructorBlockEvent;
93  epicsThread thread;
94  // pCurrent may be changed by any thread (worker or other)
95  ipAddrToAsciiTransactionPrivate * pCurrent;
96  // pActive may only be changed by the worker
97  ipAddrToAsciiTransactionPrivate * pActive;
98  unsigned cancelPendingCount;
99  bool exitFlag;
100  bool callbackInProgress;
101 };
102 }
103 
104 // - this class executes the synchronous DNS query
105 // - it creates one thread
107  public ipAddrToAsciiEngine {
108 public:
109  ipAddrToAsciiEnginePrivate() :refcount(1u), released(false) {}
111  void show ( unsigned level ) const;
112 
113  unsigned refcount;
114  bool released;
115 
116  static ipAddrToAsciiGlobal * pEngine;
117  ipAddrToAsciiTransaction & createTransaction ();
118  void release ();
119 
120 private:
122  ipAddrToAsciiEnginePrivate & operator = ( const ipAddrToAsciiEngine & );
123 };
124 
125 ipAddrToAsciiGlobal * ipAddrToAsciiEnginePrivate :: pEngine = 0;
126 static epicsThreadOnceId ipAddrToAsciiEngineGlobalMutexOnceFlag = EPICS_THREAD_ONCE_INIT;
127 
128 // the users are not required to supply a show routine
129 // for there transaction callback class
130 void ipAddrToAsciiCallBack::show ( unsigned /* level */ ) const {}
131 
132 // some noop pure virtual destructures
136 
137 static void ipAddrToAsciiEngineGlobalMutexConstruct ( void * )
138 {
139  try {
140  ipAddrToAsciiEnginePrivate::pEngine = new ipAddrToAsciiGlobal ();
141  } catch (std::exception& e) {
142  errlogPrintf("ipAddrToAsciiEnginePrivate ctor fails with: %s\n", e.what());
143  }
144 }
145 
146 void ipAddrToAsciiEngine::cleanup()
147 {
148  {
150  ipAddrToAsciiEnginePrivate::pEngine->exitFlag = true;
151  }
152  ipAddrToAsciiEnginePrivate::pEngine->laborEvent.signal();
153  ipAddrToAsciiEnginePrivate::pEngine->thread.exitWait();
156 }
157 
158 // for now its probably sufficent to allocate one
159 // DNS transaction thread for all codes sharing
160 // the same process that need DNS services but we
161 // leave our options open for the future
163 {
165  & ipAddrToAsciiEngineGlobalMutexOnceFlag,
166  ipAddrToAsciiEngineGlobalMutexConstruct, 0 );
168  throw std::runtime_error("ipAddrToAsciiEngine::allocate fails");
169  return * new ipAddrToAsciiEnginePrivate();
170 }
171 
172 ipAddrToAsciiGlobal::ipAddrToAsciiGlobal () :
173  mutex(__FILE__, __LINE__),
174  thread ( *this, "ipToAsciiProxy",
177  pCurrent ( 0 ), pActive ( 0 ), cancelPendingCount ( 0u ), exitFlag ( false ),
178  callbackInProgress ( false )
179 {
180  this->thread.start (); // start the thread
181 }
182 
183 
185 {
186  bool last;
187  {
188  epicsGuard < epicsMutex > guard ( this->pEngine->mutex );
189  if(released)
190  throw std::logic_error("Engine release() called again!");
191 
192  // released==true prevents new transactions
193  released = true;
194 
195  {
196  // cancel any pending transactions
197  tsDLIter < ipAddrToAsciiTransactionPrivate > it(pEngine->labor.firstIter());
198  while(it.valid()) {
199  ipAddrToAsciiTransactionPrivate *trn = it.pointer();
200  ++it;
201 
202  if(this==&trn->engine) {
203  trn->pending = false;
204  pEngine->labor.remove(*trn);
205  }
206  }
207 
208  // cancel transaction in lookup or callback
209  if (pEngine->pCurrent && this==&pEngine->pCurrent->engine) {
210  pEngine->pCurrent->pending = false;
211  pEngine->pCurrent = 0;
212  }
213 
214  // wait for completion of in-progress callback
215  pEngine->cancelPendingCount++;
216  while(pEngine->pActive && this==&pEngine->pActive->engine
217  && ! pEngine->thread.isCurrentThread()) {
218  epicsGuardRelease < epicsMutex > unguard ( guard );
219  pEngine->destructorBlockEvent.wait();
220  }
221  pEngine->cancelPendingCount--;
222  if(pEngine->cancelPendingCount)
223  pEngine->destructorBlockEvent.signal();
224  }
225 
226  assert(refcount>0);
227  last = 0==--refcount;
228  }
229  if(last) {
230  delete this;
231  }
232 }
233 
234 void ipAddrToAsciiEnginePrivate::show ( unsigned level ) const
235 {
236  epicsGuard < epicsMutex > guard ( this->pEngine->mutex );
237  printf ( "ipAddrToAsciiEngine at %p with %u requests pending\n",
238  static_cast <const void *> (this), this->pEngine->labor.count () );
239  if ( level > 0u ) {
241  pItem = this->pEngine->labor.firstIter ();
242  while ( pItem.valid () ) {
243  pItem->show ( level - 1u );
244  pItem++;
245  }
246  }
247  if ( level > 1u ) {
248  printf ( "mutex:\n" );
249  this->pEngine->mutex.show ( level - 2u );
250  printf ( "laborEvent:\n" );
251  this->pEngine->laborEvent.show ( level - 2u );
252  printf ( "exitFlag boolean = %u\n", this->pEngine->exitFlag );
253  printf ( "exit event:\n" );
254  }
255 }
256 
257 inline void * ipAddrToAsciiTransactionPrivate::operator new ( size_t size, tsFreeList
258  < ipAddrToAsciiTransactionPrivate, 0x80 > & freeList )
259 {
260  return freeList.allocate ( size );
261 }
262 
263 #ifdef CXX_PLACEMENT_DELETE
264 inline void ipAddrToAsciiTransactionPrivate::operator delete ( void * pTrans, tsFreeList
265  < ipAddrToAsciiTransactionPrivate, 0x80 > & freeList )
266 {
267  freeList.release ( pTrans );
268 }
269 #endif
270 
271 void ipAddrToAsciiTransactionPrivate::operator delete ( void * )
272 {
273  // Visual C++ .net appears to require operator delete if
274  // placement operator delete is defined? I smell a ms rat
275  // because if I declare placement new and delete, but
276  // comment out the placement delete definition there are
277  // no undefined symbols.
278  errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked",
279  __FILE__, __LINE__ );
280 }
281 
282 
284 {
285  epicsGuard <epicsMutex> G(this->pEngine->mutex);
286  if(this->released)
287  throw std::logic_error("createTransaction() on release()'d ipAddrToAsciiEngine");
288 
289  assert(this->refcount>0);
290 
291  ipAddrToAsciiTransactionPrivate *ret = new ( this->pEngine->transactionFreeList ) ipAddrToAsciiTransactionPrivate ( *this );
292 
293  this->refcount++;
294 
295  return * ret;
296 }
297 
298 void ipAddrToAsciiGlobal::run ()
299 {
300  epicsGuard < epicsMutex > guard ( this->mutex );
301  while ( ! this->exitFlag ) {
302  {
303  epicsGuardRelease < epicsMutex > unguard ( guard );
304  this->laborEvent.wait ();
305  }
306  while ( true ) {
307  ipAddrToAsciiTransactionPrivate * pItem = this->labor.get ();
308  if ( ! pItem ) {
309  break;
310  }
311  osiSockAddr addr = pItem->addr;
312  this->pCurrent = pItem;
313 
314  if ( this->exitFlag )
315  {
316  sockAddrToDottedIP ( & addr.sa, this->nameTmp,
317  sizeof ( this->nameTmp ) );
318  }
319  else {
320  epicsGuardRelease < epicsMutex > unguard ( guard );
321  // depending on DNS configuration, this could take a very long time
322  // so we release the lock
323  sockAddrToA ( &addr.sa, this->nameTmp, sizeof ( this->nameTmp ) );
324  }
325 
326  // the ipAddrToAsciiTransactionPrivate destructor is allowed to
327  // set pCurrent to nill and avoid blocking on a slow DNS
328  // operation
329  if ( ! this->pCurrent ) {
330  continue;
331  }
332 
333  // fix for lp:1580623
334  // a destructing cac sets pCurrent to NULL, so
335  // make local copy to avoid race when releasing the guard
336  ipAddrToAsciiTransactionPrivate *pCur = pActive = pCurrent;
337  this->callbackInProgress = true;
338 
339  {
340  epicsGuardRelease < epicsMutex > unguard ( guard );
341  // dont call callback with lock applied
342  pCur->pCB->transactionComplete ( this->nameTmp );
343  }
344 
345  this->callbackInProgress = false;
346  pActive = 0;
347 
348  if ( this->pCurrent ) {
349  this->pCurrent->pending = false;
350  this->pCurrent = 0;
351  }
352  if ( this->cancelPendingCount ) {
353  this->destructorBlockEvent.signal ();
354  }
355  }
356  }
357 }
358 
360  ( ipAddrToAsciiEnginePrivate & engineIn ) :
361  engine ( engineIn ), pCB ( 0 ), pending ( false )
362 {
363  memset ( & this->addr, '\0', sizeof ( this->addr ) );
364  this->addr.sa.sa_family = AF_UNSPEC;
365 }
366 
368 {
370  this->engine.pEngine->transactionFreeList.release ( this );
371 }
372 
374 {
375  ipAddrToAsciiGlobal *pGlobal = this->engine.pEngine;
376  bool last;
377  {
378  epicsGuard < epicsMutex > guard ( pGlobal->mutex );
379  while ( this->pending ) {
380  if ( pGlobal->pCurrent == this &&
381  pGlobal->callbackInProgress &&
382  ! pGlobal->thread.isCurrentThread() ) {
383  // cancel from another thread while callback in progress
384  // waits for callback to complete
385  assert ( pGlobal->cancelPendingCount < UINT_MAX );
386  pGlobal->cancelPendingCount++;
387  {
388  epicsGuardRelease < epicsMutex > unguard ( guard );
389  pGlobal->destructorBlockEvent.wait ();
390  }
391  assert ( pGlobal->cancelPendingCount > 0u );
392  pGlobal->cancelPendingCount--;
393  if ( ! this->pending ) {
394  if ( pGlobal->cancelPendingCount ) {
395  pGlobal->destructorBlockEvent.signal ();
396  }
397  break;
398  }
399  }
400  else {
401  if ( pGlobal->pCurrent == this ) {
402  // cancel from callback, or while lookup in progress
403  pGlobal->pCurrent = 0;
404  }
405  else {
406  // cancel before lookup starts
407  pGlobal->labor.remove ( *this );
408  }
409  this->pending = false;
410  }
411  }
412  assert(this->engine.refcount>0);
413  last = 0==--this->engine.refcount;
414  }
415  if(last) {
416  delete &this->engine;
417  }
418 }
419 
421  const osiSockAddr & addrIn, ipAddrToAsciiCallBack & cbIn )
422 {
423  bool success;
424  ipAddrToAsciiGlobal *pGlobal = this->engine.pEngine;
425 
426  {
427  epicsGuard < epicsMutex > guard ( pGlobal->mutex );
428 
429  if (this->engine.released) {
430  errlogPrintf("Warning: ipAddrToAscii on transaction with release()'d ipAddrToAsciiEngine");
431  success = false;
432 
433  } else if ( !this->pending && pGlobal->labor.count () < 16u ) {
434  // put some reasonable limit on queue expansion
435  this->addr = addrIn;
436  this->pCB = & cbIn;
437  this->pending = true;
438  pGlobal->labor.add ( *this );
439  success = true;
440  }
441  else {
442  success = false;
443  }
444  }
445 
446  if ( success ) {
447  pGlobal->laborEvent.signal ();
448  }
449  else {
450  char autoNameTmp[256];
451  sockAddrToDottedIP ( & addrIn.sa, autoNameTmp,
452  sizeof ( autoNameTmp ) );
453  cbIn.transactionComplete ( autoNameTmp );
454  }
455 }
456 
458 {
459  return this->addr;
460 }
461 
462 void ipAddrToAsciiTransactionPrivate::show ( unsigned level ) const
463 {
464  epicsGuard < epicsMutex > guard ( this->engine.pEngine->mutex );
465  char ipAddr [64];
466  sockAddrToDottedIP ( &this->addr.sa, ipAddr, sizeof ( ipAddr ) );
467  printf ( "ipAddrToAsciiTransactionPrivate for address %s\n", ipAddr );
468  if ( level > 0u ) {
469  printf ( "\tengine %p\n",
470  static_cast <void *> ( & this->engine ) );
471  this->pCB->show ( level - 1u );
472  }
473 }
unsigned epicsStdCall sockAddrToDottedIP(const struct sockaddr *paddr, char *pBuf, unsigned bufSize)
Definition: osiSock.c:118
ipAddrToAsciiTransactionPrivate(class ipAddrToAsciiEnginePrivate &engineIn)
#define assert(exp)
Declare that a condition should be true.
Definition: epicsAssert.h:70
virtual void transactionComplete(const char *pHostName)=0
struct sockaddr sa
Definition: osiSock.h:158
#define printf
Definition: epicsStdio.h:41
void ipAddrToAscii(const osiSockAddr &, ipAddrToAsciiCallBack &)
virtual void show(unsigned level) const
bool valid() const
Definition: tsDLList.h:607
LIBCOM_API unsigned int epicsStdCall epicsThreadGetStackSize(epicsThreadStackSizeClass size)
Definition: osdThread.c:466
ipAddrToAsciiTransaction & createTransaction()
#define EPICS_THREAD_ONCE_INIT
Definition: epicsThread.h:109
epicsMutex mutex
Definition: pvAccess.cpp:71
APIs for the epicsMutex mutual exclusion semaphore.
LIBCOM_API void epicsStdCall epicsThreadOnce(epicsThreadOnceId *id, EPICSTHREADFUNC, void *arg)
#define epicsThreadPriorityLow
Definition: epicsThread.h:75
Extended replacement for the Posix exit and atexit routines.
static ipAddrToAsciiGlobal * pEngine
int errlogPrintf(const char *pFormat,...)
Definition: errlog.c:105
APIs for the epicsEvent binary semaphore.
static ipAddrToAsciiEngine & allocate()
unsigned epicsStdCall sockAddrToA(const struct sockaddr *paddr, char *pBuf, unsigned bufSize)
Definition: osiSock.c:61
void show(unsigned level) const
C++ and C descriptions for a thread.
#define false
Definition: flexdef.h:85
epicsPlacementDeleteOperator((void *, tsFreeList< ipAddrToAsciiTransactionPrivate, 0x80 > &)) osiSockAddr addr