Orocos Real-Time Toolkit
2.5.0
|
00001 /*************************************************************************** 00002 tag: Peter Soetens Thu Oct 22 11:59:08 CEST 2009 FileDescriptorActivity.cpp 00003 00004 FileDescriptorActivity.cpp - description 00005 ------------------- 00006 begin : Thu October 22 2009 00007 copyright : (C) 2009 Peter Soetens 00008 email : peter@thesourcworks.com 00009 00010 *************************************************************************** 00011 * This library is free software; you can redistribute it and/or * 00012 * modify it under the terms of the GNU General Public * 00013 * License as published by the Free Software Foundation; * 00014 * version 2 of the License. * 00015 * * 00016 * As a special exception, you may use this file as part of a free * 00017 * software library without restriction. Specifically, if other files * 00018 * instantiate templates or use macros or inline functions from this * 00019 * file, or you compile this file and link it with other files to * 00020 * produce an executable, this file does not by itself cause the * 00021 * resulting executable to be covered by the GNU General Public * 00022 * License. This exception does not however invalidate any other * 00023 * reasons why the executable file might be covered by the GNU General * 00024 * Public License. * 00025 * * 00026 * This library is distributed in the hope that it will be useful, * 00027 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 00028 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * 00029 * Lesser General Public License for more details. * 00030 * * 00031 * You should have received a copy of the GNU General Public * 00032 * License along with this library; if not, write to the Free Software * 00033 * Foundation, Inc., 59 Temple Place, * 00034 * Suite 330, Boston, MA 02111-1307 USA * 00035 * * 00036 ***************************************************************************/ 00037 00038 00039 #include "FileDescriptorActivity.hpp" 00040 #include "../ExecutionEngine.hpp" 00041 #include "../base/TaskCore.hpp" 00042 #include "../Logger.hpp" 00043 00044 00045 #include <algorithm> 00046 00047 #ifdef WIN32 00048 #include <io.h> 00049 #include <fcntl.h> 00050 #define pipe(X) _pipe((X), 1024, _O_BINARY) 00051 #define close _close 00052 #define write _write 00053 #undef max 00054 00055 #else 00056 #include <sys/select.h> 00057 #include <unistd.h> 00058 #include <errno.h> 00059 #endif 00060 00061 #include <boost/cstdint.hpp> 00062 00063 using namespace RTT; 00064 using namespace extras; 00065 using namespace base; 00066 const char FileDescriptorActivity::CMD_BREAK_LOOP; 00067 const char FileDescriptorActivity::CMD_TRIGGER; 00068 const char FileDescriptorActivity::CMD_UPDATE_SETS; 00069 00070 00079 FileDescriptorActivity::FileDescriptorActivity(int priority, RunnableInterface* _r, const std::string& name ) 00080 : Activity(priority, 0.0, _r, name) 00081 , m_running(false) 00082 , m_timeout(0) 00083 { 00084 FD_ZERO(&m_fd_set); 00085 m_interrupt_pipe[0] = m_interrupt_pipe[1] = -1; 00086 } 00087 00097 FileDescriptorActivity::FileDescriptorActivity(int scheduler, int priority, RunnableInterface* _r, const std::string& name ) 00098 : Activity(scheduler, priority, 0.0, _r, name) 00099 , m_running(false) 00100 , m_timeout(0) 00101 { 00102 FD_ZERO(&m_fd_set); 00103 m_interrupt_pipe[0] = m_interrupt_pipe[1] = -1; 00104 } 00105 00106 FileDescriptorActivity::~FileDescriptorActivity() 00107 { 00108 stop(); 00109 } 00110 00111 bool FileDescriptorActivity::isRunning() const 00112 { return Activity::isRunning() && m_running; } 00113 int FileDescriptorActivity::getTimeout() const 00114 { return m_timeout; } 00115 void FileDescriptorActivity::setTimeout(int timeout) 00116 { m_timeout = timeout; } 00117 void FileDescriptorActivity::watch(int fd) 00118 { RTT::os::MutexLock lock(m_lock); 00119 if (fd < 0) 00120 { 00121 log(Error) << "negative file descriptor given to FileDescriptorActivity::watch" << endlog(); 00122 return; 00123 } 00124 00125 m_watched_fds.insert(fd); 00126 FD_SET(fd, &m_fd_set); 00127 triggerUpdateSets(); 00128 } 00129 void FileDescriptorActivity::unwatch(int fd) 00130 { RTT::os::MutexLock lock(m_lock); 00131 m_watched_fds.erase(fd); 00132 FD_CLR(fd, &m_fd_set); 00133 triggerUpdateSets(); 00134 } 00135 void FileDescriptorActivity::clearAllWatches() 00136 { RTT::os::MutexLock lock(m_lock); 00137 m_watched_fds.clear(); 00138 FD_ZERO(&m_fd_set); 00139 triggerUpdateSets(); 00140 } 00141 void FileDescriptorActivity::triggerUpdateSets() 00142 { 00143 // i works around warn_unused_result 00144 int i = write(m_interrupt_pipe[1], &CMD_UPDATE_SETS, 1); 00145 i = i; 00146 } 00147 bool FileDescriptorActivity::isUpdated(int fd) const 00148 { return FD_ISSET(fd, &m_fd_work); } 00149 bool FileDescriptorActivity::hasError() const 00150 { return m_has_error; } 00151 bool FileDescriptorActivity::hasTimeout() const 00152 { return m_has_timeout; } 00153 bool FileDescriptorActivity::isWatched(int fd) const 00154 { RTT::os::MutexLock lock(m_lock); 00155 return FD_ISSET(fd, &m_fd_set); } 00156 00157 bool FileDescriptorActivity::start() 00158 { 00159 if (pipe(m_interrupt_pipe) == -1) 00160 { 00161 log(Error) << "FileDescriptorActivity: cannot create control pipe" << endlog(); 00162 return false; 00163 } 00164 00165 if (!Activity::start()) 00166 { 00167 close(m_interrupt_pipe[0]); 00168 close(m_interrupt_pipe[1]); 00169 m_interrupt_pipe[0] = m_interrupt_pipe[1] = -1; 00170 return false; 00171 } 00172 return true; 00173 } 00174 00175 bool FileDescriptorActivity::trigger() 00176 { return write(m_interrupt_pipe[1], &CMD_TRIGGER, 1) == 1; } 00177 00178 struct fd_watch { 00179 int& fd; 00180 fd_watch(int& fd) : fd(fd) {} 00181 ~fd_watch() 00182 { 00183 close(fd); 00184 fd = -1; 00185 }; 00186 }; 00187 00188 void FileDescriptorActivity::loop() 00189 { 00190 int pipe = m_interrupt_pipe[0]; 00191 fd_watch watch_pipe_0(m_interrupt_pipe[0]); 00192 fd_watch watch_pipe_1(m_interrupt_pipe[1]); 00193 00194 while(true) 00195 { 00196 int max_fd; 00197 { RTT::os::MutexLock lock(m_lock); 00198 if (m_watched_fds.empty()) 00199 max_fd = pipe; 00200 else 00201 max_fd = std::max(pipe, *m_watched_fds.rbegin()); 00202 00203 m_fd_work = m_fd_set; 00204 } 00205 FD_SET(pipe, &m_fd_work); 00206 00207 int ret; 00208 m_running = false; 00209 if (m_timeout == 0) 00210 { 00211 ret = select(max_fd + 1, &m_fd_work, NULL, NULL, NULL); 00212 } 00213 else 00214 { 00215 timeval timeout = { m_timeout / 1000, (m_timeout % 1000) * 1000 }; 00216 ret = select(max_fd + 1, &m_fd_work, NULL, NULL, &timeout); 00217 } 00218 00219 m_has_error = false; 00220 m_has_timeout = false; 00221 if (ret == -1) 00222 { 00223 log(Error) << "FileDescriptorActivity: error in select(), errno = " << errno << endlog(); 00224 m_has_error = true; 00225 } 00226 else if (ret == 0) 00227 { 00228 log(Error) << "FileDescriptorActivity: timeout in select()" << endlog(); 00229 m_has_timeout = true; 00230 } 00231 00232 bool do_break = false, do_trigger = true; 00233 if (ret > 0 && FD_ISSET(pipe, &m_fd_work)) // breakLoop or trigger requests 00234 { // Empty all commands queued in the pipe 00235 00236 // These variables are used in order to loop with select(). See the 00237 // while() condition below. 00238 fd_set watch_pipe; 00239 timeval timeout; 00240 00241 do_trigger = false; 00242 do 00243 { 00244 boost::uint8_t code; 00245 if (read(pipe, &code, 1) == 1) 00246 { 00247 if (code == CMD_BREAK_LOOP) 00248 { 00249 do_break = true; 00250 } 00251 else if (code == CMD_UPDATE_SETS){} 00252 else 00253 do_trigger = true; 00254 } 00255 00256 // Initialize the values for the next select() call 00257 FD_ZERO(&watch_pipe); 00258 FD_SET(pipe, &watch_pipe); 00259 timeout.tv_sec = 0; 00260 timeout.tv_usec = 0; 00261 } 00262 while(select(pipe + 1, &watch_pipe, NULL, NULL, &timeout) > 0); 00263 00264 if (do_break) 00265 break; 00266 } 00267 00268 if (do_trigger) 00269 { 00270 try 00271 { 00272 m_running = true; 00273 step(); 00274 m_running = false; 00275 } 00276 catch(...) 00277 { 00278 m_running = false; 00279 throw; 00280 } 00281 } 00282 } 00283 } 00284 00285 bool FileDescriptorActivity::breakLoop() 00286 { 00287 if (write(m_interrupt_pipe[1], &CMD_BREAK_LOOP, 1) != 1) 00288 return false; 00289 00290 // either OS::SingleThread properly waits for loop() to return, or we are 00291 // called from within loop() [for instance because updateHook() called 00292 // fatal()]. In both cases, just return. 00293 return true; 00294 } 00295 00296 void FileDescriptorActivity::step() 00297 { 00298 m_running = true; 00299 if (runner != 0) 00300 runner->step(); 00301 m_running = false; 00302 } 00303 00304 bool FileDescriptorActivity::stop() 00305 { 00306 // If fatal() is called from the updateHook(), stop() will be called from 00307 // within the context and loop() will still run after this command has quit. 00308 // 00309 // This is bad and will have to be fixed in RTT 2.0 by having delayed stops 00310 // (i.e. setting the task context's state to FATAL only when loop() has 00311 // quit) 00312 return Activity::stop(); 00313 } 00314