PageRenderTime 51ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/ATF2/control-software/epics-3.14.10/base/src/libCom/misc/ipAddrToAsciiAsynchronous.cpp

http://atf2flightsim.googlecode.com/
C++ | 450 lines | 354 code | 40 blank | 56 comment | 35 complexity | 710779b87f436c446621f89e32ecbc30 MD5 | raw file
Possible License(s): BSD-2-Clause, LGPL-2.0, IPL-1.0, BSD-3-Clause
  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. *
  13. * L O S A L A M O S
  14. * Los Alamos National Laboratory
  15. * Los Alamos, New Mexico 87545
  16. *
  17. * Copyright, 1986, The Regents of the University of California.
  18. *
  19. *
  20. * Author Jeffrey O. Hill
  21. * johill@lanl.gov
  22. */
  23. #include <string>
  24. #include <climits>
  25. #include <stdexcept>
  26. #include <stdio.h>
  27. #define epicsExportSharedSymbols
  28. #include "ipAddrToAsciiAsynchronous.h"
  29. #include "epicsThread.h"
  30. #include "epicsMutex.h"
  31. #include "epicsEvent.h"
  32. #include "epicsGuard.h"
  33. #include "epicsExit.h"
  34. #include "tsDLList.h"
  35. #include "tsFreeList.h"
  36. #include "errlog.h"
  37. // - this class implements the asynchronous DNS query
  38. // - it completes early with the host name in dotted IP address form
  39. // if the ipAddrToAsciiEngine is destroyed before IO completion
  40. // or if there are too many items already in the engine's queue.
  41. class ipAddrToAsciiTransactionPrivate :
  42. public ipAddrToAsciiTransaction,
  43. public tsDLNode < ipAddrToAsciiTransactionPrivate > {
  44. public:
  45. ipAddrToAsciiTransactionPrivate ( class ipAddrToAsciiEnginePrivate & engineIn );
  46. virtual ~ipAddrToAsciiTransactionPrivate ();
  47. osiSockAddr address () const;
  48. void show ( unsigned level ) const;
  49. void * operator new ( size_t size, tsFreeList
  50. < ipAddrToAsciiTransactionPrivate, 0x80 > & );
  51. epicsPlacementDeleteOperator (( void *, tsFreeList
  52. < ipAddrToAsciiTransactionPrivate, 0x80 > & ))
  53. private:
  54. osiSockAddr addr;
  55. ipAddrToAsciiEnginePrivate & engine;
  56. ipAddrToAsciiCallBack * pCB;
  57. bool pending;
  58. void ipAddrToAscii ( const osiSockAddr &, ipAddrToAsciiCallBack & );
  59. void release ();
  60. void * operator new ( size_t );
  61. void operator delete ( void * );
  62. friend class ipAddrToAsciiEnginePrivate;
  63. ipAddrToAsciiTransactionPrivate & operator = ( const ipAddrToAsciiTransactionPrivate & );
  64. ipAddrToAsciiTransactionPrivate ( const ipAddrToAsciiTransactionPrivate & );
  65. };
  66. #ifdef _MSC_VER
  67. # pragma warning ( push )
  68. # pragma warning ( disable:4660 )
  69. #endif
  70. template class tsFreeList
  71. < ipAddrToAsciiTransactionPrivate, 0x80 >;
  72. #ifdef _MSC_VER
  73. # pragma warning ( pop )
  74. #endif
  75. extern "C" {
  76. static void ipAddrToAsciiEngineGlobalMutexConstruct ( void * );
  77. static void ipAddrToAsciiEngineShutdownRequest ( void * );
  78. }
  79. // - this class executes the synchronous DNS query
  80. // - it creates one thread
  81. class ipAddrToAsciiEnginePrivate :
  82. public ipAddrToAsciiEngine,
  83. public epicsThreadRunable {
  84. public:
  85. ipAddrToAsciiEnginePrivate ();
  86. virtual ~ipAddrToAsciiEnginePrivate ();
  87. void show ( unsigned level ) const;
  88. private:
  89. char nameTmp [1024];
  90. tsFreeList
  91. < ipAddrToAsciiTransactionPrivate, 0x80 >
  92. transactionFreeList;
  93. tsDLList < ipAddrToAsciiTransactionPrivate > labor;
  94. mutable epicsMutex mutex;
  95. epicsEvent laborEvent;
  96. epicsEvent destructorBlockEvent;
  97. epicsThread thread;
  98. ipAddrToAsciiTransactionPrivate * pCurrent;
  99. unsigned cancelPendingCount;
  100. bool exitFlag;
  101. bool callbackInProgress;
  102. static epicsMutex * pGlobalMutex;
  103. static ipAddrToAsciiEnginePrivate * pEngine;
  104. static unsigned numberOfReferences;
  105. static bool shutdownRequest;
  106. ipAddrToAsciiTransaction & createTransaction ();
  107. void release ();
  108. void run ();
  109. ipAddrToAsciiEnginePrivate ( const ipAddrToAsciiEngine & );
  110. ipAddrToAsciiEnginePrivate & operator = ( const ipAddrToAsciiEngine & );
  111. friend class ipAddrToAsciiEngine;
  112. friend class ipAddrToAsciiTransactionPrivate;
  113. friend void ipAddrToAsciiEngineShutdownRequest ( void * );
  114. friend void ipAddrToAsciiEngineGlobalMutexConstruct ( void * );
  115. };
  116. epicsMutex * ipAddrToAsciiEnginePrivate :: pGlobalMutex = 0;
  117. ipAddrToAsciiEnginePrivate * ipAddrToAsciiEnginePrivate :: pEngine = 0;
  118. unsigned ipAddrToAsciiEnginePrivate :: numberOfReferences = 0u;
  119. bool ipAddrToAsciiEnginePrivate :: shutdownRequest = false;
  120. static epicsThreadOnceId ipAddrToAsciiEngineGlobalMutexOnceFlag = 0;
  121. // the users are not required to supply a show routine
  122. // for there transaction callback class
  123. void ipAddrToAsciiCallBack::show ( unsigned /* level */ ) const {}
  124. // some noop pure virtual destructures
  125. ipAddrToAsciiCallBack::~ipAddrToAsciiCallBack () {}
  126. ipAddrToAsciiTransaction::~ipAddrToAsciiTransaction () {}
  127. ipAddrToAsciiEngine::~ipAddrToAsciiEngine () {}
  128. static void ipAddrToAsciiEngineShutdownRequest ( void * )
  129. {
  130. bool deleteGlobalMutexCondDetected = false;
  131. {
  132. epicsGuard < epicsMutex >
  133. guard ( * ipAddrToAsciiEnginePrivate :: pGlobalMutex );
  134. ipAddrToAsciiEnginePrivate :: shutdownRequest = true;
  135. deleteGlobalMutexCondDetected =
  136. ( ipAddrToAsciiEnginePrivate :: numberOfReferences == 0 );
  137. }
  138. if ( deleteGlobalMutexCondDetected ) {
  139. delete ipAddrToAsciiEnginePrivate :: pGlobalMutex;
  140. ipAddrToAsciiEnginePrivate :: pGlobalMutex = 0;
  141. }
  142. }
  143. static void ipAddrToAsciiEngineGlobalMutexConstruct ( void * )
  144. {
  145. ipAddrToAsciiEnginePrivate :: pGlobalMutex = new epicsMutex ();
  146. epicsAtExit ( ipAddrToAsciiEngineShutdownRequest, 0 );
  147. }
  148. // for now its probably sufficent to allocate one
  149. // DNS transaction thread for all codes sharing
  150. // the same process that need DNS services but we
  151. // leave our options open for the future
  152. ipAddrToAsciiEngine & ipAddrToAsciiEngine::allocate ()
  153. {
  154. epicsThreadOnce (
  155. & ipAddrToAsciiEngineGlobalMutexOnceFlag,
  156. ipAddrToAsciiEngineGlobalMutexConstruct, 0 );
  157. // since we must not own lock when checking this flag
  158. // this diagnostic has imperfect detection, but never
  159. // incorrect detection
  160. if ( ipAddrToAsciiEnginePrivate :: shutdownRequest ) {
  161. throw std :: runtime_error (
  162. "ipAddrToAsciiEngine::allocate (): "
  163. "attempts to create an "
  164. "ipAddrToAsciiEngine while the exit "
  165. "handlers are running are rejected");
  166. }
  167. epicsGuard < epicsMutex > guard ( * ipAddrToAsciiEnginePrivate::pGlobalMutex );
  168. if ( ! ipAddrToAsciiEnginePrivate::pEngine ) {
  169. ipAddrToAsciiEnginePrivate::pEngine = new ipAddrToAsciiEnginePrivate ();
  170. }
  171. ipAddrToAsciiEnginePrivate::numberOfReferences++;
  172. return * ipAddrToAsciiEnginePrivate::pEngine;
  173. }
  174. ipAddrToAsciiEnginePrivate::ipAddrToAsciiEnginePrivate () :
  175. thread ( *this, "ipToAsciiProxy",
  176. epicsThreadGetStackSize(epicsThreadStackBig),
  177. epicsThreadPriorityLow ),
  178. pCurrent ( 0 ), cancelPendingCount ( 0u ), exitFlag ( false ),
  179. callbackInProgress ( false )
  180. {
  181. this->thread.start (); // start the thread
  182. }
  183. ipAddrToAsciiEnginePrivate::~ipAddrToAsciiEnginePrivate ()
  184. {
  185. {
  186. epicsGuard < epicsMutex > guard ( this->mutex );
  187. this->exitFlag = true;
  188. }
  189. this->laborEvent.signal ();
  190. this->thread.exitWait ();
  191. }
  192. // for now its probably sufficent to allocate one
  193. // DNS transaction thread for all codes sharing
  194. // the same process that need DNS services but we
  195. // leave our options open for the future
  196. void ipAddrToAsciiEnginePrivate::release ()
  197. {
  198. bool deleteGlobalMutexCondDetected = false;
  199. epicsThreadOnce (
  200. & ipAddrToAsciiEngineGlobalMutexOnceFlag,
  201. ipAddrToAsciiEngineGlobalMutexConstruct, 0 );
  202. {
  203. epicsGuard < epicsMutex >
  204. guard ( * ipAddrToAsciiEnginePrivate::pGlobalMutex );
  205. assert ( ipAddrToAsciiEnginePrivate::numberOfReferences > 0u );
  206. ipAddrToAsciiEnginePrivate::numberOfReferences--;
  207. if ( ipAddrToAsciiEnginePrivate::numberOfReferences == 0u ) {
  208. deleteGlobalMutexCondDetected =
  209. ipAddrToAsciiEnginePrivate :: shutdownRequest;
  210. delete ipAddrToAsciiEnginePrivate :: pEngine;
  211. ipAddrToAsciiEnginePrivate :: pEngine = 0;
  212. }
  213. }
  214. if ( deleteGlobalMutexCondDetected ) {
  215. delete ipAddrToAsciiEnginePrivate :: pGlobalMutex;
  216. ipAddrToAsciiEnginePrivate :: pGlobalMutex = 0;
  217. }
  218. }
  219. void ipAddrToAsciiEnginePrivate::show ( unsigned level ) const
  220. {
  221. epicsGuard < epicsMutex > guard ( this->mutex );
  222. printf ( "ipAddrToAsciiEngine at %p with %u requests pending\n",
  223. static_cast <const void *> (this), this->labor.count () );
  224. if ( level > 0u ) {
  225. tsDLIterConst < ipAddrToAsciiTransactionPrivate >
  226. pItem = this->labor.firstIter ();
  227. while ( pItem.valid () ) {
  228. pItem->show ( level - 1u );
  229. pItem++;
  230. }
  231. }
  232. if ( level > 1u ) {
  233. printf ( "mutex:\n" );
  234. this->mutex.show ( level - 2u );
  235. printf ( "laborEvent:\n" );
  236. this->laborEvent.show ( level - 2u );
  237. printf ( "exitFlag boolean = %u\n", this->exitFlag );
  238. printf ( "exit event:\n" );
  239. }
  240. }
  241. inline void * ipAddrToAsciiTransactionPrivate::operator new ( size_t size, tsFreeList
  242. < ipAddrToAsciiTransactionPrivate, 0x80 > & freeList )
  243. {
  244. return freeList.allocate ( size );
  245. }
  246. #ifdef CXX_PLACEMENT_DELETE
  247. inline void ipAddrToAsciiTransactionPrivate::operator delete ( void * pTrans, tsFreeList
  248. < ipAddrToAsciiTransactionPrivate, 0x80 > & freeList )
  249. {
  250. freeList.release ( pTrans );
  251. }
  252. #endif
  253. void * ipAddrToAsciiTransactionPrivate::operator new ( size_t ) // X aCC 361
  254. {
  255. // The HPUX compiler seems to require this even though no code
  256. // calls it directly
  257. throw std::logic_error ( "why is the compiler calling private operator new" );
  258. }
  259. void ipAddrToAsciiTransactionPrivate::operator delete ( void * )
  260. {
  261. // Visual C++ .net appears to require operator delete if
  262. // placement operator delete is defined? I smell a ms rat
  263. // because if I declare placement new and delete, but
  264. // comment out the placement delete definition there are
  265. // no undefined symbols.
  266. errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked",
  267. __FILE__, __LINE__ );
  268. }
  269. ipAddrToAsciiTransaction & ipAddrToAsciiEnginePrivate::createTransaction ()
  270. {
  271. return * new ( this->transactionFreeList ) ipAddrToAsciiTransactionPrivate ( *this );
  272. }
  273. void ipAddrToAsciiEnginePrivate::run ()
  274. {
  275. epicsGuard < epicsMutex > guard ( this->mutex );
  276. while ( ! this->exitFlag ) {
  277. {
  278. epicsGuardRelease < epicsMutex > unguard ( guard );
  279. this->laborEvent.wait ();
  280. }
  281. while ( true ) {
  282. ipAddrToAsciiTransactionPrivate * pItem = this->labor.get ();
  283. if ( ! pItem ) {
  284. break;
  285. }
  286. osiSockAddr addr = pItem->addr;
  287. this->pCurrent = pItem;
  288. if ( this->exitFlag )
  289. {
  290. sockAddrToDottedIP ( & addr.sa, this->nameTmp,
  291. sizeof ( this->nameTmp ) );
  292. }
  293. else {
  294. epicsGuardRelease < epicsMutex > unguard ( guard );
  295. // depending on DNS configuration, this could take a very long time
  296. // so we release the lock
  297. sockAddrToA ( &addr.sa, this->nameTmp, sizeof ( this->nameTmp ) );
  298. }
  299. // the ipAddrToAsciiTransactionPrivate destructor is allowed to
  300. // set pCurrent to nill and avoid blocking on a slow DNS
  301. // operation
  302. if ( ! this->pCurrent ) {
  303. continue;
  304. }
  305. this->callbackInProgress = true;
  306. {
  307. epicsGuardRelease < epicsMutex > unguard ( guard );
  308. // dont call callback with lock applied
  309. this->pCurrent->pCB->transactionComplete ( this->nameTmp );
  310. }
  311. this->callbackInProgress = false;
  312. if ( this->pCurrent ) {
  313. this->pCurrent->pending = false;
  314. this->pCurrent = 0;
  315. }
  316. if ( this->cancelPendingCount ) {
  317. this->destructorBlockEvent.signal ();
  318. }
  319. }
  320. }
  321. }
  322. ipAddrToAsciiTransactionPrivate::ipAddrToAsciiTransactionPrivate
  323. ( ipAddrToAsciiEnginePrivate & engineIn ) :
  324. engine ( engineIn ), pCB ( 0 ), pending ( false )
  325. {
  326. memset ( & this->addr, '\0', sizeof ( this->addr ) );
  327. this->addr.sa.sa_family = AF_UNSPEC;
  328. }
  329. void ipAddrToAsciiTransactionPrivate::release ()
  330. {
  331. this->~ipAddrToAsciiTransactionPrivate ();
  332. this->engine.transactionFreeList.release ( this );
  333. }
  334. ipAddrToAsciiTransactionPrivate::~ipAddrToAsciiTransactionPrivate ()
  335. {
  336. epicsGuard < epicsMutex > guard ( this->engine.mutex );
  337. while ( this->pending ) {
  338. if ( this->engine.pCurrent == this &&
  339. this->engine.callbackInProgress &&
  340. ! this->engine.thread.isCurrentThread() ) {
  341. assert ( this->engine.cancelPendingCount < UINT_MAX );
  342. this->engine.cancelPendingCount++;
  343. {
  344. epicsGuardRelease < epicsMutex > unguard ( guard );
  345. this->engine.destructorBlockEvent.wait ();
  346. }
  347. assert ( this->engine.cancelPendingCount > 0u );
  348. this->engine.cancelPendingCount--;
  349. if ( ! this->pending ) {
  350. if ( this->engine.cancelPendingCount ) {
  351. this->engine.destructorBlockEvent.signal ();
  352. }
  353. break;
  354. }
  355. }
  356. else {
  357. if ( this->engine.pCurrent == this ) {
  358. this->engine.pCurrent = 0;
  359. }
  360. else {
  361. this->engine.labor.remove ( *this );
  362. }
  363. this->pending = false;
  364. }
  365. }
  366. }
  367. void ipAddrToAsciiTransactionPrivate::ipAddrToAscii (
  368. const osiSockAddr & addrIn, ipAddrToAsciiCallBack & cbIn )
  369. {
  370. bool success;
  371. {
  372. epicsGuard < epicsMutex > guard ( this->engine.mutex );
  373. // put some reasonable limit on queue expansion
  374. if ( !this->pending && engine.labor.count () < 16u ) {
  375. this->addr = addrIn;
  376. this->pCB = & cbIn;
  377. this->pending = true;
  378. this->engine.labor.add ( *this );
  379. success = true;
  380. }
  381. else {
  382. success = false;
  383. }
  384. }
  385. if ( success ) {
  386. this->engine.laborEvent.signal ();
  387. }
  388. else {
  389. char autoNameTmp[256];
  390. sockAddrToDottedIP ( & addrIn.sa, autoNameTmp,
  391. sizeof ( autoNameTmp ) );
  392. cbIn.transactionComplete ( autoNameTmp );
  393. }
  394. }
  395. osiSockAddr ipAddrToAsciiTransactionPrivate::address () const
  396. {
  397. return this->addr;
  398. }
  399. void ipAddrToAsciiTransactionPrivate::show ( unsigned level ) const
  400. {
  401. epicsGuard < epicsMutex > guard ( this->engine.mutex );
  402. char ipAddr [64];
  403. sockAddrToDottedIP ( &this->addr.sa, ipAddr, sizeof ( ipAddr ) );
  404. printf ( "ipAddrToAsciiTransactionPrivate for address %s\n", ipAddr );
  405. if ( level > 0u ) {
  406. printf ( "\tengine %p\n",
  407. static_cast <void *> ( & this->engine ) );
  408. this->pCB->show ( level - 1u );
  409. }
  410. }