Fix compilation with old g++ 3.3.5 and debian-sarge.
[wvstreams.git] / streams / wvfdstream.cc
blob49d97142c296b0fa2bb130cbdfb6b9977fe5acdd
1 /*
2 * Worldvisions Weaver Software:
3 * Copyright (C) 1997-2002 Net Integration Technologies, Inc.
5 * Base class for streams built on Unix file descriptors.
6 */
7 #include "wvfdstream.h"
8 #include "wvmoniker.h"
9 #include <fcntl.h>
11 #ifndef _WIN32
12 #include <sys/socket.h>
14 inline bool isselectable(int fd)
16 return true;
19 #else // _WIN32
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
28 #include "streams.h"
30 #undef errno
31 #define errno GetLastError()
33 // in win32, only sockets can be in the FD_SET for select()
34 static inline bool isselectable(int s)
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;
42 #endif // _WIN32
45 /***** WvFdStream *****/
47 static IWvStream *creator(WvStringParm s, IObject *)
49 return new WvFdStream(s.num());
52 static WvMoniker<IWvStream> reg("fd", creator);
54 WvFdStream::WvFdStream(int _rwfd)
55 : rfd(_rwfd), wfd(_rwfd)
57 shutdown_read = shutdown_write = false;
61 WvFdStream::WvFdStream(int _rfd, int _wfd)
62 : rfd(_rfd), wfd(_wfd)
64 shutdown_read = shutdown_write = false;
68 WvFdStream::~WvFdStream()
70 close();
74 static int _cloexec(int fd, bool close_on_exec)
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
84 static int _nonblock(int fd, bool nonblock)
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
97 void WvFdStream::set_nonblock(bool nonblock)
99 int rfd = getrfd(), wfd = getwfd();
100 if (rfd >= 0)
101 _nonblock(rfd, nonblock);
102 if (wfd >= 0 && rfd != wfd)
103 _nonblock(wfd, nonblock);
107 void WvFdStream::set_close_on_exec(bool close_on_exec)
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);
117 void WvFdStream::close()
119 // fprintf(stderr, "closing fdstream!\n");
120 if (!closed)
122 WvStream::close();
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");
134 bool WvFdStream::isok() const
136 return WvStream::isok() && (rfd != -1 || wfd != -1);
140 size_t WvFdStream::uread(void *buf, size_t count)
142 assert(!count || buf);
143 if (!count || !buf || !isok()) return 0;
145 int in = ::read(rfd, buf, count);
147 // a read that returns zero bytes signifies end-of-file (EOF).
148 if (in <= 0)
150 if (in < 0 && (errno==EINTR || errno==EAGAIN || errno==ENOBUFS))
151 return 0; // interrupted
153 seterr(in < 0 ? errno : 0);
154 return 0;
157 // fprintf(stderr, "read %d bytes\n", in);
158 return in;
162 size_t WvFdStream::uwrite(const void *buf, size_t count)
164 assert(!count || buf);
165 if (!buf || !count || !isok()) return 0;
166 // fprintf(stderr, "write %d bytes\n", count);
168 int out = ::write(wfd, buf, count);
170 if (out <= 0)
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!)
177 seterr(out < 0 ? err : 0); // a more critical error
178 return 0;
181 //TRACE("write obj 0x%08x, bytes %d/%d\n", (unsigned int)this, out, count);
182 return out;
186 void WvFdStream::maybe_autoclose()
188 if (stop_write && !shutdown_write && !outbuf.used())
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;
200 if (stop_read && !shutdown_read && !inbuf.used())
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;
210 WvStream::maybe_autoclose();
214 void WvFdStream::pre_select(SelectInfo &si)
216 WvStream::pre_select(si);
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))
225 if (isselectable(rfd))
226 FD_SET(rfd, &si.read);
227 else
228 si.msec_timeout = 0; // not selectable -> *always* readable
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))
235 if (isselectable(wfd))
236 FD_SET(wfd, &si.write);
237 else
238 si.msec_timeout = 0; // not selectable -> *always* writable
241 if (si.wants.isexception)
243 if (rfd >= 0 && isselectable(rfd)) FD_SET(rfd, &si.except);
244 if (wfd >= 0 && isselectable(wfd)) FD_SET(wfd, &si.except);
247 if (si.max_fd < rfd)
248 si.max_fd = rfd;
249 if (si.max_fd < wfd)
250 si.max_fd = wfd;
254 bool WvFdStream::post_select(SelectInfo &si)
256 bool result = WvStream::post_select(si);
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())
263 flush_outbuf(0);
265 // flush_outbuf() might have closed the file!
266 if (!isok())
267 return result;
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)));
278 // fprintf(stderr, "fds_post_select: %d/%d %d/%d %d\n",
279 // rfd, wfd, rforce, wforce, val);
281 if (val && si.wants.readable && read_requires_writable
282 && read_requires_writable->isok()
283 && !read_requires_writable->select(0, false, true))
284 return result;
285 if (val && si.wants.writable && write_requires_readable
286 && write_requires_readable->isok()
287 && !write_requires_readable->select(0, true, false))
288 return result;
289 return val || result;