PageRenderTime 35ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/libdangerous-snmp-base/iosystem.cpp

https://bitbucket.org/tekkamanendless/dangerous-snmp
C++ | 608 lines | 458 code | 82 blank | 68 comment | 199 complexity | dd1c95a79e577e5d25aabfb4241d6156 MD5 | raw file
  1. #include "iosystem.hpp"
  2. #include "dangerous/snmp/logger.hpp"
  3. #include <iostream>
  4. #include <stdexcept>
  5. #include <sys/epoll.h>
  6. #include <sys/types.h>
  7. #include <sys/socket.h>
  8. #include <unistd.h>
  9. namespace dangerous { namespace snmp {
  10. IoSystem::IoSystem() throw( Exception ) {
  11. isStopped = true;
  12. int result = socketpair( AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0 /* protocol */, localSockets );
  13. if( result < 0 ) {
  14. throw Exception( "Could not create the local sockets." );
  15. }
  16. }
  17. IoSystem::~IoSystem() {
  18. if( logger.system( Logger::IO ) ) {
  19. logger.out() << "IoSystem::~IoSystem: Shutting down localSockets[0]." << std::endl;
  20. }
  21. shutdown( localSockets[ 0 ], SHUT_RDWR );
  22. close( localSockets[ 0 ] );
  23. if( logger.system( Logger::IO ) ) {
  24. logger.out() << "IoSystem::~IoSystem: Shutting down localSockets[1]." << std::endl;
  25. }
  26. shutdown( localSockets[ 1 ], SHUT_RDWR );
  27. close( localSockets[ 1 ] );
  28. if( logger.system( Logger::IO ) ) {
  29. logger.out() << "IoSystem::~IoSystem: Destructor complete." << std::endl;
  30. }
  31. }
  32. void IoSystem::registerInput( int handle, std::shared_ptr<ByteStream> byteStream ) {
  33. lock.lock();
  34. if( inMap.find( handle ) == inMap.end() ) {
  35. if( logger.system( Logger::IO ) ) {
  36. logger.out() << "IoSystem::registerInput: Registering input handle " << handle << "." << std::endl;
  37. }
  38. byteStream->setLive( true );
  39. inMap[ handle ] = byteStream;
  40. } else {
  41. // TODO: Double-register?
  42. if( logger.system( Logger::IO ) ) {
  43. logger.out() << "IoSystem::registerInput: Handle " << handle << " is double-registered." << std::endl;
  44. }
  45. }
  46. lock.unlock();
  47. wakeup();
  48. }
  49. void IoSystem::registerOutput( int handle, std::shared_ptr<ByteStream> byteStream ) {
  50. lock.lock();
  51. if( outMap.find( handle ) == outMap.end() ) {
  52. if( logger.system( Logger::IO ) ) {
  53. logger.out() << "IoSystem::registerInput: Registering output handle " << handle << "." << std::endl;
  54. }
  55. outMap[ handle ] = byteStream;
  56. } else {
  57. // TODO: Double-register?
  58. if( logger.system( Logger::IO ) ) {
  59. logger.out() << "IoSystem::registerOutput: Handle " << handle << " is double-registered." << std::endl;
  60. }
  61. }
  62. lock.unlock();
  63. wakeup();
  64. }
  65. void IoSystem::unregisterInput( int handle ) {
  66. lock.lock();
  67. if( inMap.find( handle ) != inMap.end() ) {
  68. if( logger.system( Logger::IO ) ) {
  69. logger.out() << "IoSystem::registerInput: Unregistering input handle " << handle << "." << std::endl;
  70. }
  71. inMap[ handle ]->setLive( false );
  72. inMap.erase( handle );
  73. } else {
  74. // TODO: never registered?
  75. if( logger.system( Logger::IO ) ) {
  76. logger.out() << "IoSystem::unregisterInput: Handle " << handle << " was never registered." << std::endl;
  77. }
  78. }
  79. lock.unlock();
  80. wakeup();
  81. }
  82. void IoSystem::unregisterOutput( int handle ) {
  83. lock.lock();
  84. if( outMap.find( handle ) != outMap.end() ) {
  85. if( logger.system( Logger::IO ) ) {
  86. logger.out() << "IoSystem::registerInput: Unregistering output handle " << handle << "." << std::endl;
  87. }
  88. outMap.erase( handle );
  89. } else {
  90. // TODO: never registered?
  91. if( logger.system( Logger::IO ) ) {
  92. logger.out() << "IoSystem::unregisterOutput: Handle " << handle << " was never registered." << std::endl;
  93. }
  94. }
  95. lock.unlock();
  96. wakeup();
  97. }
  98. void IoSystem::wakeup() {
  99. if( logger.system( Logger::IO ) ) {
  100. logger.out() << "IoSystem::wakeup: Sending wakeup message to handle " << localSockets[ 1 ] << "." << std::endl;
  101. }
  102. char buffer[ 1 ] = { 0 };
  103. send( localSockets[ 1 ], buffer, 1, 0 );
  104. }
  105. void IoSystem::main() {
  106. //! This is the file descriptor that we'll be using for "epoll".
  107. //! "epoll" is essentially a better "select" that doesn't run into
  108. //! problems with file descriptors whose values happen to be larger
  109. //! than one thousand.
  110. int epollHandle = -1; //!< Default to an invalid file descriptor.
  111. // Create the "epoll" handle.
  112. int errorNumber = 0;
  113. epollHandle = epoll_create1( EPOLL_CLOEXEC );
  114. errorNumber = errno;
  115. if( logger.system( Logger::IO ) ) {
  116. logger.out() << "IoSystem::main: epoll_create1 returned " << epollHandle << "." << std::endl;
  117. }
  118. if( epollHandle < 0 ) {
  119. if( logger.system( Logger::IO ) ) {
  120. logger.out() << "IoSystem::main: epoll_create1 error number: " << errorNumber << "." << std::endl;
  121. }
  122. throw Exception( "Could not create epoll handle." );
  123. }
  124. // Add the local control socket to the list of things that we want
  125. // to listen for.
  126. //
  127. // Note that "epoll" will KEEP this association, so we only need to
  128. // do it this one time.
  129. {
  130. struct epoll_event event;
  131. event.events = EPOLLIN;
  132. event.data.fd = localSockets[ 0 ];
  133. int errorNumber = 0;
  134. int status = epoll_ctl( epollHandle, EPOLL_CTL_ADD, event.data.fd, &event );
  135. errorNumber = errno;
  136. if( logger.system( Logger::IO ) ) {
  137. logger.out() << "IoSystem::main: epoll_ctl (add) returned " << status << "." << std::endl;
  138. }
  139. if( status < 0 ) {
  140. if( logger.system( Logger::IO ) ) {
  141. logger.out() << "IoSystem::main: epoll_ctl (add) error number: " << errorNumber << "." << std::endl;
  142. }
  143. throw Exception( "Could not add local socket to epoll handle." );
  144. }
  145. }
  146. // "epoll" will deal with a certain number of events at a time.
  147. // Since we're doing this in a loop, it really doesn't matter what
  148. // this value is. All that matters is that we define it and use it.
  149. //! This is the number of events to get at a time when using "epoll" later.
  150. const unsigned int MAX_EVENTS = 10;
  151. //! This is the list of events that "epoll" will return later.
  152. struct epoll_event events[ MAX_EVENTS ];
  153. //! This is the mapping of in-map and out-map events that we CURRENTLY have
  154. //! set up for use with "epoll".
  155. std::map<int,int> existingEventMapping;
  156. while( ! isStopped ) {
  157. //! This is the mapping of in-map and out-map events that we WANT to have
  158. //! set up for use with "epoll".
  159. std::map<int,int> proposedEventMapping;
  160. // Update the proposed mapping.
  161. lock.lock();
  162. for( const auto& entry : inMap ) {
  163. // Before we add this socket to our list of sockets to listen for
  164. // more data from, make sure that it's still "open". We will do
  165. // this by doing a quick "peek" for a single byte. If we get back
  166. // exactly ZERO bytes (and NOT an error), then we know that this
  167. // socket has been closed; it's no good to us now.
  168. char byte = 0;
  169. int bytesAvailable = recv( entry.first, &byte, sizeof(byte), MSG_PEEK | MSG_DONTWAIT );
  170. if( bytesAvailable == 0 ) {
  171. continue;
  172. }
  173. proposedEventMapping[ entry.first ] |= EPOLLIN | EPOLLONESHOT;
  174. }
  175. for( const auto& entry : outMap ) {
  176. if( ! entry.second->hasNextPacket() ) {
  177. continue;
  178. }
  179. proposedEventMapping[ entry.first ] |= EPOLLOUT | EPOLLONESHOT;
  180. }
  181. lock.unlock();
  182. // Make any necessary changes.
  183. for( const auto& entry : proposedEventMapping ) {
  184. // If this mapping doesn't exist yet, then make it.
  185. if( existingEventMapping.find( entry.first ) == existingEventMapping.end() ) {
  186. struct epoll_event event;
  187. event.events = entry.second;
  188. event.data.fd = entry.first;
  189. int errorNumber = 0;
  190. int status = epoll_ctl( epollHandle, EPOLL_CTL_ADD, event.data.fd, &event );
  191. errorNumber = errno;
  192. if( logger.system( Logger::IO ) ) {
  193. logger.out() << "IoSystem::main: epoll_ctl (add) returned " << status << "." << std::endl;
  194. }
  195. // Note: we don't care about the error right now. It either worked or it didn't.
  196. if( status < 0 ) {
  197. if( logger.system( Logger::IO ) ) {
  198. logger.out() << "IoSystem::main: epoll_ctl (add) error number: " << errorNumber << "." << std::endl;
  199. }
  200. }
  201. } else {
  202. // Otherwise, we need to update the mapping.
  203. struct epoll_event event;
  204. event.events = entry.second;
  205. event.data.fd = entry.first;
  206. int errorNumber = 0;
  207. int status = epoll_ctl( epollHandle, EPOLL_CTL_MOD, event.data.fd, &event );
  208. errorNumber = errno;
  209. if( logger.system( Logger::IO ) ) {
  210. logger.out() << "IoSystem::main: epoll_ctl (mod) returned " << status << "." << std::endl;
  211. }
  212. // Note: we don't care about the error right now. It either worked or it didn't.
  213. if( status < 0 ) {
  214. if( logger.system( Logger::IO ) ) {
  215. logger.out() << "IoSystem::main: epoll_ctl (mod) error number: " << errorNumber << "." << std::endl;
  216. }
  217. }
  218. }
  219. }
  220. for( const auto& entry : existingEventMapping ) {
  221. // If we don't care about this handle anymore, then remove it from the "epoll" handle.
  222. if( proposedEventMapping.find( entry.first ) == proposedEventMapping.end() ) {
  223. struct epoll_event event;
  224. event.events = entry.second;
  225. event.data.fd = entry.first;
  226. int errorNumber = 0;
  227. int status = epoll_ctl( epollHandle, EPOLL_CTL_DEL, event.data.fd, &event );
  228. errorNumber = errno;
  229. if( logger.system( Logger::IO ) ) {
  230. logger.out() << "IoSystem::main: epoll_ctl (del) returned " << status << "." << std::endl;
  231. }
  232. // Note: we don't care about the error right now. It either worked or it didn't.
  233. // For example, the file descriptor may have been closed by now. "epoll" will
  234. // automatically remove it by itself in that case.
  235. if( status < 0 ) {
  236. if( logger.system( Logger::IO ) ) {
  237. logger.out() << "IoSystem::main: epoll_ctl (del) error number: " << errorNumber << "." << std::endl;
  238. }
  239. }
  240. }
  241. }
  242. // At this point, we have updated the existing mapping to match the state that we want,
  243. // so we can just swap the lists.
  244. existingEventMapping.swap( proposedEventMapping );
  245. //! This is how long to have "epoll_wait" wait before returning. This CAN be "-1" to wait
  246. //! forever, but we're going to wake up at some point just to make sure that things are
  247. //! working.
  248. std::chrono::milliseconds timeout = std::chrono::seconds( 5 );
  249. //! This is the number of file handles that had events; that is, this is the number of
  250. //! entries in the "events" array that are valid.
  251. int handleCount = epoll_wait( epollHandle, events, MAX_EVENTS, timeout.count() );
  252. errorNumber = errno;
  253. if( logger.system( Logger::IO ) ) {
  254. logger.out() << "IoSystem::main: epoll_wait returned " << handleCount << "." << std::endl;
  255. }
  256. if( handleCount < 0 ) {
  257. // TODO: Error.
  258. if( logger.system( Logger::IO ) ) {
  259. logger.out() << "IoSystem::main: epoll_wait error number: " << errorNumber << "." << std::endl;
  260. }
  261. continue;
  262. }
  263. // If we have nothing to do, then just move on.
  264. if( handleCount == 0 ) {
  265. continue;
  266. }
  267. lock.lock();
  268. for( int i = 0; i < handleCount; i++ ) {
  269. struct epoll_event* event = &events[ i ];
  270. // Input event {{{
  271. if( ( event->events & EPOLLIN ) == EPOLLIN ) {
  272. // If "localSockets[0]" is available, we really don't care. We were only using it
  273. // to wake ourselves up after making a change to "inMap" or "outMap".
  274. if( event->data.fd == localSockets[0] ) {
  275. if( logger.system( Logger::IO ) ) {
  276. logger.out() << "IoSystem::main: wakeup signal given." << std::endl;
  277. }
  278. const int BUFFER_SIZE = 8192;
  279. char buffer[ BUFFER_SIZE ];
  280. while( true ) {
  281. ssize_t bytesRead = recv( localSockets[0], buffer, (int)BUFFER_SIZE, 0 );
  282. if( bytesRead < 0 ) {
  283. int errorNumber = errno;
  284. if( errorNumber == EAGAIN || errorNumber == EWOULDBLOCK ) {
  285. break;
  286. } else {
  287. if( logger.system( Logger::IO ) ) {
  288. logger.out() << "IoSystem::main: recv: Unexpected error: " << errorNumber << std::endl;
  289. }
  290. break;
  291. }
  292. }
  293. if( bytesRead == 0 ) {
  294. // TODO: End of file?
  295. } else {
  296. // Do nothing; we just need to eat up the bytes.
  297. }
  298. }
  299. } else {
  300. const auto& entryIterator = inMap.find( event->data.fd );
  301. if( entryIterator == inMap.end() ) {
  302. // This file descriptor is no longer in use.
  303. } else {
  304. const auto& entry = *entryIterator;
  305. if( logger.system( Logger::IO ) ) {
  306. logger.out() << "IoSystem::main: handle #" << entry.first << " is available for reading." << std::endl;
  307. }
  308. const int BUFFER_SIZE = 8192;
  309. char buffer[ BUFFER_SIZE ];
  310. while( true ) {
  311. ssize_t bytesRead = recv( entry.first, buffer, (int)BUFFER_SIZE, 0 );
  312. if( bytesRead < 0 ) {
  313. int errorNumber = errno;
  314. if( logger.system( Logger::IO ) ) {
  315. logger.out() << "IoSystem::main: Could not read any bytes from handle #" << entry.first << "." << std::endl;
  316. }
  317. if( errorNumber == EAGAIN || errorNumber == EWOULDBLOCK ) {
  318. if( logger.system( Logger::IO ) ) {
  319. logger.out() << "IoSystem::main: Socket " << entry.first << " is out of data for now." << std::endl;
  320. }
  321. } else if( errorNumber == EBADF ) {
  322. if( logger.system( Logger::IO ) ) {
  323. logger.out() << "IoSystem::main: Error: Socket " << entry.first << " is an invalid file descriptor." << std::endl;
  324. }
  325. } else if( errorNumber == ECONNREFUSED ) {
  326. if( logger.system( Logger::IO ) ) {
  327. logger.out() << "IoSystem::main: Error: Socket " << entry.first << " refused connection." << std::endl;
  328. }
  329. } else if( errorNumber == EFAULT ) {
  330. if( logger.system( Logger::IO ) ) {
  331. logger.out() << "IoSystem::main: Error: Receive buffer is out of bounds." << std::endl;
  332. }
  333. } else if( errorNumber == EINTR ) {
  334. if( logger.system( Logger::IO ) ) {
  335. logger.out() << "IoSystem::main: Error: Receive call was interrupted." << std::endl;
  336. }
  337. } else if( errorNumber == EINVAL ) {
  338. if( logger.system( Logger::IO ) ) {
  339. logger.out() << "IoSystem::main: Error: Invalid argument passed to 'recv'." << std::endl;
  340. }
  341. } else if( errorNumber == ENOMEM ) {
  342. if( logger.system( Logger::IO ) ) {
  343. logger.out() << "IoSystem::main: Error: Out of memory." << std::endl;
  344. }
  345. } else if( errorNumber == ENOTCONN ) {
  346. if( logger.system( Logger::IO ) ) {
  347. logger.out() << "IoSystem::main: Error: Socket " << entry.first << " is not connected." << std::endl;
  348. }
  349. } else if( errorNumber == ENOTSOCK ) {
  350. if( logger.system( Logger::IO ) ) {
  351. logger.out() << "IoSystem::main: Error: Socket " << entry.first << " is not a socket." << std::endl;
  352. }
  353. } else {
  354. if( logger.system( Logger::IO ) ) {
  355. logger.out() << "IoSystem::main: recv: Unexpected error: " << errorNumber << std::endl;
  356. }
  357. }
  358. break;
  359. }
  360. if( logger.system( Logger::IO ) ) {
  361. logger.out() << "IoSystem::main: Read " << (int)bytesRead << " from handle #" << entry.first << "." << std::endl;
  362. }
  363. if( bytesRead == 0 ) {
  364. if( logger.system( Logger::IO ) ) {
  365. logger.out() << "IoSystem::main: End of file: handle #" << entry.first << "." << std::endl;
  366. }
  367. break;
  368. } else {
  369. // Add the newly-read bytes to the buffer.
  370. unsigned int bytesWritten = 0;
  371. entry.second->writeBytes( buffer, bytesRead, bytesWritten );
  372. // Also, if this socket is a "datagram" socket, then we can issue the "endPacket"
  373. // call, which will tell the ByteStream that a full packet has been read.
  374. int socketType = 0;
  375. socklen_t socketTypeLength = sizeof( socketType );
  376. int result = getsockopt( entry.first, SOL_SOCKET, SO_TYPE, &socketType, &socketTypeLength );
  377. if( result < 0 ) {
  378. int errorNumber = errno;
  379. if( logger.system( Logger::IO ) ) {
  380. logger.out() << "IoSystem::main: Could not call 'getsockopt' on handle #" << entry.first << "; error number: " << errorNumber << "." << std::endl;
  381. }
  382. break;
  383. }
  384. if( socketType == SOCK_DGRAM ) {
  385. entry.second->endPacket();
  386. }
  387. }
  388. // Try to clean up the ByteStream if it has gotten too large.
  389. entry.second->reset();
  390. }
  391. }
  392. }
  393. }
  394. // }}}
  395. // Output event {{{
  396. if( ( event->events & EPOLLOUT ) == EPOLLOUT ) {
  397. const auto& entryIterator = outMap.find( event->data.fd );
  398. if( entryIterator == outMap.end() ) {
  399. // This file descriptor is no longer in use.
  400. } else {
  401. const auto& entry = *entryIterator;
  402. if( logger.system( Logger::IO ) ) {
  403. logger.out() << "IoSystem::main: handle #" << entry.first << " is available for writing." << std::endl;
  404. }
  405. const unsigned int BUFFER_SIZE = 8192;
  406. char buffer[ BUFFER_SIZE ];
  407. while( entry.second->remainingReadLength() > 0 ) {
  408. unsigned int bufferSize = BUFFER_SIZE < entry.second->remainingReadLength() ? BUFFER_SIZE : entry.second->remainingReadLength();
  409. unsigned int bytesCopied = 0;
  410. bool success = entry.second->readBytes( buffer, bufferSize, bytesCopied );
  411. if( ! success ) {
  412. if( logger.system( Logger::IO ) ) {
  413. logger.out() << "IoSystem::main Could not extract " << bufferSize << " bytes from the buffer." << std::endl;
  414. }
  415. break;
  416. }
  417. ssize_t bytesWritten = send( entry.first, buffer, bytesCopied, 0 );
  418. if( bytesWritten < 0 ) {
  419. int errorNumber = errno;
  420. if( logger.system( Logger::IO ) ) {
  421. logger.out() << "IoSystem::main: Could not send " << bytesCopied << " bytes to handle #" << entry.first << "." << std::endl;
  422. }
  423. if( errorNumber == EBADF ) {
  424. if( logger.system( Logger::IO ) ) {
  425. logger.out() << "IoSystem::main: Error: Socket " << entry.first << " is an invalid file descriptor." << std::endl;
  426. }
  427. } else if( errorNumber == ECONNRESET ) {
  428. if( logger.system( Logger::IO ) ) {
  429. logger.out() << "IoSystem::main: Error: Socket " << entry.first << " reset connection." << std::endl;
  430. }
  431. } else if( errorNumber == EDESTADDRREQ ) {
  432. if( logger.system( Logger::IO ) ) {
  433. logger.out() << "IoSystem::main: Error: Peer address required." << std::endl;
  434. }
  435. } else if( errorNumber == EFAULT ) {
  436. if( logger.system( Logger::IO ) ) {
  437. logger.out() << "IoSystem::main: Error: Receive buffer is out of bounds." << std::endl;
  438. }
  439. } else if( errorNumber == EINTR ) {
  440. if( logger.system( Logger::IO ) ) {
  441. logger.out() << "IoSystem::main: Error: Receive call was interrupted." << std::endl;
  442. }
  443. } else if( errorNumber == EINVAL ) {
  444. if( logger.system( Logger::IO ) ) {
  445. logger.out() << "IoSystem::main: Error: Invalid argument passed to 'send'." << std::endl;
  446. }
  447. } else if( errorNumber == EISCONN ) {
  448. if( logger.system( Logger::IO ) ) {
  449. logger.out() << "IoSystem::main: Error: Already connected." << std::endl;
  450. }
  451. } else if( errorNumber == EMSGSIZE ) {
  452. if( logger.system( Logger::IO ) ) {
  453. logger.out() << "IoSystem::main: Error: Message is too big to send atomically." << std::endl;
  454. }
  455. } else if( errorNumber == ENOBUFS ) {
  456. if( logger.system( Logger::IO ) ) {
  457. logger.out() << "IoSystem::main: Error: Output queue is full." << std::endl;
  458. }
  459. } else if( errorNumber == ENOMEM ) {
  460. if( logger.system( Logger::IO ) ) {
  461. logger.out() << "IoSystem::main: Error: Out of memory." << std::endl;
  462. }
  463. } else if( errorNumber == ENOTCONN ) {
  464. if( logger.system( Logger::IO ) ) {
  465. logger.out() << "IoSystem::main: Error: Socket " << entry.first << " is not connected." << std::endl;
  466. }
  467. } else if( errorNumber == ENOTSOCK ) {
  468. if( logger.system( Logger::IO ) ) {
  469. logger.out() << "IoSystem::main: Error: Socket " << entry.first << " is not a socket." << std::endl;
  470. }
  471. } else if( errorNumber == EOPNOTSUPP ) {
  472. if( logger.system( Logger::IO ) ) {
  473. logger.out() << "IoSystem::main: Error: Some flags are invalid." << std::endl;
  474. }
  475. } else if( errorNumber == EPIPE ) {
  476. if( logger.system( Logger::IO ) ) {
  477. logger.out() << "IoSystem::main: Error: The local end has been shut down." << std::endl;
  478. }
  479. } else {
  480. logger.out() << "IoSystem::main: send: Unexpected error: " << errorNumber << std::endl;
  481. }
  482. break;
  483. }
  484. if( logger.system( Logger::IO ) ) {
  485. logger.out() << "IoSystem::main: Sent " << bytesCopied << " bytes to handle #" << entry.first << "." << std::endl;
  486. }
  487. }
  488. entry.second->nextPacket();
  489. // Try to clean up the ByteStream if it has gotten too large.
  490. entry.second->reset();
  491. }
  492. }
  493. // }}}
  494. }
  495. lock.unlock();
  496. }
  497. close( epollHandle );
  498. }
  499. /**
  500. * This starts the the IoSystem as a thread.
  501. **/
  502. void IoSystem::start() {
  503. if( ! isStopped ) {
  504. return;
  505. }
  506. if( logger.system( Logger::IO ) ) {
  507. logger.out() << "IoSystem::start: Starting I/O system..." << std::endl;
  508. }
  509. isStopped = false;
  510. thisThread = std::thread( std::bind( &IoSystem::main, this ) );
  511. }
  512. /**
  513. * This stops the IoSystem thread, if it's running.
  514. **/
  515. void IoSystem::stop() {
  516. if( isStopped ) {
  517. return;
  518. }
  519. if( logger.system( Logger::IO ) ) {
  520. logger.out() << "IoSystem::stop: Stopping I/O system..." << std::endl;
  521. }
  522. isStopped = true;
  523. wakeup();
  524. thisThread.join();
  525. }
  526. } }