Beispiel #1
0
/**
 * \fn static void* threadStart(void* handle)
 * \brief start the filter thread
 * \param handle the stream to use the filters on
 */
static void*
threadStart(void* handle)
{
  BufferedWriter* self = (BufferedWriter*)handle;
  BufferChain* chain = self->firstChain;

  while (self->active) {
    oml_lock(&self->lock, "bufferedWriter");
    pthread_cond_wait(&self->semaphore, &self->lock);
    // Process all chains which have data in them
    while(1) {
      if (mbuf_message(chain->mbuf) > mbuf_rdptr(chain->mbuf)) {
        // got something to read from this chain
        while (!processChain(self, chain));
      }
      // stop if we caught up to the writer

      if (chain == self->writerChain) break;

      chain = chain->next;
    }
    oml_unlock(&self->lock, "bufferedWriter");
  }
  return NULL;
}
Beispiel #2
0
/** 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(&current->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(&current->lock, __FUNCTION__);
      nextBuffer->next = current->next;
      current->next = nextBuffer; /* we have a lock on this one */
      oml_unlock(&current->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;
}
Beispiel #3
0
/** Finalise a marshalled message.
 *
 * Depending on the number of values packed, change the type of message, and
 * write the actual size in the right location of the header, in network byte
 * order.
 *
 * \param mbuf MBuffer where marshalled data is
 * \return 1
 * \see marshal_init, marshal_measurements, marshal_values
 */
int
marshal_finalize(MBuffer*  mbuf)
{
  uint8_t* buf = mbuf_message (mbuf);
  OmlBinMsgType type = marshal_get_msgtype (mbuf);
  size_t len = mbuf_message_length (mbuf);

  if (len > UINT32_MAX) {
    logwarn("Message length %d longer than maximum packet length (%d); "
            "packet will be truncated\n",
            len, UINT32_MAX);
    len = UINT32_MAX;
  }

  if (type == OMB_DATA_P && len > UINT16_MAX) {
    /*
     * We assumed a short packet, but there is too much data, so we
     * have to shift the whole buffer down by 2 bytes and convert to a
     * long packet.
     */
    uint8_t s[2] = {0};
    /* Put some padding in the buffer to make sure it has room, and maintains its invariants */
    mbuf_write (mbuf, s, sizeof (s));
    memmove (&buf[PACKET_HEADER_SIZE+2], &buf[PACKET_HEADER_SIZE],
             len - PACKET_HEADER_SIZE);
    len += 2;
    buf[2] = type = OMB_LDATA_P;
  }


  switch (type) {
  case OMB_DATA_P:
    len -= PACKET_HEADER_SIZE; // Data length minus header
    uint16_t nlen16 = htons (len);
    memcpy (&buf[3], &nlen16, sizeof (nlen16));
    break;
  case OMB_LDATA_P:
    len -= PACKET_HEADER_SIZE + 2; // Data length minus header
    uint32_t nlen32 = htonl (len); // pure data length
    memcpy (&buf[3], &nlen32, sizeof (nlen32));
    break;
  }

  return 1;
}
Beispiel #4
0
/** Marshal the array of values into an MBuffer.
 *
 * Metadata of the measurement stream should already have been written
 * with marshal_measurements(). Each element of values is written with
 * marshal_value().  Finally, the number of elements in the message is
 * updated in its header, by incrementing the relevant field
 * (depending on its OmlBinMsgType) by value_count.
 *
 * If the returned number is negative, marshalling didn't finish as
 * the provided buffer was short of the number of bytes returned (when
 * multiplied by -1); the entire message has been reset (by
 * marshal_value()), and marshalling should restart with
 * marshal_init(), after the MBuffer has been adequately resized or
 * repacked.
 *
 * Once all data has been marshalled, marshal_finalize() should be
 * called to finish preparing the message.
 *
 * \param mbuf MBuffer to write marshalled data to
 * \param values array of OmlValue of length value_count
 * \param value_count length  the values array
 * \return 1 on success, or -1 otherwise (marshalling should then restart from marshal_init())
 * \see marshal_init, marshal_measurements, marshal_value, marshal_finalize, mbuf_repack_message, mbuf_repack_message2, mbuf_resize
 */
int marshal_values(MBuffer* mbuf, OmlValue* values, int value_count)
{
  OmlValue* val = values;
  int i;

  for (i = 0; i < value_count; i++, val++) {
    if(!marshal_value(mbuf, oml_value_get_type(val), oml_value_get_value(val)))
      return -1;
  }

  uint8_t* buf = mbuf_message (mbuf);
  OmlBinMsgType type = marshal_get_msgtype (mbuf);
  switch (type) {
  case OMB_DATA_P: buf[5] += value_count; break;
  case OMB_LDATA_P: buf[7] += value_count; break;
  }
  return 1;
}
Beispiel #5
0
/** Function called after all items in a tuple have been sent
 * \see oml_writer_row_end
 *
 * This releases the lock on the BufferedWriter.
 *
 * \see BufferedWriter, bw_unlock_buf, marshal_finalize
 */
static int
owb_row_end(OmlWriter* writer, OmlMStream* ms) {
  (void)ms;
  OmlBinWriter* self = (OmlBinWriter*)writer;
  MBuffer* mbuf;
  if ((mbuf = self->mbuf) == NULL) {
    return 0; /* previous use of mbuf failed */
  }

  marshal_finalize(self->mbuf);
  if (marshal_get_msgtype (self->mbuf) == OMB_LDATA_P) {
    self->msgtype = OMB_LDATA_P; // Generate long packets from now on.
  }

  if (0 == ms->index) {
    /* This is schema0, also push the data into the meta_buf
     * to be replayed after a disconnection.
     *
     * At the moment, the oml_outs_write_f takes header information as a
     * whole, but does not push more once it has sent the initial block. Its
     * two last parameters are only used to resend the entirety of the headers
     * when a disconnection does occur, nothing before.
     *
     * We therefore send the extra piece of data the normal way, but also
     * record it, separately, in the meta_buf
     *
     * XXX: This logic should be in higher layer levels, but given the current
     * implementation, with some of it already spread down into the
     * OmlOutStream (oml_outs_write_f), this require a much bigger refactoring.
     * It is also duplicated with the OmlTextWriter (see #1101).
     */
    _bw_push_meta(self->bufferedWriter,
        mbuf_message(self->mbuf), mbuf_message_length(self->mbuf));
  }

  mbuf_begin_write(mbuf);

  self->mbuf = NULL;
  bw_unlock_buf(self->bufferedWriter);
  return 1;
}
Beispiel #6
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;
}
Beispiel #7
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;
}
Beispiel #8
0
/** Retrieve the message type from an MBuffer containing a marshalling packet.
 *
 * \param mbuf MBuffer to read the mbuf marshalling header frow
 * \return the OmlBinMsgType of the packet
 */
OmlBinMsgType
marshal_get_msgtype (MBuffer *mbuf)
{
  return (OmlBinMsgType)(mbuf_message (mbuf))[2];
}