// 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; }
/** Send data contained in one chunk. * * \warning This function acquires the lock on the BufferedWriter for the time * it takes to check the double-buffer. * * \warning This function acquires the lock on the chunk being processed for * the time it takes to check it and swap the double buffer. * * \bug The meta buffer should also be protected. * * \param self BufferedWriter to process * \param chunk link of the chunk to process * * \return 1 if chunk has been fully sent, 0 if not, -1 on continuing back-off, -2 otherwise * \see oml_outs_write_f */ static int processChunk(BufferedWriter* self, BufferChunk* chunk) { time_t now; int ret = -2; ssize_t cnt = 0; MBuffer *read_buf = NULL; assert(self); assert(self->meta_buf); assert(self->read_buf); assert(chunk); assert(chunk->mbuf); oml_lock(&self->lock, __FUNCTION__); if (mbuf_message(self->read_buf) > mbuf_rdptr(self->read_buf)) { /* There is unread data in the double buffer */ read_buf = self->read_buf; } oml_unlock(&self->lock, __FUNCTION__); oml_lock(&chunk->lock, __FUNCTION__); if ((NULL == read_buf) && (mbuf_message(chunk->mbuf) >= mbuf_rdptr(chunk->mbuf))) { /* There is unread data in the read buffer, swap MBuffers */ read_buf = chunk->mbuf; chunk->mbuf = self->read_buf; } oml_unlock(&chunk->lock, __FUNCTION__); oml_lock(&self->lock, __FUNCTION__); self->read_buf = read_buf; oml_unlock(&self->lock, __FUNCTION__); if (NULL == read_buf) { /* The current message is not after the read pointer, * we must be on the writer chunk */ ret = 1; goto processChunk_cleanup; } time(&now); if (difftime(now, self->last_failure_time) < self->backoff) { logdebug("%s: Still in back-off period (%ds)\n", self->outStream->dest, self->backoff); ret = -1; goto processChunk_cleanup; } while (mbuf_write_offset(read_buf) > mbuf_read_offset(read_buf)) { oml_lock(&self->meta_lock, __FUNCTION__); cnt = self->outStream->write(self->outStream, mbuf_rdptr(read_buf), mbuf_message_offset(read_buf) - mbuf_read_offset(read_buf), mbuf_rdptr(self->meta_buf), mbuf_fill(self->meta_buf)); oml_unlock(&self->meta_lock, __FUNCTION__); if (cnt > 0) { mbuf_read_skip(read_buf, cnt); if (self->backoff) { self->backoff = 0; loginfo("%s: Connected\n", self->outStream->dest); } } else { self->last_failure_time = now; if (!self->backoff) { self->backoff = 1; } else if (self->backoff < UINT8_MAX) { self->backoff *= 2; } logwarn("%s: Error sending, backing off for %ds\n", self->outStream->dest, self->backoff); goto processChunk_cleanup; } } ret = 1; processChunk_cleanup: return ret; }