From: Christian Grothoff Date: Mon, 1 Aug 2011 20:32:59 +0000 (+0000) Subject: LRN: Bratao's select() implementation for W32 X-Git-Tag: initial-import-from-subversion-38251~17560 X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=ff2d2e7ea5f470af1c345e7c66fe320d4c9d2d33;p=oweals%2Fgnunet.git LRN: Bratao's select() implementation for W32 --- diff --git a/src/util/disk.c b/src/util/disk.c index 4d2ec8d81..fd8e5a654 100644 --- a/src/util/disk.c +++ b/src/util/disk.c @@ -36,6 +36,8 @@ #define DEBUG_NPIPE GNUNET_NO +#define DEBUG_PIPE GNUNET_NO + /** * Block size for IO for copying files. */ @@ -55,6 +57,10 @@ #include #else #ifdef MINGW +#ifndef PIPE_BUF +#define PIPE_BUF 512 +ULONG PipeSerialNumber; +#endif #define _IFMT 0170000 /* type of file */ #define _IFLNK 0120000 /* symbolic link */ #define S_ISLNK(m) (((m)&_IFMT) == _IFLNK) @@ -643,11 +649,26 @@ GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle * h, void *result, #ifdef MINGW DWORD bytesRead; - if (!ReadFile (h->h, result, len, &bytesRead, NULL)) + if(h->type != GNUNET_PIPE) + { + if (!ReadFile (h->h, result, len, &bytesRead, NULL)) { SetErrnoFromWinError (GetLastError ()); return GNUNET_SYSERR; } + } + else + { + if (!ReadFile (h->h, result, len, NULL, h->oOverlapRead)) + { + if(GetLastError () != ERROR_IO_PENDING ) + { + SetErrnoFromWinError (GetLastError ()); + return GNUNET_SYSERR; + } + } + GetOverlappedResult(h->h, h->oOverlapRead, &bytesRead, TRUE); + } return bytesRead; #else return read (h->fd, result, len); @@ -700,11 +721,35 @@ GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h, #ifdef MINGW DWORD bytesWritten; - if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL)) + if(h->type != GNUNET_PIPE) + { + if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL)) { SetErrnoFromWinError (GetLastError ()); return GNUNET_SYSERR; } + } + else + { +#if DEBUG_PIPE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to write\n"); +#endif + if (!WriteFile (h->h, buffer, n, NULL, h->oOverlapWrite)) + { + if(GetLastError () != ERROR_IO_PENDING ) + { + SetErrnoFromWinError (GetLastError ()); +#if DEBUG_PIPE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe\n"); +#endif + return GNUNET_SYSERR; + } + } +#if DEBUG_PIPE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n"); +#endif + GetOverlappedResult(h->h, h->oOverlapWrite, &bytesWritten, TRUE); + } return bytesWritten; #else return write (h->fd, buffer, n); @@ -1388,6 +1433,8 @@ GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h) { SetErrnoFromWinError (GetLastError ()); GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "close"); + GNUNET_free (h->oOverlapRead); + GNUNET_free (h->oOverlapWrite); GNUNET_free (h); return GNUNET_SYSERR; } @@ -1647,6 +1694,148 @@ GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h) #endif } +#if WINDOWS +/* Copyright Bob Byrnes curl.com> + http://permalink.gmane.org/gmane.os.cygwin.patches/2121 +*/ +/* Create a pipe, and return handles to the read and write ends, + just like CreatePipe, but ensure that the write end permits + FILE_READ_ATTRIBUTES access, on later versions of win32 where + this is supported. This access is needed by NtQueryInformationFile, + which is used to implement select and nonblocking writes. + Note that the return value is either NO_ERROR or GetLastError, + unlike CreatePipe, which returns a bool for success or failure. */ +static int +create_selectable_pipe (PHANDLE read_pipe_ptr, + PHANDLE write_pipe_ptr, + LPSECURITY_ATTRIBUTES sa_ptr, + DWORD psize, + DWORD dwReadMode, + DWORD dwWriteMode) +{ + /* Default to error. */ + *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE; + + HANDLE read_pipe = INVALID_HANDLE_VALUE, write_pipe = INVALID_HANDLE_VALUE; + + /* Ensure that there is enough pipe buffer space for atomic writes. */ + if (psize < PIPE_BUF) + psize = PIPE_BUF; + + char pipename[MAX_PATH]; + + /* Retry CreateNamedPipe as long as the pipe name is in use. + Retrying will probably never be necessary, but we want + to be as robust as possible. */ + while (1) + { + static volatile LONG pipe_unique_id; + + snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld", + getpid (), InterlockedIncrement ((LONG *)&pipe_unique_id)); +#if DEBUG_PIPE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe: name = %s, size = %lu\n", pipename, psize); +#endif + /* Use CreateNamedPipe instead of CreatePipe, because the latter + returns a write handle that does not permit FILE_READ_ATTRIBUTES + access, on versions of win32 earlier than WinXP SP2. + CreatePipe also stupidly creates a full duplex pipe, which is + a waste, since only a single direction is actually used. + It's important to only allow a single instance, to ensure that + the pipe was not created earlier by some other process, even if + the pid has been reused. We avoid FILE_FLAG_FIRST_PIPE_INSTANCE + because that is only available for Win2k SP2 and WinXP. */ + read_pipe = CreateNamedPipeA (pipename, + PIPE_ACCESS_INBOUND | dwReadMode, + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, + 1, /* max instances */ + psize, /* output buffer size */ + psize, /* input buffer size */ + NMPWAIT_USE_DEFAULT_WAIT, + sa_ptr); + + if (read_pipe != INVALID_HANDLE_VALUE) + { +#if DEBUG_PIPE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe); +#endif + break; + } + + DWORD err = GetLastError (); + switch (err) + { + case ERROR_PIPE_BUSY: + /* The pipe is already open with compatible parameters. + Pick a new name and retry. */ +#if DEBUG_PIPE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n"); +#endif + continue; + case ERROR_ACCESS_DENIED: + /* The pipe is already open with incompatible parameters. + Pick a new name and retry. */ +#if DEBUG_PIPE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n"); +#endif + continue; + case ERROR_CALL_NOT_IMPLEMENTED: + /* We are on an older Win9x platform without named pipes. + Return an anonymous pipe as the best approximation. */ +#if DEBUG_PIPE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe not implemented, resorting to " + "CreatePipe: size = %lu\n", psize); +#endif + if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize)) + { +#if DEBUG_PIPE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", *read_pipe_ptr); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", *write_pipe_ptr); +#endif + return GNUNET_OK; + } + err = GetLastError (); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err); + return err; + default: + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err); + return err; + } + /* NOTREACHED */ + } +#if DEBUG_PIPE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename); +#endif + + /* Open the named pipe for writing. + Be sure to permit FILE_READ_ATTRIBUTES access. */ + write_pipe = CreateFileA (pipename, + GENERIC_WRITE | FILE_READ_ATTRIBUTES, + 0, /* share mode */ + sa_ptr, + OPEN_EXISTING, + dwWriteMode, /* flags and attributes */ + 0); /* handle to template file */ + + if (write_pipe == INVALID_HANDLE_VALUE) + { + /* Failure. */ + DWORD err = GetLastError (); +#if DEBUG_PIPE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err); +#endif + CloseHandle (read_pipe); + return err; + } +#if DEBUG_PIPE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe); +#endif + /* Success. */ + *read_pipe_ptr = read_pipe; + *write_pipe_ptr = write_pipe; + return GNUNET_OK; +} +#endif /** * Creates an interprocess channel @@ -1720,7 +1909,7 @@ GNUNET_DISK_pipe (int blocking, int inherit_read, int inherit_write) BOOL ret; HANDLE tmp_handle; - ret = CreatePipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0); + ret = create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0, FILE_FLAG_OVERLAPPED, FILE_FLAG_OVERLAPPED); if (!ret) { GNUNET_free (p); @@ -1763,6 +1952,18 @@ GNUNET_DISK_pipe (int blocking, int inherit_read, int inherit_write) } p->fd[0]->type = GNUNET_PIPE; p->fd[1]->type = GNUNET_PIPE; + + p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED)); + p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED)); + p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED)); + p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED)); + + p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL); + p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL); + + p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL); + p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL); + #endif return p; } @@ -1965,6 +2166,12 @@ GNUNET_DISK_npipe_create (char **fn, ret->h = h; ret->type = GNUNET_PIPE; + ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED)); + ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED)); + + ret->oOverlapRead->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + ret->oOverlapWrite->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + return ret; #else if (*fn == NULL) @@ -2029,6 +2236,10 @@ GNUNET_DISK_npipe_open (const char *fn, ret = GNUNET_malloc(sizeof(*ret)); ret->h = h; ret->type = GNUNET_PIPE; + ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED)); + ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED)); + ret->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL); + ret->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL); return ret; #else diff --git a/src/util/disk.h b/src/util/disk.h index 093d70492..793b8219f 100644 --- a/src/util/disk.h +++ b/src/util/disk.h @@ -45,6 +45,16 @@ struct GNUNET_DISK_FileHandle * Type */ enum {GNUNET_DISK_FILE, GNUNET_PIPE} type; + + /** + * Structure for overlapped reading (for pipes) + */ + OVERLAPPED *oOverlapRead; + + /** + * Structure for overlapped writing (for pipes) + */ + OVERLAPPED *oOverlapWrite; #else /** diff --git a/src/util/network.c b/src/util/network.c index 6a7453510..9c6d0c90b 100644 --- a/src/util/network.c +++ b/src/util/network.c @@ -220,6 +220,17 @@ GNUNET_NETWORK_socket_accept (const struct GNUNET_NETWORK_Handle *desc, struct GNUNET_NETWORK_Handle *ret; ret = GNUNET_malloc (sizeof (struct GNUNET_NETWORK_Handle)); +#if DEBUG_NETWORK + { + struct sockaddr name; + int namelen = sizeof (name); + int gsn = getsockname (desc->fd, &name, &namelen); + if (gsn == 0) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Accepting connection on `%s'\n", + GNUNET_a2s (&name, namelen)); + } +#endif ret->fd = accept (desc->fd, address, address_len); if (address != NULL) ret->af = address->sa_family; @@ -333,8 +344,17 @@ GNUNET_NETWORK_socket_close (struct GNUNET_NETWORK_Handle *desc) int ret; #ifdef MINGW + DWORD error = 0; +#if DEBUG_NETWORK + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "GNUNET_NETWORK_socket_close", "Closing 0x%x\n", desc->fd); +#endif + SetLastError (0); ret = closesocket (desc->fd); - SetErrnoFromWinsockError (WSAGetLastError ()); + error = WSAGetLastError (); + SetErrnoFromWinsockError (error); +#if DEBUG_NETWORK + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "GNUNET_NETWORK_socket_close", "Closed 0x%x, closesocket() returned %d, GLE is %u\n", desc->fd, ret, error); +#endif #else ret = close (desc->fd); #endif @@ -350,7 +370,7 @@ GNUNET_NETWORK_socket_close (struct GNUNET_NETWORK_Handle *desc) } #endif #endif - GNUNET_free_non_null (desc->addr); + GNUNET_free_non_null (desc->addr); GNUNET_free (desc); return (ret == 0) ? GNUNET_OK : GNUNET_SYSERR; } @@ -431,7 +451,6 @@ GNUNET_NETWORK_socket_getsockopt (const struct GNUNET_NETWORK_Handle *desc, else if (SOCKET_ERROR == ret) SetErrnoFromWinsockError (WSAGetLastError ()); - #endif return ret == 0 ? GNUNET_OK : GNUNET_SYSERR; } @@ -739,11 +758,17 @@ GNUNET_NETWORK_socket_disable_corking (struct GNUNET_NETWORK_Handle *desc) { int value = 0; int ret = 0; - +#if WINDOWS + if (0 != (ret = setsockopt (desc->fd, SOL_SOCKET, SO_SNDBUF, (char *) &value, sizeof (value)))) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "setsockopt"); + if (0 != (ret = setsockopt (desc->fd, SOL_SOCKET, SO_RCVBUF, (char *) &value, sizeof (value)))) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "setsockopt"); +#else if (0 != (ret = setsockopt (desc->fd, SOL_SOCKET, SO_SNDBUF, &value, sizeof (value)))) GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "setsockopt"); if (0 != (ret = setsockopt (desc->fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof (value)))) GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "setsockopt"); +#endif return ret == 0 ? GNUNET_OK : GNUNET_SYSERR; } @@ -984,10 +1009,27 @@ GNUNET_NETWORK_fdset_overlap (const struct GNUNET_NETWORK_FDSet *fds1, it = GNUNET_CONTAINER_slist_begin (fds1->handles); while (GNUNET_CONTAINER_slist_end (it) != GNUNET_YES) { +#if DEBUG_NETWORK + struct GNUNET_CONTAINER_SList_Iterator *t; +#endif h = (struct GNUNET_DISK_FileHandle *) GNUNET_CONTAINER_slist_get (it, NULL); +#if DEBUG_NETWORK + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Checking that FD 0x%x is in another set:\n", h->h); + for (t = GNUNET_CONTAINER_slist_begin (fds2->handles); + GNUNET_CONTAINER_slist_end (t) != GNUNET_YES; + GNUNET_CONTAINER_slist_next (t)) + { + struct GNUNET_DISK_FileHandle *fh; + fh = (struct GNUNET_DISK_FileHandle *) GNUNET_CONTAINER_slist_get (t, NULL); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "0x%x\n", fh->h); + } +#endif if (GNUNET_CONTAINER_slist_contains (fds2->handles, h, sizeof (struct GNUNET_DISK_FileHandle))) { +#if DEBUG_NETWORK + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Match!\n"); +#endif GNUNET_CONTAINER_slist_iter_destroy (it); return GNUNET_YES; } @@ -1043,40 +1085,80 @@ GNUNET_NETWORK_socket_select (struct GNUNET_NETWORK_FDSet *rfds, struct GNUNET_NETWORK_FDSet *efds, const struct GNUNET_TIME_Relative timeout) { - int nfds; + int nfds = 0; #ifdef MINGW - int handles; + int handles = 0; + int ex_handles = 0; + int read_handles = 0; + int write_handles = 0; + + int i = 0; + int retcode = 0; + DWORD ms_total = 0; + + int nsock = 0, nhandles = 0, nSockEvents = 0; + + static HANDLE hEventRead = 0; + static HANDLE hEventWrite = 0; + static HANDLE hEventException = 0; + static HANDLE hEventPipeWrite = 0; + static HANDLE hEventReadReady = 0; + + int readPipes = 0; + int writePipePos = 0; + + HANDLE handle_array[FD_SETSIZE + 2]; + int returncode = -1; + DWORD newretcode = 0; + int returnedpos = 0; + + struct GNUNET_CONTAINER_SList *handles_read, *handles_write, *handles_except; + + fd_set aread, awrite, aexcept; +#if DEBUG_NETWORK + fd_set bread, bwrite, bexcept; #endif - nfds = 0; -#ifdef MINGW - handles = 0; + + /* TODO: Make this growable */ + struct GNUNET_DISK_FileHandle *readArray[50]; +#else + struct timeval tv; #endif if (NULL != rfds) { nfds = rfds->nsds; #ifdef MINGW - handles = GNUNET_CONTAINER_slist_count (rfds->handles); + handles += read_handles = GNUNET_CONTAINER_slist_count (rfds->handles); +#if DEBUG_NETWORK + { + struct GNUNET_CONTAINER_SList_Iterator *t; + for (t = GNUNET_CONTAINER_slist_begin (rfds->handles); + GNUNET_CONTAINER_slist_end (t) != GNUNET_YES; + GNUNET_CONTAINER_slist_next (t)) + { + struct GNUNET_DISK_FileHandle *fh; + fh = (struct GNUNET_DISK_FileHandle *) GNUNET_CONTAINER_slist_get (t, NULL); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "FD 0x%x (0x%x) is SET in rfds\n", fh->h, fh); + } + } +#endif #endif } if (NULL != wfds) { nfds = GNUNET_MAX (nfds, wfds->nsds); #ifdef MINGW - handles += GNUNET_CONTAINER_slist_count (wfds->handles); + handles += write_handles = GNUNET_CONTAINER_slist_count (wfds->handles); #endif } if (NULL != efds) { nfds = GNUNET_MAX (nfds, efds->nsds); #ifdef MINGW - handles += GNUNET_CONTAINER_slist_count (efds->handles); + handles += ex_handles = GNUNET_CONTAINER_slist_count (efds->handles); #endif } - struct timeval tv; - tv.tv_sec = timeout.rel_value / GNUNET_TIME_UNIT_SECONDS.rel_value; - tv.tv_usec = - 1000 * (timeout.rel_value - (tv.tv_sec * GNUNET_TIME_UNIT_SECONDS.rel_value)); if ((nfds == 0) && (timeout.rel_value == GNUNET_TIME_UNIT_FOREVER_REL.rel_value) #ifdef MINGW && handles == 0 @@ -1090,6 +1172,9 @@ GNUNET_NETWORK_socket_select (struct GNUNET_NETWORK_FDSet *rfds, GNUNET_break (0); } #ifndef MINGW + tv.tv_sec = timeout.rel_value / GNUNET_TIME_UNIT_SECONDS.rel_value; + tv.tv_usec = + 1000 * (timeout.rel_value - (tv.tv_sec * GNUNET_TIME_UNIT_SECONDS.rel_value)); return select (nfds, (rfds != NULL) ? &rfds->sds : NULL, (wfds != NULL) ? &wfds->sds : NULL, @@ -1098,332 +1183,441 @@ GNUNET_NETWORK_socket_select (struct GNUNET_NETWORK_FDSet *rfds, ? NULL : &tv); #else - DWORD limit; - fd_set sock_read, sock_write, sock_except; - fd_set aread, awrite, aexcept; - struct GNUNET_CONTAINER_SList *handles_read, *handles_write, - *handles_except; - - int i; - struct timeval tvslice; - int retcode; - DWORD ms_total; - /* Number of milliseconds per cycle. Adapted on the fly */ - static unsigned int cycle_delay = 20; - #define SAFE_FD_ISSET(fd, set) (set != NULL && FD_ISSET(fd, set)) - /* calculate how long we need to wait in milliseconds */ if (timeout.rel_value == GNUNET_TIME_UNIT_FOREVER_REL.rel_value) ms_total = INFINITE; - else ms_total = timeout.rel_value / GNUNET_TIME_UNIT_MILLISECONDS.rel_value; - /* select() may be used as a portable way to sleep */ if (!(rfds || wfds || efds)) - { Sleep (ms_total); return 0; } - handles_read = GNUNET_CONTAINER_slist_create (); - handles_write = GNUNET_CONTAINER_slist_create (); - handles_except = GNUNET_CONTAINER_slist_create (); - - if (rfds) - sock_read = rfds->sds; + /* Events for sockets */ + if (!hEventRead) + hEventRead = CreateEvent (NULL, TRUE, FALSE, NULL); else - FD_ZERO (&sock_read); - if (wfds) - sock_write = wfds->sds; + ResetEvent (hEventRead); + if (!hEventReadReady) + hEventReadReady = CreateEvent (NULL, TRUE, TRUE, NULL); + if (!hEventWrite) + hEventWrite = CreateEvent (NULL, TRUE, FALSE, NULL); else - FD_ZERO (&sock_write); - if (efds) - sock_except = efds->sds; + ResetEvent (hEventWrite); + if (!hEventException) + hEventException = CreateEvent (NULL, TRUE, FALSE, NULL); else - FD_ZERO (&sock_except); + ResetEvent (hEventException); - /* multiplex between winsock select() and waiting on the handles */ + /* Event for pipes */ + if (!hEventPipeWrite) + hEventPipeWrite = CreateEvent (NULL, TRUE, TRUE, NULL); + readPipes = 0; + writePipePos = -1; + + handles_read = GNUNET_CONTAINER_slist_create (); + handles_write = GNUNET_CONTAINER_slist_create (); + handles_except = GNUNET_CONTAINER_slist_create (); FD_ZERO (&aread); FD_ZERO (&awrite); FD_ZERO (&aexcept); - -#if DEBUG_W32_CYCLES - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Starting a cycle, delay is %dms. nfds is %d.\n", cycle_delay, nfds); +#if DEBUG_NETWORK + FD_ZERO (&bread); + FD_ZERO (&bwrite); + FD_ZERO (&bexcept); #endif - - limit = GetTickCount () + ms_total; - - do - { - retcode = 0; - if (nfds > 0) - - { - - /* overwrite the zero'd sets here; the select call - * will clear those that are not active */ - FD_COPY (&sock_read, &aread); - FD_COPY (&sock_write, &awrite); - FD_COPY (&sock_except, &aexcept); - tvslice.tv_sec = 0; - tvslice.tv_usec = cycle_delay; -#if DEBUG_W32_CYCLES - { - for (i = 0; i < nfds; i++) - { - if (SAFE_FD_ISSET (i, &sock_read)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Going to select socket %d for reading\n", i); - } - if (SAFE_FD_ISSET (i, &sock_write)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Going to select socket %d for writing\n", i); - } - if (SAFE_FD_ISSET (i, &sock_except)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Going to select socket %d for exceptions\n", i); - } - } - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Waiting for %d microseconds, %d left\n", cycle_delay, (limit - GetTickCount ())*1000); + if (rfds) + { + FD_COPY (&rfds->sds, &aread); +#if DEBUG_NETWORK + FD_COPY (&rfds->sds, &bread); #endif - if ((retcode = - select (nfds + 1, &aread, &awrite, &aexcept, - &tvslice)) == SOCKET_ERROR) - - { - SetErrnoFromWinsockError (WSAGetLastError ()); - if (errno == ENOTSOCK) - errno = EBADF; - + } + if (wfds) + { + FD_COPY (&wfds->sds, &awrite); #if DEBUG_NETWORK - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "select"); - + FD_COPY (&wfds->sds, &bwrite); +#endif + } + if (efds) + { + FD_COPY (&efds->sds, &aexcept); +#if DEBUG_NETWORK + FD_COPY (&efds->sds, &bexcept); #endif - goto select_loop_end; - } -#if DEBUG_W32_CYCLES - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Select () returned %d, GLE is %d\n", retcode, GetLastError ()); + } + /* We will first Add the PIPES to the events */ + /* Read Pipes */ + if (rfds && read_handles) + { + struct GNUNET_CONTAINER_SList_Iterator *i; + for (i = GNUNET_CONTAINER_slist_begin (rfds->handles); + GNUNET_CONTAINER_slist_end (i) != GNUNET_YES; + GNUNET_CONTAINER_slist_next (i)) + { + struct GNUNET_DISK_FileHandle *fh; + fh = (struct GNUNET_DISK_FileHandle *) GNUNET_CONTAINER_slist_get (i, NULL); + if (fh->type == GNUNET_PIPE) + { + /* Read zero bytes to check the status of the pipe */ +#if DEBUG_NETWORK + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Reading 0 bytes from the pipe 0x%x\n", fh->h); #endif - } - - /* Poll read pipes */ - if (rfds) - { - struct GNUNET_CONTAINER_SList_Iterator *i; -#if DEBUG_W32_CYCLES - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Polling rfds for readable pipes\n"); -#endif - for (i = GNUNET_CONTAINER_slist_begin (rfds->handles); - GNUNET_CONTAINER_slist_end (i) != GNUNET_YES; - GNUNET_CONTAINER_slist_next (i)) - - { - struct GNUNET_DISK_FileHandle *fh; - DWORD dwBytes; - fh = (struct GNUNET_DISK_FileHandle *) GNUNET_CONTAINER_slist_get (i, NULL); - if (fh->type == GNUNET_PIPE) - { -#if DEBUG_W32_CYCLES - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Polling pipe 0x%x (0x%x)\n", fh, fh->h); -#endif - if (!PeekNamedPipe (fh->h, NULL, 0, NULL, &dwBytes, NULL)) - { - DWORD error_code = GetLastError (); - switch (error_code) - { - case ERROR_BROKEN_PIPE: - GNUNET_CONTAINER_slist_add (handles_read, - GNUNET_CONTAINER_SLIST_DISPOSITION_TRANSIENT, - fh, sizeof (struct GNUNET_DISK_FileHandle)); - retcode++; - break; - default: - retcode = -1; - SetErrnoFromWinError (error_code); - - #if DEBUG_NETWORK - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, - "PeekNamedPipe"); - - #endif - goto select_loop_end; - } - } - else if (dwBytes) - - { - GNUNET_CONTAINER_slist_add (handles_read, - GNUNET_CONTAINER_SLIST_DISPOSITION_TRANSIENT, - fh, sizeof (struct GNUNET_DISK_FileHandle)); - retcode++; - } - } - else - { - /* Should we wait for more bytes to read here (in case of previous EOF)? */ - GNUNET_CONTAINER_slist_add (handles_read, - GNUNET_CONTAINER_SLIST_DISPOSITION_TRANSIENT, - fh, sizeof (struct GNUNET_DISK_FileHandle)); - } - } - GNUNET_CONTAINER_slist_iter_destroy (i); - } - - /* Poll for faulty pipes */ - if (efds) - + if (!ReadFile (fh->h, NULL, 0, NULL, fh->oOverlapRead)) { -#if DEBUG_W32_CYCLES - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Polling efds for broken pipes\n"); -#endif - struct GNUNET_CONTAINER_SList_Iterator *i; - for (i = GNUNET_CONTAINER_slist_begin (efds->handles); - GNUNET_CONTAINER_slist_end (i) != GNUNET_YES; - GNUNET_CONTAINER_slist_next (i)) - - { - struct GNUNET_DISK_FileHandle *fh; - DWORD dwBytes; - - fh = (struct GNUNET_DISK_FileHandle *) GNUNET_CONTAINER_slist_get (i, NULL); - if (fh->type == GNUNET_PIPE) - { -#if DEBUG_W32_CYCLES - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Polling pipe 0x%x (0x%x)\n", fh, fh->h); -#endif - if (!PeekNamedPipe (fh->h, NULL, 0, NULL, &dwBytes, NULL)) - - { - GNUNET_CONTAINER_slist_add (handles_except, - GNUNET_CONTAINER_SLIST_DISPOSITION_TRANSIENT, - fh, sizeof (struct GNUNET_DISK_FileHandle)); - retcode++; - } - } - } - GNUNET_CONTAINER_slist_iter_destroy (i); + DWORD error_code = GetLastError(); + if (error_code == ERROR_IO_PENDING) + { +#if DEBUG_NETWORK + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding the pipe's 0x%x overlapped event to the array as %d\n", fh->h, nhandles); +#endif + handle_array[nhandles++] = fh->oOverlapRead->hEvent; + readArray[readPipes++] = fh; + } + /* + else + { + SetErrnoFromWinError (error_code); + } + */ } - - if (wfds) + else { - GNUNET_CONTAINER_slist_append (handles_write, wfds->handles); - retcode += GNUNET_CONTAINER_slist_count (wfds->handles); +#if DEBUG_NETWORK + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding the read ready event to the array as %d\n", nhandles); +#endif + handle_array[nhandles++] = hEventReadReady; + readArray[readPipes++] = fh; } + } + else + { + GNUNET_CONTAINER_slist_add (handles_read, + GNUNET_CONTAINER_SLIST_DISPOSITION_TRANSIENT, + fh, sizeof (struct GNUNET_DISK_FileHandle)); + } + } + GNUNET_CONTAINER_slist_iter_destroy (i); + } + if (wfds && write_handles) + { +#if DEBUG_NETWORK + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding the write ready event to the array as %d\n", nhandles); +#endif + handle_array[nhandles++] = hEventPipeWrite; + writePipePos = nhandles; + } + if (efds && ex_handles) + { + struct GNUNET_CONTAINER_SList_Iterator *i; + for (i = GNUNET_CONTAINER_slist_begin (efds->handles); + GNUNET_CONTAINER_slist_end (i) != GNUNET_YES; + GNUNET_CONTAINER_slist_next (i)) + { + struct GNUNET_DISK_FileHandle *fh; + DWORD dwBytes; - /* Check for closed sockets */ - for (i = 0; i < nfds; i++) - + fh = (struct GNUNET_DISK_FileHandle *) GNUNET_CONTAINER_slist_get (i, NULL); + if (fh->type == GNUNET_PIPE) + { + if (!PeekNamedPipe (fh->h, NULL, 0, NULL, &dwBytes, NULL)) { - if (SAFE_FD_ISSET (i, &sock_read)) - - { - struct sockaddr addr; - int len; - if (getpeername (i, &addr, &len) == SOCKET_ERROR) - - { - int err, len; - len = sizeof (err); - if (getsockopt - (i, SOL_SOCKET, SO_ERROR, (char *) &err, &len) == 0 - && err == WSAENOTCONN) - - { - if (!SAFE_FD_ISSET (i, &aread)) - - { - FD_SET (i, &aread); - retcode++; - } - } - } - } + GNUNET_CONTAINER_slist_add (handles_except, + GNUNET_CONTAINER_SLIST_DISPOSITION_TRANSIENT, + fh, sizeof (struct GNUNET_DISK_FileHandle)); + newretcode++; } - select_loop_end: - if (retcode == 0) + } + } + GNUNET_CONTAINER_slist_iter_destroy (i); + } + if (nfds > 0) + { + if (rfds) + { +#if DEBUG_NETWORK + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding the socket read event to the array as %d\n", nhandles); +#endif + handle_array[nhandles++] = hEventRead; + nSockEvents++; + for (i = 0; i < rfds->sds.fd_count; i++) + { + WSAEventSelect (rfds->sds.fd_array[i], hEventRead, FD_ACCEPT | FD_READ | FD_CLOSE); + nsock++; + } + } + if (wfds) + { + int wakeup = 0; +#if DEBUG_NETWORK + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding the socket write event to the array as %d\n", nhandles); +#endif + handle_array[nhandles++] = hEventWrite; + nSockEvents++; + for (i = 0; i < wfds->sds.fd_count; i++) { - /* For pipes, there have been no select() call, so the poll is - * more likely to miss first time around. For now just don't increase - * the delay for pipes only. - */ - if (nfds != 0) - cycle_delay = cycle_delay * 2 > 250000 ? 250000 : cycle_delay * 1.4; - /* Missed an I/O - double the cycle time */ -#if DEBUG_W32_CYCLES - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "The cycle missed, increased the delay to %dms\n", cycle_delay); + DWORD error; + int status; + status = send (wfds->sds.fd_array[i], NULL, 0, 0); + error = GetLastError (); +#if DEBUG_NETWORK + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pre-send to the socket %d returned %d (%u)\n", i, status, error); #endif + if (status == 0 || (error != WSAEWOULDBLOCK && error != WSAENOTCONN)) + wakeup = 1; + WSAEventSelect (wfds->sds.fd_array[i], hEventWrite, FD_WRITE | FD_CONNECT | FD_CLOSE); + nsock++; } - else + if (wakeup) + SetEvent (hEventWrite); + } + if (efds) + { +#if DEBUG_NETWORK + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding the socket error event to the array as %d\n", nhandles); +#endif + handle_array[nhandles++] = hEventException; + nSockEvents++; + for (i = 0; i < efds->sds.fd_count; i++) { - /* Successfully selected something - decrease the cycle time */ - /* Minimum is 5 microseconds. Decrease the delay by half, - * or by 5000 - whichever is higher. - */ - cycle_delay -= cycle_delay > 5000 ? GNUNET_MAX (5000, cycle_delay / 2) : cycle_delay - 5; -#if DEBUG_W32_CYCLES - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "The cycle hit, decreased the delay to %dms\n", cycle_delay); + WSAEventSelect (efds->sds.fd_array[i], hEventException, FD_OOB | FD_CLOSE); + nsock++; + } + } + } + + handle_array[nhandles] = NULL; + +#if DEBUG_NETWORK + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Number nfds : %d\n", nfds); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Number of handles : %d\n", nhandles); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "retcode : %d\n", newretcode); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Will wait : %d\n", ms_total); +#endif + + if (nhandles) + returncode = WaitForMultipleObjects (nhandles, handle_array, FALSE, ms_total); +#if DEBUG_NETWORK + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "WaitForMultipleObjects Returned : %d\n", returncode); +#endif + + returnedpos = returncode - WAIT_OBJECT_0; +#if DEBUG_NETWORK + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "return pos is : %d\n", returnedpos); +#endif + + /* FIXME: THIS LINE IS WRONG !! We should add to handles only handles that fired the events, not all ! */ + /* + if(rfds) + GNUNET_CONTAINER_slist_append (handles_read, rfds->handles); + */ + if (nhandles && (returnedpos < nhandles)) + { + DWORD waitstatus; + /* Do the select */ + if (nfds) + { + struct timeval tvslice; + tvslice.tv_sec = 0; + tvslice.tv_usec = 10; + retcode = select (nfds, &aread, &awrite, &aexcept, &tvslice); + if (retcode == -1) + retcode = 0; +#if DEBUG_NETWORK + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Select retcode : %d\n", retcode); #endif + } + /* FIXME: <= writePipePos? Really? */ + if ((writePipePos != -1) && (returnedpos <= writePipePos)) + { + GNUNET_CONTAINER_slist_append (handles_write, wfds->handles); + retcode += write_handles; +#if DEBUG_NETWORK + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Added write pipe\n"); +#endif + } +#if DEBUG_NETWORK + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ReadPipes is : %d\n", readPipes); +#endif + /* We have some pipes ready for read. */ + /* FIXME: it is supposed to work !! Only choose the Pipes who fired the event, but it is not working */ + + if (returnedpos < readPipes) + { + /* + for (i = 0; i < readPipes; i++) + { + waitstatus = WaitForSingleObject (handle_array[i], 0); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Read pipe %d wait status is : %d\n", i, waitstatus); + if (waitstatus != WAIT_OBJECT_0) + continue; + GNUNET_CONTAINER_slist_add (handles_read, + GNUNET_CONTAINER_SLIST_DISPOSITION_TRANSIENT, + readArray[i], sizeof (struct GNUNET_DISK_FileHandle)); + retcode++; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Added read Pipe\n"); } - if (retcode == 0 && nfds == 0) + */ + for (i = 0; i < readPipes; i++) { - long long diff = limit - GetTickCount (); - diff = diff > 0 ? diff : 0; -#if DEBUG_W32_CYCLES - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "No sockets, sleeping for %d or %d ms\n", cycle_delay / 1000, diff); + DWORD error; + BOOL bret; + SetLastError (0); + waitstatus = 0; + bret = PeekNamedPipe (readArray[i]->h, NULL, 0, NULL, &waitstatus, NULL); + error = GetLastError (); +#if DEBUG_NETWORK + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peek at read pipe %d (0x%x) returned %d (%d bytes available) GLE %u\n", i, readArray[i]->h, bret, waitstatus, error); +#endif + if (bret == 0 || waitstatus <= 0) + continue; + GNUNET_CONTAINER_slist_add (handles_read, + GNUNET_CONTAINER_SLIST_DISPOSITION_TRANSIENT, + readArray[i], sizeof (struct GNUNET_DISK_FileHandle)); + retcode++; +#if DEBUG_NETWORK + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Added read Pipe 0x%x (0x%x)\n", readArray[i], readArray[i]->h); #endif - Sleep (GNUNET_MIN (cycle_delay / 1000, diff)); } } - while (retcode == 0 && (ms_total == INFINITE || GetTickCount () < limit)); - - if (retcode != -1) + waitstatus = WaitForSingleObject (hEventWrite, 0); +#if DEBUG_NETWORK + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Wait for the write event returned %d\n", waitstatus); +#endif + if (waitstatus == WAIT_OBJECT_0) { - if (rfds) - { - GNUNET_NETWORK_fdset_zero (rfds); - GNUNET_NETWORK_fdset_copy_native (rfds, &aread, retcode); - GNUNET_CONTAINER_slist_clear (rfds->handles); - GNUNET_CONTAINER_slist_append (rfds->handles, handles_read); - } - if (wfds) - { - GNUNET_NETWORK_fdset_zero (wfds); - GNUNET_NETWORK_fdset_copy_native (wfds, &awrite, retcode); - GNUNET_CONTAINER_slist_clear (wfds->handles); - GNUNET_CONTAINER_slist_append (wfds->handles, handles_write); - } - if (efds) + for (i = 0; i < wfds->sds.fd_count; i++) + { + DWORD error; + int status; + int so_error = 0; + int sizeof_so_error = sizeof (so_error); + int gso_result = getsockopt (wfds->sds.fd_array[i], SOL_SOCKET, SO_ERROR, (char *) &so_error, &sizeof_so_error); + status = send (wfds->sds.fd_array[i], NULL, 0, 0); + error = GetLastError (); +#if DEBUG_NETWORK + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "send to the socket %d returned %d (%u)\n", i, status, error); +#endif + if (status == 0 + || (error != WSAEWOULDBLOCK && error != WSAENOTCONN) + || (status == -1 && gso_result == 0 && error == WSAENOTCONN && so_error == WSAECONNREFUSED)) { - GNUNET_NETWORK_fdset_zero (efds); - GNUNET_NETWORK_fdset_copy_native (efds, &aexcept, retcode); - GNUNET_CONTAINER_slist_clear (efds->handles); - GNUNET_CONTAINER_slist_append (efds->handles, handles_except); + FD_SET (wfds->sds.fd_array[i], &awrite); + retcode += 1; } + } } - + } +#if DEBUG_NETWORK + if (!nhandles || (returnedpos >= nhandles)) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Returning from _select() with nothing!\n"); +#endif + if (rfds) + { + struct GNUNET_CONTAINER_SList_Iterator *t; + for (i = 0; i < rfds->sds.fd_count; i++) + { + WSAEventSelect (rfds->sds.fd_array[i], hEventRead, 0); + nsock++; + } + for (t = GNUNET_CONTAINER_slist_begin (rfds->handles); + GNUNET_CONTAINER_slist_end (t) != GNUNET_YES; + GNUNET_CONTAINER_slist_next (t)) + { + struct GNUNET_DISK_FileHandle *fh; + fh = (struct GNUNET_DISK_FileHandle *) GNUNET_CONTAINER_slist_get (t, NULL); + if (fh->type == GNUNET_PIPE) + { + CancelIo (fh->h); + } + } + GNUNET_CONTAINER_slist_iter_destroy (t); +#if DEBUG_NETWORK + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Zeroing rfds\n"); +#endif + GNUNET_NETWORK_fdset_zero (rfds); + if (retcode != -1 && nhandles && (returnedpos < nhandles)) + GNUNET_NETWORK_fdset_copy_native (rfds, &aread, retcode); + GNUNET_CONTAINER_slist_append (rfds->handles, handles_read); + } + if (wfds) + { + for (i = 0; i < wfds->sds.fd_count; i++) + { + WSAEventSelect (wfds->sds.fd_array[i], hEventWrite, 0); + nsock++; + } +#if DEBUG_NETWORK + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Zeroing wfds\n"); +#endif + GNUNET_NETWORK_fdset_zero (wfds); + if (retcode != -1 && nhandles && (returnedpos < nhandles)) + GNUNET_NETWORK_fdset_copy_native (wfds, &awrite, retcode); + GNUNET_CONTAINER_slist_append (wfds->handles, handles_write); + } + if (efds) + { + for (i = 0; i < efds->sds.fd_count; i++) + { + WSAEventSelect (efds->sds.fd_array[i], hEventException, 0); + nsock++; + } +#if DEBUG_NETWORK + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Zeroing efds\n"); +#endif + GNUNET_NETWORK_fdset_zero (efds); + if (retcode != -1 && nhandles && (returnedpos < nhandles)) + GNUNET_NETWORK_fdset_copy_native (efds, &aexcept, retcode); + GNUNET_CONTAINER_slist_append (efds->handles, handles_except); + } GNUNET_CONTAINER_slist_destroy (handles_read); GNUNET_CONTAINER_slist_destroy (handles_write); GNUNET_CONTAINER_slist_destroy (handles_except); - - return retcode; +#if DEBUG_NETWORK + if (rfds) + { + struct GNUNET_CONTAINER_SList_Iterator *t; + for (i = 0; i < bread.fd_count; i++) + { + if (bread.fd_array[i] != 0) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "FD 0x%x is %s in rfds\n", bread.fd_array[i], (SAFE_FD_ISSET (bread.fd_array[i], rfds)) ? "SET" : "NOT SET"); + } + for (t = GNUNET_CONTAINER_slist_begin (rfds->handles); + GNUNET_CONTAINER_slist_end (t) != GNUNET_YES; + GNUNET_CONTAINER_slist_next (t)) + { + struct GNUNET_DISK_FileHandle *fh; + fh = (struct GNUNET_DISK_FileHandle *) GNUNET_CONTAINER_slist_get (t, NULL); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "FD 0x%x is SET in rfds\n", fh->h); + } + } + if (wfds) + { + for (i = 0; i < bwrite.fd_count; i++) + { + if (bwrite.fd_array[i] != 0) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "FD 0x%x is %s in wfds\n", bwrite.fd_array[i], (SAFE_FD_ISSET (bwrite.fd_array[i], rfds)) ? "SET" : "NOT SET"); + } + } + if (efds) + { + for (i = 0; i < bexcept.fd_count; i++) + { + if (bexcept.fd_array[i] != 0) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "FD 0x%x is %s in efds\n", bexcept.fd_array[i], (SAFE_FD_ISSET (bexcept.fd_array[i], rfds)) ? "SET" : "NOT SET"); + } + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Returning %d or 0\n", retcode); +#endif + if (nhandles && (returnedpos < nhandles)) + return retcode; + else #endif + return 0; }