/* * QBufPipe_Wait */ void QBufPipe_Wait( qbufPipe_t *pipe, int (*read)( qbufPipe_t *, unsigned( ** )(const void *), bool ), unsigned (**cmdHandlers)( const void * ), unsigned timeout_msec ) { while( !pipe->terminated ) { int res; bool result = false; while( Sys_Atomic_CAS( &pipe->cmdbuf_len, 0, 0, pipe->cmdbuf_mutex ) == true ) { QMutex_Lock( pipe->nonempty_mutex ); result = QCondVar_Wait( pipe->nonempty_condvar, pipe->nonempty_mutex, timeout_msec ); // don't hold the mutex, changes to cmdbuf_len are atomic anyway QMutex_Unlock( pipe->nonempty_mutex ); break; } // we're guaranteed at this point that either cmdbuf_len is > 0 // or that waiting on the condition variable has timed out res = read( pipe, cmdHandlers, result ); if( res < 0 ) { // done return; } } }
/* * QBufPipe_Finish * * Blocks until the reader thread handles all commands * or terminates with an error. */ void QBufPipe_Finish( qbufPipe_t *pipe ) { while( Sys_Atomic_CAS( &pipe->cmdbuf_len, 0, 0, pipe->cmdbuf_mutex ) == false && !pipe->terminated ) { QMutex_Lock( pipe->nonempty_mutex ); QBufPipe_Wake( pipe ); QMutex_Unlock( pipe->nonempty_mutex ); QThread_Yield(); } }
/* * QBufPipe_ReadCmds */ int QBufPipe_ReadCmds( qbufPipe_t *pipe, unsigned (**cmdHandlers)( const void * ) ) { int read = 0; if( !pipe ) { return -1; } while( Sys_Atomic_CAS( &pipe->cmdbuf_len, 0, 0, pipe->cmdbuf_mutex ) == false && !pipe->terminated ) { int cmd; int cmd_size; int read_remains; assert( pipe->bufSize >= pipe->read_pos ); if( pipe->bufSize < pipe->read_pos ) { pipe->read_pos = 0; } read_remains = pipe->bufSize - pipe->read_pos; if( sizeof( int ) > read_remains ) { // implicit reset pipe->read_pos = 0; QBufPipe_BufLenAdd( pipe, -read_remains ); } cmd = *((int *)(pipe->buf + pipe->read_pos)); if( cmd == -1 ) { // this cmd is special pipe->read_pos = 0; QBufPipe_BufLenAdd( pipe, -((int)(sizeof(int) + read_remains)) ); // atomic continue; } cmd_size = cmdHandlers[cmd](pipe->buf + pipe->read_pos); read++; if( !cmd_size ) { pipe->terminated = 1; return -1; } if( cmd_size > pipe->cmdbuf_len ) { assert( 0 ); pipe->terminated = 1; return -1; } pipe->read_pos += cmd_size; QBufPipe_BufLenAdd( pipe, -cmd_size ); // atomic break; } return read; }
/* * QBufPipe_WriteCmd * * Add new command to buffer. Never allow the distance between the reader * and the writer to grow beyond the size of the buffer. * * Note that there are race conditions here but in the worst case we're going * to erroneously drop cmd's instead of stepping on the reader's toes. */ void QBufPipe_WriteCmd( qbufPipe_t *pipe, const void *cmd, unsigned cmd_size ) { void *buf; unsigned write_remains; bool was_empty; if( !pipe ) { return; } if( pipe->terminated ) { return; } assert( pipe->bufSize >= pipe->write_pos ); if( pipe->bufSize < pipe->write_pos ) { pipe->write_pos = 0; } was_empty = Sys_Atomic_CAS( &pipe->cmdbuf_len, 0, 0, pipe->cmdbuf_mutex ) == true; write_remains = pipe->bufSize - pipe->write_pos; if( sizeof( int ) > write_remains ) { while( pipe->cmdbuf_len + cmd_size + write_remains > pipe->bufSize ) { if( pipe->blockWrite ) { QThread_Yield(); continue; } return; } // not enough space to enpipe even the reset cmd, rewind QBufPipe_BufLenAdd( pipe, write_remains ); // atomic pipe->write_pos = 0; } else if( cmd_size > write_remains ) { int *cmd; while( pipe->cmdbuf_len + sizeof( int ) + cmd_size + write_remains > pipe->bufSize ) { if( pipe->blockWrite ) { QThread_Yield(); continue; } return; } // explicit pointer reset cmd cmd = QBufPipe_AllocCmd( pipe, sizeof( int ) ); *cmd = -1; QBufPipe_BufLenAdd( pipe, sizeof( *cmd ) + write_remains ); // atomic pipe->write_pos = 0; } else { while( pipe->cmdbuf_len + cmd_size > pipe->bufSize ) { if( pipe->blockWrite ) { QThread_Yield(); continue; } return; } } buf = QBufPipe_AllocCmd( pipe, cmd_size ); memcpy( buf, cmd, cmd_size ); QBufPipe_BufLenAdd( pipe, cmd_size ); // atomic // wake the other thread waiting for signal if( was_empty ) { QMutex_Lock( pipe->nonempty_mutex ); QBufPipe_Wake( pipe ); QMutex_Unlock( pipe->nonempty_mutex ); } }
/* * QAtomic_CAS */ bool QAtomic_CAS( volatile int *value, int oldval, int newval, qmutex_t *mutex ) { return Sys_Atomic_CAS( value, oldval, newval, mutex ); }