A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
unix-fd-reader.cc
Go to the documentation of this file.
1
2/*
3 * Copyright (c) 2010 The Boeing Company
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation;
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 *
18 * Author: Tom Goff <thomas.goff@boeing.com>
19 */
20
21#include "fatal-error.h"
22#include "fd-reader.h"
23#include "log.h"
24#include "simple-ref-count.h"
25#include "simulator.h"
26
27#include <cerrno>
28#include <cstring>
29#include <fcntl.h>
30#include <sys/select.h>
31#include <thread>
32#include <unistd.h> // close()
33
40namespace ns3
41{
42
43NS_LOG_COMPONENT_DEFINE("FdReader");
44
46 : m_fd(-1),
47 m_stop(false),
48 m_destroyEvent()
49{
50 NS_LOG_FUNCTION(this);
51 m_evpipe[0] = -1;
52 m_evpipe[1] = -1;
53}
54
56{
57 NS_LOG_FUNCTION(this);
58 Stop();
59}
60
61void
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();
102 }
103
104 //
105 // Now spin up a thread to read from the fd
106 //
107 NS_LOG_LOGIC("Spinning up read thread");
108
109 m_readThread = std::thread(&FdReader::Run, this);
110}
111
112void
114{
115 NS_LOG_FUNCTION(this);
116 Stop();
117 this->Unref();
118}
119
120void
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.joinable())
139 {
140 m_readThread.join();
141 }
142
143 // close the write end of the event pipe
144 if (m_evpipe[1] != -1)
145 {
146 close(m_evpipe[1]);
147 m_evpipe[1] = -1;
148 }
149
150 // close the read end of the event pipe
151 if (m_evpipe[0] != -1)
152 {
153 close(m_evpipe[0]);
154 m_evpipe[0] = -1;
155 }
156
157 // reset everything else
158 m_fd = -1;
160 m_stop = false;
161}
162
163// This runs in a separate thread
164void
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, nullptr, nullptr, nullptr);
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 {
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
Callback template class.
Definition: callback.h:438
void Nullify()
Discard the implementation, set it to null.
Definition: callback.h:573
bool IsRunning() const
This method is syntactic sugar for !IsExpired().
Definition: event-id.cc:76
int m_evpipe[2]
Pipe used to signal events between threads.
Definition: fd-reader.h:150
FdReader()
Constructor.
void Run()
The asynchronous function which performs the read.
void Stop()
Stop the read thread and reset internal state.
bool m_stop
Signal the read thread to stop.
Definition: fd-reader.h:152
EventId m_destroyEvent
The event scheduled for destroy time which will invoke DestroyEvent and halt the thread.
Definition: fd-reader.h:158
virtual FdReader::Data DoRead()=0
The read implementation.
void Start(int fd, Callback< void, uint8_t *, ssize_t > readCallback)
Start a new read thread.
void DestroyEvent()
Event handler scheduled for destroy time to halt the thread.
Callback< void, uint8_t *, ssize_t > m_readCallback
The main thread callback function to invoke when we have data.
Definition: fd-reader.h:144
std::thread m_readThread
The thread doing the read, created and launched by Start().
Definition: fd-reader.h:147
int m_fd
The file descriptor to read from.
Definition: fd-reader.h:135
virtual ~FdReader()
Destructor.
void Ref() const
Increment the reference count.
void Unref() const
Decrement 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:625
static double zero
NS_FATAL_x macro definitions.
ns3::FdReader declaration.
#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:86
#define NS_FATAL_ERROR(msg)
Report a fatal error with a message and terminate.
Definition: fatal-error.h:179
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:202
#define NS_LOG_LOGIC(msg)
Use NS_LOG to output a message of level LOG_LOGIC.
Definition: log.h:282
#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:261
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.
Definition: fd-reader.h:91