#include #include #include #include #include #include eSocketNotifier::eSocketNotifier(eMainloop *context, int fd, int requested, bool startnow): context(*context), fd(fd), state(0), requested(requested) { if (startnow) start(); } eSocketNotifier::~eSocketNotifier() { stop(); } void eSocketNotifier::start() { if (state) stop(); context.addSocketNotifier(this); state=2; // running but not in poll yet } void eSocketNotifier::stop() { if (state) context.removeSocketNotifier(this); state=0; } // timer void eTimer::start(long msek, bool singleShot) { if (bActive) stop(); bActive = true; bSingleShot = singleShot; interval = msek; gettimeofday(&nextActivation, 0); nextActivation.tv_sec -= context.getTimeOffset(); // eDebug("this = %p\nnow sec = %d, usec = %d\nadd %d msec", this, nextActivation.tv_sec, nextActivation.tv_usec, msek); nextActivation += (msek<0 ? 0 : msek); // eDebug("next Activation sec = %d, usec = %d", nextActivation.tv_sec, nextActivation.tv_usec ); context.addTimer(this); } void eTimer::startLongTimer( int seconds ) { if (bActive) stop(); bActive = bSingleShot = true; interval = 0; gettimeofday(&nextActivation, 0); nextActivation.tv_sec -= context.getTimeOffset(); // eDebug("this = %p\nnow sec = %d, usec = %d\nadd %d sec", this, nextActivation.tv_sec, nextActivation.tv_usec, seconds); if ( seconds > 0 ) nextActivation.tv_sec += seconds; // eDebug("next Activation sec = %d, usec = %d", nextActivation.tv_sec, nextActivation.tv_usec ); context.addTimer(this); } void eTimer::stop() { if (bActive) { bActive=false; context.removeTimer(this); } } void eTimer::changeInterval(long msek) { if (bActive) // Timer is running? { context.removeTimer(this); // then stop nextActivation -= interval; // sub old interval } else bActive=true; // then activate Timer interval = msek; // set new Interval nextActivation += interval; // calc nextActivation context.addTimer(this); // add Timer to context TimerList } void eTimer::activate() // Internal Funktion... called from eApplication { context.removeTimer(this); if (!bSingleShot) { nextActivation += interval; context.addTimer(this); } else bActive=false; /*emit*/ timeout(); } void eTimer::addTimeOffset( int offset ) { nextActivation.tv_sec += offset; } // mainloop ePtrList eMainloop::existing_loops; void eMainloop::addSocketNotifier(eSocketNotifier *sn) { int fd = sn->getFD(); ASSERT(notifiers.find(fd) == notifiers.end()); notifiers[fd]=sn; } void eMainloop::removeSocketNotifier(eSocketNotifier *sn) { int fd = sn->getFD(); std::map::iterator i(notifiers.find(fd)); if (i != notifiers.end()) return notifiers.erase(i); eFatal("removed socket notifier which is not present"); } int eMainloop::processOneEvent(unsigned int twisted_timeout, PyObject **res, ePyObject additional) { int return_reason = 0; /* get current time */ if (additional && !PyDict_Check(additional)) eFatal("additional, but it's not dict"); if (additional && !res) eFatal("additional, but no res"); long poll_timeout = -1; /* infinite in case of empty timer list */ if (!m_timer_list.empty() || twisted_timeout > 0) { applyTimeOffset(); if (!m_timer_list.empty()) { /* process all timers which are ready. first remove them out of the list. */ while (!m_timer_list.empty() && (poll_timeout = timeout_usec( m_timer_list.begin()->getNextActivation() ) ) <= 0 ) { m_timer_list.begin()->activate(); applyTimeOffset(); } if (poll_timeout < 0) poll_timeout = 0; else /* convert us to ms */ poll_timeout /= 1000; } } if ((twisted_timeout > 0) && (poll_timeout > 0) && ((unsigned int)poll_timeout > twisted_timeout)) { poll_timeout = twisted_timeout; return_reason = 1; } int nativecount=notifiers.size(), fdcount=nativecount, ret=0; if (additional) fdcount += PyDict_Size(additional); // build the poll aray pollfd pfd[fdcount]; // make new pollfd array std::map::iterator it = notifiers.begin(); int i=0; for (; i < nativecount; ++i, ++it) { it->second->state = 1; // running and in poll pfd[i].fd = it->first; pfd[i].events = it->second->getRequested(); } if (additional) { PyObject *key, *val; int pos=0; while (PyDict_Next(additional, &pos, &key, &val)) { pfd[i].fd = PyObject_AsFileDescriptor(key); pfd[i++].events = PyInt_AsLong(val); } } if (this == eApp) Py_BEGIN_ALLOW_THREADS ret = ::poll(pfd, fdcount, poll_timeout); Py_END_ALLOW_THREADS else ret = ::poll(pfd, fdcount, poll_timeout); /* ret > 0 means that there are some active poll entries. */ if (ret > 0) { int i=0; return_reason = 0; for (; i < nativecount; ++i) { if (pfd[i].revents) { it = notifiers.find(pfd[i].fd); if (it != notifiers.end() && it->second->state == 1) // added and in poll { int req = it->second->getRequested(); if (pfd[i].revents & req) it->second->activate(pfd[i].revents & req); pfd[i].revents &= ~req; } if (pfd[i].revents & (POLLERR|POLLHUP|POLLNVAL)) eDebug("poll: unhandled POLLERR/HUP/NVAL for fd %d(%d)", pfd[i].fd, pfd[i].revents); } } for (; i < fdcount; ++i) { if (pfd[i].revents) { if (!*res) *res = PyList_New(0); ePyObject it = PyTuple_New(2); PyTuple_SET_ITEM(it, 0, PyInt_FromLong(pfd[i].fd)); PyTuple_SET_ITEM(it, 1, PyInt_FromLong(pfd[i].revents)); PyList_Append(*res, it); Py_DECREF(it); } } } else if (ret < 0) { /* when we got a signal, we get EINTR. */ if (errno != EINTR) eDebug("poll made error (%m)"); else return_reason = 2; /* don't assume the timeout has passed when we got a signal */ } return return_reason; } void eMainloop::addTimer(eTimer* e) { m_timer_list.insert_in_order(e); } void eMainloop::removeTimer(eTimer* e) { m_timer_list.remove(e); } int eMainloop::iterate(unsigned int twisted_timeout, PyObject **res, ePyObject dict) { int ret = 0; if (twisted_timeout) { gettimeofday(&m_twisted_timer, 0); m_twisted_timer += twisted_timeout; } /* TODO: this code just became ugly. fix that. */ do { if (m_interrupt_requested) { m_interrupt_requested = 0; return 0; } if (app_quit_now) return -1; int to = 0; if (twisted_timeout) { timeval now, timeout; gettimeofday(&now, 0); m_twisted_timer += time_offset; // apply pending offset if (m_twisted_timer<=now) // timeout return 0; timeout = m_twisted_timer - now; to = timeout.tv_sec * 1000 + timeout.tv_usec / 1000; // remove pending offset .. it is re-applied in next call of processOneEvent.. applyTimeOffset m_twisted_timer -= time_offset; } ret = processOneEvent(to, res, dict); } while ( !ret && !(res && *res) ); return ret; } int eMainloop::runLoop() { while (!app_quit_now) iterate(); return retval; } void eMainloop::reset() { app_quit_now=false; } PyObject *eMainloop::poll(ePyObject timeout, ePyObject dict) { PyObject *res=0; if (app_quit_now) Py_RETURN_NONE; int twisted_timeout = (timeout == Py_None) ? 0 : PyInt_AsLong(timeout); iterate(twisted_timeout, &res, dict); if (res) return res; return PyList_New(0); /* return empty list on timeout */ } void eMainloop::interruptPoll() { m_interrupt_requested = 1; } void eMainloop::quit(int ret) { retval = ret; app_quit_now = true; } void eMainloop::addTimeOffset(int offset) { for (ePtrList::iterator it(existing_loops.begin()); it != existing_loops.end(); ++it ) it->addInstanceTimeOffset(offset); } void eMainloop::addInstanceTimeOffset(int offset) { singleLock s(recalcLock); if (m_timer_list.empty()) time_offset=0; else { if ( time_offset ) eDebug("time_offset %d avail.. add new offset %d than new is %d", time_offset, offset, time_offset+offset); time_offset+=offset; } } void eMainloop::applyTimeOffset() { singleLock s(recalcLock); if ( time_offset ) { for (ePtrList::iterator it(m_timer_list.begin()); it != m_timer_list.end(); ++it ) it->addTimeOffset( time_offset ); m_twisted_timer += time_offset; time_offset=0; } } eApplication* eApp = 0;