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