Пример #1
0
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;
}
Пример #2
0
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;
}
Пример #3
0
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;
}
Пример #4
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;
}
Пример #5
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;
}
Пример #6
0
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;
}
Пример #7
0
/* 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;
}
Пример #8
0
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;
}
Пример #9
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;
}