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 #include <thread>
29 
30 #include "log.h"
31 #include "fatal-error.h"
32 #include "simple-ref-count.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_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.joinable(), "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 = std::thread (&FdReader::Run, this);
111 }
112 
114 {
115  NS_LOG_FUNCTION (this);
116  Stop ();
117  this->Unref ();
118 }
119 
120 void FdReader::Stop (void)
121 {
122  NS_LOG_FUNCTION (this);
123  m_stop = true;
124 
125  // signal the read thread
126  if (m_evpipe[1] != -1)
127  {
128  char zero = 0;
129  ssize_t len = write (m_evpipe[1], &zero, sizeof (zero));
130  if (len != sizeof (zero))
131  {
132  NS_LOG_WARN ("incomplete write(): " << std::strerror (errno));
133  }
134  }
135 
136  // join the read thread
137  if (m_readThread.joinable ())
138  {
139  m_readThread.join ();
140  }
141 
142  // close the write end of the event pipe
143  if (m_evpipe[1] != -1)
144  {
145  close (m_evpipe[1]);
146  m_evpipe[1] = -1;
147  }
148 
149  // close the read end of the event pipe
150  if (m_evpipe[0] != -1)
151  {
152  close (m_evpipe[0]);
153  m_evpipe[0] = -1;
154  }
155 
156  // reset everything else
157  m_fd = -1;
159  m_stop = false;
160 }
161 
162 // This runs in a separate thread
163 void FdReader::Run (void)
164 {
165  NS_LOG_FUNCTION (this);
166  int nfds;
167  fd_set rfds;
168 
169  nfds = (m_fd > m_evpipe[0] ? m_fd : m_evpipe[0]) + 1;
170 
171  FD_ZERO (&rfds);
172  FD_SET (m_fd, &rfds);
173  FD_SET (m_evpipe[0], &rfds);
174 
175  for (;;)
176  {
177  int r;
178  fd_set readfds = rfds;
179 
180  r = select (nfds, &readfds, NULL, NULL, NULL);
181  if (r == -1 && errno != EINTR)
182  {
183  NS_FATAL_ERROR ("select() failed: " << std::strerror (errno));
184  }
185 
186  if (FD_ISSET (m_evpipe[0], &readfds))
187  {
188  // drain the event pipe
189  for (;;)
190  {
191  char buf[1024];
192  ssize_t len = read (m_evpipe[0], buf, sizeof (buf));
193  if (len == 0)
194  {
195  NS_FATAL_ERROR ("event pipe closed");
196  }
197  if (len < 0)
198  {
199  if (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK)
200  {
201  break;
202  }
203  else
204  {
205  NS_FATAL_ERROR ("read() failed: " << std::strerror (errno));
206  }
207  }
208  }
209  }
210 
211  if (m_stop)
212  {
213  // this thread is done
214  break;
215  }
216 
217  if (FD_ISSET (m_fd, &readfds))
218  {
219  struct FdReader::Data data = DoRead ();
220  // reading stops when m_len is zero
221  if (data.m_len == 0)
222  {
223  break;
224  }
225  // the callback is only called when m_len is positive (data
226  // is ignored if m_len is negative)
227  else if (data.m_len > 0)
228  {
229  m_readCallback (data.m_buf, data.m_len);
230  }
231  }
232  }
233 }
234 
235 } // 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.
std::thread m_readThread
The thread doing the read, created and launched by Start().
int m_fd
The file descriptor to read from.
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:605
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.
ns3::SimpleRefCount declaration and template implementation.
ns3::Simulator declaration.
uint8_t data[writeSize]
A structure representing data read.
ns3::FdReader declaration.