Using global RX object buffer

This commit is contained in:
Pavel Kirienko
2015-05-29 16:47:02 +03:00
parent d4e49d518a
commit d20f8e7356
7 changed files with 152 additions and 46 deletions
+1
View File
@@ -35,6 +35,7 @@ const int16_t ErrLogic = 10;
const int16_t ErrPassiveMode = 11; ///< Operation not permitted in passive mode
const int16_t ErrTransferTooLong = 12; ///< Transfer of this length cannot be sent with given transfer type
const int16_t ErrInvalidConfiguration = 13;
const int16_t ErrBufferTooSmall = 14; ///< Statically allocated buffer is not large enough
/**
* @}
*/
@@ -40,7 +40,6 @@ class UAVCAN_EXPORT ReceivedDataStructure : public DataType_
{
if (_transfer_ == NULL)
{
UAVCAN_ASSERT(0);
return Ret();
}
return (_transfer_->*Fun)();
@@ -162,23 +161,71 @@ class UAVCAN_EXPORT GenericSubscriber : public GenericSubscriberBase
{ }
};
struct ReceivedDataStructureSpec : public ReceivedDataStructure<DataStruct>
{
using ReceivedDataStructure<DataStruct>::setTransfer;
};
LazyConstructor<TransferForwarder> forwarder_;
ReceivedDataStructureSpec message_;
int checkInit();
bool decodeTransfer(IncomingTransfer& transfer);
void handleIncomingTransfer(IncomingTransfer& transfer);
int genericStart(bool (Dispatcher::*registration_method)(TransferListenerBase*));
/*
* Received object and its allocators
*/
struct ReceivedDataStructureSpec : public ReceivedDataStructure<DataStruct>
{
using ReceivedDataStructure<DataStruct>::setTransfer;
};
class StructBufferStatic
{
ReceivedDataStructureSpec object_;
public:
explicit StructBufferStatic(GenericSubscriber&) { }
ReceivedDataStructureSpec* getObjectPtr() { return &object_; }
};
class StructBufferShared
{
ReceivedDataStructureSpec* object_;
public:
explicit StructBufferShared(GenericSubscriber& owner)
: object_(NULL)
{
IMarshalBuffer* const buf =
owner.getNode().getMarshalBufferProvider().getBuffer(sizeof(ReceivedDataStructureSpec));
if (buf != NULL && buf->getSize() >= sizeof(ReceivedDataStructureSpec))
{
object_ = new (buf->getDataPtr()) ReceivedDataStructureSpec;
}
}
/*
* Destructor MUST NOT be invoked
* TODO explain why
*/
ReceivedDataStructureSpec* getObjectPtr() { return object_; }
};
protected:
/**
* This value defines the threshold for stack allocation. See the typedef below.
*/
enum { MaxObjectSizeForStackAllocation = 80 };
/**
* This type resolves to either type of buffer, depending on the object size:
* - if the object is small, it will be allocated on the stack (StructBufferStatic);
* - if the object is large, it will be allocated in the shared buffer (StructBufferShared).
*/
typedef typename Select<(sizeof(DataStruct) > MaxObjectSizeForStackAllocation),
StructBufferShared,
StructBufferStatic>::Result ReceivedDataStructureBuffer;
explicit GenericSubscriber(INode& node)
: GenericSubscriberBase(node)
{ }
@@ -227,15 +274,6 @@ protected:
}
TransferListenerType* getTransferListener() { return forwarder_; }
/**
* Returns a reference to the temporary storage for decoded received messages.
* Reference to this storage is used as a parameter for subscription callbacks.
* This storage is guaranteed to stay intact after the last message was decoded, i.e.
* the application can use it to access the last received message object.
*/
ReceivedDataStructure<DataStruct>& getReceivedStructStorage() { return message_; }
const ReceivedDataStructure<DataStruct>& getReceivedStructStorage() const { return message_; }
};
// ----------------------------------------------------------------------------
@@ -250,48 +288,76 @@ int GenericSubscriber<DataSpec, DataStruct, TransferListenerType>::checkInit()
{
return 0;
}
/*
* Making sure that the buffer is large enough
* This check MUST be performed BEFORE initialization, otherwise we may encounter some terrible runtime failures.
*/
if (ReceivedDataStructureBuffer(*this).getObjectPtr() == NULL)
{
return -ErrBufferTooSmall;
}
/*
* Initializing the transfer forwarder
*/
GlobalDataTypeRegistry::instance().freeze();
const DataTypeDescriptor* const descr =
GlobalDataTypeRegistry::instance().find(DataTypeKind(DataSpec::DataTypeKind), DataSpec::getDataTypeFullName());
if (!descr)
if (descr == NULL)
{
UAVCAN_TRACE("GenericSubscriber", "Type [%s] is not registered", DataSpec::getDataTypeFullName());
return -ErrUnknownDataType;
}
forwarder_.template construct<SelfType&, const DataTypeDescriptor&, IPoolAllocator&>
(*this, *descr, node_.getAllocator());
return 0;
}
template <typename DataSpec, typename DataStruct, typename TransferListenerType>
bool GenericSubscriber<DataSpec, DataStruct, TransferListenerType>::decodeTransfer(IncomingTransfer& transfer)
void GenericSubscriber<DataSpec, DataStruct, TransferListenerType>::handleIncomingTransfer(IncomingTransfer& transfer)
{
/*
* Initializing the temporary RX structure
*/
ReceivedDataStructureBuffer rx_struct_buffer(*this);
if (rx_struct_buffer.getObjectPtr() == NULL)
{
UAVCAN_ASSERT(0); // The init method should have caught this error.
failure_count_++;
node_.getDispatcher().getTransferPerfCounter().addError();
return;
}
rx_struct_buffer.getObjectPtr()->setTransfer(&transfer);
/*
* Decoding into the temporary storage
*/
BitStream bitstream(transfer);
ScalarCodec codec(bitstream);
message_.setTransfer(&transfer);
const int decode_res = DataStruct::decode(*rx_struct_buffer.getObjectPtr(), codec);
const int decode_res = DataStruct::decode(message_, codec);
// We don't need the data anymore, the memory can be reused from the callback:
transfer.release();
if (decode_res <= 0)
{
UAVCAN_TRACE("GenericSubscriber", "Unable to decode the message [%i] [%s]",
decode_res, DataSpec::getDataTypeFullName());
failure_count_++;
node_.getDispatcher().getTransferPerfCounter().addError();
return false;
return;
}
return true;
}
template <typename DataSpec, typename DataStruct, typename TransferListenerType>
void GenericSubscriber<DataSpec, DataStruct, TransferListenerType>::handleIncomingTransfer(IncomingTransfer& transfer)
{
if (decodeTransfer(transfer))
{
handleReceivedDataStruct(message_);
}
/*
* Invoking the callback
*/
handleReceivedDataStruct(*rx_struct_buffer.getObjectPtr());
}
template <typename DataSpec, typename DataStruct, typename TransferListenerType>
@@ -17,9 +17,11 @@ namespace uavcan
class UAVCAN_EXPORT IMarshalBuffer : public ITransferBuffer
{
public:
virtual const uint8_t* getDataPtr() const = 0;
virtual uint8_t* getDataPtr() = 0;
virtual unsigned getMaxWritePos() const = 0;
virtual unsigned getSize() const = 0;
const uint8_t* getDataPtr() const { return const_cast<IMarshalBuffer*>(this)->getDataPtr(); }
};
/**
@@ -62,7 +64,7 @@ class UAVCAN_EXPORT MarshalBufferProvider : public IMarshalBufferProvider
return buf_.write(offset, data, len);
}
virtual const uint8_t* getDataPtr() const { return buf_.getRawPtr(); }
virtual uint8_t* getDataPtr() { return buf_.getRawPtr(); }
virtual unsigned getMaxWritePos() const { return buf_.getMaxWritePos(); }
@@ -61,7 +61,7 @@ struct ServiceCallID
* Note that application ALWAYS gets this result, even when it times out or fails because of some other reason.
*/
template <typename DataType>
class UAVCAN_EXPORT ServiceCallResult
class UAVCAN_EXPORT ServiceCallResult : Noncopyable
{
public:
typedef ReceivedDataStructure<typename DataType::Response> ResponseFieldType;
@@ -257,8 +257,16 @@ private:
UAVCAN_TRACE("ServiceClient::TimeoutCallbackCaller", "Timeout from nid=%d, tid=%d, dtname=%s",
int(state.getCallID().server_node_id.get()), int(state.getCallID().transfer_id.get()),
DataType::getDataTypeFullName());
typename SubscriberType::ReceivedDataStructureBuffer rx_struct_buffer(owner);
if (rx_struct_buffer.getObjectPtr() == NULL)
{
handleFatalError("RX obj buf");
}
ServiceCallResultType result(ServiceCallResultType::ErrorTimeout, state.getCallID(),
owner.getReceivedStructStorage()); // Mutable!
*rx_struct_buffer.getObjectPtr()); // Mutable!
owner.invokeCallback(result);
}
}
+3 -4
View File
@@ -178,8 +178,7 @@ TEST(ServiceClient, Basic)
ASSERT_EQ(0, nodes.b.getDispatcher().getNumServiceResponseListeners()); // Third has timed out :(
// Validating
// We use expected response instead of empty response because the last valid will be reused on fauilure
ASSERT_TRUE(handler.match(ResultType::ErrorTimeout, 99, expected_response));
ASSERT_TRUE(handler.match(ResultType::ErrorTimeout, 99, root_ns_a::StringService::Response()));
// Stray request
ASSERT_LT(0, client3.call(99, request)); // Will timeout!
@@ -341,7 +340,7 @@ TEST(ServiceClient, ConcurrentCalls)
ASSERT_EQ(1, client.getNumPendingCalls()); // One dropped
ASSERT_EQ(1, nodes.b.getDispatcher().getNumServiceResponseListeners()); // Still listening
ASSERT_TRUE(handler.match(ResultType::ErrorTimeout, 88, last_response));
ASSERT_TRUE(handler.match(ResultType::ErrorTimeout, 88, root_ns_a::StringService::Response()));
/*
* Validating the 500 ms timeout
@@ -352,7 +351,7 @@ TEST(ServiceClient, ConcurrentCalls)
ASSERT_EQ(0, client.getNumPendingCalls()); // All finished
ASSERT_EQ(0, nodes.b.getDispatcher().getNumServiceResponseListeners()); // Not listening
ASSERT_TRUE(handler.match(ResultType::ErrorTimeout, 99, last_response));
ASSERT_TRUE(handler.match(ResultType::ErrorTimeout, 99, root_ns_a::StringService::Response()));
}
@@ -19,7 +19,8 @@ using uavcan::DefaultDataTypeRegistrator;
using uavcan::MonotonicDuration;
template <typename DataType>
static bool validateDataTypeInfoResponse(const std::auto_ptr<ServiceCallResult<GetDataTypeInfo> >& resp, unsigned mask)
static bool validateDataTypeInfoResponse(const std::auto_ptr<ServiceCallResultCollector<GetDataTypeInfo>::Result>& resp,
unsigned mask)
{
if (!resp.get())
{
+34 -5
View File
@@ -51,18 +51,47 @@ struct SubscriberWithCollector
template <typename DataType>
class ServiceCallResultCollector : uavcan::Noncopyable
{
typedef uavcan::ServiceCallResult<DataType> ResultType;
typedef uavcan::ServiceCallResult<DataType> ServiceCallResult;
void handler(const ResultType& result)
public:
class Result
{
this->result.reset(new ResultType(result));
const typename ServiceCallResult::Status status_;
uavcan::ServiceCallID call_id_;
typename DataType::Response response_;
public:
Result(typename ServiceCallResult::Status arg_status,
uavcan::ServiceCallID arg_call_id,
const typename DataType::Response& arg_response)
: status_(arg_status)
, call_id_(arg_call_id)
, response_(arg_response)
{ }
bool isSuccessful() const { return status_ == ServiceCallResult::Success; }
typename ServiceCallResult::Status getStatus() const { return status_; }
uavcan::ServiceCallID getCallID() const { return call_id_; }
const typename DataType::Response& getResponse() const { return response_; }
typename DataType::Response& getResponse() { return response_; }
};
private:
void handler(const uavcan::ServiceCallResult<DataType>& tmp_result)
{
std::cout << tmp_result << std::endl;
result.reset(new Result(tmp_result.getStatus(), tmp_result.getCallID(), tmp_result.getResponse()));
}
public:
std::auto_ptr<ResultType> result;
std::auto_ptr<Result> result;
typedef uavcan::MethodBinder<ServiceCallResultCollector*,
void (ServiceCallResultCollector::*)(const ResultType&)> Binder;
void (ServiceCallResultCollector::*)(const uavcan::ServiceCallResult<DataType>&)>
Binder;
Binder bind() { return Binder(this, &ServiceCallResultCollector::handler); }
};