bottleneck testcase based on rubbos
[bottlenecks.git] / rubbos / app / httpd-2.0.64 / srclib / apr / file_io / win32 / readwrite.c
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "win32/apr_arch_file_io.h"
18 #include "apr_file_io.h"
19 #include "apr_general.h"
20 #include "apr_strings.h"
21 #include "apr_lib.h"
22 #include "apr_errno.h"
23 #include <malloc.h>
24 #include "apr_arch_atime.h"
25 #include "apr_arch_misc.h"
26
27 /*
28  * read_with_timeout() 
29  * Uses async i/o to emulate unix non-blocking i/o with timeouts.
30  */
31 static apr_status_t read_with_timeout(apr_file_t *file, void *buf, apr_size_t len, apr_size_t *nbytes)
32 {
33     apr_status_t rv;
34     *nbytes = 0;
35
36     /* Handle the zero timeout non-blocking case */
37     if (file->timeout == 0) {
38         /* Peek at the pipe. If there is no data available, return APR_EAGAIN.
39          * If data is available, go ahead and read it.
40          */
41         if (file->pipe) {
42             DWORD bytes;
43             if (!PeekNamedPipe(file->filehand, NULL, 0, NULL, &bytes, NULL)) {
44                 rv = apr_get_os_error();
45                 if (rv == APR_FROM_OS_ERROR(ERROR_BROKEN_PIPE)) {
46                     rv = APR_EOF;
47                 }
48                 return rv;
49             }
50             else {
51                 if (bytes == 0) {
52                     return APR_EAGAIN;
53                 }
54                 if (len > bytes) {
55                     len = bytes;
56                 }
57             }
58         }
59         else {
60             /* ToDo: Handle zero timeout non-blocking file i/o 
61              * This is not needed until an APR application needs to
62              * timeout file i/o (which means setting file i/o non-blocking)
63              */
64         }
65     }
66
67     if (file->pOverlapped && !file->pipe) {
68         file->pOverlapped->Offset     = (DWORD)file->filePtr;
69         file->pOverlapped->OffsetHigh = (DWORD)(file->filePtr >> 32);
70     }
71
72     rv = ReadFile(file->filehand, buf, len, nbytes, file->pOverlapped);
73
74     if (!rv) {
75         rv = apr_get_os_error();
76         if (rv == APR_FROM_OS_ERROR(ERROR_IO_PENDING)) {
77             /* Wait for the pending i/o */
78             if (file->timeout > 0) {
79                 /* timeout in milliseconds... */
80                 rv = WaitForSingleObject(file->pOverlapped->hEvent, 
81                                          (DWORD)(file->timeout/1000)); 
82             }
83             else if (file->timeout == -1) {
84                 rv = WaitForSingleObject(file->pOverlapped->hEvent, INFINITE);
85             }
86             switch (rv) {
87                 case WAIT_OBJECT_0:
88                     GetOverlappedResult(file->filehand, file->pOverlapped, 
89                                         nbytes, TRUE);
90                     rv = APR_SUCCESS;
91                     break;
92
93                 case WAIT_TIMEOUT:
94                     rv = APR_TIMEUP;
95                     break;
96
97                 case WAIT_FAILED:
98                     rv = apr_get_os_error();
99                     break;
100
101                 default:
102                     break;
103             }
104
105             if (rv != APR_SUCCESS) {
106                 if (apr_os_level >= APR_WIN_98) {
107                     CancelIo(file->filehand);
108                 }
109             }
110         }
111         else if (rv == APR_FROM_OS_ERROR(ERROR_BROKEN_PIPE)) {
112             /* Assume ERROR_BROKEN_PIPE signals an EOF reading from a pipe */
113             rv = APR_EOF;
114         } else if (rv == APR_FROM_OS_ERROR(ERROR_HANDLE_EOF)) {
115             /* Did we hit EOF reading from the handle? */
116             rv = APR_EOF;
117         }
118     } else {
119         /* OK and 0 bytes read ==> end of file */
120         if (*nbytes == 0)
121             rv = APR_EOF;
122         else
123             rv = APR_SUCCESS;
124     }
125     if (rv == APR_SUCCESS && file->pOverlapped && !file->pipe) {
126         file->filePtr += *nbytes;
127     }
128     return rv;
129 }
130
131 APR_DECLARE(apr_status_t) apr_file_read(apr_file_t *thefile, void *buf, apr_size_t *len)
132 {
133     apr_status_t rv;
134     DWORD bytes_read = 0;
135
136     if (*len <= 0) {
137         *len = 0;
138         return APR_SUCCESS;
139     }
140
141     /* If the file is open for xthread support, allocate and
142      * initialize the overlapped and io completion event (hEvent). 
143      * Threads should NOT share an apr_file_t or its hEvent.
144      */
145     if ((thefile->flags & APR_XTHREAD) && !thefile->pOverlapped ) {
146         thefile->pOverlapped = (OVERLAPPED*) apr_pcalloc(thefile->pool, 
147                                                          sizeof(OVERLAPPED));
148         thefile->pOverlapped->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
149         if (!thefile->pOverlapped->hEvent) {
150             rv = apr_get_os_error();
151             return rv;
152         }
153     }
154
155     /* Handle the ungetchar if there is one */
156     if (thefile->ungetchar != -1) {
157         bytes_read = 1;
158         *(char *)buf = (char)thefile->ungetchar;
159         buf = (char *)buf + 1;
160         (*len)--;
161         thefile->ungetchar = -1;
162         if (*len == 0) {
163             *len = bytes_read;
164             return APR_SUCCESS;
165         }
166     }
167     if (thefile->buffered) {
168         char *pos = (char *)buf;
169         apr_size_t blocksize;
170         apr_size_t size = *len;
171
172         apr_thread_mutex_lock(thefile->mutex);
173
174         if (thefile->direction == 1) {
175             rv = apr_file_flush(thefile);
176             if (rv != APR_SUCCESS) {
177                 apr_thread_mutex_unlock(thefile->mutex);
178                 return rv;
179             }
180             thefile->bufpos = 0;
181             thefile->direction = 0;
182             thefile->dataRead = 0;
183         }
184
185         rv = 0;
186         while (rv == 0 && size > 0) {
187             if (thefile->bufpos >= thefile->dataRead) {
188                 apr_size_t read;
189                 rv = read_with_timeout(thefile, thefile->buffer, 
190                                        APR_FILE_BUFSIZE, &read);
191                 if (read == 0) {
192                     if (rv == APR_EOF)
193                         thefile->eof_hit = TRUE;
194                     break;
195                 }
196                 else {
197                     thefile->dataRead = read;
198                     thefile->filePtr += thefile->dataRead;
199                     thefile->bufpos = 0;
200                 }
201             }
202
203             blocksize = size > thefile->dataRead - thefile->bufpos ? thefile->dataRead - thefile->bufpos : size;
204             memcpy(pos, thefile->buffer + thefile->bufpos, blocksize);
205             thefile->bufpos += blocksize;
206             pos += blocksize;
207             size -= blocksize;
208         }
209
210         *len = pos - (char *)buf;
211         if (*len) {
212             rv = APR_SUCCESS;
213         }
214         apr_thread_mutex_unlock(thefile->mutex);
215     } else {  
216         /* Unbuffered i/o */
217         apr_size_t nbytes;
218         rv = read_with_timeout(thefile, buf, *len, &nbytes);
219         if (rv == APR_EOF)
220             thefile->eof_hit = TRUE;
221         *len = nbytes;
222     }
223
224     return rv;
225 }
226
227 APR_DECLARE(apr_status_t) apr_file_write(apr_file_t *thefile, const void *buf, apr_size_t *nbytes)
228 {
229     apr_status_t rv;
230     DWORD bwrote;
231
232     /* If the file is open for xthread support, allocate and
233      * initialize the overlapped and io completion event (hEvent). 
234      * Threads should NOT share an apr_file_t or its hEvent.
235      */
236     if ((thefile->flags & APR_XTHREAD) && !thefile->pOverlapped ) {
237         thefile->pOverlapped = (OVERLAPPED*) apr_pcalloc(thefile->pool, 
238                                                          sizeof(OVERLAPPED));
239         thefile->pOverlapped->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
240         if (!thefile->pOverlapped->hEvent) {
241             rv = apr_get_os_error();
242             return rv;
243         }
244     }
245
246     if (thefile->buffered) {
247         char *pos = (char *)buf;
248         apr_size_t blocksize;
249         apr_size_t size = *nbytes;
250
251         apr_thread_mutex_lock(thefile->mutex);
252
253         if (thefile->direction == 0) {
254             // Position file pointer for writing at the offset we are logically reading from
255             apr_off_t offset = thefile->filePtr - thefile->dataRead + thefile->bufpos;
256             DWORD offlo = (DWORD)offset;
257             DWORD offhi = (DWORD)(offset >> 32);
258             if (offset != thefile->filePtr)
259                 SetFilePointer(thefile->filehand, offlo, &offhi, FILE_BEGIN);
260             thefile->bufpos = thefile->dataRead = 0;
261             thefile->direction = 1;
262         }
263
264         rv = 0;
265         while (rv == 0 && size > 0) {
266             if (thefile->bufpos == APR_FILE_BUFSIZE)   // write buffer is full
267                 rv = apr_file_flush(thefile);
268
269             blocksize = size > APR_FILE_BUFSIZE - thefile->bufpos ? APR_FILE_BUFSIZE - thefile->bufpos : size;
270             memcpy(thefile->buffer + thefile->bufpos, pos, blocksize);
271             thefile->bufpos += blocksize;
272             pos += blocksize;
273             size -= blocksize;
274         }
275
276         apr_thread_mutex_unlock(thefile->mutex);
277         return rv;
278     } else {
279         if (!thefile->pipe) {
280             apr_off_t offset = 0;
281             apr_status_t rc;
282             if (thefile->append) {
283                 /* apr_file_lock will mutex the file across processes.
284                  * The call to apr_thread_mutex_lock is added to avoid
285                  * a race condition between LockFile and WriteFile 
286                  * that occasionally leads to deadlocked threads.
287                  */
288                 apr_thread_mutex_lock(thefile->mutex);
289                 rc = apr_file_lock(thefile, APR_FLOCK_EXCLUSIVE);
290                 if (rc != APR_SUCCESS) {
291                     apr_thread_mutex_unlock(thefile->mutex);
292                     return rc;
293                 }
294                 rc = apr_file_seek(thefile, APR_END, &offset);
295                 if (rc != APR_SUCCESS) {
296                     apr_thread_mutex_unlock(thefile->mutex);
297                     return rc;
298                 }
299             }
300             if (thefile->pOverlapped) {
301                 thefile->pOverlapped->Offset     = (DWORD)thefile->filePtr;
302                 thefile->pOverlapped->OffsetHigh = (DWORD)(thefile->filePtr >> 32);
303             }
304             rv = WriteFile(thefile->filehand, buf, *nbytes, &bwrote,
305                            thefile->pOverlapped);
306             if (thefile->append) {
307                 apr_file_unlock(thefile);
308                 apr_thread_mutex_unlock(thefile->mutex);
309             }
310         }
311         else {
312             rv = WriteFile(thefile->filehand, buf, *nbytes, &bwrote,
313                            thefile->pOverlapped);
314         }
315         if (rv) {
316             *nbytes = bwrote;
317             rv = APR_SUCCESS;
318         }
319         else {
320             (*nbytes) = 0;
321             rv = apr_get_os_error();
322             if (rv == APR_FROM_OS_ERROR(ERROR_IO_PENDING)) {
323  
324                 DWORD timeout_ms;
325
326                 if (thefile->timeout == 0) {
327                     timeout_ms = 0;
328                 }
329                 else if (thefile->timeout < 0) {
330                     timeout_ms = INFINITE;
331                 }
332                 else {
333                     timeout_ms = (DWORD)(thefile->timeout / 1000);
334                 }
335                
336                 rv = WaitForSingleObject(thefile->pOverlapped->hEvent, timeout_ms);
337                 switch (rv) {
338                     case WAIT_OBJECT_0:
339                         GetOverlappedResult(thefile->filehand, thefile->pOverlapped, nbytes, TRUE);
340                         rv = APR_SUCCESS;
341                         break;
342                     case WAIT_TIMEOUT:
343                         rv = APR_TIMEUP;
344                         break;
345                     case WAIT_FAILED:
346                         rv = apr_get_os_error();
347                         break;
348                     default:
349                         break;
350                 }
351                 if (rv != APR_SUCCESS) {
352                     if (apr_os_level >= APR_WIN_98)
353                         CancelIo(thefile->filehand);
354                 }
355             }
356         }
357         if (rv == APR_SUCCESS && thefile->pOverlapped && !thefile->pipe) {
358             thefile->filePtr += *nbytes;
359         }
360     }
361     return rv;
362 }
363 /* ToDo: Write for it anyway and test the oslevel!
364  * Too bad WriteFileGather() is not supported on 95&98 (or NT prior to SP2)
365  */
366 APR_DECLARE(apr_status_t) apr_file_writev(apr_file_t *thefile,
367                                      const struct iovec *vec,
368                                      apr_size_t nvec, 
369                                      apr_size_t *nbytes)
370 {
371     apr_status_t rv = APR_SUCCESS;
372     apr_size_t i;
373     DWORD bwrote = 0;
374     char *buf;
375
376     *nbytes = 0;
377     for (i = 0; i < nvec; i++) {
378         buf = vec[i].iov_base;
379         bwrote = vec[i].iov_len;
380         rv = apr_file_write(thefile, buf, &bwrote);
381         *nbytes += bwrote;
382         if (rv != APR_SUCCESS) {
383             break;
384         }
385     }
386     return rv;
387 }
388
389 APR_DECLARE(apr_status_t) apr_file_putc(char ch, apr_file_t *thefile)
390 {
391     DWORD len = 1;
392
393     return apr_file_write(thefile, &ch, &len);
394 }
395
396 APR_DECLARE(apr_status_t) apr_file_ungetc(char ch, apr_file_t *thefile)
397 {
398     thefile->ungetchar = (unsigned char) ch;
399     return APR_SUCCESS;
400 }
401
402 APR_DECLARE(apr_status_t) apr_file_getc(char *ch, apr_file_t *thefile)
403 {
404     apr_status_t rc;
405     int bread;
406
407     bread = 1;
408     rc = apr_file_read(thefile, ch, &bread);
409
410     if (rc) {
411         return rc;
412     }
413     
414     if (bread == 0) {
415         thefile->eof_hit = TRUE;
416         return APR_EOF;
417     }
418     return APR_SUCCESS; 
419 }
420
421 APR_DECLARE(apr_status_t) apr_file_puts(const char *str, apr_file_t *thefile)
422 {
423     DWORD len = strlen(str);
424
425     return apr_file_write(thefile, str, &len);
426 }
427
428 APR_DECLARE(apr_status_t) apr_file_gets(char *str, int len, apr_file_t *thefile)
429 {
430     apr_size_t readlen;
431     apr_status_t rv = APR_SUCCESS;
432     int i;    
433
434     for (i = 0; i < len-1; i++) {
435         readlen = 1;
436         rv = apr_file_read(thefile, str+i, &readlen);
437
438         if (rv != APR_SUCCESS && rv != APR_EOF)
439             return rv;
440
441         if (readlen == 0) {
442             /* If we have bytes, defer APR_EOF to the next call */
443             if (i > 0)
444                 rv = APR_SUCCESS;
445             break;
446         }
447         
448         if (str[i] == '\n') {
449             i++; /* don't clobber this char below */
450             break;
451         }
452     }
453     str[i] = 0;
454     return rv;
455 }
456
457 APR_DECLARE(apr_status_t) apr_file_flush(apr_file_t *thefile)
458 {
459     if (thefile->buffered) {
460         DWORD written = 0;
461         apr_status_t rc = 0;
462
463         if (thefile->direction == 1 && thefile->bufpos) {
464             if (!WriteFile(thefile->filehand, thefile->buffer, thefile->bufpos, &written, NULL))
465                 rc = apr_get_os_error();
466             thefile->filePtr += written;
467
468             if (rc == 0)
469                 thefile->bufpos = 0;
470         }
471
472         return rc;
473     }
474
475     /* There isn't anything to do if we aren't buffering the output
476      * so just return success.
477      */
478     return APR_SUCCESS; 
479 }
480
481 struct apr_file_printf_data {
482     apr_vformatter_buff_t vbuff;
483     apr_file_t *fptr;
484     char *buf;
485 };
486
487 static int file_printf_flush(apr_vformatter_buff_t *buff)
488 {
489     struct apr_file_printf_data *data = (struct apr_file_printf_data *)buff;
490
491     if (apr_file_write_full(data->fptr, data->buf,
492                             data->vbuff.curpos - data->buf, NULL)) {
493         return -1;
494     }
495
496     data->vbuff.curpos = data->buf;
497     return 0;
498 }
499
500 APR_DECLARE_NONSTD(int) apr_file_printf(apr_file_t *fptr, 
501                                         const char *format, ...)
502 {
503     struct apr_file_printf_data data;
504     va_list ap;
505     int count;
506
507     data.buf = malloc(HUGE_STRING_LEN);
508     if (data.buf == NULL) {
509         return 0;
510     }
511     data.vbuff.curpos = data.buf;
512     data.vbuff.endpos = data.buf + HUGE_STRING_LEN;
513     data.fptr = fptr;
514     va_start(ap, format);
515     count = apr_vformatter(file_printf_flush,
516                            (apr_vformatter_buff_t *)&data, format, ap);
517     /* apr_vformatter does not call flush for the last bits */
518     if (count >= 0) file_printf_flush((apr_vformatter_buff_t *)&data);
519
520     va_end(ap);
521
522     free(data.buf);
523     return count;
524 }