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 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation;
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 *
17 * Author: Tom Goff <thomas.goff@boeing.com>
18 */
19
20#include "fatal-error.h"
21#include "fd-reader.h"
22#include "log.h"
23#include "simple-ref-count.h"
24#include "simulator.h"
25
26#include <cerrno>
27#include <cstring>
28#include <fcntl.h>
29#include <winsock.h>
30
31// #define pipe(fds) _pipe(fds,4096, _O_BINARY)
32
33/**
34 * \file
35 * \ingroup system
36 * ns3::FdReader implementation.
37 */
38
39namespace ns3
40{
41
42NS_LOG_COMPONENT_DEFINE("FdReader");
43
44// conditional compilation to avoid Doxygen errors
45#ifdef __WIN32__
46bool FdReader::winsock_initialized = false;
47#endif
48
50 : m_fd(-1),
51 m_stop(false),
52 m_destroyEvent()
53{
54 NS_LOG_FUNCTION(this);
55 m_evpipe[0] = -1;
56 m_evpipe[1] = -1;
57}
58
59FdReader::~FdReader()
60{
61 NS_LOG_FUNCTION(this);
62 Stop();
63}
64
65void
66FdReader::Start(int fd, Callback<void, uint8_t*, ssize_t> readCallback)
67{
68 NS_LOG_FUNCTION(this << fd << &readCallback);
69 int tmp;
70
71 if (!winsock_initialized)
72 {
73 WSADATA wsaData;
74 tmp = WSAStartup(MAKEWORD(2, 2), &wsaData);
75 NS_ASSERT_MSG(tmp != NO_ERROR, "Error at WSAStartup()");
76 winsock_initialized = true;
77 }
78
79 NS_ASSERT_MSG(!m_readThread.joinable(), "read thread already exists");
80
81 // create a pipe for inter-thread event notification
82 m_evpipe[0] = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
83 m_evpipe[1] = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
84 if ((static_cast<uint64_t>(m_evpipe[0]) == INVALID_SOCKET) ||
85 (static_cast<uint64_t>(m_evpipe[1]) == INVALID_SOCKET))
86 {
87 NS_FATAL_ERROR("pipe() failed: " << std::strerror(errno));
88 }
89
90 // make the read end non-blocking
91 ULONG iMode = 1;
92 tmp = ioctlsocket(m_evpipe[0], FIONBIO, &iMode);
93 if (tmp != NO_ERROR)
94 {
95 NS_FATAL_ERROR("fcntl() failed: " << std::strerror(errno));
96 }
97
98 m_fd = fd;
99 m_readCallback = readCallback;
100
101 //
102 // We're going to spin up a thread soon, so we need to make sure we have
103 // a way to tear down that thread when the simulation stops. Do this by
104 // scheduling a "destroy time" method to make sure the thread exits before
105 // proceeding.
106 //
107 if (!m_destroyEvent.IsRunning())
108 {
109 // hold a reference to ensure that this object is not
110 // deallocated before the destroy-time event fires
111 this->Ref();
112 m_destroyEvent = Simulator::ScheduleDestroy(&FdReader::DestroyEvent, this);
113 }
114
115 //
116 // Now spin up a thread to read from the fd
117 //
118 NS_LOG_LOGIC("Spinning up read thread");
119
120 m_readThread = std::thread(&FdReader::Run, this);
121}
122
123void
124FdReader::DestroyEvent()
125{
126 NS_LOG_FUNCTION(this);
127 Stop();
128 this->Unref();
129}
130
131void
132FdReader::Stop()
133{
134 NS_LOG_FUNCTION(this);
135 m_stop = true;
136
137 // signal the read thread
138 if (m_evpipe[1] != -1)
139 {
140 char zero = 0;
141 ssize_t len = send(m_evpipe[1], &zero, sizeof(zero), 0);
142 if (len != sizeof(zero))
143 {
144 NS_LOG_WARN("incomplete write(): " << std::strerror(errno));
145 }
146 }
147
148 if (m_readThread.joinable())
149 {
150 m_readThread.join();
151 }
152
153 // close the write end of the event pipe
154 if (m_evpipe[1] != -1)
155 {
156 closesocket(m_evpipe[1]);
157 m_evpipe[1] = -1;
158 }
159
160 // close the read end of the event pipe
161 if (m_evpipe[0] != -1)
162 {
163 closesocket(m_evpipe[0]);
164 m_evpipe[0] = -1;
165 }
166
167 // reset everything else
168 m_fd = -1;
169 m_readCallback.Nullify();
170 m_stop = false;
171}
172
173// This runs in a separate thread
174void
175FdReader::Run()
176{
177 NS_LOG_FUNCTION(this);
178 int nfds;
179 fd_set rfds;
180
181 nfds = (m_fd > m_evpipe[0] ? m_fd : m_evpipe[0]) + 1;
182
183 FD_ZERO(&rfds);
184 FD_SET(m_fd, &rfds);
185 FD_SET(m_evpipe[0], &rfds);
186
187 for (;;)
188 {
189 int r;
190 fd_set readfds = rfds;
191
192 r = select(nfds, &readfds, nullptr, nullptr, nullptr);
193 if (r == -1 && errno != EINTR)
194 {
195 NS_FATAL_ERROR("select() failed: " << std::strerror(errno));
196 }
197
198 if (FD_ISSET(m_evpipe[0], &readfds))
199 {
200 // drain the event pipe
201 for (;;)
202 {
203 char buf[1024];
204 ssize_t len = recv(m_evpipe[0], buf, sizeof(buf), 0);
205 if (len == 0)
206 {
207 NS_FATAL_ERROR("event pipe closed");
208 }
209 if (len < 0)
210 {
211 if (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK)
212 {
213 break;
214 }
215 else
216 {
217 NS_FATAL_ERROR("read() failed: " << std::strerror(errno));
218 }
219 }
220 }
221 }
222
223 if (m_stop)
224 {
225 // this thread is done
226 break;
227 }
228
229 if (FD_ISSET(m_fd, &readfds))
230 {
231 FdReader::Data data = DoRead();
232 // reading stops when m_len is zero
233 if (data.m_len == 0)
234 {
235 break;
236 }
237 // the callback is only called when m_len is positive (data
238 // is ignored if m_len is negative)
239 else if (data.m_len > 0)
240 {
241 m_readCallback(data.m_buf, data.m_len);
242 }
243 }
244 }
245}
246
247} // 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: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]