int run_test(int argc, ACE_TCHAR *argv[]) { u_long mask = ACE_LOG_MSG->priority_mask(ACE_Log_Msg::PROCESS); ACE_LOG_MSG->priority_mask(mask | LM_TRACE | LM_DEBUG, ACE_Log_Msg::PROCESS); ACE_DEBUG((LM_DEBUG,"(%P|%t) write data container test start\n")); parse_args (argc, argv); try { TestParticipantImpl* tpi = new TestParticipantImpl(); DDS_TEST* test = new DDS_TEST(); ::DDS::DataWriterQos dw_qos; test->get_default_datawriter_qos(dw_qos); try { // the real testing. { //Test Case 1 scope ACE_DEBUG((LM_INFO, ACE_TEXT("\n\n==== TEST case 1 : Reliable, Keep All, max_samples_per_instance = 2, max samples = 3.\n") ACE_TEXT("Single instance: Should block on third obtain buffer due to max_samples_per_instance\n") ACE_TEXT("===============================================\n"))); dw_qos.history.kind = DDS::KEEP_ALL_HISTORY_QOS; dw_qos.history.depth = ::DDS::LENGTH_UNLIMITED; dw_qos.resource_limits.max_samples_per_instance = MAX_SAMPLES_PER_INSTANCE; dw_qos.resource_limits.max_samples = MAX_SAMPLES; Test::SimpleDataWriterImpl* fast_dw = new Test::SimpleDataWriterImpl(); test->substitute_dw_particpant(fast_dw, tpi); WriteDataContainer* test_data_container = test->get_test_data_container(dw_qos, fast_dw); test->log_dw_qos_limits(dw_qos); test->log_perceived_qos_limits(test_data_container); test->log_send_state_lists("Initial Setup:", test_data_container); Test::Simple foo1; foo1.key = 1; foo1.count = 1; ACE_Message_Block* mb = test->dds_marshal(fast_dw, foo1, OpenDDS::DCPS::KEY_ONLY_MARSHALING); ::DDS::InstanceHandle_t handle1 = DDS::HANDLE_NIL; DDS::ReturnCode_t retval = test_data_container->register_instance(handle1, mb); test->log_send_state_lists("After registering instance 1", test_data_container); if (retval != DDS::RETCODE_OK) { ACE_ERROR((LM_ERROR, "register instance failed\n")); } DDS::ReturnCode_t ret; //obtain the WriteDataContainer's lock as would be normal //when obtained by the datawriter during a write before accessing //the WriteDataContainer ACE_GUARD_RETURN (ACE_Recursive_Thread_Mutex, guard, test->lock_wdc(test_data_container), ::DDS::RETCODE_ERROR); DataSampleElement* element_0 = 0; ret = test_data_container->obtain_buffer(element_0, handle1); test->log_send_state_lists("After obtain buffer", test_data_container); if (ret != DDS::RETCODE_OK) { ACE_ERROR((LM_ERROR, "obtain buffer failed for element 0\n")); } element_0->set_sample(mb); if (ret != DDS::RETCODE_OK) { ACE_ERROR((LM_ERROR, "failed to write element 0\n")); return ret; } ret = test_data_container->enqueue(element_0, handle1); test->log_send_state_lists("After enqueue", test_data_container); SendStateDataSampleList temp; test_data_container->get_unsent_data(temp); test->log_send_state_lists("After get_unsent_data", test_data_container); if (ret != DDS::RETCODE_OK) { ACE_ERROR((LM_ERROR, "failed to enqueue element 0\n")); } ACE_Message_Block* mb1 = test->dds_marshal(fast_dw, foo1, OpenDDS::DCPS::KEY_ONLY_MARSHALING); DataSampleElement* element_1 = 0; ret = test_data_container->obtain_buffer(element_1, handle1); test->log_send_state_lists("After obtain buffer", test_data_container); if (ret != DDS::RETCODE_OK) { ACE_ERROR((LM_ERROR, "obtain buffer failed for element 1\n")); } element_1->set_sample(mb1); if (ret != DDS::RETCODE_OK) { return ret; } ret = test_data_container->enqueue(element_1, handle1); test->log_send_state_lists("After enqueue", test_data_container); temp.reset(); test_data_container->get_unsent_data(temp); test->log_send_state_lists("After get_unsent_data", test_data_container); if (ret != DDS::RETCODE_OK) { ACE_ERROR((LM_ERROR, "failed to enqueue element 1\n")); } DataSampleElement* element_2 = 0; ret = test_data_container->obtain_buffer(element_2, handle1); test->log_send_state_lists("After obtain buffer which should block", test_data_container); TEST_ASSERT(errno == ETIME); test->log_send_state_lists("After TEST_ASSERT timeout", test_data_container); test_data_container->unregister_all(); guard.release(); delete test_data_container; delete fast_dw; } //End Test Case 1 scope { //Test Case 2 scope //===================================================== ACE_DEBUG((LM_INFO, ACE_TEXT("\n\n==== TEST case 2 : Reliable, Keep All, max samples = 2.\n") ACE_TEXT("Write 1 sample ea. on 3 instances: Should block on third obtain buffer due to max_samples\n") ACE_TEXT("===============================================\n"))); test->get_default_datawriter_qos(dw_qos); dw_qos.history.kind = ::DDS::KEEP_ALL_HISTORY_QOS; dw_qos.history.depth = ::DDS::LENGTH_UNLIMITED; dw_qos.resource_limits.max_samples = 2; Test::SimpleDataWriterImpl* fast_dw = new Test::SimpleDataWriterImpl(); test->substitute_dw_particpant(fast_dw, tpi); WriteDataContainer* test_data_container = test->get_test_data_container(dw_qos, fast_dw); test->log_dw_qos_limits(dw_qos); test->log_perceived_qos_limits(test_data_container); test->log_send_state_lists("Initial Setup:", test_data_container); Test::Simple foo1; foo1.key = 1; foo1.count = 1; ACE_Message_Block* mb1 = test->dds_marshal(fast_dw, foo1, OpenDDS::DCPS::KEY_ONLY_MARSHALING); ::DDS::InstanceHandle_t handle1 = DDS::HANDLE_NIL; DDS::ReturnCode_t retval = test_data_container->register_instance(handle1, mb1); test->log_send_state_lists("After register instance", test_data_container); if (retval != DDS::RETCODE_OK) { ACE_ERROR((LM_ERROR, "register instance failed\n")); } DDS::ReturnCode_t ret; //obtain the WriteDataContainer's lock as would be normal //when obtained by the datawriter during a write before accessing //the WriteDataContainer ACE_GUARD_RETURN (ACE_Recursive_Thread_Mutex, guard, test->lock_wdc(test_data_container), ::DDS::RETCODE_ERROR); DataSampleElement* element_1 = 0; ret = test_data_container->obtain_buffer(element_1, handle1); test->log_send_state_lists("After obtain buffer", test_data_container); if (ret != DDS::RETCODE_OK) { ACE_ERROR((LM_ERROR, "obtain buffer failed for element 1\n")); } element_1->set_sample(mb1); if (ret != DDS::RETCODE_OK) { ACE_ERROR((LM_ERROR, "failed to write element 1\n")); return ret; } ret = test_data_container->enqueue(element_1, handle1); test->log_send_state_lists("After enqueue", test_data_container); SendStateDataSampleList temp; test_data_container->get_unsent_data(temp); test->log_send_state_lists("After get_unsent_data", test_data_container); if (ret != DDS::RETCODE_OK) { ACE_ERROR((LM_ERROR, "failed to enqueue element 1\n")); } Test::Simple foo2; foo2.key = 2; foo2.count = 1; ACE_Message_Block* mb2 = test->dds_marshal(fast_dw, foo2, OpenDDS::DCPS::KEY_ONLY_MARSHALING); ::DDS::InstanceHandle_t handle2 = DDS::HANDLE_NIL; retval = test_data_container->register_instance(handle2, mb2); test->log_send_state_lists("After register instance 2", test_data_container); DataSampleElement* element_2 = 0; ret = test_data_container->obtain_buffer(element_2, handle2); test->log_send_state_lists("After obtain buffer", test_data_container); if (ret != DDS::RETCODE_OK) { ACE_ERROR((LM_ERROR, "obtain buffer failed for element 2\n")); } element_2->set_sample(mb2); if (ret != DDS::RETCODE_OK) { return ret; } ret = test_data_container->enqueue(element_2, handle2); test->log_send_state_lists("After enqueue", test_data_container); temp.reset(); test_data_container->get_unsent_data(temp); test->log_send_state_lists("After get_unsent_data", test_data_container); if (ret != DDS::RETCODE_OK) { ACE_ERROR((LM_ERROR, "failed to enqueue element 2\n")); } Test::Simple foo3; foo3.key = 3; foo3.count = 1; ACE_Message_Block* mb3 = test->dds_marshal(fast_dw, foo3, OpenDDS::DCPS::KEY_ONLY_MARSHALING); ::DDS::InstanceHandle_t handle3 = DDS::HANDLE_NIL; retval = test_data_container->register_instance(handle3, mb3); test->log_send_state_lists("After register instance 3", test_data_container); DataSampleElement* element_3 = 0; ret = test_data_container->obtain_buffer(element_3, handle3); test->log_send_state_lists("After obtain buffer which should block", test_data_container); TEST_ASSERT(errno == ETIME); test->log_send_state_lists("After TEST_ASSERT timeout", test_data_container); test_data_container->unregister_all(); guard.release(); delete test_data_container; delete fast_dw; } //End Test Case 2 scope { //Test Case 3 scope //===================================================== ACE_DEBUG((LM_INFO, ACE_TEXT("\n\n==== TEST case 3 : Reliable, Keep Last (1), max samples = 2.\n") ACE_TEXT("Write 1 sample ea. on 3 instances: Should block on third obtain buffer due to max_samples\n") ACE_TEXT("Detached thread to call data_delivered while fourth obtain buffer attempt is blocked\n") ACE_TEXT("Blocked writer should wakeup and complete write\n") ACE_TEXT("===============================================\n"))); test->get_default_datawriter_qos(dw_qos); dw_qos.resource_limits.max_samples = 2; dw_qos.reliability.max_blocking_time.sec = 2; dw_qos.reliability.max_blocking_time.nanosec = MAX_BLOCKING_TIME_NANO; Test::SimpleDataWriterImpl* fast_dw = new Test::SimpleDataWriterImpl(); test->substitute_dw_particpant(fast_dw, tpi); WriteDataContainer* test_data_container = test->get_test_data_container(dw_qos, fast_dw); test->log_dw_qos_limits(dw_qos); test->log_perceived_qos_limits(test_data_container); test->log_send_state_lists("Initial Setup:", test_data_container); Test::Simple foo1; foo1.key = 1; foo1.count = 1; ACE_Message_Block* mb1 = test->dds_marshal(fast_dw, foo1, OpenDDS::DCPS::KEY_ONLY_MARSHALING); ::DDS::InstanceHandle_t handle1 = DDS::HANDLE_NIL; DDS::ReturnCode_t retval = test_data_container->register_instance(handle1, mb1); test->log_send_state_lists("After register instance", test_data_container); if (retval != DDS::RETCODE_OK) { ACE_ERROR((LM_ERROR, "register instance failed\n")); } DDS::ReturnCode_t ret; //obtain the WriteDataContainer's lock as would be normal //when obtained by the datawriter during a write before accessing //the WriteDataContainer ACE_GUARD_RETURN (ACE_Recursive_Thread_Mutex, guard, test->lock_wdc(test_data_container), ::DDS::RETCODE_ERROR); DataSampleElement* element_1 = 0; ret = test_data_container->obtain_buffer(element_1, handle1); test->log_send_state_lists("After obtain buffer", test_data_container); if (ret != DDS::RETCODE_OK) { ACE_ERROR((LM_ERROR, "obtain buffer failed for element 1\n")); } element_1->set_sample(mb1); if (ret != DDS::RETCODE_OK) { ACE_ERROR((LM_ERROR, "failed to write element 1\n")); return ret; } ret = test_data_container->enqueue(element_1, handle1); test->log_send_state_lists("After enqueue", test_data_container); SendStateDataSampleList temp; test_data_container->get_unsent_data(temp); test->log_send_state_lists("After get_unsent_data", test_data_container); if (ret != DDS::RETCODE_OK) { ACE_ERROR((LM_ERROR, "failed to enqueue element 1\n")); } Test::Simple foo2; foo2.key = 2; foo2.count = 1; ACE_Message_Block* mb2 = test->dds_marshal(fast_dw, foo2, OpenDDS::DCPS::KEY_ONLY_MARSHALING); ::DDS::InstanceHandle_t handle2 = DDS::HANDLE_NIL; retval = test_data_container->register_instance(handle2, mb2); test->log_send_state_lists("After register instance 2", test_data_container); DataSampleElement* element_2 = 0; ret = test_data_container->obtain_buffer(element_2, handle2); test->log_send_state_lists("After obtain buffer", test_data_container); if (ret != DDS::RETCODE_OK) { ACE_ERROR((LM_ERROR, "obtain buffer failed for element 2\n")); } element_2->set_sample(mb2); if (ret != DDS::RETCODE_OK) { return ret; } ret = test_data_container->enqueue(element_2, handle2); test->log_send_state_lists("After enqueue", test_data_container); temp.reset(); test_data_container->get_unsent_data(temp); test->log_send_state_lists("After get_unsent_data", test_data_container); if (ret != DDS::RETCODE_OK) { ACE_ERROR((LM_ERROR, "failed to enqueue element 2\n")); } Test::Simple foo3; foo3.key = 3; foo3.count = 1; ACE_Message_Block* mb3 = test->dds_marshal(fast_dw, foo3, OpenDDS::DCPS::KEY_ONLY_MARSHALING); ::DDS::InstanceHandle_t handle3 = DDS::HANDLE_NIL; retval = test_data_container->register_instance(handle3, mb3); test->log_send_state_lists("After register instance 3", test_data_container); DataSampleElement* element_3_first_time_blocks = 0; ret = test_data_container->obtain_buffer(element_3_first_time_blocks, handle3); test->log_send_state_lists("After 3rd obtain buffer which should block", test_data_container); TEST_ASSERT(errno == ETIME); test->log_send_state_lists("After TEST_ASSERT 3rd obtain_buffer TIMED OUT", test_data_container); // Create reader thread. test->prep_delayed_deliver(test_data_container, element_1); // Create delayed deliver thread to wait then call data_delivered // simulating transport being finished with a sample Delayed_Deliver_Handler ddh (test); ddh.activate(); DataSampleElement* element_3 = 0; test->log_send_state_lists("About to attempt to obtain_buffer for the 4th time", test_data_container); ret = test_data_container->obtain_buffer(element_3, handle3); test->log_send_state_lists("After 4th obtain buffer which should block then wakeup", test_data_container); if (ret != DDS::RETCODE_OK) { ACE_ERROR((LM_ERROR, "obtain buffer failed for element 3\n")); } element_3->set_sample(mb3); if (ret != DDS::RETCODE_OK) { return ret; } ret = test_data_container->enqueue(element_3, handle3); test->log_send_state_lists("After enqueue", test_data_container); temp.reset(); test_data_container->get_unsent_data(temp); test->log_send_state_lists("After get_unsent_data", test_data_container); if (ret != DDS::RETCODE_OK) { ACE_ERROR((LM_ERROR, "failed to enqueue element 3\n")); } TEST_ASSERT(ret == DDS::RETCODE_OK); test->log_send_state_lists("After TEST_ASSERT 4th obtain_buffer successful (block & wakeup)", test_data_container); ddh.wait(); test_data_container->unregister_all(); guard.release(); delete test_data_container; delete fast_dw; } //End Test Case 3 scope { //Test Case 4 scope //===================================================== ACE_DEBUG((LM_INFO, ACE_TEXT("\n\n==== TEST case 4 : Reliable, Keep Last (depth = 2), max samples = 3.\n") ACE_TEXT("Write 3 samples on 1 instance: Should block on third obtain buffer due to depth\n") ACE_TEXT("===============================================\n"))); test->get_default_datawriter_qos(dw_qos); dw_qos.history.depth = 2; dw_qos.resource_limits.max_samples = MAX_SAMPLES; Test::SimpleDataWriterImpl* fast_dw = new Test::SimpleDataWriterImpl(); test->substitute_dw_particpant(fast_dw, tpi); WriteDataContainer* test_data_container = test->get_test_data_container(dw_qos, fast_dw); test->log_dw_qos_limits(dw_qos); test->log_perceived_qos_limits(test_data_container); test->log_send_state_lists("Initial Setup:", test_data_container); Test::Simple foo1; foo1.key = 1; foo1.count = 1; ACE_Message_Block* mb = test->dds_marshal(fast_dw, foo1, OpenDDS::DCPS::KEY_ONLY_MARSHALING); ::DDS::InstanceHandle_t handle1 = DDS::HANDLE_NIL;//fast_dw->register_instance(foo1); DDS::ReturnCode_t retval = test_data_container->register_instance(handle1, mb); test->log_send_state_lists("After register instance", test_data_container); if (retval != DDS::RETCODE_OK) { ACE_ERROR((LM_ERROR, "register instance failed\n")); } DDS::ReturnCode_t ret; //obtain the WriteDataContainer's lock as would be normal //when obtained by the datawriter during a write before accessing //the WriteDataContainer ACE_GUARD_RETURN (ACE_Recursive_Thread_Mutex, guard, test->lock_wdc(test_data_container), ::DDS::RETCODE_ERROR); DataSampleElement* element_0 = 0; ret = test_data_container->obtain_buffer(element_0, handle1); test->log_send_state_lists("After obtain buffer", test_data_container); if (ret != DDS::RETCODE_OK) { ACE_ERROR((LM_ERROR, "obtain buffer failed for element 0\n")); } element_0->set_sample(mb); if (ret != DDS::RETCODE_OK) { ACE_ERROR((LM_ERROR, "failed to write element 0\n")); return ret; } ret = test_data_container->enqueue(element_0, handle1); test->log_send_state_lists("After enqueue", test_data_container); SendStateDataSampleList temp; test_data_container->get_unsent_data(temp); test->log_send_state_lists("After get_unsent_data", test_data_container); if (ret != DDS::RETCODE_OK) { ACE_ERROR((LM_ERROR, "failed to enqueue element 0\n")); } DataSampleElement* element_1 = 0; ret = test_data_container->obtain_buffer(element_1, handle1); test->log_send_state_lists("After obtain buffer", test_data_container); if (ret != DDS::RETCODE_OK) { ACE_ERROR((LM_ERROR, "obtain buffer failed for element 1\n")); } ACE_Message_Block* mb1 = test->dds_marshal(fast_dw, foo1, OpenDDS::DCPS::KEY_ONLY_MARSHALING); element_1->set_sample(mb1); if (ret != DDS::RETCODE_OK) { return ret; } ret = test_data_container->enqueue(element_1, handle1); test->log_send_state_lists("After enqueue", test_data_container); temp.reset(); test_data_container->get_unsent_data(temp); test->log_send_state_lists("After get_unsent_data", test_data_container); if (ret != DDS::RETCODE_OK) { ACE_ERROR((LM_ERROR, "failed to enqueue element 1\n")); } DataSampleElement* element_2 = 0; ret = test_data_container->obtain_buffer(element_2, handle1); test->log_send_state_lists("After obtain buffer which should block", test_data_container); TEST_ASSERT(errno == ETIME); test->log_send_state_lists("After TEST_ASSERT timeout", test_data_container); test_data_container->unregister_all(); guard.release(); delete test_data_container; delete fast_dw; } //End Test Case 4 scope }//test scope catch (const TestException&) { ACE_ERROR ((LM_ERROR, ACE_TEXT("(%P|%t) TestException caught in main.cpp. "))); return 1; } //======== clean up ============ delete test; delete tpi; } //xxx dp::Entity::Object::muxtex_refcount_ = 1 catch (const TestException&) { ACE_ERROR ((LM_ERROR, ACE_TEXT("(%P|%t) TestException caught in main.cpp. "))); return 1; } catch (const CORBA::Exception& ex) { ex._tao_print_exception ("Exception caught in main.cpp:"); return 1; } catch (...) { ACE_ERROR ((LM_ERROR, ACE_TEXT("(%P|%t) unknown exception caught in main.cpp. "))); return 1; } return test_failed; }
int DDS_TEST::test(ACE_TString host, u_short port) { if (host.empty() || port == 0) { std::cerr << "ERROR: -h <host> and -p <port> options are required\n"; return 1; } // 0. initialization ACE_INET_Addr remote_addr(port, host.c_str()); TransportInst_rch inst = TheTransportRegistry->create_inst("my_rtps", "rtps_udp"); RtpsUdpInst* rtps_inst = dynamic_cast<RtpsUdpInst*>(inst.in()); if (!rtps_inst) { std::cerr << "ERROR: Failed to cast to RtpsUdpInst\n"; return 1; } rtps_inst->datalink_release_delay_ = 0; rtps_inst->heartbeat_period_ = ACE_Time_Value(0, 100*1000 /*microseconds*/); TransportConfig_rch cfg = TheTransportRegistry->create_config("cfg"); cfg->instances_.push_back(inst); TheTransportRegistry->global_config(cfg); RepoIdBuilder local; local.federationId(0x01234567); // guidPrefix1 local.participantId(0x89abcdef); // guidPrefix2 local.entityKey(0x012345); local.entityKind(ENTITYKIND_USER_WRITER_WITH_KEY); OpenDDS::RTPS::GUID_t local_guid(local); const OpenDDS::RTPS::GuidPrefix_t& local_prefix = local_guid.guidPrefix; RepoIdBuilder remote; // these values must match what's in subscriber.cpp remote.federationId(0x01234567); // guidPrefix1 remote.participantId(0xefcdab89); // guidPrefix2 remote.entityKey(0x452310); remote.entityKind(ENTITYKIND_USER_READER_WITH_KEY); LocatorSeq locators; locators.length(1); locators[0].kind = (remote_addr.get_type() == AF_INET6) ? LOCATOR_KIND_UDPv6 : LOCATOR_KIND_UDPv4; locators[0].port = port; address_to_bytes(locators[0].address, remote_addr); size_t size_locator = 0, padding_locator = 0; gen_find_size(locators, size_locator, padding_locator); ACE_Message_Block mb_locator(size_locator + padding_locator + 1); Serializer ser_loc(&mb_locator, ACE_CDR_BYTE_ORDER, Serializer::ALIGN_CDR); ser_loc << locators; ser_loc << ACE_OutputCDR::from_boolean(false); // requires inline QoS SimpleDataWriter sdw(local_guid); sdw.enable_transport(true /*reliable*/, false /*durable*/); AssociationData subscription; subscription.remote_id_ = remote; subscription.remote_reliable_ = false; subscription.remote_data_.length(1); subscription.remote_data_[0].transport_type = "rtps_udp"; subscription.remote_data_[0].data.replace( static_cast<CORBA::ULong>(mb_locator.length()), &mb_locator); if (!sdw.init(subscription)) { std::cerr << "publisher TransportClient::associate() failed\n"; return 1; } // 1. send by directly writing an RTPS Message to the socket const Header hdr = { {'R', 'T', 'P', 'S'}, PROTOCOLVERSION, VENDORID_OPENDDS, {local_prefix[0], local_prefix[1], local_prefix[2], local_prefix[3], local_prefix[4], local_prefix[5], local_prefix[6], local_prefix[7], local_prefix[8], local_prefix[9], local_prefix[10], local_prefix[11]} }; const ACE_Time_Value now = ACE_OS::gettimeofday(); log_time(now); const double conv = 4294.967296; // NTP fractional (2^-32) sec per microsec const InfoTimestampSubmessage it = { {INFO_TS, 1, 8}, {static_cast<ACE_CDR::Long>(now.sec()), static_cast<ACE_CDR::ULong>(now.usec() * conv)} }; DataSubmessage ds = { {DATA, 7, 20 + 24 + 12 + sizeof(text)}, 0, 16, ENTITYID_UNKNOWN, local_guid.entityId, {0, 1}, ParameterList() }; TestMsg data; data.key = 0x09230923; data.value = text; ds.inlineQos.length(1); OpenDDS::RTPS::KeyHash_t hash; marshal_key_hash(data, hash); ds.inlineQos[0].key_hash(hash); const ACE_CDR::ULong encap = 0x00000100; // {CDR_LE, options} in BE format size_t size = 0, padding = 0; gen_find_size(hdr, size, padding); gen_find_size(it, size, padding); gen_find_size(ds, size, padding); find_size_ulong(size, padding); gen_find_size(data, size, padding); ACE_Message_Block msg(size + padding); Serializer ser(&msg, host_is_bigendian, Serializer::ALIGN_CDR); bool ok = (ser << hdr) && (ser << it) && (ser << ds) && (ser << encap) && (ser << data); if (!ok) { std::cerr << "ERROR: failed to serialize RTPS message\n"; return 1; } ACE_INET_Addr local_addr; ACE_SOCK_Dgram sock(local_addr); ssize_t res = sock.send(msg.rd_ptr(), msg.length(), remote_addr); if (res < 0) { std::cerr << "ERROR: error in send()\n"; return 1; } else { std::ostringstream oss; oss << "Sent " << res << " bytes.\n"; ACE_DEBUG((LM_INFO, oss.str().c_str())); } // 2a. send control messages through the OpenDDS transport // Send an instance registration { TestMsg control_sample; control_sample.key = 0x04030201; DataSampleHeader dsh; dsh.message_id_ = INSTANCE_REGISTRATION; dsh.sequence_ = SequenceNumber::SEQUENCENUMBER_UNKNOWN(); dsh.publication_id_ = local_guid; dsh.key_fields_only_ = true; // Calculate the data buffer length size = 0; padding = 0; OpenDDS::DCPS::KeyOnly<const TestMsg> ko_instance_data(control_sample); find_size_ulong(size, padding); // encap gen_find_size(ko_instance_data, size, padding); dsh.message_length_ = static_cast<ACE_UINT32>(size + padding); ACE_Message_Block* ir_mb = new ACE_Message_Block(DataSampleHeader::max_marshaled_size(), ACE_Message_Block::MB_DATA, new ACE_Message_Block(dsh.message_length_)); *ir_mb << dsh; OpenDDS::DCPS::Serializer serializer(ir_mb->cont(), host_is_bigendian, Serializer::ALIGN_CDR); ok = (serializer << encap) && (serializer << ko_instance_data); if (!ok) { std::cerr << "ERROR: failed to serialize data for instance registration\n"; return 1; } ::DDS_TEST::force_inline_qos(false); // No inline QoS sdw.send_control(dsh, ir_mb); // Send a dispose instance { dsh.message_id_ = DISPOSE_INSTANCE; ACE_Message_Block* di_mb = new ACE_Message_Block(DataSampleHeader::max_marshaled_size(), ACE_Message_Block::MB_DATA, new ACE_Message_Block(dsh.message_length_)); *di_mb << dsh; OpenDDS::DCPS::Serializer serializer(di_mb->cont(), host_is_bigendian, Serializer::ALIGN_CDR); ok = (serializer << encap) && (serializer << ko_instance_data); if (!ok) { std::cerr << "ERROR: failed to serialize data for dispose instance\n"; return 1; } ::DDS_TEST::force_inline_qos(true); // Inline QoS sdw.inline_qos_mode_ = SimpleDataWriter::PARTIAL_MOD_QOS; sdw.send_control(dsh, di_mb); } // Send an unregister instance { dsh.message_id_ = UNREGISTER_INSTANCE; ACE_Message_Block* ui_mb = new ACE_Message_Block(DataSampleHeader::max_marshaled_size(), ACE_Message_Block::MB_DATA, new ACE_Message_Block(dsh.message_length_)); *ui_mb << dsh; OpenDDS::DCPS::Serializer serializer(ui_mb->cont(), host_is_bigendian, Serializer::ALIGN_CDR); ok = (serializer << encap) && (serializer << ko_instance_data); if (!ok) { std::cerr << "ERROR: failed to serialize data for unregister instance\n"; return 1; } ::DDS_TEST::force_inline_qos(true); // Inline QoS sdw.inline_qos_mode_ = SimpleDataWriter::FULL_MOD_QOS; sdw.send_control(dsh, ui_mb); } // Send a dispose & unregister instance { dsh.message_id_ = DISPOSE_UNREGISTER_INSTANCE; ACE_Message_Block* ui_mb = new ACE_Message_Block(DataSampleHeader::max_marshaled_size(), ACE_Message_Block::MB_DATA, new ACE_Message_Block(dsh.message_length_)); *ui_mb << dsh; OpenDDS::DCPS::Serializer serializer(ui_mb->cont(), host_is_bigendian, Serializer::ALIGN_CDR); ok = (serializer << encap) && (serializer << ko_instance_data); if (!ok) { std::cerr << "ERROR: failed to serialize data for dispose unregister instance\n"; return 1; } ::DDS_TEST::force_inline_qos(true); // Inline QoS sdw.inline_qos_mode_ = SimpleDataWriter::FULL_MOD_QOS; sdw.send_control(dsh, ui_mb); } } // 2b. send sample data through the OpenDDS transport TransportSendElementAllocator alloc(2, sizeof(TransportSendElementAllocator)); DataSampleElement elements[] = { DataSampleElement(local_guid, &sdw, 0, &alloc, 0), // Data Sample DataSampleElement(local_guid, &sdw, 0, &alloc, 0), // Data Sample (key=99 means end) }; SendStateDataSampleList list; list.head_ = elements; list.size_ = sizeof(elements) / sizeof(elements[0]); list.tail_ = &elements[list.size() - 1]; for (int i = 0; i < list.size() - 1; ++i) { DDS_TEST::set_next_send_sample(elements[i], &elements[i + 1]); } // Send a regular data sample int index = 0; DataSampleHeader& dsh = elements[index].header_; dsh.message_id_ = SAMPLE_DATA; dsh.publication_id_ = local_guid; dsh.sequence_ = 3; // test GAP generation const ACE_Time_Value tv = ACE_OS::gettimeofday(); log_time(tv); DDS::Time_t st = time_value_to_time(tv); dsh.source_timestamp_sec_ = st.sec; dsh.source_timestamp_nanosec_ = st.nanosec; // Calculate the data buffer length size = 0; padding = 0; find_size_ulong(size, padding); // encap gen_find_size(data, size, padding); dsh.message_length_ = static_cast<ACE_UINT32>(size + padding); elements[index].sample_ = new ACE_Message_Block(DataSampleHeader::max_marshaled_size(), ACE_Message_Block::MB_DATA, new ACE_Message_Block(dsh.message_length_)); *elements[index].sample_ << dsh; Serializer ser2(elements[index].sample_->cont(), host_is_bigendian, Serializer::ALIGN_CDR); ok = (ser2 << encap) && (ser2 << data); if (!ok) { std::cerr << "ERROR: failed to serialize data for elements[" << index << "]\n"; return 1; } // Send a data sample with a key of 99 to terminate the subscriber index++; DataSampleHeader& dsh2 = elements[index].header_; dsh2.sequence_ = dsh.sequence_+1; dsh2.message_id_ = SAMPLE_DATA; dsh.publication_id_ = local_guid; dsh2.key_fields_only_ = false; const ACE_Time_Value tv2 = ACE_OS::gettimeofday(); log_time(tv2); DDS::Time_t st2 = time_value_to_time(tv2); dsh2.source_timestamp_sec_ = st2.sec; dsh2.source_timestamp_nanosec_ = st2.nanosec; data.key = 99; data.value = ""; // Calculate the data buffer length size = 0; padding = 0; find_size_ulong(size, padding); // encap gen_find_size(data, size, padding); dsh2.message_length_ = static_cast<ACE_UINT32>(size + padding); elements[index].sample_ = new ACE_Message_Block(DataSampleHeader::max_marshaled_size(), ACE_Message_Block::MB_DATA, new ACE_Message_Block(dsh2.message_length_)); *elements[index].sample_ << dsh2; Serializer ser3(elements[index].sample_->cont(), host_is_bigendian, Serializer::ALIGN_CDR); ok = (ser3 << encap) && (ser3 << data.key) && (ser3 << data.value); if (!ok) { std::cerr << "ERROR: failed to serialize data for elements[" << index << "]\n"; return 1; } sdw.callbacks_expected_ = list.size(); ::DDS_TEST::force_inline_qos(true); // Inline QoS sdw.send(list); while (sdw.callbacks_expected_) { ACE_OS::sleep(1); } // Allow enough time for a HEARTBEAT message to be generated ACE_OS::sleep(rtps_inst->heartbeat_period_ * 2.0); // 3. cleanup sdw.disassociate(subscription.remote_id_); TheServiceParticipant->shutdown(); ACE_Thread_Manager::instance()->wait(); return 0; }
bool OpenDDS::DCPS::DataDurabilityCache::insert( DDS::DomainId_t domain_id, char const * topic_name, char const * type_name, SendStateDataSampleList & the_data, DDS::DurabilityServiceQosPolicy const & qos) { if (the_data.size() == 0) return true; // Nothing to cache. // Apply DURABILITY_SERVICE QoS HISTORY and RESOURCE_LIMITS related // settings prior to data insertion into the cache. CORBA::Long const depth = get_instance_sample_list_depth( qos.history_kind, qos.history_depth, qos.max_samples_per_instance); // Iterator to first DataSampleElement to be copied. SendStateDataSampleList::iterator element(the_data.begin()); if (depth < 0) return false; // Should never occur. else if (depth == 0) return true; // Nothing else to do. Discard all data. else if (the_data.size() > depth) { // N.B. Dropping data samples does not take into account // those samples which are not actually persisted (i.e. // samples with the coherent_sample_ flag set). The spec // does not provide any guidance in this case, therefore // we opt for the simplest solution and assume that there // are no change sets when calculating the number of // samples to drop. // Drop "old" samples. Only keep the "depth" most recent // samples, i.e. those found at the tail end of the // SendStateDataSampleList. ssize_t const advance_amount = the_data.size() - depth; std::advance(element, advance_amount); } // ----------- // Copy samples to the domain/topic/type-specific cache. key_type const key(domain_id, topic_name, type_name, this->allocator_.get()); SendStateDataSampleList::iterator the_end(the_data.end()); sample_list_type * sample_list = 0; typedef DurabilityQueue<sample_data_type> data_queue_type; data_queue_type ** slot = 0; data_queue_type * samples = 0; // sample_list_type::value_type using OpenDDS::FileSystemStorage::Directory; using OpenDDS::FileSystemStorage::File; Directory::Ptr dir; std::vector<std::string> path; { ACE_Allocator * const allocator = this->allocator_.get(); ACE_GUARD_RETURN(ACE_SYNCH_MUTEX, guard, this->lock_, false); if (this->kind_ == DDS::PERSISTENT_DURABILITY_QOS) { try { dir = Directory::create(this->data_dir_.c_str()); std::ostringstream oss; oss << domain_id; path.push_back(oss.str()); path.push_back(topic_name); path.push_back(type_name); dir = dir->get_dir(path); // dir is now the "type" directory, which is shared by all datawriters // of the domain/topic/type. We actually need a new directory per // datawriter and this assumes that insert() is called once per // datawriter, as is currently the case. dir = dir->create_next_dir(); path.push_back(dir->name()); // for use by the Cleanup_Handler } catch (const std::exception& ex) { if (DCPS_debug_level > 0) { ACE_ERROR((LM_ERROR, ACE_TEXT("(%P|%t) DataDurabilityCache::insert ") ACE_TEXT("couldn't create directory for PERSISTENT ") ACE_TEXT("data: %C\n"), ex.what())); } dir = 0; } } if (this->samples_->find(key, sample_list, allocator) != 0) { // Create a new list (actually an ACE_Array_Base<>) with the // appropriate allocator passed to its constructor. ACE_NEW_MALLOC_RETURN( sample_list, static_cast<sample_list_type *>( allocator->malloc(sizeof(sample_list_type))), sample_list_type(1, static_cast<data_queue_type *>(0), allocator), false); if (this->samples_->bind(key, sample_list, allocator) != 0) return false; } data_queue_type ** const begin = &((*sample_list)[0]); data_queue_type ** const end = begin + sample_list->size(); // Find an empty slot in the array. This is a linear search but // that should be fine for the common case, i.e. a small number of // DataWriters that push data into the cache. slot = std::find(begin, end, static_cast<data_queue_type *>(0)); if (slot == end) { // No available slots. Grow the array accordingly. size_t const old_len = sample_list->size(); sample_list->size(old_len + 1); data_queue_type ** new_begin = &((*sample_list)[0]); slot = new_begin + old_len; } ACE_NEW_MALLOC_RETURN( samples, static_cast<data_queue_type *>( allocator->malloc(sizeof(data_queue_type))), data_queue_type(allocator), false); // Insert the samples in to the sample list. *slot = samples; if (!dir.is_nil()) { samples->fs_path_ = path; } for (SendStateDataSampleList::iterator i(element); i != the_end; ++i) { DataSampleElement& elem = *i; // N.B. Do not persist samples with coherent changes. // To verify, we check the DataSampleHeader for the // coherent_change_ flag. The DataSampleHeader will // always be the first message block in the chain. // // It should be noted that persisting coherent changes // is a non-trivial task, and should be handled when // finalizing persistence profile conformance. if (DataSampleHeader::test_flag(COHERENT_CHANGE_FLAG, elem.get_sample())) { continue; // skip coherent sample } sample_data_type sample(elem, allocator); if (samples->enqueue_tail(sample) != 0) return false; if (!dir.is_nil()) { try { File::Ptr f = dir->create_next_file(); std::ofstream os; if (!f->write(os)) return false; DDS::Time_t timestamp; const char * data; size_t len; sample.get_sample(data, len, timestamp); os << timestamp.sec << ' ' << timestamp.nanosec << ' '; os.write(data, len); } catch (const std::exception& ex) { if (DCPS_debug_level > 0) { ACE_ERROR((LM_ERROR, ACE_TEXT("(%P|%t) DataDurabilityCache::insert ") ACE_TEXT("couldn't write sample for PERSISTENT ") ACE_TEXT("data: %C\n"), ex.what())); } } } } } // ----------- // Schedule cleanup timer. //FUTURE: The cleanup delay needs to be persisted (if QoS is persistent) ACE_Time_Value const cleanup_delay( duration_to_time_value(qos.service_cleanup_delay)); if (cleanup_delay > ACE_Time_Value::zero) { if (OpenDDS::DCPS::DCPS_debug_level >= 4) { ACE_DEBUG((LM_DEBUG, ACE_TEXT("OpenDDS (%P|%t) Scheduling durable data ") ACE_TEXT("cleanup for\n") ACE_TEXT("OpenDDS (%P|%t) (domain_id, topic, type) ") ACE_TEXT("== (%d, %C, %C)\n"), domain_id, topic_name, type_name)); } Cleanup_Handler * const cleanup = new Cleanup_Handler(*sample_list, slot - &(*sample_list)[0], this->allocator_.get(), path, this->data_dir_); ACE_Event_Handler_var safe_cleanup(cleanup); // Transfer ownership long const tid = this->reactor_->schedule_timer(cleanup, 0, // ACT cleanup_delay); if (tid == -1) { ACE_GUARD_RETURN(ACE_SYNCH_MUTEX, guard, this->lock_, false); ACE_DES_FREE(samples, this->allocator_->free, DurabilityQueue<sample_data_type>); *slot = 0; return false; } else { { ACE_GUARD_RETURN(ACE_SYNCH_MUTEX, guard, this->lock_, false); this->cleanup_timer_ids_.push_back(tid); } cleanup->timer_id(tid, &this->cleanup_timer_ids_); } } return true; }