Orocos Real-Time Toolkit  2.5.0
FileDescriptorActivity.cpp
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