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