/* Copyright (C) 1999-2001 Stefan Westerfeld stefan@space.twc.de This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "qiomanager.h" #include "qiomanager_p.h" #include #include #include "debug.h" #include "dispatcher.h" #include "thread.h" using namespace std; using namespace Arts; /* * Collected incompatibilities of QIOManager (compared against StdIOManager): * * - StdIOManager catches up timers (i.e. if a 100ms timer hasn't been * notified for half a second, it will get notified five times to * catch up the lost time * - StdIOManager is able to watch the same filedescriptor twice for reading * - StdIOManager sends notifications soon after they have been produced, * whereas we use a hackish 50ms timer to deliver them */ /* * Fallback for the case where we should perform blocking */ namespace Arts { class QIOManagerBlocking : public StdIOManager { public: void setLevel(int newLevel) { level = newLevel; } }; } /* * QIOManager is a singleton (or at least supposed to be used only once at * most, and the behaviour would pretty undefined if you violate this), so we * use static variables for private data here. */ static int qioLevel; static QIOManager *qioManager = 0; static QIOManagerBlocking *qioManagerBlocking = 0; static bool qioBlocking; /* * QIOWatch: */ QIOWatch::QIOWatch(int fd, int type, IONotify *notify, QSocketNotifier::Type qtype, bool reentrant) : _fd(fd), _type(type), _client(notify), _reentrant(reentrant) { qsocketnotify = new QSocketNotifier(fd,qtype,this); connect(qsocketnotify,SIGNAL(activated(int)),this,SLOT(notify(int))); } void QIOWatch::notify(int socket) { arts_assert(socket == _fd); qioManager->dispatch(this); } /* * QTimeWatch: */ QTimeWatch::QTimeWatch(int milliseconds, TimeNotify *notify) { timer = new QTimer(this); connect( timer, SIGNAL(timeout()), this, SLOT(notify()) ); timer->start(milliseconds); _client = notify; } void QTimeWatch::notify() { qioManager->dispatch(this); } /* * Handle NotificationManager::the()->run() from time to time */ namespace Arts { class HandleNotifications : public TimeNotify { public: void notifyTime() { Arts::Dispatcher::the()->ioManager()->removeTimer(this); NotificationManager::the()->run(); delete this; } }; } /* * QIOManager: */ QIOManager::QIOManager() { assert(!qioManager); qioManager = this; qioLevel = 0; qioBlocking = true; qioManagerBlocking = new QIOManagerBlocking(); } QIOManager::~QIOManager() { assert(qioManager); qioManager = 0; delete qioManagerBlocking; qioManagerBlocking = 0; } void QIOManager::processOneEvent(bool blocking) { assert(SystemThreads::the()->isMainThread()); if(qioBlocking) { qioLevel++; if(qioLevel == 1) Dispatcher::lock(); /* * we explicitly take the level to qioManagerBlocking, so that it * will process reentrant watchFDs only */ qioManagerBlocking->setLevel(qioLevel); qioManagerBlocking->processOneEvent(blocking); if(qioLevel == 1) Dispatcher::unlock(); qioLevel--; } else { if(blocking) qApp->processOneEvent(); else qApp->processEvents(0); } } void QIOManager::run() { arts_warning("QIOManager::run() not implemented."); } void QIOManager::terminate() { arts_warning("QIOManager::terminate() not implemented."); } void QIOManager::watchFD(int fd, int types, IONotify *notify) { bool r = (types & IOType::reentrant) != 0; if(types & IOType::read) { fdList.push_back( new QIOWatch(fd, IOType::read, notify, QSocketNotifier::Read, r) ); } if(types & IOType::write) { fdList.push_back( new QIOWatch(fd, IOType::write, notify, QSocketNotifier::Write, r) ); } if(types & IOType::except) { fdList.push_back( new QIOWatch(fd, IOType::except, notify, QSocketNotifier::Exception, r) ); } if(r) qioManagerBlocking->watchFD(fd, types, notify); } void QIOManager::remove(IONotify *notify, int types) { list::iterator i; i = fdList.begin(); while(i != fdList.end()) { QIOWatch *w = *i; if(w->type() & types && w->client() == notify) { delete w; fdList.erase(i); i = fdList.begin(); } else i++; } qioManagerBlocking->remove(notify, types); } void QIOManager::addTimer(int milliseconds, TimeNotify *notify) { if (milliseconds == -1 && notify == 0) { // HACK: in order to not add a virtual function to IOManager we're calling addTimer with // magic values. This call tells the ioManager that notifications are pending and // NotificationManager::run() should get called soon. notify = new HandleNotifications(); milliseconds = 0; } timeList.push_back(new QTimeWatch(milliseconds,notify)); } void QIOManager::removeTimer(TimeNotify *notify) { list::iterator i; i = timeList.begin(); while(i != timeList.end()) { QTimeWatch *w = *i; if(w->client() == notify) { delete w; timeList.erase(i); i = timeList.begin(); } else i++; } } void QIOManager::dispatch(QIOWatch *ioWatch) { qioLevel++; if(qioLevel == 1) Dispatcher::lock(); /* * FIXME: there is main loop pollution for (qioBlocking == false) here: * * As QIOManager will never disable the socket notifiers that are not * to be carried out reentrant, these will (maybe) fire again and again, * so that CPU is wasted. */ if(qioLevel == 1 || ioWatch->reentrant()) ioWatch->client()->notifyIO(ioWatch->fd(),ioWatch->type()); if(qioLevel == 1) Dispatcher::unlock(); qioLevel--; } void QIOManager::dispatch(QTimeWatch *timeWatch) { qioLevel++; if(qioLevel == 1) Dispatcher::lock(); // timers are never done reentrant if(qioLevel == 1) timeWatch->client()->notifyTime(); if(qioLevel == 1) Dispatcher::unlock(); qioLevel--; } bool QIOManager::blocking() { return qioBlocking; } void QIOManager::setBlocking(bool blocking) { qioBlocking = blocking; } #include "qiomanager_p.moc"