src/core/unix-fd-reader.cc
changeset 6777 ba6d0ca3e28a
parent 6776 dec2a010a39f
child 6778 bb040260d75c
child 6807 82daf3da652a
equal deleted inserted replaced
6776:dec2a010a39f 6777:ba6d0ca3e28a
     1 /* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
       
     2 
       
     3 /*
       
     4  * Copyright (c) 2010 The Boeing Company
       
     5  *
       
     6  * This program is free software; you can redistribute it and/or modify
       
     7  * it under the terms of the GNU General Public License version 2 as
       
     8  * published by the Free Software Foundation;
       
     9  *
       
    10  * This program is distributed in the hope that it will be useful,
       
    11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
       
    13  * GNU General Public License for more details.
       
    14  *
       
    15  * You should have received a copy of the GNU General Public License
       
    16  * along with this program; if not, write to the Free Software
       
    17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
       
    18  *
       
    19  * Author: Tom Goff <thomas.goff@boeing.com>
       
    20  */
       
    21 
       
    22 #include <errno.h>
       
    23 #include <string.h>
       
    24 #include <unistd.h>
       
    25 #include <fcntl.h>
       
    26 
       
    27 #include "ns3/log.h"
       
    28 #include "ns3/fatal-error.h"
       
    29 #include "ns3/simple-ref-count.h"
       
    30 #include "ns3/system-thread.h"
       
    31 #include "ns3/simulator.h"
       
    32 
       
    33 #include "unix-fd-reader.h"
       
    34 
       
    35 NS_LOG_COMPONENT_DEFINE ("FdReader");
       
    36 
       
    37 namespace ns3 {
       
    38 
       
    39 FdReader::FdReader ()
       
    40   : m_fd (-1), m_readCallback (0), m_readThread (0), m_stop (false),
       
    41     m_destroyEvent ()
       
    42 {
       
    43   m_evpipe[0] = -1;
       
    44   m_evpipe[1] = -1;
       
    45 }
       
    46 
       
    47 FdReader::~FdReader ()
       
    48 {
       
    49   Stop ();
       
    50 }
       
    51 
       
    52 void FdReader::Start (int fd, Callback<void, uint8_t *, ssize_t> readCallback)
       
    53 {
       
    54   int tmp;
       
    55 
       
    56   NS_ASSERT_MSG (m_readThread == 0, "read thread already exists");
       
    57 
       
    58   // create a pipe for inter-thread event notification
       
    59   tmp = pipe (m_evpipe);
       
    60   if (tmp == -1)
       
    61     {
       
    62       NS_FATAL_ERROR ("pipe() failed: " << strerror (errno));
       
    63     }
       
    64 
       
    65   // make the read end non-blocking
       
    66   tmp = fcntl(m_evpipe[0], F_GETFL);
       
    67   if (tmp == -1)
       
    68     {
       
    69       NS_FATAL_ERROR ("fcntl() failed: " << strerror (errno));
       
    70     }
       
    71   if (fcntl(m_evpipe[0], F_SETFL, tmp | O_NONBLOCK) == -1)
       
    72     {
       
    73       NS_FATAL_ERROR ("fcntl() failed: " << strerror (errno));
       
    74     }
       
    75 
       
    76   m_fd = fd;
       
    77   m_readCallback = readCallback;
       
    78 
       
    79   //
       
    80   // We're going to spin up a thread soon, so we need to make sure we have
       
    81   // a way to tear down that thread when the simulation stops.  Do this by
       
    82   // scheduling a "destroy time" method to make sure the thread exits before
       
    83   // proceeding.
       
    84   //
       
    85   if (! m_destroyEvent.IsRunning ())
       
    86     {
       
    87       // hold a reference to ensure that this object is not
       
    88       // deallocated before the destroy-time event fires
       
    89       this->Ref ();
       
    90       m_destroyEvent =
       
    91         Simulator::ScheduleDestroy (&FdReader::DestroyEvent, this);
       
    92     }
       
    93 
       
    94   //
       
    95   // Now spin up a thread to read from the fd
       
    96   //
       
    97   NS_LOG_LOGIC ("Spinning up read thread");
       
    98 
       
    99   m_readThread = Create<SystemThread> (MakeCallback (&FdReader::Run, this));
       
   100   m_readThread->Start ();
       
   101 }
       
   102 
       
   103 void FdReader::DestroyEvent (void)
       
   104 {
       
   105   Stop ();
       
   106   this->Unref ();
       
   107 }
       
   108 
       
   109 void FdReader::Stop (void)
       
   110 {
       
   111   m_stop = true;
       
   112 
       
   113   // signal the read thread and close the write end of the event pipe
       
   114   if (m_evpipe[1] != -1)
       
   115     {
       
   116       char zero = 0;
       
   117       ssize_t len = write (m_evpipe[1], &zero, sizeof (zero));
       
   118       if (len != sizeof (zero))
       
   119         NS_LOG_WARN ("incomplete write(): " << strerror (errno));
       
   120       close (m_evpipe[1]);
       
   121       m_evpipe[1] = -1;
       
   122     }
       
   123 
       
   124   // join the read thread
       
   125   if (m_readThread != 0)
       
   126     {
       
   127       m_readThread->Join ();
       
   128       m_readThread = 0;
       
   129     }
       
   130 
       
   131   // close the read end of the event pipe
       
   132   if (m_evpipe[0] != -1)
       
   133     {
       
   134       close (m_evpipe[0]);
       
   135       m_evpipe[0] = -1;
       
   136     }
       
   137 
       
   138   // reset everything else
       
   139   m_fd = -1;
       
   140   m_readCallback.Nullify ();
       
   141   m_stop = false;
       
   142 }
       
   143 
       
   144 // This runs in a separate thread
       
   145 void FdReader::Run (void)
       
   146 {
       
   147   int nfds;
       
   148   fd_set rfds;
       
   149 
       
   150   nfds = (m_fd > m_evpipe[0] ? m_fd : m_evpipe[0]) + 1;
       
   151 
       
   152   FD_ZERO (&rfds);
       
   153   FD_SET (m_fd, &rfds);
       
   154   FD_SET (m_evpipe[0], &rfds);
       
   155 
       
   156   for (;;)
       
   157     {
       
   158       int r;
       
   159       fd_set readfds = rfds;
       
   160 
       
   161       r = select (nfds, &readfds, NULL, NULL, NULL);
       
   162       if (r == -1 && errno != EINTR)
       
   163         {
       
   164           NS_FATAL_ERROR ("select() failed: " << strerror (errno));
       
   165 	}
       
   166 
       
   167       if (FD_ISSET (m_evpipe[0], &readfds))
       
   168         {
       
   169           // drain the event pipe
       
   170           ssize_t len;
       
   171           for (;;)
       
   172             {
       
   173               char buf[1024];
       
   174               len = read (m_evpipe[0], buf, sizeof (buf));
       
   175               if (len == 0)
       
   176                 {
       
   177                   NS_FATAL_ERROR ("event pipe closed");
       
   178                 }
       
   179               if (len < 0)
       
   180                 {
       
   181                   break;
       
   182                 }
       
   183             }
       
   184 
       
   185           if (len < 0 && errno != EAGAIN && errno != EWOULDBLOCK)
       
   186             {
       
   187               NS_LOG_WARN ("read() failed: " << strerror (errno));
       
   188               break;
       
   189             }
       
   190 	}
       
   191 
       
   192       if (m_stop)
       
   193         {
       
   194           // this thread is done
       
   195           break;
       
   196         }
       
   197 
       
   198       if (FD_ISSET (m_fd, &readfds))
       
   199         {
       
   200           struct FdReader::Data data = DoRead ();
       
   201           // reading stops when m_len is zero
       
   202           if (data.m_len == 0)
       
   203             {
       
   204               break;
       
   205             }
       
   206           // the callback is only called when m_len is positive (data
       
   207           // is ignored if m_len is negative)
       
   208           else if (data.m_len > 0)
       
   209             {
       
   210               m_readCallback (data.m_buf, data.m_len);
       
   211             }
       
   212         }
       
   213     }
       
   214 }
       
   215 
       
   216 } // namespace ns3