A Discrete-Event Network Simulator
API
unix-fd-reader.cc
Go to the documentation of this file.
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 <cerrno>
23 #include <cstring>
24 #include <unistd.h> // close()
25 #include <fcntl.h>
26 
27 #include "log.h"
28 #include "fatal-error.h"
29 #include "simple-ref-count.h"
30 #include "system-thread.h"
31 #include "simulator.h"
32 
33 #include "unix-fd-reader.h"
34 
41 namespace ns3 {
42 
43 NS_LOG_COMPONENT_DEFINE ("FdReader");
44 
46  : m_fd (-1), m_readCallback (0), m_readThread (0), m_stop (false),
47  m_destroyEvent ()
48 {
49  NS_LOG_FUNCTION (this);
50  m_evpipe[0] = -1;
51  m_evpipe[1] = -1;
52 }
53 
55 {
56  NS_LOG_FUNCTION (this);
57  Stop ();
58 }
59 
61 {
62  NS_LOG_FUNCTION (this << fd << &readCallback);
63  int tmp;
64 
65  NS_ASSERT_MSG (m_readThread == 0, "read thread already exists");
66 
67  // create a pipe for inter-thread event notification
68  tmp = pipe (m_evpipe);
69  if (tmp == -1)
70  {
71  NS_FATAL_ERROR ("pipe() failed: " << std::strerror (errno));
72  }
73 
74  // make the read end non-blocking
75  tmp = fcntl (m_evpipe[0], F_GETFL);
76  if (tmp == -1)
77  {
78  NS_FATAL_ERROR ("fcntl() failed: " << std::strerror (errno));
79  }
80  if (fcntl (m_evpipe[0], F_SETFL, tmp | O_NONBLOCK) == -1)
81  {
82  NS_FATAL_ERROR ("fcntl() failed: " << std::strerror (errno));
83  }
84 
85  m_fd = fd;
86  m_readCallback = readCallback;
87 
88  //
89  // We're going to spin up a thread soon, so we need to make sure we have
90  // a way to tear down that thread when the simulation stops. Do this by
91  // scheduling a "destroy time" method to make sure the thread exits before
92  // proceeding.
93  //
94  if (!m_destroyEvent.IsRunning ())
95  {
96  // hold a reference to ensure that this object is not
97  // deallocated before the destroy-time event fires
98  this->Ref ();
101  }
102 
103  //
104  // Now spin up a thread to read from the fd
105  //
106  NS_LOG_LOGIC ("Spinning up read thread");
107 
108  m_readThread = Create<SystemThread> (MakeCallback (&FdReader::Run, this));
109  m_readThread->Start ();
110 }
111 
113 {
114  NS_LOG_FUNCTION (this);
115  Stop ();
116  this->Unref ();
117 }
118 
119 void FdReader::Stop (void)
120 {
121  NS_LOG_FUNCTION (this);
122  m_stop = true;
123 
124  // signal the read thread
125  if (m_evpipe[1] != -1)
126  {
127  char zero = 0;
128  ssize_t len = write (m_evpipe[1], &zero, sizeof (zero));
129  if (len != sizeof (zero))
130  NS_LOG_WARN ("incomplete write(): " << std::strerror (errno));
131  }
132 
133  // join the read thread
134  if (m_readThread != 0)
135  {
136  m_readThread->Join ();
137  m_readThread = 0;
138  }
139 
140  // close the write end of the event pipe
141  if (m_evpipe[1] != -1)
142  {
143  close (m_evpipe[1]);
144  m_evpipe[1] = -1;
145  }
146 
147  // close the read end of the event pipe
148  if (m_evpipe[0] != -1)
149  {
150  close (m_evpipe[0]);
151  m_evpipe[0] = -1;
152  }
153 
154  // reset everything else
155  m_fd = -1;
157  m_stop = false;
158 }
159 
160 // This runs in a separate thread
161 void FdReader::Run (void)
162 {
163  NS_LOG_FUNCTION (this);
164  int nfds;
165  fd_set rfds;
166 
167  nfds = (m_fd > m_evpipe[0] ? m_fd : m_evpipe[0]) + 1;
168 
169  FD_ZERO (&rfds);
170  FD_SET (m_fd, &rfds);
171  FD_SET (m_evpipe[0], &rfds);
172 
173  for (;;)
174  {
175  int r;
176  fd_set readfds = rfds;
177 
178  r = select (nfds, &readfds, NULL, NULL, NULL);
179  if (r == -1 && errno != EINTR)
180  {
181  NS_FATAL_ERROR ("select() failed: " << std::strerror (errno));
182  }
183 
184  if (FD_ISSET (m_evpipe[0], &readfds))
185  {
186  // drain the event pipe
187  for (;;)
188  {
189  char buf[1024];
190  ssize_t len = read (m_evpipe[0], buf, sizeof (buf));
191  if (len == 0)
192  {
193  NS_FATAL_ERROR ("event pipe closed");
194  }
195  if (len < 0)
196  {
197  if (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK)
198  {
199  break;
200  }
201  else
202  {
203  NS_FATAL_ERROR ("read() failed: " << std::strerror (errno));
204  }
205  }
206  }
207  }
208 
209  if (m_stop)
210  {
211  // this thread is done
212  break;
213  }
214 
215  if (FD_ISSET (m_fd, &readfds))
216  {
217  struct FdReader::Data data = DoRead ();
218  // reading stops when m_len is zero
219  if (data.m_len == 0)
220  {
221  break;
222  }
223  // the callback is only called when m_len is positive (data
224  // is ignored if m_len is negative)
225  else if (data.m_len > 0)
226  {
227  m_readCallback (data.m_buf, data.m_len);
228  }
229  }
230  }
231 }
232 
233 } // namespace ns3
NS_FATAL_x macro definitions.
void Start(int fd, Callback< void, uint8_t *, ssize_t > readCallback)
Start a new read thread.
void DestroyEvent(void)
Event handler scheduled for destroy time to halt the thread.
A structure representing data read.
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by "...
void Unref(void) const
Decrement the reference count.
virtual ~FdReader()
Destructor.
NS_ASSERT_MSG(false, "Ipv4AddressGenerator::MaskToIndex(): Impossible")
System-independent thread class ns3::SystemThread declaration.
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:202
#define NS_FATAL_ERROR(msg)
Report a fatal error with a message and terminate.
Definition: fatal-error.h:162
ns3::Simulator declaration.
Callback< void, uint8_t *, ssize_t > m_readCallback
The main thread callback function to invoke when we have data.
static double zero
bool m_stop
Signal the read thread to stop.
void Run(void)
The asynchronous function which performs the read.
void Stop(void)
Stop the read thread and reset internal state.
ns3::FdReader declaration.
Ptr< SystemThread > m_readThread
The thread doing the read, created and launched by Start().
int m_fd
The file descriptor to read from.
uint8_t data[writeSize]
FdReader()
Constructor.
Callback< R > MakeCallback(R(T::*memPtr)(void), OBJ objPtr)
Definition: callback.h:1489
Every class exported by the ns3 library is enclosed in the ns3 namespace.
EventId m_destroyEvent
The event scheduled for destroy time which will invoke DestroyEvent and halt the thread.
NS_LOG_LOGIC("Net device "<< nd<< " is not bridged")
#define NS_LOG_WARN(msg)
Use NS_LOG to output a message of level LOG_WARN.
Definition: log.h:262
bool IsRunning(void) const
This method is syntactic sugar for !IsExpired().
Definition: event-id.cc:65
void Nullify(void)
Discard the implementation, set it to null.
Definition: callback.h:1274
virtual FdReader::Data DoRead(void)=0
The read implementation.
static EventId ScheduleDestroy(MEM mem_ptr, OBJ obj)
Schedule an event to expire when Simulator::Destroy is called.
Definition: simulator.h:1677
void Ref(void) const
Increment the reference count.
int m_evpipe[2]
Pipe used to signal events between threads.
Debug message logging.
ns3::SimpleRefCount declaration and template implementation.