/** Find the next empty write chunk, sets self->writerChunk to it and returns * it. * * We only use the next one if it is empty. If not, we essentially just filled * up the last chunk and wrapped around to the socket reader. In that case, we * either create a new chunk if the overall buffer can still grow, or we drop * the data from the current one. * * \warning A lock on the current writer chunk should be held prior to calling * this function. It will be released, and the returned writerChunk will be * similarly locked. * * \param self BufferedWriter pointer * \param current locked BufferChunk to use or from which to find the next * \return a locked BufferChunk in which data can be stored */ BufferChunk* getNextWriteChunk(BufferedWriter* self, BufferChunk* current) { int nlost; BufferChunk* nextBuffer; assert(current != NULL); nextBuffer = current->next; oml_unlock(¤t->lock, __FUNCTION__); assert(nextBuffer != NULL); oml_lock(&self->lock, __FUNCTION__); if (nextBuffer == self->nextReaderChunk) { if (self->unallocatedBuffers > 0) { /* The next buffer is the next to be read, but we can allocate more, * allocate a new buffer, insert it after the current writer, and use it */ nextBuffer = createBufferChunk(self); assert(nextBuffer); /** \todo Use existing buffer if allocation fails */ oml_unlock(&self->lock, __FUNCTION__); oml_lock(¤t->lock, __FUNCTION__); nextBuffer->next = current->next; current->next = nextBuffer; /* we have a lock on this one */ oml_unlock(¤t->lock, __FUNCTION__); oml_lock(&self->lock, __FUNCTION__); } else { /* The next buffer is the next to be read, and we cannot allocate more, * use it, dropping unread data, and advance the read pointer */ self->nextReaderChunk = nextBuffer->next; } } self->writerChunk = nextBuffer; nlost = bw_msgcount_reset(self); self->nlost += nlost; oml_unlock(&self->lock, __FUNCTION__); oml_lock(&nextBuffer->lock, __FUNCTION__); if (nlost) { logwarn("%s: Dropping %d samples (%dB)\n", self->outStream->dest, nlost, mbuf_fill(nextBuffer->mbuf)); } mbuf_clear2(nextBuffer->mbuf, 0); // Now we just need to copy the message from current to self->writerChunk int msgSize = mbuf_message_length(current->mbuf); if (msgSize > 0) { mbuf_write(nextBuffer->mbuf, mbuf_message(current->mbuf), msgSize); mbuf_reset_write(current->mbuf); } return nextBuffer; }
// return 1 if chain has been fully sent, 0 otherwise int processChain( BufferedWriter* self, BufferChain* chain ) { uint8_t* buf = mbuf_rdptr(chain->mbuf); size_t size = mbuf_message_offset(chain->mbuf) - mbuf_read_offset(chain->mbuf); size_t sent = 0; chain->reading = 1; oml_unlock(&self->lock, "processChain"); /* don't keep lock while transmitting */ MBuffer* meta = self->meta_buf; while (size > sent) { long cnt = self->writeFunc(self->writeFuncHdl, (void*)(buf + sent), size - sent, meta->rdptr, meta->fill); if (cnt > 0) { sent += cnt; } else { /* ERROR: Sleep a bit and try again */ /* To be on the safe side, we rewind to the beginning of the * chain and try to resend everything - this is especially important * if the underlying stream needs to reopen and resync. */ mbuf_reset_read(chain->mbuf); size = mbuf_message_offset(chain->mbuf) - mbuf_read_offset(chain->mbuf); sent = 0; sleep(1); } } // get lock back to see what happened while we were busy oml_lock_persistent(self); mbuf_read_skip(chain->mbuf, sent); if (mbuf_write_offset(chain->mbuf) == mbuf_read_offset(chain->mbuf)) { // seem to have sent everything so far, reset chain // mbuf_clear2(chain->mbuf, 0); mbuf_clear2(chain->mbuf, 1); chain->reading = 0; return 1; } return 0; }
// This function finds the next empty write chain, sets +self->writeChain+ to // it and returns. // // We only use the next one if it is empty. If not, we // essentially just filled up the last chain and wrapped // around to the socket reader. In that case, we either create a new chain // if the overall buffer can still grow, or we drop the data from the current one. // // This assumes that the current thread holds the +self->lock+ and the lock on // the +self->writeChain+. // BufferChain* getNextWriteChain( BufferedWriter* self, BufferChain* current ) { assert(current != NULL); BufferChain* nextBuffer = current->next; assert(nextBuffer != NULL); BufferChain* resChain = NULL; if (mbuf_remaining(nextBuffer->mbuf) == 0) { // It's empty, we can use it mbuf_clear2(nextBuffer->mbuf, 0); resChain = nextBuffer; } else if (self->chainsAvailable > 0) { // insert new chain between current and next one. BufferChain* newBuffer = createBufferChain(self); newBuffer->next = nextBuffer; current->next = newBuffer; resChain = newBuffer; } else { // Filled up buffer, time to drop data and reuse current buffer // Current buffer holds most recent added data (we drop from the queue's tail //assert(current->reading == 0); assert(current->reading == 0); o_log (O_LOG_WARN, "Dropping %d bytes of measurement data\n", mbuf_fill(current->mbuf)); mbuf_repack_message2(current->mbuf); return current; } // Now we just need to copy the +message+ from +current+ to +resChain+ int msgSize = mbuf_message_length(current->mbuf); if (msgSize > 0) { mbuf_write(resChain->mbuf, mbuf_message(current->mbuf), msgSize); mbuf_reset_write(current->mbuf); } return resChain; }