Fixed wrong handling of shared messages

The shared reference count was not shared but copied. msg_t cannot
store the refcnt itsef but has to store a pointer to an externally
allocated (shared) refcnter. The changes to lmsg are reverted to
use content_t again. Howver, this introduces an allocation in v2_decoder
when creating the message which can be avoided. When allocating the reception
buffer, space is allocated for the maximum number of reference counts
(8192 / max_vsm_size = 8192/64 = 128 zmq:atomic_counter objects). This
increases the buffer by 128*sizeof(atomic_counter) = 128*4 = 512 bytes only.
When creating a message, the refcnt member is set to the address of one of the
pre-allocated atomic_counter_t objects. To do so, a new msg_t type zcmsg
is introduced because msg::copy must discriminate between the message types
when releasing memory.
This commit is contained in:
Jens Auer
2015-06-13 17:30:36 +02:00
parent dfe1908008
commit 51cb57e2c9
5 changed files with 235 additions and 113 deletions

View File

@@ -29,6 +29,7 @@
#include <stdlib.h>
#include <string.h>
#include <cmath>
#include "platform.hpp"
#ifdef ZMQ_HAVE_WINDOWS
@@ -44,7 +45,8 @@
zmq::shared_message_memory_allocator::shared_message_memory_allocator(size_t bufsize_):
buf(NULL),
bufsize( 0 ),
maxsize( bufsize_ )
maxsize( bufsize_ ),
msg_refcnt( NULL )
{
}
@@ -67,9 +69,14 @@ unsigned char* zmq::shared_message_memory_allocator::allocate()
// @todo aligmnet padding may be needed
if (!buf)
{
buf = (unsigned char *) malloc(bufsize + sizeof(zmq::atomic_counter_t));
// allocate memory for reference counters together with reception buffer
size_t const maxCounters = std::ceil( (double)max_size / (double)msg_t::max_vsm_size);
size_t const allocationsize = maxsize + sizeof(zmq::atomic_counter_t) + maxCounters * sizeof(zmq::atomic_counter_t);
buf = (unsigned char *) malloc(allocationsize);
alloc_assert (buf);
new(buf) atomic_counter_t(1);
msg_refcnt = reinterpret_cast<zmq::atomic_counter_t*>( buf + sizeof(atomic_counter_t) + maxsize );
}
bufsize = maxsize;
@@ -81,6 +88,7 @@ void zmq::shared_message_memory_allocator::deallocate()
free(buf);
buf = NULL;
bufsize = 0;
msg_refcnt = NULL;
}
unsigned char* zmq::shared_message_memory_allocator::release()
@@ -88,16 +96,11 @@ unsigned char* zmq::shared_message_memory_allocator::release()
unsigned char* b = buf;
buf = NULL;
bufsize = 0;
msg_refcnt = NULL;
return b;
}
void zmq::shared_message_memory_allocator::reset(unsigned char* b)
{
deallocate();
buf = b;
}
void zmq::shared_message_memory_allocator::inc_ref()
{
((zmq::atomic_counter_t*)buf)->add(1);
@@ -204,6 +207,7 @@ int zmq::v2_decoder_t::size_ready(uint64_t msg_size, unsigned char const* read_p
// the current message can exceed the current buffer. We have to copy the buffer
// data into a new message and complete it in the next receive.
if (unlikely ((unsigned char*)read_pos + msg_size > (data() + size())))
{
// a new message has started, but the size would exceed the pre-allocated arena
@@ -214,12 +218,13 @@ int zmq::v2_decoder_t::size_ready(uint64_t msg_size, unsigned char const* read_p
{
// construct message using n bytes from the buffer as storage
// increase buffer ref count
rc = in_progress.init( (unsigned char*)read_pos,
msg_size, shared_message_memory_allocator::call_dec_ref,
buffer() );
// if the message will be a large message, pass a valid refcnt memory location as well
rc = in_progress.init( (unsigned char*)read_pos, msg_size,
shared_message_memory_allocator::call_dec_ref, buffer(),
create_refcnt() );
// For small messages, data has been copied and refcount does not have to be increased
if (in_progress.is_lmsg())
if (in_progress.is_zcmsg())
{
inc_ref();
}