You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by st...@locus.apache.org on 2000/07/03 05:21:16 UTC
cvs commit: apache-2.0/src/lib/apr/threadproc/win32 proc.c
stoddard 00/07/02 20:21:16
Modified: src/lib/apr/file_io/win32 fileio.h open.c pipe.c readwrite.c
src/lib/apr/threadproc/win32 proc.c
Log:
Win32: First cut at implementing non-blocking pipes with timeout on Windows NT.
Revision Changes Path
1.27 +14 -7 apache-2.0/src/lib/apr/file_io/win32/fileio.h
Index: fileio.h
===================================================================
RCS file: /home/cvs/apache-2.0/src/lib/apr/file_io/win32/fileio.h,v
retrieving revision 1.26
retrieving revision 1.27
diff -u -r1.26 -r1.27
--- fileio.h 2000/06/22 16:05:29 1.26
+++ fileio.h 2000/07/03 03:21:09 1.27
@@ -103,18 +103,19 @@
struct ap_file_t {
ap_pool_t *cntxt;
HANDLE filehand;
- char *fname;
- DWORD dwFileAttributes;
- int eof_hit;
- int pipe;
+ BOOLEAN pipe; // Is this a pipe of a file?
+ OVERLAPPED *pOverlapped;
ap_interval_time_t timeout;
- int buffered;
- int ungetchar; /* Last char provided by an unget op. (-1 = no char)*/
+ /* File specific info */
+ char *fname;
char *demonfname;
char *lowerdemonfname;
+ DWORD dwFileAttributes;
+ int eof_hit;
+ BOOLEAN buffered; // Use buffered I/O?
+ int ungetchar; // Last char provided by an unget op. (-1 = no char)
int append;
-
off_t size;
ap_time_t atime;
ap_time_t mtime;
@@ -127,6 +128,9 @@
int direction; // buffer being used for 0 = read, 1 = write
ap_ssize_t filePtr; // position in file of handle
ap_lock_t *mutex; // mutex semaphore, must be owned to access the above fields
+
+ /* Pipe specific info */
+
};
struct ap_dir_t {
@@ -143,5 +147,8 @@
const char *szFile);
char * canonical_filename(struct ap_pool_t *pCont, const char *szFile);
+ap_status_t ap_create_nt_pipe(ap_file_t **in, ap_file_t **out,
+ BOOLEAN bAsyncRead, BOOLEAN bAsyncWrite,
+ ap_pool_t *p);
#endif /* ! FILE_IO_H */
1.43 +4 -3 apache-2.0/src/lib/apr/file_io/win32/open.c
Index: open.c
===================================================================
RCS file: /home/cvs/apache-2.0/src/lib/apr/file_io/win32/open.c,v
retrieving revision 1.42
retrieving revision 1.43
diff -u -r1.42 -r1.43
--- open.c 2000/06/22 04:42:51 1.42
+++ open.c 2000/07/03 03:21:10 1.43
@@ -67,10 +67,11 @@
ap_status_t file_cleanup(void *thefile)
{
ap_file_t *file = thefile;
- if (!CloseHandle(file->filehand)) {
- return GetLastError();
- }
+ CloseHandle(file->filehand);
file->filehand = INVALID_HANDLE_VALUE;
+ if (file->pOverlapped) {
+ CloseHandle(file->pOverlapped->hEvent);
+ }
return APR_SUCCESS;
}
1.24 +116 -55 apache-2.0/src/lib/apr/file_io/win32/pipe.c
Index: pipe.c
===================================================================
RCS file: /home/cvs/apache-2.0/src/lib/apr/file_io/win32/pipe.c,v
retrieving revision 1.23
retrieving revision 1.24
diff -u -r1.23 -r1.24
--- pipe.c 2000/06/30 18:14:48 1.23
+++ pipe.c 2000/07/03 03:21:11 1.24
@@ -63,72 +63,89 @@
#include <sys/stat.h>
#include "misc.h"
-static ap_status_t setpipeblockmode(ap_file_t *pipe, DWORD dwMode) {
- if (!SetNamedPipeHandleState(pipe->filehand, &dwMode, NULL, NULL)) {
- return GetLastError();
- }
+ap_status_t ap_set_pipe_timeout(ap_file_t *thepipe, ap_interval_time_t timeout)
+{
+ thepipe->timeout = timeout;
return APR_SUCCESS;
}
-ap_status_t ap_set_pipe_timeout(ap_file_t *thepipe, ap_interval_time_t timeout)
+ap_status_t ap_create_pipe(ap_file_t **in, ap_file_t **out, ap_pool_t *p)
{
- ap_status_t rc = APR_SUCCESS;
- ap_oslevel_e oslevel;
+ SECURITY_ATTRIBUTES sa;
- /* This code relies on the fact that anonymous pipes (which
- * do not support nonblocking I/O) are really named pipes
- * (which support nonblocking I/O) on Windows NT.
- */
- if (thepipe->pipe == 1) {
- if (ap_get_oslevel(thepipe->cntxt, &oslevel) == APR_SUCCESS &&
- oslevel >= APR_WIN_NT) {
- /* Temporary hack to make CGIs work in alpha5
- * NT doesn't support timing out non-blocking pipes. Specifically,
- * NT has no event notification to tell you when data has arrived
- * on a pipe. select, WaitForSingleObject or WSASelect, et. al.
- * do not tell you when data is available. You have to poll the read
- * which just sucks. I will implement this using async i/o later.
- * For now, if ap_set_pipe_timeout is set with a timeout, just make
- * the pipe full blocking...*/
- if (timeout > 0) {
- setpipeblockmode(thepipe, PIPE_WAIT);
- return APR_SUCCESS;
- }
- if (timeout >= 0) {
- /* Set the pipe non-blocking if it was previously blocking */
- if (thepipe->timeout < 0) {
- rc = setpipeblockmode(thepipe, PIPE_NOWAIT);
- }
- }
- else if (thepipe->timeout >= 0) {
- rc = setpipeblockmode(thepipe, PIPE_WAIT);
- }
- }
- else {
- /* can't make anonymous pipes non-blocking on Win9x */
- rc = APR_ENOTIMPL;
- }
- thepipe->timeout = timeout;
- }
- else {
- /* Timeout not valid for file i/o (yet...) */
- rc = APR_EINVAL;
+ sa.nLength = sizeof(sa);
+ sa.bInheritHandle = TRUE;
+ sa.lpSecurityDescriptor = NULL;
+
+ (*in) = (ap_file_t *)ap_pcalloc(p, sizeof(ap_file_t));
+ (*in)->cntxt = p;
+ (*in)->fname = ap_pstrdup(p, "PIPE");
+ (*in)->pipe = 1;
+ (*in)->timeout = -1;
+ (*in)->ungetchar = -1;
+ (*in)->eof_hit = 0;
+ (*in)->filePtr = 0;
+ (*in)->bufpos = 0;
+ (*in)->dataRead = 0;
+ (*in)->direction = 0;
+
+ (*out) = (ap_file_t *)ap_pcalloc(p, sizeof(ap_file_t));
+ (*out)->cntxt = p;
+ (*out)->fname = ap_pstrdup(p, "PIPE");
+ (*out)->pipe = 1;
+ (*out)->timeout = -1;
+ (*out)->ungetchar = -1;
+ (*out)->eof_hit = 0;
+ (*out)->filePtr = 0;
+ (*out)->bufpos = 0;
+ (*out)->dataRead = 0;
+ (*out)->direction = 0;
+
+ if (!CreatePipe(&(*in)->filehand, &(*out)->filehand, &sa, 0)) {
+ return GetLastError();
}
- return rc;
+ return APR_SUCCESS;
}
-ap_status_t ap_create_pipe(ap_file_t **in, ap_file_t **out, ap_pool_t *cont)
+/* ap_create_nt_pipe()
+ * An internal (for now) APR function created for use by ap_create_process()
+ * when setting up pipes to communicate with the child process.
+ * ap_create_nt_pipe() allows setting the blocking mode of each end of
+ * the pipe when the pipe is created (rather than after the pipe is created).
+ * A pipe handle must be opened in full async i/o mode in order to
+ * emulate Unix non-blocking pipes with timeouts.
+ *
+ * In general, we don't want to enable child side pipe handles for async i/o.
+ * This prevents us from enabling both ends of the pipe for async i/o in
+ * ap_create_pipe.
+ *
+ * Why not use NamedPipes on NT which support setting pipe state to
+ * non-blocking? On NT, even though you can set a pipe non-blocking,
+ * there is no clean way to set event driven non-zero timeouts (e.g select(),
+ * WaitForSinglelObject, et. al. will not detect pipe i/o). On NT, you
+ * have to poll the pipe to detech i/o on a non-blocking pipe.
+ *
+ * wgs
+ */
+ap_status_t ap_create_nt_pipe(ap_file_t **in, ap_file_t **out,
+ BOOLEAN bAsyncRead, BOOLEAN bAsyncWrite,
+ ap_pool_t *p)
{
+ ap_oslevel_e level;
SECURITY_ATTRIBUTES sa;
+ static unsigned long id = 0;
+ DWORD dwPipeMode;
+ DWORD dwOpenMode;
+ char name[50];
sa.nLength = sizeof(sa);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;
- (*in) = (ap_file_t *)ap_pcalloc(cont, sizeof(ap_file_t));
- (*in)->cntxt = cont;
- (*in)->fname = ap_pstrdup(cont, "PIPE");
+ (*in) = (ap_file_t *)ap_pcalloc(p, sizeof(ap_file_t));
+ (*in)->cntxt = p;
+ (*in)->fname = ap_pstrdup(p, "PIPE");
(*in)->pipe = 1;
(*in)->timeout = -1;
(*in)->ungetchar = -1;
@@ -137,10 +154,11 @@
(*in)->bufpos = 0;
(*in)->dataRead = 0;
(*in)->direction = 0;
+ (*in)->pOverlapped = NULL;
- (*out) = (ap_file_t *)ap_pcalloc(cont, sizeof(ap_file_t));
- (*out)->cntxt = cont;
- (*out)->fname = ap_pstrdup(cont, "PIPE");
+ (*out) = (ap_file_t *)ap_pcalloc(p, sizeof(ap_file_t));
+ (*out)->cntxt = p;
+ (*out)->fname = ap_pstrdup(p, "PIPE");
(*out)->pipe = 1;
(*out)->timeout = -1;
(*out)->ungetchar = -1;
@@ -149,9 +167,52 @@
(*out)->bufpos = 0;
(*out)->dataRead = 0;
(*out)->direction = 0;
+ (*out)->pOverlapped = NULL;
- if (!CreatePipe(&(*in)->filehand, &(*out)->filehand, &sa, 0)) {
- return GetLastError();
+ if (ap_get_oslevel(p, &level) == APR_SUCCESS && level >= APR_WIN_NT) {
+ /* Create the read end of the pipe */
+ dwOpenMode = PIPE_ACCESS_INBOUND;
+ if (bAsyncRead) {
+ dwOpenMode |= FILE_FLAG_OVERLAPPED;
+ (*in)->pOverlapped = (OVERLAPPED*) ap_pcalloc(p, sizeof(OVERLAPPED));
+ (*in)->pOverlapped->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ /* register a cleanup for the event handle... */
+ }
+
+ dwPipeMode = 0;
+
+ sprintf(name, "\\\\.\\pipe\\%d.%d", getpid(), id++);
+
+ (*in)->filehand = CreateNamedPipe(name,
+ dwOpenMode,
+ dwPipeMode,
+ 1, //nMaxInstances,
+ 8182, //nOutBufferSize,
+ 8192, //nInBufferSize,
+ 1, //nDefaultTimeOut,
+ &sa);
+
+ /* Create the write end of the pipe */
+ dwOpenMode = FILE_ATTRIBUTE_NORMAL;
+ if (bAsyncWrite) {
+ dwOpenMode |= FILE_FLAG_OVERLAPPED;
+ (*out)->pOverlapped = (OVERLAPPED*) ap_pcalloc(p, sizeof(OVERLAPPED));
+ (*out)->pOverlapped->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ }
+
+ (*out)->filehand = CreateFile(name,
+ GENERIC_WRITE, // access mode
+ 0, // share mode
+ &sa, // Security attributes
+ OPEN_EXISTING, // dwCreationDisposition
+ dwOpenMode, // Pipe attributes
+ NULL); // handle to template file
+ }
+ else {
+ /* Pipes on Win9* are blocking. Live with it. */
+ if (!CreatePipe(&(*in)->filehand, &(*out)->filehand, &sa, 0)) {
+ return GetLastError();
+ }
}
return APR_SUCCESS;
1.43 +98 -31 apache-2.0/src/lib/apr/file_io/win32/readwrite.c
Index: readwrite.c
===================================================================
RCS file: /home/cvs/apache-2.0/src/lib/apr/file_io/win32/readwrite.c,v
retrieving revision 1.42
retrieving revision 1.43
diff -u -r1.42 -r1.43
--- readwrite.c 2000/06/22 04:42:51 1.42
+++ readwrite.c 2000/07/03 03:21:11 1.43
@@ -60,13 +60,96 @@
#include <malloc.h>
#include "atime.h"
-ap_status_t ap_read(ap_file_t *thefile, void *buf, ap_ssize_t *nbytes)
+/*
+ * read_with_timeout()
+ * Uses async i/o to emulate unix non-blocking i/o with timeouts.
+ */
+static ap_status_t read_with_timeout(ap_file_t *file, void *buf, ap_ssize_t len, ap_ssize_t *nbytes)
{
+ ap_status_t rv;
+ *nbytes = 0;
+
+ /* Handle the zero timeout non-blocking case */
+ if (file->timeout == 0) {
+ /* Peek at the pipe. If there is no data available, return APR_EAGAIN.
+ * If data is available, go ahead and read it.
+ */
+ if (file->pipe) {
+ char tmpbuf[5];
+ DWORD dwBytesRead;
+ DWORD dwBytesAvail;
+ DWORD dwBytesLeftThisMsg;
+ if (!PeekNamedPipe(file->filehand, // handle to pipe to copy from
+ &tmpbuf, // pointer to data buffer
+ sizeof(tmpbuf), // size, in bytes, of data buffer
+ &dwBytesRead, // pointer to number of bytes read
+ &dwBytesAvail, // pointer to total number of bytes available
+ &dwBytesLeftThisMsg)) { // pointer to unread bytes in this message
+ rv = GetLastError();
+ if (rv = ERROR_BROKEN_PIPE)
+ return APR_SUCCESS;
+ }
+ else {
+ if (dwBytesRead == 0) {
+ return APR_EAGAIN;
+ }
+ }
+ }
+ else {
+ /* ToDo: Handle zero timeout non-blocking file i/o
+ * This is not needed until an APR application needs to
+ * timeout file i/o (which means setting file i/o non-blocking)
+ */
+ }
+ }
+
+ rv = ReadFile(file->filehand, buf, len, nbytes, file->pOverlapped);
+
+ if (!rv) {
+ rv = GetLastError();
+ if (rv == ERROR_IO_PENDING) {
+ /* Wait for the pending i/o */
+ if (file->timeout > 0) {
+ rv = WaitForSingleObject(file->pOverlapped->hEvent, file->timeout/1000); // timeout in milliseconds...
+ }
+ else if (file->timeout == -1) {
+ rv = WaitForSingleObject(file->pOverlapped->hEvent, INFINITE);
+ }
+ switch (rv) {
+ case WAIT_OBJECT_0:
+ GetOverlappedResult(file->filehand, file->pOverlapped, nbytes, TRUE);
+ rv = APR_SUCCESS;
+ break;
+ case WAIT_TIMEOUT:
+ rv = APR_TIMEUP;
+ break;
+ case WAIT_FAILED:
+ rv = GetLastError();
+ break;
+ default:
+ break;
+ }
+ if (rv != APR_SUCCESS) {
+ CancelIo(file->filehand);
+ }
+ }
+ else if (rv == ERROR_BROKEN_PIPE) {
+ /* Assume ERROR_BROKEN_PIPE signals an EOF reading from a pipe */
+ rv = APR_SUCCESS; /* APR_EOF? */
+ }
+ } else {
+ rv = APR_SUCCESS;
+ }
+ return rv;
+}
+
+ap_status_t ap_read(ap_file_t *thefile, void *buf, ap_ssize_t *len)
+{
ap_ssize_t rv;
DWORD bytes_read = 0;
- if (*nbytes <= 0) {
- *nbytes = 0;
+ if (*len <= 0) {
+ *len = 0;
return APR_SUCCESS;
}
@@ -75,17 +158,17 @@
bytes_read = 1;
*(char *)buf = (char)thefile->ungetchar;
buf = (char *)buf + 1;
- (*nbytes)--;
+ (*len)--;
thefile->ungetchar = -1;
- if (*nbytes == 0) {
- *nbytes = bytes_read;
+ if (*len == 0) {
+ *len = bytes_read;
return APR_SUCCESS;
}
}
if (thefile->buffered) {
char *pos = (char *)buf;
ap_ssize_t blocksize;
- ap_ssize_t size = *nbytes;
+ ap_ssize_t size = *len;
ap_lock(thefile->mutex);
@@ -99,9 +182,10 @@
rv = 0;
while (rv == 0 && size > 0) {
if (thefile->bufpos >= thefile->dataRead) {
- rv = ReadFile(thefile->filehand, thefile->buffer, APR_FILE_BUFSIZE, &thefile->dataRead, NULL ) ? 0 : GetLastError();
+ rv = read_with_timeout(thefile, thefile->buffer,
+ APR_FILE_BUFSIZE, &thefile->dataRead);
if (thefile->dataRead == 0) {
- if (rv == 0) {
+ if (rv == APR_SUCCESS) {
thefile->eof_hit = TRUE;
rv = APR_EOF;
}
@@ -119,33 +203,16 @@
size -= blocksize;
}
- *nbytes = pos - (char *)buf;
- if (*nbytes) {
+ *len = pos - (char *)buf;
+ if (*len) {
rv = 0;
}
ap_unlock(thefile->mutex);
} else {
/* Unbuffered i/o */
- DWORD dwBytesRead;
- if (ReadFile(thefile->filehand, buf, *nbytes, &dwBytesRead, NULL)) {
- *nbytes = bytes_read + dwBytesRead;
- if (*nbytes) {
- return APR_SUCCESS;
- }
- else {
- return APR_EOF;
- }
- }
-
- *nbytes = 0;
- rv = GetLastError();
- if (rv == ERROR_BROKEN_PIPE) {
- /* Assume ERROR_BROKEN_PIPE signals an EOF reading from a pipe */
- return APR_SUCCESS;
- } else if (rv == ERROR_NO_DATA) {
- /* Receive this error on a read to a pipe in nonblocking mode */
- return APR_EAGAIN;
- }
+ ap_ssize_t nbytes;
+ rv = read_with_timeout(thefile, buf, *len, &nbytes);
+ *len = nbytes;
}
return rv;
1.33 +40 -27 apache-2.0/src/lib/apr/threadproc/win32/proc.c
Index: proc.c
===================================================================
RCS file: /home/cvs/apache-2.0/src/lib/apr/threadproc/win32/proc.c,v
retrieving revision 1.32
retrieving revision 1.33
diff -u -r1.32 -r1.33
--- proc.c 2000/06/20 19:36:48 1.32
+++ proc.c 2000/07/03 03:21:13 1.33
@@ -92,61 +92,74 @@
ap_int32_t out, ap_int32_t err)
{
ap_status_t stat;
+ BOOLEAN bAsyncRead, bAsyncWrite;
if (in) {
- if ((stat = ap_create_pipe(&attr->child_in, &attr->parent_in,
- attr->cntxt)) != APR_SUCCESS) {
- return stat;
- }
switch (in) {
case APR_FULL_BLOCK:
+ bAsyncRead = bAsyncWrite = FALSE;
break;
case APR_PARENT_BLOCK:
- ap_set_pipe_timeout(attr->child_in, 0);
+ bAsyncRead = FALSE;
+ bAsyncWrite = TRUE;
break;
case APR_CHILD_BLOCK:
- ap_set_pipe_timeout(attr->parent_in, 0);
+ bAsyncRead = TRUE;
+ bAsyncWrite = FALSE;
break;
default:
- ap_set_pipe_timeout(attr->child_in, 0);
- ap_set_pipe_timeout(attr->parent_in, 0);
+ bAsyncRead = TRUE;
+ bAsyncWrite = TRUE;
+ }
+ if ((stat = ap_create_nt_pipe(&attr->child_in, &attr->parent_in,
+ bAsyncRead, bAsyncWrite,
+ attr->cntxt)) != APR_SUCCESS) {
+ return stat;
}
}
if (out) {
- if ((stat = ap_create_pipe(&attr->parent_out, &attr->child_out,
- attr->cntxt)) != APR_SUCCESS) {
- return stat;
- }
switch (out) {
case APR_FULL_BLOCK:
+ bAsyncRead = bAsyncWrite = FALSE;
break;
case APR_PARENT_BLOCK:
- ap_set_pipe_timeout(attr->child_out, 0);
+ bAsyncRead = FALSE;
+ bAsyncWrite = TRUE;
break;
case APR_CHILD_BLOCK:
- ap_set_pipe_timeout(attr->parent_out, 0);
+ bAsyncRead = TRUE;
+ bAsyncWrite = FALSE;
break;
default:
- ap_set_pipe_timeout(attr->child_out, 0);
- ap_set_pipe_timeout(attr->parent_out, 0);
+ bAsyncRead = TRUE;
+ bAsyncWrite = TRUE;
+ }
+ if ((stat = ap_create_nt_pipe(&attr->parent_out, &attr->child_out,
+ bAsyncRead, bAsyncWrite,
+ attr->cntxt)) != APR_SUCCESS) {
+ return stat;
}
}
if (err) {
- if ((stat = ap_create_pipe(&attr->parent_err, &attr->child_err,
- attr->cntxt)) != APR_SUCCESS) {
- return stat;
- }
switch (err) {
case APR_FULL_BLOCK:
+ bAsyncRead = bAsyncWrite = FALSE;
break;
case APR_PARENT_BLOCK:
- ap_set_pipe_timeout(attr->child_err, 0);
+ bAsyncRead = FALSE;
+ bAsyncWrite = TRUE;
break;
case APR_CHILD_BLOCK:
- ap_set_pipe_timeout(attr->parent_err, 0);
+ bAsyncRead = TRUE;
+ bAsyncWrite = FALSE;
break;
default:
- ap_set_pipe_timeout(attr->child_err, 0);
- ap_set_pipe_timeout(attr->parent_err, 0);
+ bAsyncRead = TRUE;
+ bAsyncWrite = TRUE;
+ }
+ if ((stat = ap_create_nt_pipe(&attr->parent_err, &attr->child_err,
+ bAsyncRead, bAsyncWrite,
+ attr->cntxt)) != APR_SUCCESS) {
+ return stat;
}
}
return APR_SUCCESS;
@@ -337,15 +350,15 @@
}
else {
if (attr->child_in) {
- ap_close(attr->parent_in);
+ CloseHandle(attr->parent_in->filehand);
attr->parent_in->filehand = hParentindup;
}
if (attr->child_out) {
- ap_close(attr->parent_out);
+ CloseHandle(attr->parent_out->filehand);
attr->parent_out->filehand = hParentoutdup;
}
if (attr->child_err) {
- ap_close(attr->parent_err);
+ CloseHandle(attr->parent_err->filehand);
attr->parent_err->filehand = hParenterrdup;
}
}