blob: a6b85e2165263ea3e840bc025d985ce7dd4bf166 [file] [log] [blame]
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
2 *
3 * This is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This software 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 software; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
16 * USA.
17 */
18
Adam Tkac8aee1a82009-09-04 12:08:56 +000019#ifdef HAVE_CONFIG_H
20#include <config.h>
Adam Tkac49e5ce62008-11-14 12:25:34 +000021#endif
22
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000023#include <stdio.h>
24#include <string.h>
25#ifdef _WIN32
26#include <winsock2.h>
27#define write(s,b,l) send(s,(const char*)b,l,0)
28#define EWOULDBLOCK WSAEWOULDBLOCK
29#undef errno
30#define errno WSAGetLastError()
31#undef EINTR
32#define EINTR WSAEINTR
33#else
34#include <sys/types.h>
35#include <errno.h>
36#include <unistd.h>
37#include <sys/time.h>
38#endif
39
Adam Tkac49e5ce62008-11-14 12:25:34 +000040/* Old systems have select() in sys/time.h */
41#ifdef HAVE_SYS_SELECT_H
42#include <sys/select.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000043#endif
44
45#include <rdr/FdOutStream.h>
46#include <rdr/Exception.h>
47
48
49using namespace rdr;
50
51enum { DEFAULT_BUF_SIZE = 16384 };
52
Pierre Ossman4ce51ff2011-10-25 15:13:13 +000053FdOutStream::FdOutStream(int fd_, bool blocking_, int timeoutms_, int bufSize_)
54 : fd(fd_), blocking(blocking_), timeoutms(timeoutms_),
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000055 bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0)
56{
Pierre Ossman4ce51ff2011-10-25 15:13:13 +000057 ptr = start = sentUpTo = new U8[bufSize];
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000058 end = start + bufSize;
59}
60
61FdOutStream::~FdOutStream()
62{
63 try {
Pierre Ossman4ce51ff2011-10-25 15:13:13 +000064 blocking = true;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000065 flush();
66 } catch (Exception&) {
67 }
68 delete [] start;
69}
70
71void FdOutStream::setTimeout(int timeoutms_) {
72 timeoutms = timeoutms_;
73}
74
Pierre Ossman4ce51ff2011-10-25 15:13:13 +000075void FdOutStream::setBlocking(bool blocking_) {
76 blocking = blocking_;
77}
78
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000079int FdOutStream::length()
80{
Pierre Ossman4ce51ff2011-10-25 15:13:13 +000081 return offset + ptr - sentUpTo;
82}
83
84int FdOutStream::bufferUsage()
85{
86 return ptr - sentUpTo;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000087}
88
89void FdOutStream::flush()
90{
Pierre Ossman4ce51ff2011-10-25 15:13:13 +000091 int timeoutms_;
92
93 if (blocking)
94 timeoutms_ = timeoutms;
95 else
96 timeoutms_ = 0;
97
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000098 while (sentUpTo < ptr) {
Pierre Ossman4ce51ff2011-10-25 15:13:13 +000099 int n = writeWithTimeout((const void*) sentUpTo,
100 ptr - sentUpTo, timeoutms_);
101
102 // Timeout?
103 if (n == 0) {
104 // If non-blocking then we're done here
105 if (!blocking)
106 break;
107
108 // Otherwise try blocking (with possible timeout)
109 if ((timeoutms_ == 0) && (timeoutms != 0)) {
110 timeoutms_ = timeoutms;
111 break;
112 }
113
114 // Proper timeout
115 throw TimedOut();
116 }
117
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000118 sentUpTo += n;
119 offset += n;
120 }
121
Pierre Ossman4ce51ff2011-10-25 15:13:13 +0000122 // Managed to flush everything?
123 if (sentUpTo == ptr)
124 ptr = sentUpTo = start;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000125}
126
127
128int FdOutStream::overrun(int itemSize, int nItems)
129{
130 if (itemSize > bufSize)
131 throw Exception("FdOutStream overrun: max itemSize exceeded");
132
Pierre Ossman4ce51ff2011-10-25 15:13:13 +0000133 // First try to get rid of the data we have
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000134 flush();
135
Pierre Ossman4ce51ff2011-10-25 15:13:13 +0000136 // Still not enough space?
137 if (itemSize > end - ptr) {
138 // Can we shuffle things around?
139 // (don't do this if it gains us less than 25%)
140 if ((sentUpTo - start > bufSize / 4) &&
141 (itemSize < bufSize - (ptr - sentUpTo))) {
142 memmove(start, sentUpTo, ptr - sentUpTo);
143 ptr = start + (ptr - sentUpTo);
144 sentUpTo = start;
145 } else {
146 // Have to get rid of more data, so turn off non-blocking
147 // for a bit...
148 bool realBlocking;
149
150 realBlocking = blocking;
151 blocking = true;
152 flush();
153 blocking = realBlocking;
154 }
155 }
156
157 // Can we fit all the items asked for?
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000158 if (itemSize * nItems > end - ptr)
159 nItems = (end - ptr) / itemSize;
160
161 return nItems;
162}
163
164//
165// writeWithTimeout() writes up to the given length in bytes from the given
166// buffer to the file descriptor. If there is a timeout set and that timeout
167// expires, it throws a TimedOut exception. Otherwise it returns the number of
168// bytes written. It never attempts to write() unless select() indicates that
169// the fd is writable - this means it can be used on an fd which has been set
170// non-blocking. It also has to cope with the annoying possibility of both
171// select() and write() returning EINTR.
172//
173
Pierre Ossman4ce51ff2011-10-25 15:13:13 +0000174int FdOutStream::writeWithTimeout(const void* data, int length, int timeoutms)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000175{
176 int n;
177
178 do {
179
180 do {
181 fd_set fds;
182 struct timeval tv;
183 struct timeval* tvp = &tv;
184
185 if (timeoutms != -1) {
186 tv.tv_sec = timeoutms / 1000;
187 tv.tv_usec = (timeoutms % 1000) * 1000;
188 } else {
189 tvp = 0;
190 }
191
192 FD_ZERO(&fds);
193 FD_SET(fd, &fds);
194#ifdef _WIN32_WCE
195 // NB: This fixes a broken Winsock2 select() behaviour. select()
196 // never returns for non-blocking sockets, unless they're already
197 // ready to be written to...
198 u_long zero = 0; ioctlsocket(fd, FIONBIO, &zero);
199#endif
200 n = select(fd+1, 0, &fds, 0, tvp);
201#ifdef _WIN32_WCE
202 u_long one = 0; ioctlsocket(fd, FIONBIO, &one);
203#endif
204 } while (n < 0 && errno == EINTR);
205
206 if (n < 0) throw SystemException("select",errno);
207
Pierre Ossman4ce51ff2011-10-25 15:13:13 +0000208 if (n == 0) return 0;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000209
210 do {
211 n = ::write(fd, data, length);
212 } while (n < 0 && (errno == EINTR));
213
214 // NB: This outer loop simply fixes a broken Winsock2 EWOULDBLOCK
215 // condition, found only under Win98 (first edition), with slow
216 // network connections. Should in fact never ever happen...
217 } while (n < 0 && (errno == EWOULDBLOCK));
218
219 if (n < 0) throw SystemException("write",errno);
220
221 return n;
222}