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
43namespace ns3 {
44
45NS_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 //
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
120void 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
163void 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:604
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.