A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
win32-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 "simple-ref-count.h"
13#include "simulator.h"
14
15#include <cerrno>
16#include <cstring>
17#include <fcntl.h>
18#include <winsock.h>
19
20// #define pipe(fds) _pipe(fds,4096, _O_BINARY)
21
22/**
23 * \file
24 * \ingroup system
25 * ns3::FdReader implementation.
26 */
27
28namespace ns3
29{
30
31NS_LOG_COMPONENT_DEFINE("FdReader");
32
33// conditional compilation to avoid Doxygen errors
34#ifdef __WIN32__
35bool FdReader::winsock_initialized = false;
36#endif
37
39 : m_fd(-1),
40 m_stop(false),
41 m_destroyEvent()
42{
43 NS_LOG_FUNCTION(this);
44 m_evpipe[0] = -1;
45 m_evpipe[1] = -1;
46}
47
48FdReader::~FdReader()
49{
50 NS_LOG_FUNCTION(this);
51 Stop();
52}
53
54void
55FdReader::Start(int fd, Callback<void, uint8_t*, ssize_t> readCallback)
56{
57 NS_LOG_FUNCTION(this << fd << &readCallback);
58 int tmp;
59
60 if (!winsock_initialized)
61 {
62 WSADATA wsaData;
63 tmp = WSAStartup(MAKEWORD(2, 2), &wsaData);
64 NS_ASSERT_MSG(tmp != NO_ERROR, "Error at WSAStartup()");
65 winsock_initialized = true;
66 }
67
68 NS_ASSERT_MSG(!m_readThread.joinable(), "read thread already exists");
69
70 // create a pipe for inter-thread event notification
71 m_evpipe[0] = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
72 m_evpipe[1] = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
73 if ((static_cast<uint64_t>(m_evpipe[0]) == INVALID_SOCKET) ||
74 (static_cast<uint64_t>(m_evpipe[1]) == INVALID_SOCKET))
75 {
76 NS_FATAL_ERROR("pipe() failed: " << std::strerror(errno));
77 }
78
79 // make the read end non-blocking
80 ULONG iMode = 1;
81 tmp = ioctlsocket(m_evpipe[0], FIONBIO, &iMode);
82 if (tmp != NO_ERROR)
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.IsPending())
97 {
98 // hold a reference to ensure that this object is not
99 // deallocated before the destroy-time event fires
100 this->Ref();
101 m_destroyEvent = Simulator::ScheduleDestroy(&FdReader::DestroyEvent, this);
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
113FdReader::DestroyEvent()
114{
115 NS_LOG_FUNCTION(this);
116 Stop();
117 this->Unref();
118}
119
120void
121FdReader::Stop()
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 = send(m_evpipe[1], &zero, sizeof(zero), 0);
131 if (len != sizeof(zero))
132 {
133 NS_LOG_WARN("incomplete write(): " << std::strerror(errno));
134 }
135 }
136
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 closesocket(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 closesocket(m_evpipe[0]);
153 m_evpipe[0] = -1;
154 }
155
156 // reset everything else
157 m_fd = -1;
158 m_readCallback.Nullify();
159 m_stop = false;
160}
161
162// This runs in a separate thread
163void
164FdReader::Run()
165{
166 NS_LOG_FUNCTION(this);
167 int nfds;
168 fd_set rfds;
169
170 nfds = (m_fd > m_evpipe[0] ? m_fd : m_evpipe[0]) + 1;
171
172 FD_ZERO(&rfds);
173 FD_SET(m_fd, &rfds);
174 FD_SET(m_evpipe[0], &rfds);
175
176 for (;;)
177 {
178 int r;
179 fd_set readfds = rfds;
180
181 r = select(nfds, &readfds, nullptr, nullptr, nullptr);
182 if (r == -1 && errno != EINTR)
183 {
184 NS_FATAL_ERROR("select() failed: " << std::strerror(errno));
185 }
186
187 if (FD_ISSET(m_evpipe[0], &readfds))
188 {
189 // drain the event pipe
190 for (;;)
191 {
192 char buf[1024];
193 ssize_t len = recv(m_evpipe[0], buf, sizeof(buf), 0);
194 if (len == 0)
195 {
196 NS_FATAL_ERROR("event pipe closed");
197 }
198 if (len < 0)
199 {
200 if (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK)
201 {
202 break;
203 }
204 else
205 {
206 NS_FATAL_ERROR("read() failed: " << std::strerror(errno));
207 }
208 }
209 }
210 }
211
212 if (m_stop)
213 {
214 // this thread is done
215 break;
216 }
217
218 if (FD_ISSET(m_fd, &readfds))
219 {
220 FdReader::Data data = DoRead();
221 // reading stops when m_len is zero
222 if (data.m_len == 0)
223 {
224 break;
225 }
226 // the callback is only called when m_len is positive (data
227 // is ignored if m_len is negative)
228 else if (data.m_len > 0)
229 {
230 m_readCallback(data.m_buf, data.m_len);
231 }
232 }
233 }
234}
235
236} // namespace ns3
FdReader()
Constructor.
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]