A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
unix-fd-reader.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2010 The Boeing Company
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 *
6 * Author: Tom Goff <thomas.goff@boeing.com>
7 */
8
9#include "fatal-error.h"
10#include "fd-reader.h"
11#include "log.h"
12#include "simulator.h"
13
14#include <cerrno>
15#include <cstring>
16#include <fcntl.h>
17#include <sys/select.h>
18#include <thread>
19#include <unistd.h> // close()
20
21/**
22 * @file
23 * @ingroup system
24 * ns3::FdReader implementation.
25 */
26
27namespace ns3
28{
29
30NS_LOG_COMPONENT_DEFINE("FdReader");
31
33 : m_fd(-1),
34 m_stop(false),
36{
37 NS_LOG_FUNCTION(this);
38 m_evpipe[0] = -1;
39 m_evpipe[1] = -1;
40}
41
43{
44 NS_LOG_FUNCTION(this);
45 Stop();
46}
47
48void
50{
51 NS_LOG_FUNCTION(this << fd << &readCallback);
52 int tmp;
53
54 NS_ASSERT_MSG(!m_readThread.joinable(), "read thread already exists");
55
56 // create a pipe for inter-thread event notification
57 tmp = pipe(m_evpipe);
58 if (tmp == -1)
59 {
60 NS_FATAL_ERROR("pipe() failed: " << std::strerror(errno));
61 }
62
63 // make the read end non-blocking
64 tmp = fcntl(m_evpipe[0], F_GETFL);
65 if (tmp == -1)
66 {
67 NS_FATAL_ERROR("fcntl() failed: " << std::strerror(errno));
68 }
69 if (fcntl(m_evpipe[0], F_SETFL, tmp | O_NONBLOCK) == -1)
70 {
71 NS_FATAL_ERROR("fcntl() failed: " << std::strerror(errno));
72 }
73
74 m_fd = fd;
75 m_readCallback = readCallback;
76
77 //
78 // We're going to spin up a thread soon, so we need to make sure we have
79 // a way to tear down that thread when the simulation stops. Do this by
80 // scheduling a "destroy time" method to make sure the thread exits before
81 // proceeding.
82 //
83 if (!m_destroyEvent.IsPending())
84 {
85 // hold a reference to ensure that this object is not
86 // deallocated before the destroy-time event fires
87 this->Ref();
89 }
90
91 //
92 // Now spin up a thread to read from the fd
93 //
94 NS_LOG_LOGIC("Spinning up read thread");
95
96 m_readThread = std::thread(&FdReader::Run, this);
97}
98
99void
101{
102 NS_LOG_FUNCTION(this);
103 Stop();
104 this->Unref();
105}
106
107void
109{
110 NS_LOG_FUNCTION(this);
111 m_stop = true;
112
113 // signal the read thread
114 if (m_evpipe[1] != -1)
115 {
116 char zero = 0;
117 ssize_t len = write(m_evpipe[1], &zero, sizeof(zero));
118 if (len != sizeof(zero))
119 {
120 NS_LOG_WARN("incomplete write(): " << std::strerror(errno));
121 }
122 }
123
124 // join the read thread
125 if (m_readThread.joinable())
126 {
127 m_readThread.join();
128 }
129
130 // close the write end of the event pipe
131 if (m_evpipe[1] != -1)
132 {
133 close(m_evpipe[1]);
134 m_evpipe[1] = -1;
135 }
136
137 // close the read end of the event pipe
138 if (m_evpipe[0] != -1)
139 {
140 close(m_evpipe[0]);
141 m_evpipe[0] = -1;
142 }
143
144 // reset everything else
145 m_fd = -1;
146 m_readCallback.Nullify();
147 m_stop = false;
148}
149
150// This runs in a separate thread
151void
153{
154 NS_LOG_FUNCTION(this);
155 int nfds;
156 fd_set rfds;
157
158 nfds = (m_fd > m_evpipe[0] ? m_fd : m_evpipe[0]) + 1;
159
160 FD_ZERO(&rfds);
161 FD_SET(m_fd, &rfds);
162 FD_SET(m_evpipe[0], &rfds);
163
164 for (;;)
165 {
166 int r;
167 fd_set readfds = rfds;
168
169 r = select(nfds, &readfds, nullptr, nullptr, nullptr);
170 if (r == -1 && errno != EINTR)
171 {
172 NS_FATAL_ERROR("select() failed: " << std::strerror(errno));
173 }
174
175 if (FD_ISSET(m_evpipe[0], &readfds))
176 {
177 // drain the event pipe
178 for (;;)
179 {
180 char buf[1024];
181 ssize_t len = read(m_evpipe[0], buf, sizeof(buf));
182 if (len == 0)
183 {
184 NS_FATAL_ERROR("event pipe closed");
185 }
186 if (len < 0)
187 {
188 if (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK)
189 {
190 break;
191 }
192 else
193 {
194 NS_FATAL_ERROR("read() failed: " << std::strerror(errno));
195 }
196 }
197 }
198 }
199
200 if (m_stop)
201 {
202 // this thread is done
203 break;
204 }
205
206 if (FD_ISSET(m_fd, &readfds))
207 {
209 // reading stops when m_len is zero
210 if (data.m_len == 0)
211 {
212 break;
213 }
214 // the callback is only called when m_len is positive (data
215 // is ignored if m_len is negative)
216 else if (data.m_len > 0)
217 {
218 m_readCallback(data.m_buf, data.m_len);
219 }
220 }
221 }
222}
223
224} // namespace ns3
uint32_t r
Callback template class.
Definition callback.h:428
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:631
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:194
#define NS_LOG_LOGIC(msg)
Use NS_LOG to output a message of level LOG_LOGIC.
Definition log.h:274
#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:253
Debug message logging.
Every class exported by the ns3 library is enclosed in the ns3 namespace.
ns3::Simulator declaration.
uint8_t data[writeSize]
A structure representing data read.
Definition fd-reader.h:80