FileDescriptorActivity.cpp
Go to the documentation of this file.
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_us(0)
00083     , m_period(0)
00084     , m_has_error(false)
00085     , m_has_timeout(false)
00086 {
00087     FD_ZERO(&m_fd_set);
00088     FD_ZERO(&m_fd_work);
00089     m_interrupt_pipe[0] = m_interrupt_pipe[1] = -1;
00090 }
00091 
00101 FileDescriptorActivity::FileDescriptorActivity(int scheduler, int priority, RunnableInterface* _r, const std::string& name )
00102     : Activity(scheduler, priority, 0.0, _r, name)
00103     , m_running(false)
00104     , m_timeout_us(0)
00105     , m_period(0)
00106     , m_has_error(false)
00107     , m_has_timeout(false)
00108 {
00109     FD_ZERO(&m_fd_set);
00110     FD_ZERO(&m_fd_work);
00111     m_interrupt_pipe[0] = m_interrupt_pipe[1] = -1;
00112 }
00113 
00114 FileDescriptorActivity::FileDescriptorActivity(int scheduler, int priority, Seconds period, RunnableInterface* _r, const std::string& name )
00115     : Activity(scheduler, priority, 0.0, _r, name)      // actual period == 0.0
00116     , m_running(false)
00117     , m_timeout_us(0)
00118     , m_period(period >= 0.0 ? period : 0.0)        // intended period
00119     , m_has_error(false)
00120     , m_has_timeout(false)
00121 {
00122     FD_ZERO(&m_fd_set);
00123     FD_ZERO(&m_fd_work);
00124     m_interrupt_pipe[0] = m_interrupt_pipe[1] = -1;
00125 }
00126 
00127 FileDescriptorActivity::FileDescriptorActivity(int scheduler, int priority, Seconds period, unsigned cpu_affinity, RunnableInterface* _r, const std::string& name )
00128     : Activity(scheduler, priority, 0.0, cpu_affinity, _r, name)        // actual period == 0.0
00129     , m_running(false)
00130     , m_timeout_us(0)
00131     , m_period(period >= 0.0 ? period : 0.0)        // intended period
00132     , m_has_error(false)
00133     , m_has_timeout(false)
00134 {
00135     FD_ZERO(&m_fd_set);
00136     FD_ZERO(&m_fd_work);
00137     m_interrupt_pipe[0] = m_interrupt_pipe[1] = -1;
00138 }
00139 
00140 FileDescriptorActivity::~FileDescriptorActivity()
00141 {
00142     stop();
00143 }
00144 
00145 Seconds FileDescriptorActivity::getPeriod() const
00146 { return m_period; }
00147 
00148 bool FileDescriptorActivity::setPeriod(Seconds p)
00149 {
00150         if (p < 0)
00151         return false;
00152         m_period = p;
00153         return true;
00154 }
00155 
00156 bool FileDescriptorActivity::isRunning() const
00157 { return Activity::isRunning() && m_running; }
00158 int FileDescriptorActivity::getTimeout() const
00159 { return m_timeout_us / 1000; }
00160 int FileDescriptorActivity::getTimeout_us() const
00161 { return m_timeout_us; }
00162 void FileDescriptorActivity::setTimeout(int timeout)
00163 {
00164         setTimeout_us(timeout * 1000);
00165 }
00166 void FileDescriptorActivity::setTimeout_us(int timeout_us)
00167 {
00168         if (0 <= timeout_us)
00169         {
00170                 m_timeout_us = timeout_us;
00171         }
00172         else
00173         {
00174         log(Error) << "Ignoring invalid timeout (" << timeout_us << ")" << endlog();
00175     }
00176 }
00177 void FileDescriptorActivity::watch(int fd)
00178 { RTT::os::MutexLock lock(m_lock);
00179     if (fd < 0)
00180     {
00181         log(Error) << "negative file descriptor given to FileDescriptorActivity::watch" << endlog();
00182         return;
00183     }
00184 
00185     m_watched_fds.insert(fd);
00186     FD_SET(fd, &m_fd_set);
00187     triggerUpdateSets();
00188 }
00189 void FileDescriptorActivity::unwatch(int fd)
00190 { RTT::os::MutexLock lock(m_lock);
00191     m_watched_fds.erase(fd);
00192     FD_CLR(fd, &m_fd_set);
00193     triggerUpdateSets();
00194 }
00195 void FileDescriptorActivity::clearAllWatches()
00196 { RTT::os::MutexLock lock(m_lock);
00197     m_watched_fds.clear();
00198     FD_ZERO(&m_fd_set);
00199     triggerUpdateSets();
00200 }
00201 void FileDescriptorActivity::triggerUpdateSets()
00202 {
00203     // i works around warn_unused_result
00204     int i = write(m_interrupt_pipe[1], &CMD_UPDATE_SETS, 1);
00205     i = i;
00206 }
00207 bool FileDescriptorActivity::isUpdated(int fd) const
00208 { return FD_ISSET(fd, &m_fd_work); }
00209 bool FileDescriptorActivity::hasError() const
00210 { return m_has_error; }
00211 bool FileDescriptorActivity::hasTimeout() const
00212 { return m_has_timeout; }
00213 bool FileDescriptorActivity::isWatched(int fd) const
00214 { RTT::os::MutexLock lock(m_lock);
00215     return FD_ISSET(fd, &m_fd_set); }
00216 
00217 bool FileDescriptorActivity::start()
00218 {
00219     if ( isActive() )
00220         return false;
00221 
00222     if (pipe(m_interrupt_pipe) == -1)
00223     {
00224         log(Error) << "FileDescriptorActivity: cannot create control pipe" << endlog();
00225         return false;
00226     }
00227 
00228     if (!Activity::start())
00229     {
00230         close(m_interrupt_pipe[0]);
00231         close(m_interrupt_pipe[1]);
00232         m_interrupt_pipe[0] = m_interrupt_pipe[1] = -1;
00233         log(Error) << "FileDescriptorActivity: Activity::start() failed" << endlog();
00234         return false;
00235     }
00236     return true;
00237 }
00238 
00239 bool FileDescriptorActivity::trigger()
00240 { 
00241     if (isActive() ) 
00242         return write(m_interrupt_pipe[1], &CMD_TRIGGER, 1) == 1; 
00243     else
00244         return false;
00245 }
00246 
00247 struct fd_watch {
00248     int& fd;
00249     fd_watch(int& fd) : fd(fd) {}
00250     ~fd_watch()
00251     {
00252         if (fd != -1) 
00253             close(fd);
00254         fd = -1;
00255     };
00256 };
00257 
00258 void FileDescriptorActivity::loop()
00259 {
00260     int pipe = m_interrupt_pipe[0];
00261     fd_watch watch_pipe_0(m_interrupt_pipe[0]);
00262     fd_watch watch_pipe_1(m_interrupt_pipe[1]);
00263 
00264     while(true)
00265     {
00266         int max_fd;
00267         { RTT::os::MutexLock lock(m_lock);
00268             if (m_watched_fds.empty())
00269                 max_fd = pipe;
00270             else
00271                 max_fd = std::max(pipe, *m_watched_fds.rbegin());
00272 
00273             m_fd_work = m_fd_set;
00274         }
00275         FD_SET(pipe, &m_fd_work);
00276 
00277         int ret;
00278         m_running = false;
00279         if (m_timeout_us == 0)
00280         {
00281             ret = select(max_fd + 1, &m_fd_work, NULL, NULL, NULL);
00282         }
00283         else
00284         {
00285                         static const int USECS_PER_SEC = 1000000;
00286             timeval timeout = { m_timeout_us / USECS_PER_SEC,
00287                                 m_timeout_us % USECS_PER_SEC};
00288             ret = select(max_fd + 1, &m_fd_work, NULL, NULL, &timeout);
00289         }
00290 
00291         m_has_error   = false;
00292         m_has_timeout = false;
00293         if (ret == -1)
00294         {
00295             log(Error) << "FileDescriptorActivity: error in select(), errno = " << errno << endlog();
00296             m_has_error = true;
00297         }
00298         else if (ret == 0)
00299         {
00300             log(Error) << "FileDescriptorActivity: timeout in select()" << endlog();
00301             m_has_timeout = true;
00302         }
00303 
00304         bool do_break = false, do_trigger = true;
00305         if (ret > 0 && FD_ISSET(pipe, &m_fd_work)) // breakLoop or trigger requests
00306         { // Empty all commands queued in the pipe
00307 
00308             // These variables are used in order to loop with select(). See the
00309             // while() condition below.
00310             fd_set watch_pipe;
00311             timeval timeout;
00312 
00313             do_trigger = false;
00314             do
00315             {
00316                 boost::uint8_t code;
00317                 if (read(pipe, &code, 1) == 1)
00318                 {
00319                     if (code == CMD_BREAK_LOOP)
00320                     {
00321                         do_break = true;
00322                     }
00323                     else if (code == CMD_UPDATE_SETS){}
00324                     else
00325                         do_trigger = true;
00326                 }
00327 
00328                 // Initialize the values for the next select() call
00329                 FD_ZERO(&watch_pipe);
00330                 FD_SET(pipe, &watch_pipe);
00331                 timeout.tv_sec  = 0;
00332                 timeout.tv_usec = 0;
00333             }
00334             while(select(pipe + 1, &watch_pipe, NULL, NULL, &timeout) > 0);
00335 
00336             if (do_break)
00337                 break;
00338         }
00339 
00340         if (do_trigger)
00341         {
00342             try
00343             {
00344                 m_running = true;
00345                 step();
00346                 m_running = false;
00347             }
00348             catch(...)
00349             {
00350                 m_running = false;
00351                 throw;
00352             }
00353         }
00354     }
00355 }
00356 
00357 bool FileDescriptorActivity::breakLoop()
00358 {
00359     if (write(m_interrupt_pipe[1], &CMD_BREAK_LOOP, 1) != 1)
00360         return false;
00361 
00362     // either OS::SingleThread properly waits for loop() to return, or we are
00363     // called from within loop() [for instance because updateHook() called
00364     // fatal()]. In both cases, just return.
00365     return true;
00366 }
00367 
00368 void FileDescriptorActivity::step()
00369 {
00370     m_running = true;
00371     if (runner != 0)
00372         runner->step();
00373     m_running = false;
00374 }
00375 
00376 bool FileDescriptorActivity::stop()
00377 {
00378     // If fatal() is called from the updateHook(), stop() will be called from
00379     // within the context and loop() will still run after this command has quit.
00380     //
00381     // This is bad and will have to be fixed in RTT 2.0 by having delayed stops
00382     // (i.e. setting the task context's state to FATAL only when loop() has
00383     // quit)
00384     if ( Activity::stop() == true )
00385     {
00386         fd_watch watch_pipe_0(m_interrupt_pipe[0]);
00387         fd_watch watch_pipe_1(m_interrupt_pipe[1]);
00388         return true;
00389     }
00390     return false;
00391 }
00392 


rtt
Author(s): RTT Developers
autogenerated on Wed Aug 26 2015 16:15:48