/** Create a BufferedWriter instance * * \param outStream opaque OmlOutStream handler * \param queueCapacity maximal size [B] of the internal queue queueCapaity/chunkSize will be used (at least 2) * \param chunkSize size [B] of buffer space allocated at a time, set to 0 for default (DEF_CHAIN_BUFFER_SIZE) * \return an instance pointer if successful, NULL otherwise * * \see DEF_CHAIN_BUFFER_SIZE */ BufferedWriter* bw_create(OmlOutStream* outStream, long queueCapacity, long chunkSize) { long nchunks; BufferedWriter* self = NULL; assert(outStream>=0); assert(queueCapacity>=0); assert(chunkSize>=0); if((self = (BufferedWriter*)oml_malloc(sizeof(BufferedWriter)))) { memset(self, 0, sizeof(BufferedWriter)); self->outStream = outStream; /* This forces a 'connected' INFO message upon first connection */ self->backoff = 1; self->bufSize = chunkSize > 0 ? chunkSize : DEF_CHAIN_BUFFER_SIZE; nchunks = queueCapacity / self->bufSize; self->unallocatedBuffers = (nchunks > 2) ? nchunks : 2; /* at least two chunks */ logdebug ("%s: Buffer size %dB (%d chunks of %dB)\n", self->outStream->dest, self->unallocatedBuffers*self->bufSize, self->unallocatedBuffers, self->bufSize); if(NULL == (self->writerChunk = self->nextReaderChunk = self->firstChunk = createBufferChunk(self))) { oml_free(self); self = NULL; } else if(NULL == (self->meta_buf = mbuf_create())) { destroyBufferChain(self); oml_free(self); self = NULL; } else if(NULL == (self->read_buf = mbuf_create())) { destroyBufferChain(self); oml_free(self); self = NULL; } else { /* Initialize mutex and condition variable objects */ pthread_cond_init(&self->semaphore, NULL); pthread_mutex_init(&self->lock, NULL); logdebug3("%s: initialised mutex %p\n", self->outStream->dest, &self->lock); pthread_mutex_init(&self->meta_lock, NULL); logdebug3("%s: initialised mutex %p\n", self->outStream->dest, &self->meta_lock); /* Initialize and set thread detached attribute */ pthread_attr_t tattr; pthread_attr_init(&tattr); pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE); self->active = 1; pthread_create(&self->readerThread, &tattr, bufferedWriterThread, (void*)self); } } return (BufferedWriter*)self; }
/** Initialise a BufferChunk for a BufferedWriter. * * \warning A lock on the BufferedWriter should be held if the readerThread * has already been started. * * \param self BufferedWriter pointer * \return a pointer to the newly-created BufferChunk, or NULL on error */ BufferChunk* createBufferChunk(BufferedWriter* self) { size_t initsize = 0.1 * self->bufSize; MBuffer* buf = mbuf_create2(self->bufSize, initsize); if (buf == NULL) { return NULL; } BufferChunk* chunk = (BufferChunk*)oml_malloc(sizeof(BufferChunk)); if (chunk == NULL) { mbuf_destroy(buf); return NULL; } memset(chunk, 0, sizeof(BufferChunk)); // set state chunk->mbuf = buf; chunk->targetBufSize = self->bufSize; chunk->next = chunk; pthread_mutex_init(&chunk->lock, NULL); logdebug3("%s: initialised chunk mutex %p\n", self->outStream->dest, &chunk->lock); self->unallocatedBuffers--; logdebug("Allocated chunk of size %dB (up to %d), %d remaining\n", initsize, self->bufSize, self->unallocatedBuffers); return chunk; }
/** Unock a mutex * \param mutexP pointer to mutex to unlock * \param mutexName name of mutex; used for error reporting only * \see oml_lock */ void oml_unlock(pthread_mutex_t* mutexP, const char* mutexName) { if (mutexP) { logdebug3("%s unlocking %p\n", mutexName, mutexP); if (pthread_mutex_unlock(mutexP)) { logwarn("%s: Couldn't unlock mutex (%s)\n", mutexName, strerror(errno)); } } }
/** Lock a mutex * \param mutexP pointer to mutex lock * \param mutexName name of mutex; used for error reporting only * \return 0 if successful, -1 otherwise * \see oml_unlock */ int oml_lock(pthread_mutex_t* mutexP, const char* mutexName) { logdebug3("%s trying to lock %p\n", mutexName, mutexP); if (mutexP) { if (pthread_mutex_lock(mutexP)) { logwarn("%s: Couldn't get mutex lock (%s)\n", mutexName, strerror(errno)); return -1; } } return 0; }
/** Marshal a single OmlValueU of type OmlValueT into mbuf. * * Usually called by marshal_values(). On failure, the whole message writing is * reset using mbuf_reset_write(), and marshalling should restart with * marshal_init(), after the MBuffer has been adequately resized or repacked. * * \param mbuf MBuffer to write marshalled data to * \param val_type OmlValueT representing the type of val * \param val pointer to OmlValueU, of type val_type, to marshall * \return 1 on success, or 0 otherwise (marshalling should then restart from marshal_init()) * \see marshal_values, marshal_init, mbuf_reset_write, mbuf_repack_message, mbuf_repack_message2, mbuf_resize */ int marshal_value(MBuffer* mbuf, OmlValueT val_type, OmlValueU* val) { switch (val_type) { case OML_LONG_VALUE: { long v = oml_value_clamp_long (omlc_get_long(*val)); uint32_t uv = (uint32_t)v; uint32_t nv = htonl(uv); uint8_t buf[LONG_T_SIZE+1]; buf[0] = LONG_T; memcpy(&buf[1], &nv, sizeof (nv)); logdebug3("Marshalling long %ld\n", nv); int result = mbuf_write (mbuf, buf, LENGTH (buf)); if (result == -1) { logerror("Failed to marshal OML_LONG_VALUE (mbuf_write())\n"); mbuf_reset_write (mbuf); return 0; } break; } case OML_INT32_VALUE: case OML_UINT32_VALUE: case OML_INT64_VALUE: case OML_UINT64_VALUE: { uint8_t buf[UINT64_T_SIZE+1]; // Max integer size uint32_t uv32; uint32_t nv32; uint64_t uv64; uint64_t nv64; uint8_t *p_nv; if (oml_size_map[val_type] == 4) { uv32 = omlc_get_uint32(*val); nv32 = htonl(uv32); p_nv = (uint8_t*)&nv32; logdebug3("Marshalling %s %" PRIu32 "\n", oml_type_to_s(val_type), uv32); } else { uv64 = omlc_get_uint64(*val); nv64 = htonll(uv64); p_nv = (uint8_t*)&nv64; logdebug3("Marshalling %s %" PRIu64 "\n", oml_type_to_s(val_type), uv64); } buf[0] = oml_type_map[val_type]; memcpy(&buf[1], p_nv, oml_size_map[val_type]); int result = mbuf_write (mbuf, buf, oml_size_map[val_type] + 1); if (result == -1) { logerror("Failed to marshal %s value (mbuf_write())\n", oml_type_to_s (val_type)); mbuf_reset_write (mbuf); return 0; } break; } case OML_DOUBLE_VALUE: { uint8_t type = DOUBLE_T; double v = omlc_get_double(*val); int exp; double mant = frexp(v, &exp); int8_t nexp = (int8_t)exp; logdebug3("Marshalling double %f\n", v); if (isnan(v)) { type = DOUBLE_NAN; nexp = 0; mant = 0; } else if (nexp != exp) { logerror("Double number '%lf' is out of bounds, sending NaN\n", v); type = DOUBLE_NAN; nexp = 0; mant = 0; } int32_t imant = (int32_t)(mant * (1 << BIG_L)); uint32_t nmant = htonl(imant); uint8_t buf[6] = { type, 0, 0, 0, 0, nexp }; memcpy(&buf[1], &nmant, sizeof (nmant)); int result = mbuf_write (mbuf, buf, LENGTH (buf)); if (result == -1) { logerror("Failed to marshal OML_DOUBLE_VALUE (mbuf_write())\n"); mbuf_reset_write (mbuf); return 0; } break; } case OML_STRING_VALUE: { char* str = omlc_get_string_ptr(*val); if (str == NULL) { str = ""; logdebug("Attempting to send a NULL string; sending empty string instead\n"); } size_t len = strlen(str); if (len > STRING_T_MAX_SIZE) { logerror("Truncated string '%s'\n", str); len = STRING_T_MAX_SIZE; } logdebug3("Marshalling string '%s' of length %d\n", str, len); uint8_t buf[2] = { STRING_T, (uint8_t)(len & 0xff) }; int result = mbuf_write (mbuf, buf, LENGTH (buf)); if (result == -1) { logerror("Failed to marshal OML_STRING_VALUE type and length (mbuf_write())\n"); mbuf_reset_write (mbuf); return 0; } result = mbuf_write (mbuf, (uint8_t*)str, len); if (result == -1) { logerror("Failed to marshal OML_STRING_VALUE (mbuf_write())\n"); mbuf_reset_write (mbuf); return 0; } break; } case OML_BLOB_VALUE: { int result = 0; void *blob = omlc_get_blob_ptr(*val); size_t length = omlc_get_blob_length(*val); if (blob == NULL || length == 0) { logdebug ("Attempting to send NULL or empty blob; blob of length 0 will be sent\n"); length = 0; } uint8_t buf[5] = { BLOB_T, 0, 0, 0, 0 }; size_t n_length = htonl (length); memcpy (&buf[1], &n_length, 4); logdebug3("Marshalling blob of size %d\n", length); result = mbuf_write (mbuf, buf, sizeof (buf)); if (result == -1) { logerror ("Failed to marshall OML_BLOB_VALUE type and length (mbuf_write())\n"); mbuf_reset_write (mbuf); return 0; } result = mbuf_write (mbuf, blob, length); if (result == -1) { logerror ("Failed to marshall %d bytes of OML_BLOB_VALUE data\n", length); mbuf_reset_write (mbuf); return 0; } break; } case OML_GUID_VALUE: { /* FIXME: Wrap with UINT64 marshalling, just change the type */ uint64_t nv64; uint8_t buf[GUID_T_SIZE+1]; buf[0] = GUID_T; nv64 = htonll(omlc_get_guid(*val)); memcpy(&buf[1], &nv64, sizeof(nv64)); logdebug3("Marshalling GUID %" PRIu64 "\n", nv64); if (-1 == mbuf_write(mbuf, buf, LENGTH(buf))) { logerror("Failed to marshal OML_GUID_VALUE (mbuf_write())\n"); mbuf_reset_write(mbuf); return 0; } break; } case OML_BOOL_VALUE: { uint8_t buf; if (!omlc_get_bool(*val)) { buf = BOOL_FALSE_T; } else { buf = BOOL_TRUE_T; } logdebug3("Marshalling boolean %d\n", BOOL_TRUE_T == buf); if (-1 == mbuf_write(mbuf, &buf, 1)) { logerror("Failed to marshal OML_BOOL_VALUE (mbuf_write())\n"); mbuf_reset_write(mbuf); return 0; } break; } case OML_VECTOR_INT32_VALUE: case OML_VECTOR_UINT32_VALUE: { size_t i; uint8_t buf[VECTOR_T_SIZE] = { VECTOR_T, 0, 0, 0 }; uint16_t hn = omlc_get_vector_nof_elts(*val); uint16_t nn = htons(hn); buf[1] = vector_protocol_map[val_type]; memcpy(&buf[2], &nn, sizeof(nn)); if(mbuf_write(mbuf, buf, VECTOR_T_SIZE) == 0) { uint32_t elts[hn]; uint32_t *v = omlc_get_vector_ptr(*val); for(i = 0; i < hn; i++) elts[i] = htonl(*((uint32_t*)(v+i))); if(mbuf_write(mbuf, (const uint8_t*)(elts), sizeof(elts)) == -1) { logerror("%s(): failed to marshal %s of size %" PRIu16 " (mbuf_write())\n", __func__, oml_type_to_s(val_type), hn); mbuf_reset_write(mbuf); return 0; } } else { logerror("%s(): failed to marshal %s length (mbuf_write())\n", __func__, oml_type_to_s(val_type)); mbuf_reset_write(mbuf); return 0; } break; } case OML_VECTOR_INT64_VALUE: case OML_VECTOR_UINT64_VALUE: case OML_VECTOR_DOUBLE_VALUE: { size_t i; uint8_t buf[VECTOR_T_SIZE] = { VECTOR_T, 0, 0, 0 }; uint16_t hn = omlc_get_vector_nof_elts(*val); uint16_t nn = htons(hn); buf[1] = vector_protocol_map[val_type]; memcpy(&buf[2], &nn, sizeof(nn)); if(mbuf_write(mbuf, buf, VECTOR_T_SIZE) == 0) { uint64_t elts[hn]; uint64_t *v = omlc_get_vector_ptr(*val); for(i = 0; i < hn; i++) elts[i] = htonll(*((uint64_t*)(v+i))); if(mbuf_write(mbuf, (const uint8_t*)(elts), sizeof(elts)) == -1) { logerror("%s(): failed to marshal %s of size %" PRIu16 " (mbuf_write())\n", __func__, oml_type_to_s(val_type), hn); mbuf_reset_write(mbuf); return 0; } } else { logerror("%s(): failed to marshal %s length (mbuf_write())\n", __func__, oml_type_to_s(val_type)); mbuf_reset_write(mbuf); return 0; } break; } case OML_VECTOR_BOOL_VALUE: { size_t i; uint8_t buf[VECTOR_T_SIZE] = { VECTOR_T, 0, 0, 0 }; uint16_t hn = omlc_get_vector_nof_elts(*val); uint16_t nn = htons(hn); buf[1] = vector_protocol_map[val_type]; memcpy(&buf[2], &nn, sizeof(nn)); if(mbuf_write(mbuf, buf, VECTOR_T_SIZE) == 0) { uint8_t elts[hn]; bool *v = omlc_get_vector_ptr(*val); for(i = 0; i < hn; i++) elts[i] = v[i] ? BOOL_TRUE_T : BOOL_FALSE_T; if(mbuf_write(mbuf, (const uint8_t*)(elts), hn) == -1) { logerror("%s(): failed to marshal %s of size %" PRIu16 " (mbuf_write())\n", __func__, oml_type_to_s(val_type), hn); mbuf_reset_write(mbuf); return 0; } } else { logerror("%s(): failed to marshal %s length (mbuf_write())\n", __func__, oml_type_to_s(val_type)); mbuf_reset_write(mbuf); return 0; } break; } default: logerror("%s(): Unsupported value type '%d'\n", __func__, val_type); return 0; } return 1; }
/** Unmarshals the next content of an MBuffer into a OmlValue * * \param mbuf MBuffer to read from * \param value pointer to OmlValue to unmarshall the read data into * \return 1 if successful, 0 otherwise */ int unmarshal_value(MBuffer *mbuf, OmlValue *value) { if (mbuf_rd_remaining(mbuf) == 0) { logerror("Tried to unmarshal a value from the buffer, but didn't receive enough data to do that\n"); return 0; } int type = mbuf_read_byte (mbuf); if (type == -1) return 0; switch (type) { case LONG_T: { uint8_t buf [LONG_T_SIZE]; if (mbuf_read (mbuf, buf, LENGTH (buf)) == -1) { logerror("Failed to unmarshal OML_LONG_VALUE; not enough data?\n"); return 0; } uint32_t hv = ntohl(*((uint32_t*)buf)); int32_t v = (int32_t)(hv); /* * The server no longer needs to know about OML_LONG_VALUE, as the * marshalling process now maps OML_LONG_VALUE into OML_INT32_VALUE * (by truncating to [INT_MIN, INT_MAX]. Therefore, unmarshall a * LONG_T value into an OML_INT32_VALUE object. */ oml_value_set_type(value, OML_INT32_VALUE); omlc_set_int32(*oml_value_get_value(value), v); break; } case INT32_T: case UINT32_T: case INT64_T: case UINT64_T: { uint8_t buf [UINT64_T_SIZE]; // Maximum integer size OmlValueT oml_type = protocol_type_map[type]; if (mbuf_read (mbuf, buf, protocol_size_map[type]) == -1) { logerror("Failed to unmarshall %d value; not enough data?\n", type); return 0; } oml_value_set_type(value, oml_type); switch (type) { case INT32_T: omlc_set_int32(*oml_value_get_value(value), ntohl(*((int32_t*)buf))); logdebug3("Unmarshalled %s %" PRId32 "\n", oml_type_to_s(oml_type), omlc_get_int32(*oml_value_get_value(value))); break; case UINT32_T: omlc_set_uint32(*oml_value_get_value(value), ntohl(*((uint32_t*)buf))); logdebug3("Unmarshalled %s %" PRIu32 "\n", oml_type_to_s(oml_type), omlc_get_uint32(*oml_value_get_value(value))); break; case INT64_T: omlc_set_int64(*oml_value_get_value(value), ntohll(*((int64_t*)buf))); logdebug3("Unmarshalled %s %" PRId64 "\n", oml_type_to_s(oml_type), omlc_get_int64(*oml_value_get_value(value))); break; case UINT64_T: omlc_set_uint64(*oml_value_get_value(value), ntohll(*((uint64_t*)buf))); logdebug3("Unmarshalled %s %" PRIu64 "\n", oml_type_to_s(oml_type), omlc_get_uint64(*oml_value_get_value(value))); break; default: logerror("Integer morphed, something magic has just happened\n"); return 0; } break; } case DOUBLE_T: { uint8_t buf [DOUBLE_T_SIZE]; OmlValueT oml_type = protocol_type_map[type]; if (mbuf_read (mbuf, buf, LENGTH (buf)) == -1) { logerror("Failed to unmarshal OML_DOUBLE_VALUE; not enough data?\n"); return 0; } int hmant = (int)ntohl(*((uint32_t*)buf)); double mant = hmant * 1.0 / (1 << BIG_L); int exp = (int8_t) buf[4]; double v = ldexp(mant, exp); oml_value_set_type(value, oml_type); omlc_set_double(*oml_value_get_value(value), v); logdebug3("Unmarshalled double %f\n", omlc_get_double(*oml_value_get_value(value))); break; } case DOUBLE_NAN: { OmlValueT oml_type = protocol_type_map[type]; mbuf_read_skip(mbuf, DOUBLE_T_SIZE); /* The data is irrelevant */ oml_value_set_type(value, oml_type); omlc_set_double(*oml_value_get_value(value), NAN); logdebug("Received NaN\n"); break; } case STRING_T: { int len = 0; uint8_t buf [STRING_T_MAX_SIZE]; len = mbuf_read_byte (mbuf); if (len == -1 || mbuf_read (mbuf, buf, len) == -1) { logerror("Failed to unmarshal OML_STRING_VALUE; not enough data?\n"); return 0; } oml_value_set_type(value, OML_STRING_VALUE); omlc_set_string_copy(*oml_value_get_value(value), buf, len); logdebug3("Unmarshalled string '%s' of length %d\n", omlc_get_string_ptr(*oml_value_get_value(value)), len); break; } case BLOB_T: { uint32_t n_len; if (mbuf_read (mbuf, (uint8_t*)&n_len, 4) == -1) { logerror ("Failed to unmarshal OML_BLOB_VALUE length field; not enough data?\n"); return 0; } size_t len = ntohl (n_len); size_t remaining = mbuf_rd_remaining (mbuf); if (len > remaining) { logerror ("Failed to unmarshal OML_BLOB_VALUE data: not enough data available " "(wanted %d, but only have %d bytes\n", len, remaining); return 0; } void *ptr = mbuf_rdptr (mbuf); oml_value_set_type(value, OML_BLOB_VALUE); omlc_set_blob (*oml_value_get_value(value), ptr, len); /*XXX*/ logdebug3("Unmarshalled blob of size %d\n", len); mbuf_read_skip (mbuf, len); break; } case GUID_T: { uint64_t nv64; uint8_t buf[GUID_T_SIZE]; if(mbuf_read(mbuf, buf, GUID_T_SIZE) == -1) { logerror("Failed to unmarshall OML_GUID_VALUE data; not enough data?\n"); return 0; } memcpy(&nv64, buf, sizeof(nv64)); oml_value_set_type(value, OML_GUID_VALUE); omlc_set_guid(*oml_value_get_value(value), ntohll(nv64)); logdebug3("Unmarshalled GUID %" PRIu64 "\n", omlc_get_guid(*oml_value_get_value(value))); break; } case BOOL_FALSE_T: case BOOL_TRUE_T: oml_value_set_type(value, OML_BOOL_VALUE); omlc_set_bool(*oml_value_get_value(value), (type == BOOL_TRUE_T)?OMLC_BOOL_TRUE:OMLC_BOOL_FALSE); logdebug3("Unmarshalled boolean %d\n", OMLC_BOOL_TRUE == omlc_get_bool(*oml_value_get_value(value))); break; case VECTOR_T: { uint16_t i, nof_elts; int type = mbuf_read_byte(mbuf); if(-1 == type) { logerror("%s(): failed to unmarshall VECTOR_T length\n", __func__); return 0; } if(mbuf_read(mbuf,(uint8_t*)(&nof_elts), sizeof(nof_elts)) == -1) { logerror("%s(): failed to unmarshall VECTOR_T length\n", __func__); return 0; } nof_elts = ntohs(nof_elts); OmlValueT oml_type = vector_type_map[type]; OmlValueU *v = oml_value_get_value(value); switch(type) { case INT32_T: case UINT32_T: { size_t bytes = nof_elts * sizeof(uint32_t); uint32_t *elts = oml_calloc(nof_elts, sizeof(uint32_t)); if(mbuf_read(mbuf, (uint8_t*)(elts), nof_elts * sizeof(uint32_t)) == -1) { logerror("%s(): failed to unmarshall OML_VECTOR_(U)INT32_VALUE\n", __func__); return 0; } for(i = 0; i < nof_elts; i++) elts[i] = ntohl(elts[i]); oml_value_set_type(value, oml_type); omlc_set_vector_ptr(*v, elts); omlc_set_vector_length(*v, bytes); omlc_set_vector_size(*v, bytes); omlc_set_vector_nof_elts(*v, nof_elts); omlc_set_vector_elt_size(*v, sizeof(uint32_t)); break; } case INT64_T: case UINT64_T: case DOUBLE64_T: { size_t bytes = nof_elts * sizeof(uint64_t); uint64_t *elts = oml_calloc(nof_elts, sizeof(uint64_t)); if(mbuf_read(mbuf, (uint8_t*)(elts), nof_elts * sizeof(uint64_t)) == -1) { logerror("%s(): failed to unmarshall OML_VECTOR_(U)INT64_VALUE\n", __func__); return 0; } for(i = 0; i < nof_elts; i++) elts[i] = ntohll(elts[i]); oml_value_set_type(value, oml_type); omlc_set_vector_ptr(*v, elts); omlc_set_vector_length(*v, bytes); omlc_set_vector_size(*v, bytes); omlc_set_vector_nof_elts(*v, nof_elts); omlc_set_vector_elt_size(*v, sizeof(uint64_t)); break; } case BOOL_T: { uint8_t y[nof_elts]; size_t bytes = nof_elts * sizeof(bool); bool *elts = oml_calloc(nof_elts, sizeof(bool)); if(mbuf_read(mbuf, y, nof_elts) == -1) { logerror("%s(): failed to unmarshall OML_VECTOR_BOOL_VALUE\n", __func__); return 0; } for(i = 0; i < nof_elts; i++) elts[i] = ((BOOL_TRUE_T == y[i]) ? true : false); oml_value_set_type(value, oml_type); omlc_set_vector_ptr(*v, elts); omlc_set_vector_length(*v, bytes); omlc_set_vector_size(*v, bytes); omlc_set_vector_nof_elts(*v, nof_elts); omlc_set_vector_elt_size(*v, sizeof(bool)); break; } default: logerror("%s(): bad type for array (t=%d)\n", __func__, type); break; } break; } default: logerror("%s: Unsupported value type '%d'\n", __FUNCTION__, type); return 0; } return 1; }