WvStreams
wvfdstream.cc
1/*
2 * Worldvisions Weaver Software:
3 * Copyright (C) 1997-2002 Net Integration Technologies, Inc.
4 *
5 * Base class for streams built on Unix file descriptors.
6 */
7#include "wvfdstream.h"
8#include "wvmoniker.h"
9#include <fcntl.h>
10
11#ifndef _WIN32
12#include <sys/socket.h>
13
14inline bool isselectable(int fd)
15{
16 return true;
17}
18
19#else // _WIN32
20
21#define getsockopt(a,b,c,d,e) getsockopt(a,b,c,(char *)d, e)
22#define SHUT_RD SD_RECEIVE
23#define SHUT_WR SD_SEND
24#define ENOBUFS WSAENOBUFS
25#undef EAGAIN
26#define EAGAIN WSAEWOULDBLOCK
27
28#include "streams.h"
29
30#undef errno
31#define errno GetLastError()
32
33// in win32, only sockets can be in the FD_SET for select()
34static inline bool isselectable(int s)
35{
36 // if _get_osfhandle() works, it's a msvcrt fd, not a winsock handle.
37 // msvcrt fds can't be select()ed on correctly.
38 return ((HANDLE)_get_osfhandle(s) == INVALID_HANDLE_VALUE)
39 ? true : false;
40}
41
42#endif // _WIN32
43
44
45/***** WvFdStream *****/
46
47static IWvStream *creator(WvStringParm s, IObject *)
48{
49 return new WvFdStream(s.num());
50}
51
52static WvMoniker<IWvStream> reg("fd", creator);
53
55 : rfd(_rwfd), wfd(_rwfd)
56{
57 shutdown_read = shutdown_write = false;
58}
59
60
61WvFdStream::WvFdStream(int _rfd, int _wfd)
62 : rfd(_rfd), wfd(_wfd)
63{
64 shutdown_read = shutdown_write = false;
65}
66
67
69{
70 close();
71}
72
73
74static int _cloexec(int fd, bool close_on_exec)
75{
76#ifndef _WIN32 // there is no exec() in win32, so this is meaningless there
77 return fcntl(fd, F_SETFD, close_on_exec ? FD_CLOEXEC : 0);
78#else
79 return 0;
80#endif
81}
82
83
84static int _nonblock(int fd, bool nonblock)
85{
86#ifndef _WIN32
87 int flag = fcntl(fd, F_GETFL);
88 return fcntl(fd, F_SETFL,
89 (flag & ~O_NONBLOCK) | (nonblock ? O_NONBLOCK : 0));
90#else
91 u_long arg = nonblock ? 1 : 0;
92 return ioctlsocket(fd, FIONBIO, &arg);
93#endif
94}
95
96
97void WvFdStream::set_nonblock(bool nonblock)
98{
99 int rfd = getrfd(), wfd = getwfd();
100 if (rfd >= 0)
101 _nonblock(rfd, nonblock);
102 if (wfd >= 0 && rfd != wfd)
103 _nonblock(wfd, nonblock);
104}
105
106
107void WvFdStream::set_close_on_exec(bool close_on_exec)
108{
109 int rfd = getrfd(), wfd = getwfd();
110 if (rfd >= 0)
111 _cloexec(rfd, close_on_exec);
112 if (wfd >= 0 && rfd != wfd)
113 _cloexec(wfd, close_on_exec);
114}
115
116
118{
119 // fprintf(stderr, "closing fdstream!\n");
120 if (!closed)
121 {
123 //fprintf(stderr, "closing%d:%d/%d\n", (int)this, rfd, wfd);
124 if (rfd >= 0)
125 ::close(rfd);
126 if (wfd >= 0 && wfd != rfd)
127 ::close(wfd);
128 rfd = wfd = -1;
129 //fprintf(stderr, "closed!\n");
130 }
131}
132
133
135{
136 return WvStream::isok() && (rfd != -1 || wfd != -1);
137}
138
139
140size_t WvFdStream::uread(void *buf, size_t count)
141{
142 assert(!count || buf);
143 if (!count || !buf || !isok()) return 0;
144
145 int in = ::read(rfd, buf, count);
146
147 // a read that returns zero bytes signifies end-of-file (EOF).
148 if (in <= 0)
149 {
150 if (in < 0 && (errno==EINTR || errno==EAGAIN || errno==ENOBUFS))
151 return 0; // interrupted
152
153 seterr(in < 0 ? errno : 0);
154 return 0;
155 }
156
157 // fprintf(stderr, "read %d bytes\n", in);
158 return in;
159}
160
161
162size_t WvFdStream::uwrite(const void *buf, size_t count)
163{
164 assert(!count || buf);
165 if (!buf || !count || !isok()) return 0;
166 // fprintf(stderr, "write %d bytes\n", count);
167
168 int out = ::write(wfd, buf, count);
169
170 if (out <= 0)
171 {
172 int err = errno;
173 // fprintf(stderr, "(fd%d-err-%d)", wfd, err);
174 if (out < 0 && (err == ENOBUFS || err==EAGAIN))
175 return 0; // kernel buffer full - data not written (yet!)
176
177 seterr(out < 0 ? err : 0); // a more critical error
178 return 0;
179 }
180
181 //TRACE("write obj 0x%08x, bytes %d/%d\n", (unsigned int)this, out, count);
182 return out;
183}
184
185
187{
188 if (stop_write && !shutdown_write && !outbuf.used())
189 {
190 shutdown_write = true;
191 if (wfd < 0)
192 return;
193 if (rfd != wfd)
194 ::close(wfd);
195 else
196 ::shutdown(wfd, SHUT_WR); // might be a socket
197 wfd = -1;
198 }
199
200 if (stop_read && !shutdown_read && !inbuf.used())
201 {
202 shutdown_read = true;
203 if (rfd != wfd)
204 ::close(rfd);
205 else
206 ::shutdown(rfd, SHUT_RD); // might be a socket
207 rfd = -1;
208 }
209
211}
212
213
215{
217
218#if 0
219 fprintf(stderr, "%d/%d wr:%d ww:%d wx:%d inh:%d\n", rfd, wfd,
220 si.wants.readable, si.wants.writable, si.wants.isexception,
221 si.inherit_request);
222#endif
223 if (si.wants.readable && (rfd >= 0))
224 {
225 if (isselectable(rfd))
226 FD_SET(rfd, &si.read);
227 else
228 si.msec_timeout = 0; // not selectable -> *always* readable
229 }
230
231 // FIXME: outbuf flushing should really be in WvStream::pre_select()
232 // instead! But it's hard to get the equivalent behaviour there.
233 if ((si.wants.writable || outbuf.used() || autoclose_time) && (wfd >= 0))
234 {
235 if (isselectable(wfd))
236 FD_SET(wfd, &si.write);
237 else
238 si.msec_timeout = 0; // not selectable -> *always* writable
239 }
240
241 if (si.wants.isexception)
242 {
243 if (rfd >= 0 && isselectable(rfd)) FD_SET(rfd, &si.except);
244 if (wfd >= 0 && isselectable(wfd)) FD_SET(wfd, &si.except);
245 }
246
247 if (si.max_fd < rfd)
248 si.max_fd = rfd;
249 if (si.max_fd < wfd)
250 si.max_fd = wfd;
251}
252
253
255{
256 bool result = WvStream::post_select(si);
257
258 // flush the output buffer if possible
259 size_t outbuf_used = outbuf.used();
260 if (wfd >= 0 && (outbuf_used || autoclose_time)
261 && FD_ISSET(wfd, &si.write) && should_flush())
262 {
263 flush_outbuf(0);
264
265 // flush_outbuf() might have closed the file!
266 if (!isok())
267 return result;
268 }
269
270 bool rforce = si.wants.readable && !isselectable(rfd),
271 wforce = si.wants.writable && !isselectable(wfd);
272 bool val =
273 (rfd >= 0 && (rforce || FD_ISSET(rfd, &si.read)))
274 || (wfd >= 0 && (wforce || FD_ISSET(wfd, &si.write)))
275 || (rfd >= 0 && (FD_ISSET(rfd, &si.except)))
276 || (wfd >= 0 && (FD_ISSET(wfd, &si.except)));
277
278 // fprintf(stderr, "fds_post_select: %d/%d %d/%d %d\n",
279 // rfd, wfd, rforce, wforce, val);
280
281 if (val && si.wants.readable && read_requires_writable
283 && !read_requires_writable->select(0, false, true))
284 return result;
285 if (val && si.wants.writable && write_requires_readable
287 && !write_requires_readable->select(0, true, false))
288 return result;
289 return val || result;
290}
The basic interface which is included by all other XPLC interfaces and objects.
Definition: IObject.h:65
size_t used() const
Returns the number of elements in the buffer currently available for reading.
Definition: wvbufbase.h:92
A WvFastString acts exactly like a WvString, but can take (const char *) strings without needing to a...
Definition: wvstring.h:94
int num() const
Return a stdc++ string with the contents of this string.
Definition: wvstring.h:286
Base class for streams built on Unix file descriptors.
Definition: wvfdstream.h:21
bool shutdown_read
Have we actually shut down the read/write sides?
Definition: wvfdstream.h:30
virtual void maybe_autoclose()
Auto-close the stream if the time is right.
Definition: wvfdstream.cc:186
WvFdStream(int rwfd=-1)
Creates a WvStream from an existing file descriptor.
Definition: wvfdstream.cc:54
int getrfd() const
Returns the Unix file descriptor for reading from this stream.
Definition: wvfdstream.h:63
virtual size_t uread(void *buf, size_t count)
unbuffered I/O functions; these ignore the buffer, which is handled by read().
Definition: wvfdstream.cc:140
virtual bool isok() const
return true if the stream is actually usable right now
Definition: wvfdstream.cc:134
int getwfd() const
Returns the Unix file descriptor for writing to this stream.
Definition: wvfdstream.h:70
void set_nonblock(bool nonblock)
Make the fds on this stream blocking or non-blocking.
Definition: wvfdstream.cc:97
virtual void close()
Closes the file descriptors.
Definition: wvfdstream.cc:117
virtual ~WvFdStream()
Destroys the stream and invokes close().
Definition: wvfdstream.cc:68
void set_close_on_exec(bool close_on_exec)
Make the fds on this stream close-on-exec or not.
Definition: wvfdstream.cc:107
virtual bool post_select(SelectInfo &si)
post_select() is called after ::select(), and returns true if this object is now ready.
Definition: wvfdstream.cc:254
int rfd
The file descriptor for reading.
Definition: wvfdstream.h:24
virtual void pre_select(SelectInfo &si)
pre_select() sets up for eventually calling ::select().
Definition: wvfdstream.cc:214
virtual size_t uwrite(const void *buf, size_t count)
unbuffered I/O functions; these ignore the buffer, which is handled by write().
Definition: wvfdstream.cc:162
int wfd
The file descriptor for writing.
Definition: wvfdstream.h:27
A type-safe version of WvMonikerBase that lets you provide create functions for object types other th...
Definition: wvmoniker.h:62
virtual bool post_select(SelectInfo &si)
post_select() is called after ::select(), and returns true if this object is now ready.
Definition: wvstream.cc:875
virtual bool isok() const
return true if the stream is actually usable right now
Definition: wvstream.cc:445
WvStream * write_requires_readable
If this is set, select() doesn't return true for write unless the given stream also returns true for ...
Definition: wvstream.h:42
bool stop_read
True if noread()/nowrite()/close() have been called, respectively.
Definition: wvstream.h:57
virtual size_t write(const void *buf, size_t count)
Write data to the stream.
Definition: wvstream.cc:532
virtual bool should_flush()
Returns true if we want to flush the output buffer right now.
Definition: wvstream.cc:724
virtual void pre_select(SelectInfo &si)
pre_select() sets up for eventually calling ::select().
Definition: wvstream.cc:844
bool select(time_t msec_timeout)
Return true if any of the requested features are true on the stream.
Definition: wvstream.h:376
virtual void maybe_autoclose()
Auto-close the stream if the time is right.
Definition: wvstream.cc:583
virtual size_t read(void *buf, size_t count)
read a data block on the stream.
Definition: wvstream.cc:490
virtual void close()
Close the stream if it is open; isok() becomes false from now on.
Definition: wvstream.cc:341
virtual void seterr(int _errnum)
Override seterr() from WvError so that it auto-closes the stream.
Definition: wvstream.cc:451
WvStream * read_requires_writable
If this is set, select() doesn't return true for read unless the given stream also returns true for w...
Definition: wvstream.h:36
the data structure used by pre_select()/post_select() and internally by select().
Definition: iwvstream.h:50