rc_t BufferQPushBuffer (BufferQ * self, const Buffer * buff, timeout_t * tm) { rc_t rc = 0; timeout_t t; assert (self != NULL); assert (buff != NULL); if (tm == NULL) /* do we need the default timeout? */ { tm = &t; rc = TimeoutInit (tm, self->timeout); } if (rc == 0) { rc = KQueuePush (self->q, buff, tm); if (rc == 0) { /* share ownership of the buffer removing keep alive reference */ rc = BufferAddRef (buff); } } return rc; }
rc_t BufferQPopBuffer (BufferQ * self, const Buffer ** buff, timeout_t * tm) { rc_t rc = 0; timeout_t t; void * p; LOGMSG (klogDebug10, "BufferQPopBuffer"); assert (self != NULL); assert (buff != NULL); if (tm == NULL) { LOGMSG (klogDebug10, "BufferQPopBuffer tm was NULL"); tm = &t; rc = TimeoutInit (tm, self->timeout); } if (rc == 0) { LOGMSG (klogDebug10, "BufferQPopBuffer call KQueuePop"); rc = KQueuePop (self->q, &p, tm); PLOGMSG (klogDebug10, "BufferQPopBuffer back from KQueuePop $(rc)", PLOG_U32(rc), rc); if (rc == 0) *buff = p; else { *buff = NULL; } } LOGMSG (klogDebug10, "leave BufferQPopBuffer"); return rc; }
static rc_t KQueueFileFlush ( KQueueFile *self, KQueueFileBuffer *b ) { rc_t rc; /* timeout is in mS */ timeout_t tm; QFMSG ( "%s: initializing timeout for %,umS\n", __func__, self->timeout_ms); TimeoutInit ( & tm, self->timeout_ms ); /* push buffer */ QFMSG ( "%s: pushing buffer...\n", __func__ ); rc = KQueuePush ( self -> q, b, & tm ); QFMSG ( "%s: ...done: rc = %R\n", __func__, rc ); if ( rc != 0 ) { /* see if the bg thread exited */ if ( GetRCState ( rc ) == rcReadonly ) { rc = RC ( rcApp, rcFile, rcWriting, rcTransfer, rcIncomplete ); QFMSG ( "%s: resetting rc to %R\n", __func__, rc ); } return rc; } QFMSG ( "%s: forgetting about buffer\n", __func__ ); self -> b = NULL; return 0; }
/*@ MPIE_IOLoop - Handle all registered I/O Input Parameters: . timeoutSeconds - Seconds until this routine should return with a timeout error. If negative, no timeout. If 0, return immediatedly after a nonblocking check for I/O. Return Value: Returns zero on success. Returns 'IOLOOP_TIMEOUT' if the timeout is reached and 'IOLOOP_ERROR' on other errors. @*/ int MPIE_IOLoop(int timeoutSeconds) { int i, maxfd, fd, nfds, rc = 0, rc2; fd_set readfds, writefds; int (*handler) (int, int, void *); struct timeval tv; /* Loop on the fds, with the timeout */ TimeoutInit(timeoutSeconds); while (1) { tv.tv_sec = TimeoutGetRemaining(); tv.tv_usec = 0; /* Determine the active FDs */ FD_ZERO(&readfds); FD_ZERO(&writefds); /* maxfd is the maximum active fd */ maxfd = -1; for (i = 0; i <= maxFD; i++) { if (handlesByFD[i].handler) { fd = handlesByFD[i].fd; if (handlesByFD[i].rdwr & IO_READ) { FD_SET(fd, &readfds); maxfd = i; } if (handlesByFD[i].rdwr & IO_WRITE) { FD_SET(fd, &writefds); maxfd = i; } } } if (maxfd < 0) break; /* DBG_PRINTF(("Calling select with readfds = %x writefds = %x\n", */ /* *(int *)&readfds, *(int*)&writefds)); */ MPIE_SYSCALL(nfds, select, (maxfd + 1, &readfds, &writefds, 0, &tv)); if (nfds < 0 && (errno == EINTR || errno == 0)) { /* Continuing through EINTR */ /* We allow errno == 0 as a synonym for EINTR. We've seen this * on Solaris; in addition, we set errno to 0 after a failed * waitpid in the process routines, and if the OS isn't careful, * the value of errno may get ECHILD instead of EINTR when the * signal handler returns (we suspect Linux of this problem), * which is why we have the signal handler in process.c reset * errno to 0 (we may need to allow ECHILD here (!)) */ /* FIXME: an EINTR may also mean that a process has exited * (SIGCHILD). If all processes have exited, we may want to * exit */ DBG_PRINTF(("errno = EINTR in select\n")); continue; } if (nfds < 0) { /* Serious error */ MPL_internal_sys_error_printf("select", errno, 0); break; } if (nfds == 0) { /* Timeout from select */ DBG_PRINTF(("Timeout in select\n")); return IOLOOP_TIMEOUT; } /* nfds > 0 */ DBG_PRINTF(("Found some fds to process (n = %d)\n", nfds)); for (fd = 0; fd <= maxfd; fd++) { if (FD_ISSET(fd, &writefds)) { handler = handlesByFD[fd].handler; if (handler) { rc = (*handler) (fd, IO_WRITE, handlesByFD[fd].extra_data); } if (rc == 1) { /* EOF? */ MPIE_SYSCALL(rc2, close, (fd)); handlesByFD[fd].rdwr = 0; FD_CLR(fd, &writefds); } } if (FD_ISSET(fd, &readfds)) { handler = handlesByFD[fd].handler; if (handler) { rc = (*handler) (fd, IO_READ, handlesByFD[fd].extra_data); } if (rc == 1) { /* EOF? */ MPIE_SYSCALL(rc2, close, (fd)); handlesByFD[fd].rdwr = 0; FD_CLR(fd, &readfds); } } } } DBG_PRINTF(("Returning from IOLOOP handler\n")); return 0; }
/* WriteAll * TimedWriteAll * write to stream until "size" bytes have been transferred * or until no further progress can be made * * "buffer" [ IN ] and "size" [ IN ] - data to be written * * "num_writ" [ OUT, NULL OKAY ] - optional return parameter * giving number of bytes actually written * * "tm" [ IN/OUT, NULL OKAY ] - an optional indicator of * blocking behavior. not all implementations will support * timed writes. a NULL timeout will block indefinitely, * a value of "tm->mS == 0" will have non-blocking behavior * if supported by implementation, and "tm->mS > 0" will indicate * a maximum wait timeout. */ LIB_EXPORT rc_t CC KStreamWriteAll ( KStream *self, const void *buffer, size_t size, size_t *num_writ ) { rc_t rc; const uint8_t *b; size_t total, count; size_t ignore; if ( num_writ == NULL ) num_writ = & ignore; * num_writ = 0; if ( self == NULL ) return RC ( rcNS, rcStream, rcWriting, rcSelf, rcNull ); if ( ! self -> write_enabled ) return RC ( rcNS, rcStream, rcWriting, rcStream, rcNoPerm ); if ( size == 0 ) return 0; if ( buffer == NULL ) return RC ( rcNS, rcStream, rcWriting, rcBuffer, rcNull ); switch ( self -> vt -> v1 . maj ) { case 1: count = 0; rc = ( * self -> vt -> v1 . write ) ( self, buffer, size, & count ); total = count; if ( rc == 0 && count != 0 && count < size ) { if ( self -> vt -> v1 . min >= 1 ) { timeout_t no_block; TimeoutInit ( & no_block, 0 ); for ( b = buffer; total < size; total += count ) { count = 0; rc = ( * self -> vt -> v1 . timed_write ) ( self, b + total, size - total, & count, & no_block ); if ( rc != 0 ) break; if ( count == 0 ) break; } } else { for ( b = buffer; total < size; total += count ) { count = 0; rc = ( * self -> vt -> v1 . write ) ( self, b + total, size - total, & count ); if ( rc != 0 ) break; if ( count == 0 ) break; } } } break; default: return RC ( rcNS, rcStream, rcWriting, rcInterface, rcBadVersion ); } * num_writ = total; if ( total == size ) return 0; if ( rc == 0 ) return RC ( rcNS, rcStream, rcWriting, rcTransfer, rcIncomplete ); return rc; }
LIB_EXPORT rc_t CC KStreamTimedReadAll ( const KStream *self, void *buffer, size_t bsize, size_t *num_read, struct timeout_t *tm ) { rc_t rc; uint8_t *b; size_t total, count; if ( num_read == NULL ) return RC ( rcNS, rcStream, rcReading, rcParam, rcNull ); * num_read = 0; if ( self == NULL ) return RC ( rcNS, rcStream, rcReading, rcSelf, rcNull ); if ( ! self -> read_enabled ) return RC ( rcNS, rcStream, rcReading, rcStream, rcNoPerm ); if ( buffer == NULL ) return RC ( rcNS, rcStream, rcReading, rcBuffer, rcNull ); if ( bsize == 0 ) return RC ( rcNS, rcStream, rcReading, rcBuffer, rcInsufficient ); switch ( self -> vt -> v1 . maj ) { case 1: if ( self -> vt -> v1 . min >= 1 ) { count = 0; rc = ( * self -> vt -> v1 . timed_read ) ( self, buffer, bsize, & count, tm ); total = count; if ( rc == 0 && count != 0 && count < bsize ) { timeout_t no_block; TimeoutInit ( & no_block, 0 ); for ( b = buffer; total < bsize; total += count ) { count = 0; rc = ( * self -> vt -> v1 . timed_read ) ( self, b + total, bsize - total, & count, & no_block ); if ( rc != 0 ) break; if ( count == 0 ) break; } } break; } if ( tm == NULL ) { for ( rc = 0, b = buffer, total = 0; total < bsize; total += count ) { count = 0; rc = ( * self -> vt -> v1 . read ) ( self, b + total, bsize - total, & count ); if ( rc != 0 ) break; if ( count == 0 ) break; } break; } /* no break */ default: return RC ( rcNS, rcStream, rcReading, rcInterface, rcBadVersion ); } if ( total != 0 ) { * num_read = total; return 0; } return rc; }
/* RunWrite * runs write loop on background thread */ static rc_t CC KQueueFileRunWrite ( const KThread *t, void *data ) { KQueueFile *self = data; rc_t rc; do { size_t num_writ; KQueueFileBuffer *b; /* timeout is in mS */ timeout_t tm; TimeoutInit ( & tm, self->timeout_ms ); /* pop buffer */ rc = KQueuePop ( self -> q, ( void** ) & b, & tm ); if ( rc != 0 ) { /* see if the fg thread is done */ if ( GetRCState ( rc ) == rcDone && GetRCObject ( rc ) == ( enum RCObject )rcData ) { rc = 0; } else if ( GetRCObject(rc) == ( enum RCObject )rcTimeout && GetRCState(rc) == rcExhausted ) { rc = 0; continue; } break; } /* queue won't accept NULL object references */ assert ( b != NULL ); /* look at buffer for end of input */ if ( b -> rc != 0 || b -> bytes == 0 ) { free ( b ); break; } /* write to file */ rc = KFileWriteAll ( self -> f, b -> pos, b -> data, b -> bytes, & num_writ ); assert ( num_writ == b -> bytes || rc != 0 ); /* TBD - need a better way to deal with this */ if ( rc != 0 ) { PLOGERR ( klogSys, ( klogSys, rc, "$(func): wrote $(num_writ) of $(bytes) bytes to file", "func=%s,num_writ=%zu,bytes=%zu", __func__, num_writ, b -> bytes ) ); } /* done with buffer */ free ( b ); } while ( rc == 0 ); /* going to exit thread */ KQueueSeal ( self -> q ); return rc; }
static rc_t CC KQueueFileRead ( const KQueueFile *cself, uint64_t pos, void *buffer, size_t bsize, size_t *num_read ) { rc_t rc; size_t to_read; KQueueFile *self = ( KQueueFile* ) cself; KQueueFileBuffer *b = self -> b; assert ( num_read != NULL ); assert ( * num_read == 0 ); assert ( buffer != NULL && bsize != 0 ); /* detect attempt to back up */ if ( b != NULL && pos < b -> pos ) { QFMSG ( "%s: detected attempt to back up from %lu to %lu\n", __func__, b -> pos, pos ); return RC ( rcApp, rcFile, rcReading, rcConstraint, rcViolated ); } QFMSG ( "%s: loop to read %zu bytes at offset %lu\n", __func__, bsize, pos ); while ( ! KQueueFileBufferContains ( b, pos ) ) { /* fetch next buffer */ KQueueFileBuffer *tmp; /* timeout is in mS */ timeout_t tm; QFMSG ( "%s: initializing timeout for %,umS\n", __func__, self->timeout_ms ); TimeoutInit ( & tm, self->timeout_ms ); /* pop buffer */ QFMSG ( "%s: popping buffer...\n", __func__ ); rc = KQueuePop ( self -> q, ( void** ) & tmp, & tm ); QFMSG ( "%s: ...done: rc = %R\n", __func__, rc ); if ( rc != 0 ) { /* see if the bg thread is done */ if ( GetRCState ( rc ) == rcDone && GetRCObject ( rc ) == ( enum RCObject )rcData ) { QFMSG ( "%s: BG thread has exited with end-of-input\n", __func__ ); return 0; } else if( GetRCObject(rc) == ( enum RCObject )rcTimeout && GetRCState(rc) == rcExhausted ) { continue; /* timeout is not a problem, try again */ } QFMSG ( "%s: read failed with rc = %R\n", __func__, rc ); return rc; } /* queue is not supposed to be able to contain NULL */ assert ( tmp != NULL ); /* buffer may indicate read error or end of input */ if ( tmp -> rc != 0 || tmp -> bytes == 0 ) { rc = tmp -> rc; QFMSG ( "%s: BG thread has delivered %zu size buffer with rc = %R\n", __func__, tmp -> bytes, rc ); free ( tmp ); return rc; } /* free existing buffer */ if ( b != NULL ) { QFMSG ( "%s: freeing buffer of %zu bytes at position %lu\n", __func__, b -> bytes, b -> pos ); free ( b ); } /* take new buffer */ self -> b = b = tmp; QFMSG ( "%s: caching buffer of %zu bytes at position %lu\n", __func__, b -> bytes, b -> pos ); } pos -= b -> pos; to_read = b -> bytes - ( size_t ) pos; if ( to_read > bsize ) to_read = bsize; QFMSG ( "%s: copying %zu bytes from buffer at local offset %lu\n", __func__, to_read, pos ); memcpy ( buffer, & b -> data [ pos ], to_read ); QFMSG ( "%s: successful read\n", __func__ ); * num_read = to_read; return 0; }
/* RunRead * runs read loop on background thread */ static rc_t CC KQueueFileRunRead ( const KThread *t, void *data ) { KQueueFile *self = data; rc_t rc; bool loop; uint64_t pos; size_t num_read; QFMSG ( "BG: %s: running read-ahead loop\n", __func__ ); for ( rc = 0, loop = true, pos = self -> start_pos; loop; pos += num_read ) { timeout_t tm; KQueueFileBuffer *b = malloc ( sizeof * b - sizeof b -> data + self -> bsize ); if ( b == NULL ) { rc = RC ( rcApp, rcFile, rcReading, rcMemory, rcExhausted ); QFMSG ( "BG: %s: failed to allocate %zu byte buffer\n", __func__, self -> bsize ); b = malloc ( sizeof * b ); if ( b == NULL ) break; num_read = 0; b -> rc = rc; QFMSG ( "BG: %s: created empty buffer with rc = %R.\n", __func__, b -> rc ); loop = false; } else { QFMSG ( "BG: %s: reading %zu byte buffer\n", __func__, self -> bsize ); b -> rc = KFileReadAll ( self -> f, pos, b -> data, self -> bsize, & num_read ); QFMSG ( "BG: %s: read %zu bytes, rc = %R\n", __func__, num_read, b -> rc ); if ( b -> rc != 0 || num_read == 0 ) loop = false; } /* update buffer data */ b -> pos = pos; b -> bytes = num_read; #if _DEBUGGING b -> align = 0; #endif PushAgain: /* timeout is in mS */ QFMSG ( "BG: %s: initializing timeout for %,umS\n", __func__, self->timeout_ms ); TimeoutInit ( & tm, self->timeout_ms ); /* push buffer */ QFMSG ( "BG: %s: pushing buffer...\n", __func__ ); rc = KQueuePush ( self -> q, b, & tm ); QFMSG ( "BG: %s: ...done: rc = %R\n", __func__, rc ); if ( rc != 0 ) { /* if queue has been sealed by fg */ if ( GetRCState ( rc ) == rcReadonly ) { rc = 0; QFMSG ( "BG: %s: clearing rc\n", __func__ ); } else if( GetRCObject(rc) == ( enum RCObject )rcTimeout && GetRCState(rc) == rcExhausted ) { goto PushAgain; } QFMSG ( "BG: %s: dousing buffer\n", __func__ ); free ( b ); break; } } /* going to exit thread */ QFMSG ( "BG: %s: sealing thread\n", __func__ ); KQueueSeal ( self -> q ); QFMSG ( "BG: %s: exit with rc = %R\n", __func__, rc ); return rc; }