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 * SPDX-License-Identifier: GPL-2.0-only
6 *
7 * Author: Tom Goff <thomas.goff@boeing.com>
8 */
9
10#include "fatal-error.h"
11#include "fd-reader.h"
12#include "log.h"
13#include "simple-ref-count.h"
14#include "simulator.h"
15
16#include <cerrno>
17#include <cstring>
18#include <fcntl.h>
19#include <sys/select.h>
20#include <thread>
21#include <unistd.h> // close()
22
23/**
24 * \file
25 * \ingroup system
26 * ns3::FdReader implementation.
27 */
28
29namespace ns3
30{
31
32NS_LOG_COMPONENT_DEFINE("FdReader");
33
35 : m_fd(-1),
36 m_stop(false),
37 m_destroyEvent()
38{
39 NS_LOG_FUNCTION(this);
40 m_evpipe[0] = -1;
41 m_evpipe[1] = -1;
42}
43
45{
46 NS_LOG_FUNCTION(this);
47 Stop();
48}
49
50void
52{
53 NS_LOG_FUNCTION(this << fd << &readCallback);
54 int tmp;
55
56 NS_ASSERT_MSG(!m_readThread.joinable(), "read thread already exists");
57
58 // create a pipe for inter-thread event notification
59 tmp = pipe(m_evpipe);
60 if (tmp == -1)
61 {
62 NS_FATAL_ERROR("pipe() failed: " << std::strerror(errno));
63 }
64
65 // make the read end non-blocking
66 tmp = fcntl(m_evpipe[0], F_GETFL);
67 if (tmp == -1)
68 {
69 NS_FATAL_ERROR("fcntl() failed: " << std::strerror(errno));
70 }
71 if (fcntl(m_evpipe[0], F_SETFL, tmp | O_NONBLOCK) == -1)
72 {
73 NS_FATAL_ERROR("fcntl() failed: " << std::strerror(errno));
74 }
75
76 m_fd = fd;
77 m_readCallback = readCallback;
78
79 //
80 // We're going to spin up a thread soon, so we need to make sure we have
81 // a way to tear down that thread when the simulation stops. Do this by
82 // scheduling a "destroy time" method to make sure the thread exits before
83 // proceeding.
84 //
86 {
87 // hold a reference to ensure that this object is not
88 // deallocated before the destroy-time event fires
89 this->Ref();
91 }
92
93 //
94 // Now spin up a thread to read from the fd
95 //
96 NS_LOG_LOGIC("Spinning up read thread");
97
98 m_readThread = std::thread(&FdReader::Run, this);
99}
100
101void
103{
104 NS_LOG_FUNCTION(this);
105 Stop();
106 this->Unref();
107}
108
109void
111{
112 NS_LOG_FUNCTION(this);
113 m_stop = true;
114
115 // signal the read thread
116 if (m_evpipe[1] != -1)
117 {
118 char zero = 0;
119 ssize_t len = write(m_evpipe[1], &zero, sizeof(zero));
120 if (len != sizeof(zero))
121 {
122 NS_LOG_WARN("incomplete write(): " << std::strerror(errno));
123 }
124 }
125
126 // join the read thread
127 if (m_readThread.joinable())
128 {
129 m_readThread.join();
130 }
131
132 // close the write end of the event pipe
133 if (m_evpipe[1] != -1)
134 {
135 close(m_evpipe[1]);
136 m_evpipe[1] = -1;
137 }
138
139 // close the read end of the event pipe
140 if (m_evpipe[0] != -1)
141 {
142 close(m_evpipe[0]);
143 m_evpipe[0] = -1;
144 }
145
146 // reset everything else
147 m_fd = -1;
149 m_stop = false;
150}
151
152// This runs in a separate thread
153void
155{
156 NS_LOG_FUNCTION(this);
157 int nfds;
158 fd_set rfds;
159
160 nfds = (m_fd > m_evpipe[0] ? m_fd : m_evpipe[0]) + 1;
161
162 FD_ZERO(&rfds);
163 FD_SET(m_fd, &rfds);
164 FD_SET(m_evpipe[0], &rfds);
165
166 for (;;)
167 {
168 int r;
169 fd_set readfds = rfds;
170
171 r = select(nfds, &readfds, nullptr, nullptr, nullptr);
172 if (r == -1 && errno != EINTR)
173 {
174 NS_FATAL_ERROR("select() failed: " << std::strerror(errno));
175 }
176
177 if (FD_ISSET(m_evpipe[0], &readfds))
178 {
179 // drain the event pipe
180 for (;;)
181 {
182 char buf[1024];
183 ssize_t len = read(m_evpipe[0], buf, sizeof(buf));
184 if (len == 0)
185 {
186 NS_FATAL_ERROR("event pipe closed");
187 }
188 if (len < 0)
189 {
190 if (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK)
191 {
192 break;
193 }
194 else
195 {
196 NS_FATAL_ERROR("read() failed: " << std::strerror(errno));
197 }
198 }
199 }
200 }
201
202 if (m_stop)
203 {
204 // this thread is done
205 break;
206 }
207
208 if (FD_ISSET(m_fd, &readfds))
209 {
211 // reading stops when m_len is zero
212 if (data.m_len == 0)
213 {
214 break;
215 }
216 // the callback is only called when m_len is positive (data
217 // is ignored if m_len is negative)
218 else if (data.m_len > 0)
219 {
220 m_readCallback(data.m_buf, data.m_len);
221 }
222 }
223 }
224}
225
226} // namespace ns3
Callback template class.
Definition callback.h:422
void Nullify()
Discard the implementation, set it to null.
Definition callback.h:561
bool IsPending() const
This method is syntactic sugar for !IsExpired().
Definition event-id.cc:65
int m_evpipe[2]
Pipe used to signal events between threads.
Definition fd-reader.h:139
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:141
EventId m_destroyEvent
The event scheduled for destroy time which will invoke DestroyEvent and halt the thread.
Definition fd-reader.h:147
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:133
std::thread m_readThread
The thread doing the read, created and launched by Start().
Definition fd-reader.h:136
int m_fd
The file descriptor to read from.
Definition fd-reader.h:124
virtual ~FdReader()
Destructor.
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:611
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:75
#define NS_FATAL_ERROR(msg)
Report a fatal error with a message and terminate.
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition log.h:191
#define NS_LOG_LOGIC(msg)
Use NS_LOG to output a message of level LOG_LOGIC.
Definition log.h:271
#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:250
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:80