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
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.
System-independent thread class ns3::SystemThread declaration.
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:205
#define NS_FATAL_ERROR(msg)
Report a fatal error with a message and terminate.
Definition: fatal-error.h:165
ns3::Simulator declaration.
Callback< void, uint8_t *, ssize_t > m_readCallback
The main thread callback function to invoke when we have data.
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
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.
#define NS_LOG_LOGIC(msg)
Use NS_LOG to output a message of level LOG_LOGIC.
Definition: log.h:289
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.
#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_LOG_WARN(msg)
Use NS_LOG to output a message of level LOG_WARN.
Definition: log.h:265
bool IsRunning(void) const
This method is syntactic sugar for !IsExpired().
Definition: event-id.cc:71
void Nullify(void)
Discard the implementation, set it to null.
Definition: callback.h:1391
virtual FdReader::Data DoRead(void)=0
The read implementation.
void Ref(void) const
Increment the reference count.
int m_evpipe[2]
Pipe used to signal events between threads.
Debug message logging.
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.