PageRenderTime 50ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/mordor/iomanager_kqueue.cpp

http://github.com/mozy/mordor
C++ | 371 lines | 347 code | 23 blank | 1 comment | 101 complexity | dbc286fbc597af906f530c1d3e1ece69 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. // Copyright (c) 2009 - Mozy, Inc.
  2. #include "pch.h"
  3. #ifdef BSD
  4. #include "iomanager_kqueue.h"
  5. #include <boost/exception_ptr.hpp>
  6. #include "assert.h"
  7. #include "fiber.h"
  8. namespace Mordor {
  9. static Logger::ptr g_log = Log::lookup("mordor:iomanager");
  10. IOManager::IOManager(size_t threads, bool useCaller, bool autoStart)
  11. : Scheduler(threads, useCaller)
  12. {
  13. m_kqfd = kqueue();
  14. MORDOR_LOG_LEVEL(g_log, m_kqfd <= 0 ? Log::ERROR : Log::TRACE) << this
  15. << " kqueue(): " << m_kqfd;
  16. if (m_kqfd <= 0) {
  17. MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("kqueue");
  18. }
  19. int rc = pipe(m_tickleFds);
  20. MORDOR_LOG_LEVEL(g_log, rc ? Log::ERROR : Log::VERBOSE) << this << " pipe(): "
  21. << rc << " (" << lastError() << ")";
  22. if (rc) {
  23. close(m_kqfd);
  24. MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("pipe");
  25. }
  26. MORDOR_ASSERT(m_tickleFds[0] > 0);
  27. MORDOR_ASSERT(m_tickleFds[1] > 0);
  28. struct kevent event;
  29. EV_SET(&event, m_tickleFds[0], EVFILT_READ, EV_ADD, 0, 0, NULL);
  30. rc = kevent(m_kqfd, &event, 1, NULL, 0, NULL);
  31. MORDOR_LOG_LEVEL(g_log, rc ? Log::ERROR : Log::VERBOSE) << this << " kevent("
  32. << m_kqfd << ", (" << m_tickleFds[0] << ", EVFILT_READ, EV_ADD)): " << rc
  33. << " (" << lastError() << ")";
  34. if (rc) {
  35. close(m_tickleFds[0]);
  36. close(m_tickleFds[1]);
  37. close(m_kqfd);
  38. MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("kevent");
  39. }
  40. if (autoStart) {
  41. try {
  42. start();
  43. } catch (...) {
  44. close(m_tickleFds[0]);
  45. close(m_tickleFds[1]);
  46. close(m_kqfd);
  47. throw;
  48. }
  49. }
  50. }
  51. IOManager::~IOManager()
  52. {
  53. stop();
  54. close(m_kqfd);
  55. MORDOR_LOG_TRACE(g_log) << this << " close(" << m_kqfd << ")";
  56. close(m_tickleFds[0]);
  57. MORDOR_LOG_VERBOSE(g_log) << this << " close(" << m_tickleFds[0] << ")";
  58. close(m_tickleFds[1]);
  59. }
  60. bool
  61. IOManager::stopping()
  62. {
  63. unsigned long long timeout;
  64. return stopping(timeout);
  65. }
  66. void
  67. IOManager::registerEvent(int fd, Event events,
  68. boost::function<void ()> dg)
  69. {
  70. MORDOR_ASSERT(fd > 0);
  71. MORDOR_ASSERT(Scheduler::getThis());
  72. MORDOR_ASSERT(Fiber::getThis());
  73. Event eventsKey = events;
  74. if (eventsKey == CLOSE)
  75. eventsKey = READ;
  76. boost::mutex::scoped_lock lock(m_mutex);
  77. AsyncEvent &e = m_pendingEvents[std::pair<int, Event>(fd, eventsKey)];
  78. memset(&e.event, 0, sizeof(struct kevent));
  79. e.event.ident = fd;
  80. e.event.flags = EV_ADD;
  81. switch (events) {
  82. case READ:
  83. e.event.filter = EVFILT_READ;
  84. break;
  85. case WRITE:
  86. e.event.filter = EVFILT_WRITE;
  87. break;
  88. case CLOSE:
  89. e.event.filter = EVFILT_READ;
  90. break;
  91. default:
  92. MORDOR_NOTREACHED();
  93. }
  94. if (events == READ || events == WRITE) {
  95. MORDOR_ASSERT(!e.m_dg && !e.m_fiber);
  96. e.m_dg = dg;
  97. if (!dg)
  98. e.m_fiber = Fiber::getThis();
  99. e.m_scheduler = Scheduler::getThis();
  100. } else {
  101. MORDOR_ASSERT(!e.m_dgClose && !e.m_fiberClose);
  102. e.m_dgClose = dg;
  103. if (!dg)
  104. e.m_fiberClose = Fiber::getThis();
  105. e.m_schedulerClose = Scheduler::getThis();
  106. }
  107. int rc = kevent(m_kqfd, &e.event, 1, NULL, 0, NULL);
  108. MORDOR_LOG_LEVEL(g_log, rc ? Log::ERROR : Log::VERBOSE) << this << " kevent("
  109. << m_kqfd << ", (" << fd << ", " << events << ", EV_ADD)): " << rc
  110. << " (" << lastError() << ")";
  111. if (rc)
  112. MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("kevent");
  113. }
  114. void
  115. IOManager::cancelEvent(int fd, Event events)
  116. {
  117. Event eventsKey = events;
  118. if (eventsKey == CLOSE)
  119. eventsKey = READ;
  120. boost::mutex::scoped_lock lock(m_mutex);
  121. std::map<std::pair<int, Event>, AsyncEvent>::iterator it =
  122. m_pendingEvents.find(std::pair<int, Event>(fd, eventsKey));
  123. if (it == m_pendingEvents.end())
  124. return;
  125. AsyncEvent &e = it->second;
  126. MORDOR_ASSERT(e.event.ident == (unsigned)fd);
  127. Scheduler *scheduler;
  128. Fiber::ptr fiber;
  129. boost::function<void ()> dg;
  130. if (events == READ) {
  131. scheduler = e.m_scheduler;
  132. fiber.swap(e.m_fiber);
  133. dg.swap(e.m_dg);
  134. if (e.m_fiberClose || e.m_dgClose) {
  135. if (dg || fiber) {
  136. if (dg)
  137. scheduler->schedule(dg);
  138. else
  139. scheduler->schedule(fiber);
  140. }
  141. return;
  142. }
  143. } else if (events == CLOSE) {
  144. scheduler = e.m_schedulerClose;
  145. fiber.swap(e.m_fiberClose);
  146. dg.swap(e.m_dgClose);
  147. if (e.m_fiber || e.m_dg) {
  148. if (dg || fiber) {
  149. if (dg)
  150. scheduler->schedule(dg);
  151. else
  152. scheduler->schedule(fiber);
  153. }
  154. return;
  155. }
  156. } else if (events == WRITE) {
  157. scheduler = e.m_scheduler;
  158. fiber.swap(e.m_fiber);
  159. dg.swap(e.m_dg);
  160. } else {
  161. MORDOR_NOTREACHED();
  162. }
  163. e.event.flags = EV_DELETE;
  164. int rc = kevent(m_kqfd, &e.event, 1, NULL, 0, NULL);
  165. MORDOR_LOG_LEVEL(g_log, rc ? Log::ERROR : Log::VERBOSE) << this << " kevent("
  166. << m_kqfd << ", (" << fd << ", " << eventsKey << ", EV_DELETE)): " << rc
  167. << " (" << lastError() << ")";
  168. if (rc)
  169. MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("kevent");
  170. if (dg)
  171. scheduler->schedule(&dg);
  172. else
  173. scheduler->schedule(&fiber);
  174. m_pendingEvents.erase(it);
  175. }
  176. void
  177. IOManager::unregisterEvent(int fd, Event events)
  178. {
  179. Event eventsKey = events;
  180. if (eventsKey == CLOSE)
  181. eventsKey = READ;
  182. boost::mutex::scoped_lock lock(m_mutex);
  183. std::map<std::pair<int, Event>, AsyncEvent>::iterator it =
  184. m_pendingEvents.find(std::pair<int, Event>(fd, eventsKey));
  185. if (it == m_pendingEvents.end())
  186. return;
  187. AsyncEvent &e = it->second;
  188. MORDOR_ASSERT(e.event.ident == (unsigned)fd);
  189. if (events == READ) {
  190. e.m_fiber.reset();
  191. e.m_dg = NULL;
  192. if (e.m_fiberClose || e.m_dgClose) {
  193. return;
  194. }
  195. } else if (events == CLOSE) {
  196. e.m_fiberClose.reset();
  197. e.m_dgClose = NULL;
  198. if (e.m_fiber || e.m_dg) {
  199. return;
  200. }
  201. }
  202. e.event.flags = EV_DELETE;
  203. int rc = kevent(m_kqfd, &e.event, 1, NULL, 0, NULL);
  204. MORDOR_LOG_LEVEL(g_log, rc ? Log::ERROR : Log::VERBOSE) << this << " kevent("
  205. << m_kqfd << ", (" << fd << ", " << eventsKey << ", EV_DELETE)): " << rc
  206. << " (" << lastError() << ")";
  207. if (rc)
  208. MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("kevent");
  209. m_pendingEvents.erase(it);
  210. }
  211. bool
  212. IOManager::stopping(unsigned long long &nextTimeout)
  213. {
  214. nextTimeout = nextTimer();
  215. if (nextTimeout == ~0ull && Scheduler::stopping()) {
  216. boost::mutex::scoped_lock lock(m_mutex);
  217. if (m_pendingEvents.empty())
  218. return true;
  219. }
  220. return false;
  221. }
  222. void
  223. IOManager::idle()
  224. {
  225. struct kevent events[64];
  226. while (true) {
  227. unsigned long long nextTimeout;
  228. if (stopping(nextTimeout))
  229. return;
  230. int rc;
  231. do {
  232. struct timespec *timeout = NULL, timeoutStorage;
  233. if (nextTimeout != ~0ull) {
  234. timeout = &timeoutStorage;
  235. timeout->tv_sec = nextTimeout / 1000000;
  236. timeout->tv_nsec = (nextTimeout % 1000000) * 1000;
  237. }
  238. rc = kevent(m_kqfd, NULL, 0, events, 64, timeout);
  239. if (rc < 0 && errno == EINTR)
  240. nextTimeout = nextTimer();
  241. else
  242. break;
  243. } while (true);
  244. MORDOR_LOG_LEVEL(g_log, rc < 0 ? Log::ERROR : Log::VERBOSE) << this
  245. << " kevent(" << m_kqfd << "): " << rc << " (" << lastError()
  246. << ")";
  247. if (rc < 0)
  248. MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("kevent");
  249. std::vector<boost::function<void ()> > expired = processTimers();
  250. if (!expired.empty()) {
  251. schedule(expired.begin(), expired.end());
  252. expired.clear();
  253. }
  254. boost::exception_ptr exception;
  255. for(int i = 0; i < rc; ++i) {
  256. struct kevent &event = events[i];
  257. if ((int)event.ident == m_tickleFds[0]) {
  258. unsigned char dummy;
  259. MORDOR_VERIFY(read(m_tickleFds[0], &dummy, 1) == 1);
  260. MORDOR_LOG_VERBOSE(g_log) << this << " received tickle";
  261. continue;
  262. }
  263. boost::mutex::scoped_lock lock(m_mutex);
  264. Event key;
  265. switch (event.filter) {
  266. case EVFILT_READ:
  267. key = READ;
  268. break;
  269. case EVFILT_WRITE:
  270. key = WRITE;
  271. break;
  272. default:
  273. MORDOR_NOTREACHED();
  274. }
  275. std::map<std::pair<int, Event>, AsyncEvent>::iterator it =
  276. m_pendingEvents.find(std::pair<int, Event>((int)event.ident, key));
  277. if (it == m_pendingEvents.end())
  278. continue;
  279. AsyncEvent &e = it->second;
  280. bool remove = false;
  281. bool eof = !!(event.flags & EV_EOF);
  282. if ( (event.flags & EV_EOF) || (!e.m_dgClose && !e.m_fiberClose) ) {
  283. remove = true;
  284. event.flags = EV_DELETE;
  285. int rc2 = kevent(m_kqfd, &event, 1, NULL, 0, NULL);
  286. MORDOR_LOG_LEVEL(g_log, rc2 ? Log::ERROR : Log::VERBOSE)
  287. << this << " kevent(" << m_kqfd << ", (" << event.ident
  288. << ", " << event.filter << ", EV_DELETE)): " << rc2 << " ("
  289. << lastError() << ")";
  290. if (rc2) {
  291. try {
  292. MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("kevent");
  293. } catch (boost::exception &ex) {
  294. #ifdef OSX
  295. int *err = boost::get_error_info<errinfo_nativeerror>(ex);
  296. if (err != NULL && *err == EINPROGRESS) {
  297. MORDOR_LOG_VERBOSE(g_log) << this << " kevent(" << m_kqfd << ", (" << event.ident
  298. << ", " << event.filter << ", EV_DELETE)): " << rc2 << " ("
  299. << lastError() << ") ignored";
  300. } else {
  301. exception = boost::current_exception();
  302. continue;
  303. }
  304. #else
  305. exception = boost::current_exception();
  306. continue;
  307. #endif
  308. }
  309. }
  310. }
  311. if (e.m_dg) {
  312. e.m_scheduler->schedule(&e.m_dg);
  313. } else if (e.m_fiber) {
  314. e.m_scheduler->schedule(&e.m_fiber);
  315. }
  316. if (eof && e.event.filter == EVFILT_READ) {
  317. if (e.m_dgClose) {
  318. e.m_schedulerClose->schedule(&e.m_dgClose);
  319. } else if (e.m_fiberClose) {
  320. e.m_schedulerClose->schedule(&e.m_fiberClose);
  321. }
  322. }
  323. if (remove)
  324. m_pendingEvents.erase(it);
  325. }
  326. if (exception)
  327. boost::rethrow_exception(exception);
  328. try {
  329. Fiber::yield();
  330. } catch (OperationAbortedException &) {
  331. return;
  332. }
  333. }
  334. }
  335. void
  336. IOManager::tickle()
  337. {
  338. if (!hasIdleThreads()) {
  339. MORDOR_LOG_VERBOSE(g_log) << this << " 0 idle thread, no tickle.";
  340. return;
  341. }
  342. int rc = write(m_tickleFds[1], "T", 1);
  343. MORDOR_LOG_VERBOSE(g_log) << this << " write(" << m_tickleFds[1] << ", 1): "
  344. << rc << " (" << lastError() << ")";
  345. MORDOR_VERIFY(rc == 1);
  346. }
  347. }
  348. #endif