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
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.
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().
virtual bool isok() const
return true if the stream is actually usable right now
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.
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.
virtual bool post_select(SelectInfo &si)
post_select() is called after select(), and returns true if this object is now ready.
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().
virtual size_t uwrite(const void *buf, size_t count)
unbuffered I/O functions; these ignore the buffer, which is handled by write().
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