WvStreams
|
00001 /* 00002 * Worldvisions Weaver Software: 00003 * Copyright (C) 1997-2002 Net Integration Technologies, Inc. 00004 * 00005 * Wraps another WvStream and attaches it to the normal Qt 00006 * event loop. If you are using this object to manage all of your 00007 * streams, then you do not need to have a normal WvStreams 00008 * select()/callback() loop in your application at all. 00009 * 00010 * However, should you leave the Qt event loop and wish to continue 00011 * using this WvStream, call qt_detach() first, then run a normal 00012 * WvStreams event loop. If you do not do this, events may be 00013 * lost!! You may resume the Qt event loop at any time after the 00014 * WvStreams event loop has exited by calling qt_attach(). 00015 * 00016 * Note: You do not need to add all of the WvStreams used in a Qt 00017 * application to a single WvStreamList wrapped by a 00018 * WvQtStreamClone so long as each top-level stream is wrapped 00019 * by a WvQtStreamClone to take care of calling select() 00020 * and callback() from within the Qt event loop. 00021 */ 00022 #include "wvqtstreamclone.moc" 00023 00024 // number of slots used by the separate chaining hashtable 00025 // note: can store more than this number of elements in the table 00026 #define NUM_SLOTS 41 // must be prime 00027 00028 WvQtStreamClone::WvQtStreamClone(IWvStream *_cloned, int msec_timeout) : 00029 WvStreamClone(_cloned), msec_timeout(msec_timeout), 00030 pending_callback(false), first_time(true), select_in_progress(false), 00031 last_max_fd(-1), 00032 notify_readable(NUM_SLOTS), 00033 notify_writable(NUM_SLOTS), 00034 notify_exception(NUM_SLOTS) 00035 { 00036 _cloned->addRef(); 00037 setclone(_cloned); 00038 notify_readable.setAutoDelete(true); 00039 notify_writable.setAutoDelete(true); 00040 notify_exception.setAutoDelete(true); 00041 qt_attach(); 00042 } 00043 00044 00045 WvQtStreamClone::~WvQtStreamClone() 00046 { 00047 } 00048 00049 00050 void WvQtStreamClone::pre_poll() 00051 { 00052 // prepare lists of file descriptors 00053 _build_selectinfo(si, msec_timeout, 00054 false, false, false, true); 00055 00056 // set up a timer to wake us up to poll again (for alarms) 00057 // we don't try to catch the timer signal; we use it only to force 00058 // Qt's event loop to restart so our hook gets called again 00059 select_timer.stop(); 00060 if (si.msec_timeout >= 0) 00061 select_timer.start(si.msec_timeout, true /*singleshot*/); 00062 00063 // set up necessary QSocketNotifiers, unfortunately there is no 00064 // better way to iterate over the set of file descriptors 00065 for (int fd = 0; fd <= si.max_fd; ++fd) 00066 { 00067 if (FD_ISSET(fd, &si.read)) 00068 { 00069 QSocketNotifier *n = notify_readable.find(fd); 00070 if (! n) 00071 { 00072 n = new QSocketNotifier(fd, QSocketNotifier::Read); 00073 notify_readable.insert(fd, n); 00074 QObject::connect(n, SIGNAL(activated(int)), 00075 this, SLOT(fd_readable(int))); 00076 } 00077 } else 00078 notify_readable.remove(fd); 00079 00080 if (FD_ISSET(fd, &si.write)) 00081 { 00082 QSocketNotifier *n = notify_writable.find(fd); 00083 if (! n) 00084 { 00085 n = new QSocketNotifier(fd, QSocketNotifier::Write); 00086 notify_writable.insert(fd, n); 00087 QObject::connect(n, SIGNAL(activated(int)), 00088 this, SLOT(fd_writable(int))); 00089 } 00090 } else 00091 notify_writable.remove(fd); 00092 00093 if (FD_ISSET(fd, &si.except)) 00094 { 00095 QSocketNotifier *n = notify_exception.find(fd); 00096 if (! n) 00097 { 00098 n = new QSocketNotifier(fd, QSocketNotifier::Exception); 00099 notify_exception.insert(fd, n); 00100 QObject::connect(n, SIGNAL(activated(int)), 00101 this, SLOT(fd_exception(int))); 00102 } 00103 } else 00104 notify_exception.remove(fd); 00105 } 00106 00107 // remove stale notifiers 00108 for (int fd = si.max_fd + 1; fd <= last_max_fd; ++fd) 00109 { 00110 notify_readable.remove(fd); 00111 notify_writable.remove(fd); 00112 notify_exception.remove(fd); 00113 } 00114 last_max_fd = si.max_fd; 00115 00116 // clear select lists 00117 FD_ZERO(&si.read); 00118 FD_ZERO(&si.write); 00119 FD_ZERO(&si.except); 00120 } 00121 00122 00123 void WvQtStreamClone::post_poll() 00124 { 00125 // cleanup and invoke callbacks 00126 bool sure = _process_selectinfo(si, true); 00127 if (sure || pending_callback) 00128 { 00129 pending_callback = false; 00130 callback(); 00131 if (globalstream) globalstream->callback(); 00132 } 00133 } 00134 00135 00136 void WvQtStreamClone::set_timeout(int msec_timeout) 00137 { 00138 this->msec_timeout = msec_timeout; 00139 } 00140 00141 00142 void WvQtStreamClone::qt_begin_event_loop_hook() 00143 { 00144 // select not done yet? 00145 if (select_in_progress) return; 00146 00147 // finish the last polling stage 00148 if (! first_time) 00149 post_poll(); 00150 else 00151 first_time = false; 00152 // start the next polling stage 00153 pre_poll(); 00154 select_in_progress = true; 00155 } 00156 00157 00158 void WvQtStreamClone::qt_detach() 00159 { 00160 // finish the last polling stage 00161 if (! first_time) 00162 { 00163 select_in_progress = false; 00164 post_poll(); 00165 last_max_fd = -1; 00166 first_time = true; 00167 } 00168 // remove any remaining Qt objects 00169 select_timer.stop(); 00170 notify_readable.clear(); 00171 notify_writable.clear(); 00172 notify_exception.clear(); 00173 QObject::disconnect(qApp, SIGNAL(guiThreadAwake()), 00174 this, SLOT(qt_begin_event_loop_hook())); 00175 QObject::disconnect(& select_timer, SIGNAL(timeout()), 00176 this, SLOT(select_timer_expired())); 00177 } 00178 00179 00180 void WvQtStreamClone::qt_attach() 00181 { 00182 // hook into the Qt event loop before each iteration 00183 QObject::connect(qApp, SIGNAL(guiThreadAwake()), 00184 this, SLOT(qt_begin_event_loop_hook())); 00185 QObject::connect(& select_timer, SIGNAL(timeout()), 00186 this, SLOT(select_timer_expired())); 00187 } 00188 00189 00190 void WvQtStreamClone::select_timer_expired() 00191 { 00192 select_in_progress = false; 00193 } 00194 00195 00196 void WvQtStreamClone::fd_readable(int fd) 00197 { 00198 FD_SET(fd, &si.read); 00199 pending_callback = true; 00200 select_in_progress = false; 00201 } 00202 00203 00204 void WvQtStreamClone::fd_writable(int fd) 00205 { 00206 FD_SET(fd, &si.write); 00207 pending_callback = true; 00208 select_in_progress = false; 00209 } 00210 00211 00212 void WvQtStreamClone::fd_exception(int fd) 00213 { 00214 FD_SET(fd, &si.except); 00215 pending_callback = true; 00216 select_in_progress = false; 00217 } 00218 00219 void WvQtStreamClone::execute() 00220 { 00221 WvStreamClone::execute(); 00222 } 00223 00224 00225 void WvQtStreamClone::setclone(IWvStream *newclone) 00226 { 00227 WvStreamClone::setclone(newclone); 00228 00229 if (newclone != NULL) 00230 my_type = WvString("WvQtStreamClone:%s", newclone->wstype()); 00231 else 00232 my_type = "WvQtStreamClone:(none)"; 00233 }