diff --git a/docs/manual/config/config_file_reference.rst b/docs/manual/config/config_file_reference.rst index eeec2ecabd..4d47617f71 100644 --- a/docs/manual/config/config_file_reference.rst +++ b/docs/manual/config/config_file_reference.rst @@ -714,7 +714,7 @@ The default value is: ``default`` //CycloneDDS/Domain/Internal ============================ -Children: `//CycloneDDS/Domain/Internal/AccelerateRexmitBlockSize`_, `//CycloneDDS/Domain/Internal/AckDelay`_, `//CycloneDDS/Domain/Internal/AutoReschedNackDelay`_, `//CycloneDDS/Domain/Internal/BuiltinEndpointSet`_, `//CycloneDDS/Domain/Internal/BurstSize`_, `//CycloneDDS/Domain/Internal/ControlTopic`_, `//CycloneDDS/Domain/Internal/DefragReliableMaxSamples`_, `//CycloneDDS/Domain/Internal/DefragUnreliableMaxSamples`_, `//CycloneDDS/Domain/Internal/DeliveryQueueMaxSamples`_, `//CycloneDDS/Domain/Internal/EnableExpensiveChecks`_, `//CycloneDDS/Domain/Internal/GenerateKeyhash`_, `//CycloneDDS/Domain/Internal/HeartbeatInterval`_, `//CycloneDDS/Domain/Internal/LateAckMode`_, `//CycloneDDS/Domain/Internal/LivelinessMonitoring`_, `//CycloneDDS/Domain/Internal/MaxParticipants`_, `//CycloneDDS/Domain/Internal/MaxQueuedRexmitBytes`_, `//CycloneDDS/Domain/Internal/MaxQueuedRexmitMessages`_, `//CycloneDDS/Domain/Internal/MaxSampleSize`_, `//CycloneDDS/Domain/Internal/MeasureHbToAckLatency`_, `//CycloneDDS/Domain/Internal/MonitorPort`_, `//CycloneDDS/Domain/Internal/MultipleReceiveThreads`_, `//CycloneDDS/Domain/Internal/NackDelay`_, `//CycloneDDS/Domain/Internal/PreEmptiveAckDelay`_, `//CycloneDDS/Domain/Internal/PrimaryReorderMaxSamples`_, `//CycloneDDS/Domain/Internal/PrioritizeRetransmit`_, `//CycloneDDS/Domain/Internal/RediscoveryBlacklistDuration`_, `//CycloneDDS/Domain/Internal/RetransmitMerging`_, `//CycloneDDS/Domain/Internal/RetransmitMergingPeriod`_, `//CycloneDDS/Domain/Internal/RetryOnRejectBestEffort`_, `//CycloneDDS/Domain/Internal/SPDPResponseMaxDelay`_, `//CycloneDDS/Domain/Internal/ScheduleTimeRounding`_, `//CycloneDDS/Domain/Internal/SecondaryReorderMaxSamples`_, `//CycloneDDS/Domain/Internal/SocketReceiveBufferSize`_, `//CycloneDDS/Domain/Internal/SocketSendBufferSize`_, `//CycloneDDS/Domain/Internal/SquashParticipants`_, `//CycloneDDS/Domain/Internal/SynchronousDeliveryLatencyBound`_, `//CycloneDDS/Domain/Internal/SynchronousDeliveryPriorityThreshold`_, `//CycloneDDS/Domain/Internal/Test`_, `//CycloneDDS/Domain/Internal/UnicastResponseToSPDPMessages`_, `//CycloneDDS/Domain/Internal/UseMulticastIfMreqn`_, `//CycloneDDS/Domain/Internal/Watermarks`_, `//CycloneDDS/Domain/Internal/WriteBatch`_, `//CycloneDDS/Domain/Internal/WriterLingerDuration`_ +Children: `//CycloneDDS/Domain/Internal/AccelerateRexmitBlockSize`_, `//CycloneDDS/Domain/Internal/AckDelay`_, `//CycloneDDS/Domain/Internal/AutoReschedNackDelay`_, `//CycloneDDS/Domain/Internal/BuiltinEndpointSet`_, `//CycloneDDS/Domain/Internal/BurstSize`_, `//CycloneDDS/Domain/Internal/ControlTopic`_, `//CycloneDDS/Domain/Internal/DefragReliableMaxSamples`_, `//CycloneDDS/Domain/Internal/DefragUnreliableMaxSamples`_, `//CycloneDDS/Domain/Internal/DeliveryQueueMaxSamples`_, `//CycloneDDS/Domain/Internal/EnableExpensiveChecks`_, `//CycloneDDS/Domain/Internal/GenerateKeyhash`_, `//CycloneDDS/Domain/Internal/HeartbeatInterval`_, `//CycloneDDS/Domain/Internal/LateAckMode`_, `//CycloneDDS/Domain/Internal/LivelinessMonitoring`_, `//CycloneDDS/Domain/Internal/MaxParticipants`_, `//CycloneDDS/Domain/Internal/MaxQueuedRexmitBytes`_, `//CycloneDDS/Domain/Internal/MaxQueuedRexmitMessages`_, `//CycloneDDS/Domain/Internal/MaxSampleSize`_, `//CycloneDDS/Domain/Internal/MeasureHbToAckLatency`_, `//CycloneDDS/Domain/Internal/MonitorPort`_, `//CycloneDDS/Domain/Internal/MultipleReceiveThreads`_, `//CycloneDDS/Domain/Internal/NackDelay`_, `//CycloneDDS/Domain/Internal/PreEmptiveAckDelay`_, `//CycloneDDS/Domain/Internal/PrimaryReorderMaxSamples`_, `//CycloneDDS/Domain/Internal/PrioritizeRetransmit`_, `//CycloneDDS/Domain/Internal/RediscoveryBlacklistDuration`_, `//CycloneDDS/Domain/Internal/RetransmitMerging`_, `//CycloneDDS/Domain/Internal/RetransmitMergingPeriod`_, `//CycloneDDS/Domain/Internal/RetryOnRejectBestEffort`_, `//CycloneDDS/Domain/Internal/SPDPResponseMaxDelay`_, `//CycloneDDS/Domain/Internal/SecondaryReorderMaxSamples`_, `//CycloneDDS/Domain/Internal/SocketReceiveBufferSize`_, `//CycloneDDS/Domain/Internal/SocketSendBufferSize`_, `//CycloneDDS/Domain/Internal/SquashParticipants`_, `//CycloneDDS/Domain/Internal/SynchronousDeliveryLatencyBound`_, `//CycloneDDS/Domain/Internal/SynchronousDeliveryPriorityThreshold`_, `//CycloneDDS/Domain/Internal/Test`_, `//CycloneDDS/Domain/Internal/UnicastResponseToSPDPMessages`_, `//CycloneDDS/Domain/Internal/UseMulticastIfMreqn`_, `//CycloneDDS/Domain/Internal/Watermarks`_, `//CycloneDDS/Domain/Internal/WriteBatch`_, `//CycloneDDS/Domain/Internal/WriterLingerDuration`_ The Internal elements deal with a variety of settings that are evolving and that are not necessarily fully supported. For the majority of the Internal settings the functionality is supported, but the right to change the way the options control the functionality is reserved. This includes renaming or moving options. @@ -1249,20 +1249,6 @@ The unit must be specified explicitly. Recognised units: ns, us, ms, s, min, hr, The default value is: ``0 ms`` -.. _`//CycloneDDS/Domain/Internal/ScheduleTimeRounding`: - -//CycloneDDS/Domain/Internal/ScheduleTimeRounding -------------------------------------------------- - -Number-with-unit - -This setting allows the timing of scheduled events to be rounded up so that more events can be handled in a single cycle of the event queue. The default is 0 and causes no rounding at all, i.e. are scheduled exactly, whereas a value of 10ms would mean that events are rounded up to the nearest 10 milliseconds. - -The unit must be specified explicitly. Recognised units: ns, us, ms, s, min, hr, day. - -The default value is: ``0 ms`` - - .. _`//CycloneDDS/Domain/Internal/SecondaryReorderMaxSamples`: //CycloneDDS/Domain/Internal/SecondaryReorderMaxSamples @@ -2660,10 +2646,10 @@ The categorisation of tracing output is incomplete and hence most of the verbosi The default value is: ``none`` .. - generated from ddsi_config.h[d0e749e6d58d560b8172dcb0cb109d6e5f6897ba] + generated from ddsi_config.h[af204240792218d7541714019cac9ae65b87878f] generated from ddsi__cfgunits.h[be1b976c6e9466472b0c331487c05180ec1052d4] - generated from ddsi__cfgelems.h[63812c3c423805d6d3ad61b1706651329935ac40] - generated from ddsi_config.c[3564342f60516165409e1a563f2346bc91895f15] + generated from ddsi__cfgelems.h[8090d347c12ef665e43b0de41fa57f309c4e9980] + generated from ddsi_config.c[c8b22d1fe853f4cc6545b65610d441dee5558011] generated from _confgen.h[f2d235d5551cbf920a8a2962831dddeabd2856ac] generated from _confgen.c[d74e4fd06e485c5d299dbcc7741cbdb95c5ec706] generated from generate_rnc.c[a2ec6e48d33ac14a320c8ec3f320028a737920e0] diff --git a/docs/manual/options.md b/docs/manual/options.md index 605fc408aa..75b7255459 100644 --- a/docs/manual/options.md +++ b/docs/manual/options.md @@ -478,7 +478,7 @@ The default value is: `default` ### //CycloneDDS/Domain/Internal -Children: [AccelerateRexmitBlockSize](#cycloneddsdomaininternalacceleraterexmitblocksize), [AckDelay](#cycloneddsdomaininternalackdelay), [AutoReschedNackDelay](#cycloneddsdomaininternalautoreschednackdelay), [BuiltinEndpointSet](#cycloneddsdomaininternalbuiltinendpointset), [BurstSize](#cycloneddsdomaininternalburstsize), [ControlTopic](#cycloneddsdomaininternalcontroltopic), [DefragReliableMaxSamples](#cycloneddsdomaininternaldefragreliablemaxsamples), [DefragUnreliableMaxSamples](#cycloneddsdomaininternaldefragunreliablemaxsamples), [DeliveryQueueMaxSamples](#cycloneddsdomaininternaldeliveryqueuemaxsamples), [EnableExpensiveChecks](#cycloneddsdomaininternalenableexpensivechecks), [GenerateKeyhash](#cycloneddsdomaininternalgeneratekeyhash), [HeartbeatInterval](#cycloneddsdomaininternalheartbeatinterval), [LateAckMode](#cycloneddsdomaininternallateackmode), [LivelinessMonitoring](#cycloneddsdomaininternallivelinessmonitoring), [MaxParticipants](#cycloneddsdomaininternalmaxparticipants), [MaxQueuedRexmitBytes](#cycloneddsdomaininternalmaxqueuedrexmitbytes), [MaxQueuedRexmitMessages](#cycloneddsdomaininternalmaxqueuedrexmitmessages), [MaxSampleSize](#cycloneddsdomaininternalmaxsamplesize), [MeasureHbToAckLatency](#cycloneddsdomaininternalmeasurehbtoacklatency), [MonitorPort](#cycloneddsdomaininternalmonitorport), [MultipleReceiveThreads](#cycloneddsdomaininternalmultiplereceivethreads), [NackDelay](#cycloneddsdomaininternalnackdelay), [PreEmptiveAckDelay](#cycloneddsdomaininternalpreemptiveackdelay), [PrimaryReorderMaxSamples](#cycloneddsdomaininternalprimaryreordermaxsamples), [PrioritizeRetransmit](#cycloneddsdomaininternalprioritizeretransmit), [RediscoveryBlacklistDuration](#cycloneddsdomaininternalrediscoveryblacklistduration), [RetransmitMerging](#cycloneddsdomaininternalretransmitmerging), [RetransmitMergingPeriod](#cycloneddsdomaininternalretransmitmergingperiod), [RetryOnRejectBestEffort](#cycloneddsdomaininternalretryonrejectbesteffort), [SPDPResponseMaxDelay](#cycloneddsdomaininternalspdpresponsemaxdelay), [ScheduleTimeRounding](#cycloneddsdomaininternalscheduletimerounding), [SecondaryReorderMaxSamples](#cycloneddsdomaininternalsecondaryreordermaxsamples), [SocketReceiveBufferSize](#cycloneddsdomaininternalsocketreceivebuffersize), [SocketSendBufferSize](#cycloneddsdomaininternalsocketsendbuffersize), [SquashParticipants](#cycloneddsdomaininternalsquashparticipants), [SynchronousDeliveryLatencyBound](#cycloneddsdomaininternalsynchronousdeliverylatencybound), [SynchronousDeliveryPriorityThreshold](#cycloneddsdomaininternalsynchronousdeliveryprioritythreshold), [Test](#cycloneddsdomaininternaltest), [UnicastResponseToSPDPMessages](#cycloneddsdomaininternalunicastresponsetospdpmessages), [UseMulticastIfMreqn](#cycloneddsdomaininternalusemulticastifmreqn), [Watermarks](#cycloneddsdomaininternalwatermarks), [WriteBatch](#cycloneddsdomaininternalwritebatch), [WriterLingerDuration](#cycloneddsdomaininternalwriterlingerduration) +Children: [AccelerateRexmitBlockSize](#cycloneddsdomaininternalacceleraterexmitblocksize), [AckDelay](#cycloneddsdomaininternalackdelay), [AutoReschedNackDelay](#cycloneddsdomaininternalautoreschednackdelay), [BuiltinEndpointSet](#cycloneddsdomaininternalbuiltinendpointset), [BurstSize](#cycloneddsdomaininternalburstsize), [ControlTopic](#cycloneddsdomaininternalcontroltopic), [DefragReliableMaxSamples](#cycloneddsdomaininternaldefragreliablemaxsamples), [DefragUnreliableMaxSamples](#cycloneddsdomaininternaldefragunreliablemaxsamples), [DeliveryQueueMaxSamples](#cycloneddsdomaininternaldeliveryqueuemaxsamples), [EnableExpensiveChecks](#cycloneddsdomaininternalenableexpensivechecks), [GenerateKeyhash](#cycloneddsdomaininternalgeneratekeyhash), [HeartbeatInterval](#cycloneddsdomaininternalheartbeatinterval), [LateAckMode](#cycloneddsdomaininternallateackmode), [LivelinessMonitoring](#cycloneddsdomaininternallivelinessmonitoring), [MaxParticipants](#cycloneddsdomaininternalmaxparticipants), [MaxQueuedRexmitBytes](#cycloneddsdomaininternalmaxqueuedrexmitbytes), [MaxQueuedRexmitMessages](#cycloneddsdomaininternalmaxqueuedrexmitmessages), [MaxSampleSize](#cycloneddsdomaininternalmaxsamplesize), [MeasureHbToAckLatency](#cycloneddsdomaininternalmeasurehbtoacklatency), [MonitorPort](#cycloneddsdomaininternalmonitorport), [MultipleReceiveThreads](#cycloneddsdomaininternalmultiplereceivethreads), [NackDelay](#cycloneddsdomaininternalnackdelay), [PreEmptiveAckDelay](#cycloneddsdomaininternalpreemptiveackdelay), [PrimaryReorderMaxSamples](#cycloneddsdomaininternalprimaryreordermaxsamples), [PrioritizeRetransmit](#cycloneddsdomaininternalprioritizeretransmit), [RediscoveryBlacklistDuration](#cycloneddsdomaininternalrediscoveryblacklistduration), [RetransmitMerging](#cycloneddsdomaininternalretransmitmerging), [RetransmitMergingPeriod](#cycloneddsdomaininternalretransmitmergingperiod), [RetryOnRejectBestEffort](#cycloneddsdomaininternalretryonrejectbesteffort), [SPDPResponseMaxDelay](#cycloneddsdomaininternalspdpresponsemaxdelay), [SecondaryReorderMaxSamples](#cycloneddsdomaininternalsecondaryreordermaxsamples), [SocketReceiveBufferSize](#cycloneddsdomaininternalsocketreceivebuffersize), [SocketSendBufferSize](#cycloneddsdomaininternalsocketsendbuffersize), [SquashParticipants](#cycloneddsdomaininternalsquashparticipants), [SynchronousDeliveryLatencyBound](#cycloneddsdomaininternalsynchronousdeliverylatencybound), [SynchronousDeliveryPriorityThreshold](#cycloneddsdomaininternalsynchronousdeliveryprioritythreshold), [Test](#cycloneddsdomaininternaltest), [UnicastResponseToSPDPMessages](#cycloneddsdomaininternalunicastresponsetospdpmessages), [UseMulticastIfMreqn](#cycloneddsdomaininternalusemulticastifmreqn), [Watermarks](#cycloneddsdomaininternalwatermarks), [WriteBatch](#cycloneddsdomaininternalwritebatch), [WriterLingerDuration](#cycloneddsdomaininternalwriterlingerduration) The Internal elements deal with a variety of settings that are evolving and that are not necessarily fully supported. For the majority of the Internal settings the functionality is supported, but the right to change the way the options control the functionality is reserved. This includes renaming or moving options. @@ -855,16 +855,6 @@ The unit must be specified explicitly. Recognised units: ns, us, ms, s, min, hr, The default value is: `0 ms` -#### //CycloneDDS/Domain/Internal/ScheduleTimeRounding -Number-with-unit - -This setting allows the timing of scheduled events to be rounded up so that more events can be handled in a single cycle of the event queue. The default is 0 and causes no rounding at all, i.e. are scheduled exactly, whereas a value of 10ms would mean that events are rounded up to the nearest 10 milliseconds. - -The unit must be specified explicitly. Recognised units: ns, us, ms, s, min, hr, day. - -The default value is: `0 ms` - - #### //CycloneDDS/Domain/Internal/SecondaryReorderMaxSamples Integer @@ -1864,10 +1854,10 @@ While none prevents any message from being written to a DDSI2 log file. The categorisation of tracing output is incomplete and hence most of the verbosity levels and categories are not of much use in the current release. This is an ongoing process and here we describe the target situation rather than the current situation. Currently, the most useful verbosity levels are config, fine and finest. The default value is: `none` - + - - + + diff --git a/etc/cyclonedds.rnc b/etc/cyclonedds.rnc index cc701d9252..79fd0dc00c 100644 --- a/etc/cyclonedds.rnc +++ b/etc/cyclonedds.rnc @@ -607,13 +607,6 @@ CycloneDDS configuration""" ] ] duration }? & [ a:documentation [ xml:lang="en" """ -

This setting allows the timing of scheduled events to be rounded up so that more events can be handled in a single cycle of the event queue. The default is 0 and causes no rounding at all, i.e. are scheduled exactly, whereas a value of 10ms would mean that events are rounded up to the nearest 10 milliseconds.

-

The unit must be specified explicitly. Recognised units: ns, us, ms, s, min, hr, day.

-

The default value is: 0 ms

""" ] ] - element ScheduleTimeRounding { - duration - }? - & [ a:documentation [ xml:lang="en" """

This element sets the maximum size in samples of a secondary re-order administration. The secondary re-order administration is per reader needing historical data.

The default value is: 128

""" ] ] element SecondaryReorderMaxSamples { @@ -1293,10 +1286,10 @@ MIIEpAIBAAKCAQEA3HIh...AOBaaqSV37XBUJg==
duration_inf = xsd:token { pattern = "inf|0|(\d+(\.\d*)?([Ee][\-+]?\d+)?|\.\d+([Ee][\-+]?\d+)?) *([num]?s|min|hr|day)" } memsize = xsd:token { pattern = "0|(\d+(\.\d*)?([Ee][\-+]?\d+)?|\.\d+([Ee][\-+]?\d+)?) *([kMG]i?)?B" } } -# generated from ddsi_config.h[d0e749e6d58d560b8172dcb0cb109d6e5f6897ba] +# generated from ddsi_config.h[af204240792218d7541714019cac9ae65b87878f] # generated from ddsi__cfgunits.h[be1b976c6e9466472b0c331487c05180ec1052d4] -# generated from ddsi__cfgelems.h[63812c3c423805d6d3ad61b1706651329935ac40] -# generated from ddsi_config.c[3564342f60516165409e1a563f2346bc91895f15] +# generated from ddsi__cfgelems.h[8090d347c12ef665e43b0de41fa57f309c4e9980] +# generated from ddsi_config.c[c8b22d1fe853f4cc6545b65610d441dee5558011] # generated from _confgen.h[f2d235d5551cbf920a8a2962831dddeabd2856ac] # generated from _confgen.c[d74e4fd06e485c5d299dbcc7741cbdb95c5ec706] # generated from generate_rnc.c[a2ec6e48d33ac14a320c8ec3f320028a737920e0] diff --git a/etc/cyclonedds.xsd b/etc/cyclonedds.xsd index 703f6b8002..8f69c4c2ec 100644 --- a/etc/cyclonedds.xsd +++ b/etc/cyclonedds.xsd @@ -583,7 +583,6 @@ CycloneDDS configuration - @@ -954,14 +953,6 @@ CycloneDDS configuration <p>Maximum pseudo-random delay in milliseconds between discovering aremote participant and responding to it.</p> <p>The unit must be specified explicitly. Recognised units: ns, us, ms, s, min, hr, day.</p> -<p>The default value is: <code>0 ms</code></p> - - - - - -<p>This setting allows the timing of scheduled events to be rounded up so that more events can be handled in a single cycle of the event queue. The default is 0 and causes no rounding at all, i.e. are scheduled exactly, whereas a value of 10ms would mean that events are rounded up to the nearest 10 milliseconds.</p> -<p>The unit must be specified explicitly. Recognised units: ns, us, ms, s, min, hr, day.</p> <p>The default value is: <code>0 ms</code></p> @@ -1964,10 +1955,10 @@ MIIEpAIBAAKCAQEA3HIh...AOBaaqSV37XBUJg==<br> - + - - + + diff --git a/src/core/ddsc/tests/deadline.c b/src/core/ddsc/tests/deadline.c index 8da9a90972..3321437830 100644 --- a/src/core/ddsc/tests/deadline.c +++ b/src/core/ddsc/tests/deadline.c @@ -446,9 +446,11 @@ CU_Theory((int32_t n_inst, uint8_t unreg_nth, uint8_t dispose_nth), ddsc_deadlin //deadline callback function, this function's purpose is to delay the monitor thread such that while instance's //deadline may expire, the event thread is blocked by this function, and updates to instances are "queued" if they happen //during this block. these queued updates happen the next time the expiration callbacks fire -static void cb (struct ddsi_xevent *xev, void *ptr, ddsrt_mtime_t tm) +static void cb (struct ddsi_domaingv *gv, struct ddsi_xevent *xev, struct ddsi_xpack *xp, void *ptr, ddsrt_mtime_t tm) { + (void) gv; (void) xev; + (void) xp; (void) ptr; (void) tm; dds_sleepfor(DEADLINE); @@ -544,11 +546,11 @@ CU_Test(ddsc_deadline, update) } struct ddsi_domaingv *gvptr = get_domaingv (wr); - struct ddsi_xevent *xev = ddsi_qxev_callback( + struct ddsi_xevent *xev = ddsi_qxev_callback ( gvptr->xevents, ddsrt_mtime_add_duration(ddsrt_time_monotonic(), (dds_duration_t)(0.5*DEADLINE)), cb, - NULL); //this should sleep the thread that updates the statuses from 0.5*DEADLINE to 1.5*DEADLINE + NULL, 0, true); //this should sleep the thread that updates the statuses from 0.5*DEADLINE to 1.5*DEADLINE CU_ASSERT_FATAL(xev != NULL); Space_Type1 msg1 = { 1, 0, 0 }, @@ -599,7 +601,7 @@ CU_Test(ddsc_deadline, update) //msg1 should have expired 3 times, msg2 should have expired 2 times check_statuses(wr, rd, expired_1, expired_2, tw1, tw2, ih1, ih2); - ddsi_delete_xevent_callback(xev); + ddsi_delete_xevent(xev); dds_delete(pp); } diff --git a/src/core/ddsc/tests/nwpart.c b/src/core/ddsc/tests/nwpart.c index 64e6255a0d..b7897a7e47 100644 --- a/src/core/ddsc/tests/nwpart.c +++ b/src/core/ddsc/tests/nwpart.c @@ -27,6 +27,7 @@ #include "ddsi__misc.h" #include "ddsi__addrset.h" #include "ddsi__discovery.h" +#include "ddsi__discovery_endpoint.h" #include "ddsi__plist.h" #include "ddsi__tran.h" diff --git a/src/core/ddsi/CMakeLists.txt b/src/core/ddsi/CMakeLists.txt index 07e6f65b17..ffa7bf3596 100644 --- a/src/core/ddsi/CMakeLists.txt +++ b/src/core/ddsi/CMakeLists.txt @@ -64,6 +64,9 @@ set(srcs_ddsi ddsi_guid.c ddsi_bswap.c ddsi_discovery.c + ddsi_discovery_addrset.c + ddsi_discovery_spdp.c + ddsi_discovery_endpoint.c ddsi_debmon.c ddsi_init.c ddsi_lat_estim.c @@ -82,6 +85,7 @@ set(srcs_ddsi ddsi_xevent.c ddsi_xmsg.c ddsi_freelist.c + ddsi_hbcontrol.c ) set(hdrs_ddsi @@ -188,6 +192,9 @@ set(hdrs_private_ddsi ddsi__bitset.h ddsi__bswap.h ddsi__discovery.h + ddsi__discovery_addrset.h + ddsi__discovery_spdp.h + ddsi__discovery_endpoint.h ddsi__debmon.h ddsi__hbcontrol.h ddsi__inverse_uint32_set.h @@ -222,6 +229,14 @@ if(ENABLE_DEADLINE_MISSED) ddsi_deadline.h ) endif() +if(ENABLE_TOPIC_DISCOVERY) + list(APPEND srcs_ddsi + ddsi_discovery_topic.c + ) + list(APPEND hdrs_private_ddsi + ddsi__discovery_topic.h + ) +endif() if(ENABLE_TYPE_DISCOVERY) list(APPEND srcs_ddsi ddsi_xt_typeinfo.c diff --git a/src/core/ddsi/defconfig.c b/src/core/ddsi/defconfig.c index 79aa712029..9bd70f308c 100644 --- a/src/core/ddsi/defconfig.c +++ b/src/core/ddsi/defconfig.c @@ -103,10 +103,10 @@ void ddsi_config_init_default (struct ddsi_config *cfg) cfg->shm_log_lvl = INT32_C (4); #endif /* DDS_HAS_SHM */ } -/* generated from ddsi_config.h[d0e749e6d58d560b8172dcb0cb109d6e5f6897ba] */ +/* generated from ddsi_config.h[af204240792218d7541714019cac9ae65b87878f] */ /* generated from ddsi__cfgunits.h[be1b976c6e9466472b0c331487c05180ec1052d4] */ -/* generated from ddsi__cfgelems.h[63812c3c423805d6d3ad61b1706651329935ac40] */ -/* generated from ddsi_config.c[3564342f60516165409e1a563f2346bc91895f15] */ +/* generated from ddsi__cfgelems.h[8090d347c12ef665e43b0de41fa57f309c4e9980] */ +/* generated from ddsi_config.c[c8b22d1fe853f4cc6545b65610d441dee5558011] */ /* generated from _confgen.h[f2d235d5551cbf920a8a2962831dddeabd2856ac] */ /* generated from _confgen.c[d74e4fd06e485c5d299dbcc7741cbdb95c5ec706] */ /* generated from generate_rnc.c[a2ec6e48d33ac14a320c8ec3f320028a737920e0] */ diff --git a/src/core/ddsi/include/dds/ddsi/ddsi_config.h b/src/core/ddsi/include/dds/ddsi/ddsi_config.h index 06c1e00e7d..fb5cc26936 100644 --- a/src/core/ddsi/include/dds/ddsi/ddsi_config.h +++ b/src/core/ddsi/include/dds/ddsi/ddsi_config.h @@ -373,7 +373,6 @@ struct ddsi_config int64_t ack_delay; int64_t nack_delay; int64_t preemptive_ack_delay; - int64_t schedule_time_rounding; int64_t auto_resched_nack_delay; int64_t ds_grace_period; uint32_t max_queued_rexmit_bytes; diff --git a/src/core/ddsi/include/dds/ddsi/ddsi_xevent.h b/src/core/ddsi/include/dds/ddsi/ddsi_xevent.h index 0605cee657..1c771ae956 100644 --- a/src/core/ddsi/include/dds/ddsi/ddsi_xevent.h +++ b/src/core/ddsi/include/dds/ddsi/ddsi_xevent.h @@ -22,39 +22,66 @@ extern "C" { struct ddsi_xevent; struct ddsi_xeventq; -/** - * @component timed_events - * @remark: locks EVQ for the duration of the operation - * @param ev the event - */ -void ddsi_delete_xevent (struct ddsi_xevent *ev); +struct ddsi_domaingv; +struct ddsi_xpack; + +typedef void (*ddsi_xevent_cb_t) (struct ddsi_domaingv *gv, struct ddsi_xevent *ev, struct ddsi_xpack *xp, void *arg, ddsrt_mtime_t tnow); -/** +/** @brief removes an event from the event queue and frees it * @component timed_events - * @remark: locks EVQ for the duration of the operation - * @param ev the event + * + * @remark: may be called from inside the event handler + * @remark: if the event was created with the `sync_on_delete` flag set, it blocks until + * the system guarantees that it is not and will not be executing anymore + * + * @param[in] ev the event to be deleted */ -void ddsi_delete_xevent_callback (struct ddsi_xevent *ev); +void ddsi_delete_xevent (struct ddsi_xevent *ev) + ddsrt_nonnull_all; -/** +/** @brief reschedules the event iff `tsched` precedes the time for which it is currently scheduled * @component timed_events - * @remark: locks EVQ for the duration of the operation - * @param ev the event + * + * @remark: may be called from inside the event handler + * + * @param[in] ev the event to be rescheduled + * @param[in] tsched new scheduled time + * + * @return whether the event was actually rescheduled, i.e., whether `tsched` preceded the + * time for which it was scheduled before + * + * @retval 0 not rescheduled + * @retval 1 rescheduled */ -int ddsi_resched_xevent_if_earlier (struct ddsi_xevent *ev, ddsrt_mtime_t tsched); +int ddsi_resched_xevent_if_earlier (struct ddsi_xevent *ev, ddsrt_mtime_t tsched) + ddsrt_nonnull_all; -/** +/** @brief creates a new event object on the given event queue for invoking `cb` in the + * future on the thread handling this queue * @component timed_events * - * @remark: cb will be called with now = NEVER if the event is still enqueued when when ddsi_xeventq_free starts cleaning up + * @remark: delete does not block until an ongoing callback finishes, unless + * `sync_on_delete` is set + * @remark: just before invoking the callback, the schedule time is set to NEVER + * @remark: cb will be called with now = NEVER if the event is still enqueued when + * `ddsi_xeventq_free` starts cleaning up (a consequence of the combination of + * this and being allowed to delete the event from within the callback is that + * one need not always store the returned event pointer) + * + * @param[in] evq event queue + * @param[in] tsched timestamp scheduled, may be NEVER + * @param[in] cb callback function pointer + * @param[in] arg argument (copied into ddsi_xevent, may be a null pointer + * when arg_size = 0) + * @param[in] arg_size size of argument in bytes + * @param[in] sync_on_delete whether `ddsi_delete_xevent` must block until the + * system guarantees that `cb` is not and will not be executing + * anymore * - * @param evq event queue - * @param tsched timestamp scheduled - * @param cb callback function pointer - * @param arg arguments for the callback - * @return struct ddsi_xevent* + * @return the new event object */ -struct ddsi_xevent *ddsi_qxev_callback (struct ddsi_xeventq *evq, ddsrt_mtime_t tsched, void (*cb) (struct ddsi_xevent *xev, void *arg, ddsrt_mtime_t now), void *arg); +struct ddsi_xevent *ddsi_qxev_callback (struct ddsi_xeventq *evq, ddsrt_mtime_t tsched, ddsi_xevent_cb_t cb, const void *arg, size_t arg_size, bool sync_on_delete) + ddsrt_nonnull((1,3)); #if defined (__cplusplus) } diff --git a/src/core/ddsi/src/ddsi__acknack.h b/src/core/ddsi/src/ddsi__acknack.h index 294d468657..a71bb48f37 100644 --- a/src/core/ddsi/src/ddsi__acknack.h +++ b/src/core/ddsi/src/ddsi__acknack.h @@ -56,8 +56,12 @@ struct ddsi_add_acknack_info { /** @component incoming_rtps */ void ddsi_sched_acknack_if_needed (struct ddsi_xevent *ev, struct ddsi_proxy_writer *pwr, struct ddsi_pwr_rd_match *rwn, ddsrt_mtime_t tnow, bool avoid_suppressed_nack); -/** @component incoming_rtps */ -struct ddsi_xmsg *ddsi_make_and_resched_acknack (struct ddsi_xevent *ev, struct ddsi_proxy_writer *pwr, struct ddsi_pwr_rd_match *rwn, ddsrt_mtime_t tnow, bool avoid_suppressed_nack); +struct ddsi_acknack_xevent_cb_arg { + ddsi_guid_t pwr_guid; + ddsi_guid_t rd_guid; +}; + +void ddsi_acknack_xevent_cb (struct ddsi_domaingv *gv, struct ddsi_xevent *ev, struct ddsi_xpack *xp, void *varg, ddsrt_mtime_t tnow); #if defined (__cplusplus) } diff --git a/src/core/ddsi/src/ddsi__cfgelems.h b/src/core/ddsi/src/ddsi__cfgelems.h index 8b916aae50..fbaa9c711b 100644 --- a/src/core/ddsi/src/ddsi__cfgelems.h +++ b/src/core/ddsi/src/ddsi__cfgelems.h @@ -1352,9 +1352,9 @@ static struct cfgelem internal_cfgelems[] = { "writer and sending a pre-emptive AckNack to discover the available " "range of data.

"), UNIT("duration")), - STRING("ScheduleTimeRounding", NULL, 1, "0 ms", - MEMBER(schedule_time_rounding), - FUNCTIONS(0, uf_duration_ms_1hr, 0, pf_duration), + STRING(DEPRECATED("ScheduleTimeRounding"), NULL, 1, "0 ms", + NOMEMBER, + FUNCTIONS(0, uf_nop_duration_ms_1hr, 0, 0), DESCRIPTION( "

This setting allows the timing of scheduled events to be rounded " "up so that more events can be handled in a single cycle of the event " diff --git a/src/core/ddsi/src/ddsi__discovery.h b/src/core/ddsi/src/ddsi__discovery.h index 121cadbd5a..fdd2e5f410 100644 --- a/src/core/ddsi/src/ddsi__discovery.h +++ b/src/core/ddsi/src/ddsi__discovery.h @@ -26,38 +26,33 @@ struct ddsi_reader; struct ddsi_rsample_info; struct ddsi_rdata; struct ddsi_plist; +struct ddsi_xevent; +struct ddsi_xpack; +struct ddsi_domaingv; -struct ddsi_participant_builtin_topic_data_locators { - struct ddsi_locators_one def_uni[MAX_XMIT_CONNS], meta_uni[MAX_XMIT_CONNS]; - struct ddsi_locators_one def_multi, meta_multi; -}; +typedef enum ddsi_sedp_kind { + SEDP_KIND_READER, + SEDP_KIND_WRITER, + SEDP_KIND_TOPIC +} ddsi_sedp_kind_t; /** @component discovery */ -void ddsi_get_participant_builtin_topic_data (const struct ddsi_participant *pp, ddsi_plist_t *dst, struct ddsi_participant_builtin_topic_data_locators *locs); +struct ddsi_writer *ddsi_get_sedp_writer (const struct ddsi_participant *pp, unsigned entityid) + ddsrt_nonnull_all; /** @component discovery */ -struct ddsi_addrset *ddsi_get_endpoint_addrset (const struct ddsi_domaingv *gv, const ddsi_plist_t *datap, struct ddsi_addrset *proxypp_as_default, const ddsi_locator_t *rst_srcloc); +struct ddsi_proxy_participant *ddsi_implicitly_create_proxypp (struct ddsi_domaingv *gv, const ddsi_guid_t *ppguid, ddsi_plist_t *datap /* note: potentially modifies datap */, const ddsi_guid_prefix_t *src_guid_prefix, ddsi_vendorid_t vendorid, ddsrt_wctime_t timestamp, ddsi_seqno_t seq) + ddsrt_nonnull_all; /** @component discovery */ -int ddsi_spdp_write (struct ddsi_participant *pp); +bool ddsi_check_sedp_kind_and_guid (ddsi_sedp_kind_t sedp_kind, const ddsi_guid_t *entity_guid) + ddsrt_nonnull_all; /** @component discovery */ -int ddsi_spdp_dispose_unregister (struct ddsi_participant *pp); - -/** @component discovery */ -int ddsi_sedp_write_topic (struct ddsi_topic *tp, bool alive); - -/** @component discovery */ -int ddsi_sedp_write_writer (struct ddsi_writer *wr); - -/** @component discovery */ -int ddsi_sedp_write_reader (struct ddsi_reader *rd); - -/** @component discovery */ -int ddsi_sedp_dispose_unregister_writer (struct ddsi_writer *wr); - -/** @component discovery */ -int ddsi_sedp_dispose_unregister_reader (struct ddsi_reader *rd); +bool ddsi_handle_sedp_checks (struct ddsi_domaingv * const gv, ddsi_sedp_kind_t sedp_kind, ddsi_guid_t *entity_guid, ddsi_plist_t *datap, + const ddsi_guid_prefix_t *src_guid_prefix, ddsi_vendorid_t vendorid, ddsrt_wctime_t timestamp, + struct ddsi_proxy_participant **proxypp, ddsi_guid_t *ppguid) + ddsrt_nonnull_all; /** @component discovery */ int ddsi_builtins_dqueue_handler (const struct ddsi_rsample_info *sampleinfo, const struct ddsi_rdata *fragchain, const ddsi_guid_t *rdguid, void *qarg); diff --git a/src/core/ddsi/src/ddsi__discovery_addrset.h b/src/core/ddsi/src/ddsi__discovery_addrset.h new file mode 100644 index 0000000000..ced4f80932 --- /dev/null +++ b/src/core/ddsi/src/ddsi__discovery_addrset.h @@ -0,0 +1,75 @@ +/* + * Copyright(c) 2023 ZettaScale Technology and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License + * v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ +#ifndef DDSI__DISCOVERY_ADDRSET_H +#define DDSI__DISCOVERY_ADDRSET_H + +#include "dds/ddsi/ddsi_domaingv.h" // FIXME: MAX_XMIT_CONNS + +#if defined (__cplusplus) +extern "C" { +#endif + +typedef struct ddsi_interface_set { + bool xs[MAX_XMIT_CONNS]; +} ddsi_interface_set_t; + +/** @brief Initializes an interface set to all-false + * @component discovery + * + * @param[out] intfs interface set to initialize */ +void ddsi_interface_set_init (ddsi_interface_set_t *intfs) + ddsrt_nonnull_all; + +/** @brief Whether multicast locators are to be included in discovery information for this domain + * @component discovery + * + * @param[in] gv domain + * @return true iff multicast locators are to be included */ +bool ddsi_include_multicast_locator_in_discovery (const struct ddsi_domaingv *gv) + ddsrt_nonnull_all; + +/** @brief Constructs a new address set from uni- and multicast locators received in SPDP or SEDP + * @component discovery + * + * The construction process uses heuristics for determining which interfaces appear to be applicable for and uses + * this information to set (1) the transmit sockets and (2) choose the interfaces with which to associate multicast + * addresses. + * + * Loopback addresses are accepted if it can be determined that they originate on the same machine: + * - if all enabled interfaces are loopback interfaces, the peer must be on the same host (this ought to be cached) + * - if all advertised addresses are loopback addresses + * - if there is a non-unicast address that matches one of the (enabled) addresses of the host + * + * Unicast addresses are matched against interface addresses to determine whether the address is likely to be + * reachable without any routing. If so, the address is assigned to the interface and the interface is marked as + * "enabled" for the purposes of multicast handling. If not, it is associated with the first enabled non-loopback + * interface on the assumption that unicast routing works fine (but the interface is not "enabled" for multicast + * handling). + * + * Multicast addresses are added only for interfaces that are "enabled" based on unicast processing. If none are + * and the source locator matches an interface, it will enable that interface. + * + * @param[in] gv domain state, needed for interfaces, transports, tracing + * @param[in] uc list of advertised unicast locators + * @param[in] mc list of advertised multicast locators + * @param[in] srcloc source address for discovery packet, or "invalid" + * @param[in,out] inherited_intfs set of applicable interfaces, may be NULL + * + * @return new addrset, possibly empty */ +struct ddsi_addrset *ddsi_addrset_from_locatorlists (const struct ddsi_domaingv *gv, const ddsi_locators_t *uc, const ddsi_locators_t *mc, const ddsi_locator_t *srcloc, const ddsi_interface_set_t *inherited_intfs) + ddsrt_attribute_warn_unused_result ddsrt_nonnull((1,2,3,4)); + +#if defined (__cplusplus) +} +#endif + +#endif /* DDSI__DISCOVERY_ADDRSET_H */ diff --git a/src/core/ddsi/src/ddsi__discovery_endpoint.h b/src/core/ddsi/src/ddsi__discovery_endpoint.h new file mode 100644 index 0000000000..8e7bdaaef1 --- /dev/null +++ b/src/core/ddsi/src/ddsi__discovery_endpoint.h @@ -0,0 +1,63 @@ +/* + * Copyright(c) 2006 to 2021 ZettaScale Technology and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License + * v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ +#ifndef DDSI__DISCOVERY_ENDPOINT_H +#define DDSI__DISCOVERY_ENDPOINT_H + +#include "dds/ddsi/ddsi_unused.h" +#include "dds/ddsi/ddsi_domaingv.h" +#include "ddsi__discovery.h" + +#if defined (__cplusplus) +extern "C" { +#endif + +struct ddsi_participant; +struct ddsi_topic; +struct ddsi_writer; +struct ddsi_reader; +struct ddsi_rsample_info; +struct ddsi_rdata; +struct ddsi_plist; +struct ddsi_xevent; +struct ddsi_xpack; +struct ddsi_domaingv; +struct ddsi_receiver_state; + +/** @component discovery */ +struct ddsi_addrset *ddsi_get_endpoint_addrset (const struct ddsi_domaingv *gv, const ddsi_plist_t *datap, struct ddsi_addrset *proxypp_as_default, const ddsi_locator_t *rst_srcloc) + ddsrt_attribute_warn_unused_result ddsrt_nonnull((1,2,3)); + +/** @component discovery */ +int ddsi_sedp_write_writer (struct ddsi_writer *wr) ddsrt_nonnull_all; + +/** @component discovery */ +int ddsi_sedp_write_reader (struct ddsi_reader *rd) ddsrt_nonnull_all; + +/** @component discovery */ +int ddsi_sedp_dispose_unregister_writer (struct ddsi_writer *wr) ddsrt_nonnull_all; + +/** @component discovery */ +int ddsi_sedp_dispose_unregister_reader (struct ddsi_reader *rd) ddsrt_nonnull_all; + +/** @component discovery */ +void ddsi_handle_sedp_alive_endpoint (const struct ddsi_receiver_state *rst, ddsi_seqno_t seq, ddsi_plist_t *datap /* note: potentially modifies datap */, ddsi_sedp_kind_t sedp_kind, const ddsi_guid_prefix_t *src_guid_prefix, ddsi_vendorid_t vendorid, ddsrt_wctime_t timestamp) + ddsrt_nonnull_all; + +/** @component discovery */ +void ddsi_handle_sedp_dead_endpoint (const struct ddsi_receiver_state *rst, ddsi_plist_t *datap, ddsi_sedp_kind_t sedp_kind, ddsrt_wctime_t timestamp) + ddsrt_nonnull_all; + +#if defined (__cplusplus) +} +#endif + +#endif /* DDSI__DISCOVERY_ENDPOINT_H */ diff --git a/src/core/ddsi/src/ddsi__discovery_spdp.h b/src/core/ddsi/src/ddsi__discovery_spdp.h new file mode 100644 index 0000000000..47213d29f9 --- /dev/null +++ b/src/core/ddsi/src/ddsi__discovery_spdp.h @@ -0,0 +1,65 @@ +/* + * Copyright(c) 2023 ZettaScale Technology and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License + * v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ +#ifndef DDSI__DISCOVERY_SPDP_H +#define DDSI__DISCOVERY_SPDP_H + +#include "dds/ddsi/ddsi_unused.h" +#include "dds/ddsi/ddsi_domaingv.h" // FIXME: MAX_XMIT_CONNS + +#if defined (__cplusplus) +extern "C" { +#endif + +struct ddsi_participant; +struct ddsi_topic; +struct ddsi_writer; +struct ddsi_reader; +struct ddsi_rsample_info; +struct ddsi_rdata; +struct ddsi_plist; +struct ddsi_xevent; +struct ddsi_xpack; +struct ddsi_domaingv; + +struct ddsi_receiver_state; +struct ddsi_serdata; + +struct ddsi_participant_builtin_topic_data_locators { + struct ddsi_locators_one def_uni[MAX_XMIT_CONNS], meta_uni[MAX_XMIT_CONNS]; + struct ddsi_locators_one def_multi, meta_multi; +}; + +/** @component discovery */ +void ddsi_get_participant_builtin_topic_data (const struct ddsi_participant *pp, ddsi_plist_t *dst, struct ddsi_participant_builtin_topic_data_locators *locs) + ddsrt_nonnull_all; + +/** @component discovery */ +int ddsi_spdp_write (struct ddsi_participant *pp); + +/** @component discovery */ +int ddsi_spdp_dispose_unregister (struct ddsi_participant *pp); + +struct ddsi_spdp_broadcast_xevent_cb_arg { + ddsi_guid_t pp_guid; +}; + +/** @component discovery */ +void ddsi_spdp_broadcast_xevent_cb (struct ddsi_domaingv *gv, struct ddsi_xevent *ev, UNUSED_ARG (struct ddsi_xpack *xp), void *varg, ddsrt_mtime_t tnow); + +/** @component discovery */ +void ddsi_handle_spdp (const struct ddsi_receiver_state *rst, ddsi_entityid_t pwr_entityid, ddsi_seqno_t seq, const struct ddsi_serdata *serdata); + +#if defined (__cplusplus) +} +#endif + +#endif /* DDSI__DISCOVERY_SPDP_H */ diff --git a/src/core/ddsi/src/ddsi__discovery_topic.h b/src/core/ddsi/src/ddsi__discovery_topic.h new file mode 100644 index 0000000000..47abb7a84e --- /dev/null +++ b/src/core/ddsi/src/ddsi__discovery_topic.h @@ -0,0 +1,49 @@ +/* + * Copyright(c) 2023 ZettaScale Technology and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License + * v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ +#ifndef DDSI__DISCOVERY_TOPIC_H +#define DDSI__DISCOVERY_TOPIC_H + +#include "dds/ddsi/ddsi_unused.h" +#include "dds/ddsi/ddsi_domaingv.h" + +#if defined (__cplusplus) +extern "C" { +#endif + +struct ddsi_participant; +struct ddsi_topic; +struct ddsi_writer; +struct ddsi_reader; +struct ddsi_rsample_info; +struct ddsi_rdata; +struct ddsi_plist; +struct ddsi_xevent; +struct ddsi_xpack; +struct ddsi_domaingv; +struct ddsi_receiver_state; + +/** @component discovery */ +int ddsi_sedp_write_topic (struct ddsi_topic *tp, bool alive) ddsrt_nonnull_all; + +/** @component discovery */ +void ddsi_handle_sedp_alive_topic (const struct ddsi_receiver_state *rst, ddsi_seqno_t seq, ddsi_plist_t *datap /* note: potentially modifies datap */, const ddsi_guid_prefix_t *src_guid_prefix, ddsi_vendorid_t vendorid, ddsrt_wctime_t timestamp) + ddsrt_nonnull_all; + +/** @component discovery */ +void ddsi_handle_sedp_dead_topic (const struct ddsi_receiver_state *rst, ddsi_plist_t *datap, ddsrt_wctime_t timestamp) + ddsrt_nonnull_all; + +#if defined (__cplusplus) +} +#endif + +#endif /* DDSI__DISCOVERY_TOPIC_H */ diff --git a/src/core/ddsi/src/ddsi__endpoint.h b/src/core/ddsi/src/ddsi__endpoint.h index 0c62c17a4e..cf3c74998b 100644 --- a/src/core/ddsi/src/ddsi__endpoint.h +++ b/src/core/ddsi/src/ddsi__endpoint.h @@ -27,6 +27,7 @@ struct ddsi_participant; struct ddsi_type_pair; struct ddsi_entity_common; struct ddsi_endpoint_common; +struct ddsi_alive_state; struct dds_qos; struct ddsi_ldur_fhnode { diff --git a/src/core/ddsi/src/ddsi__hbcontrol.h b/src/core/ddsi/src/ddsi__hbcontrol.h index 0cf1478246..ded4501880 100644 --- a/src/core/ddsi/src/ddsi__hbcontrol.h +++ b/src/core/ddsi/src/ddsi__hbcontrol.h @@ -49,6 +49,11 @@ struct ddsi_xmsg *ddsi_writer_hbcontrol_create_heartbeat (struct ddsi_writer *wr struct ddsi_xmsg *ddsi_writer_hbcontrol_p2p(struct ddsi_writer *wr, const struct ddsi_whc_state *whcst, int hbansreq, struct ddsi_proxy_reader *prd); #endif +struct ddsi_heartbeat_xevent_cb_arg { + ddsi_guid_t wr_guid; +}; + +void ddsi_heartbeat_xevent_cb (struct ddsi_domaingv *gv, struct ddsi_xevent *ev, struct ddsi_xpack *xp, void *varg, ddsrt_mtime_t tnow); #if defined (__cplusplus) } diff --git a/src/core/ddsi/src/ddsi__pmd.h b/src/core/ddsi/src/ddsi__pmd.h index c8379062ca..e16addd98d 100644 --- a/src/core/ddsi/src/ddsi__pmd.h +++ b/src/core/ddsi/src/ddsi__pmd.h @@ -27,6 +27,7 @@ extern "C" { struct ddsi_domaingv; struct ddsi_thread_state; struct ddsi_guid; +struct ddsi_xevent; struct ddsi_xpack; struct ddsi_participant; struct ddsi_receiver_state; @@ -48,6 +49,13 @@ void ddsi_write_pmd_message (struct ddsi_thread_state * const ts1, struct ddsi_x /** @component pmd */ void ddsi_handle_pmd_message (const struct ddsi_receiver_state *rst, struct ddsi_serdata *sample_common); +struct ddsi_write_pmd_message_xevent_cb_arg { + ddsi_guid_t pp_guid; +}; + +/** @component pmd */ +void ddsi_write_pmd_message_xevent_cb (struct ddsi_domaingv *gv, struct ddsi_xevent *ev, struct ddsi_xpack *xp, void *varg, ddsrt_mtime_t tnow); + #if defined (__cplusplus) } #endif diff --git a/src/core/ddsi/src/ddsi__proxy_endpoint.h b/src/core/ddsi/src/ddsi__proxy_endpoint.h index b07891ed5a..d52395d210 100644 --- a/src/core/ddsi/src/ddsi__proxy_endpoint.h +++ b/src/core/ddsi/src/ddsi__proxy_endpoint.h @@ -49,6 +49,11 @@ struct ddsi_entity_common *ddsi_entity_common_from_proxy_endpoint_common (const /** @component ddsi_proxy_endpoint */ bool ddsi_is_proxy_endpoint (const struct ddsi_entity_common *e); +/** @component ddsi_proxy_endpoint */ +void ddsi_send_entityid_to_pwr (struct ddsi_proxy_writer *pwr, const ddsi_guid_t *guid); + +/** @component ddsi_proxy_endpoint */ +void ddsi_send_entityid_to_prd (struct ddsi_proxy_reader *prd, const ddsi_guid_t *guid); /** * @brief To create a new proxy writer diff --git a/src/core/ddsi/src/ddsi__transmit.h b/src/core/ddsi/src/ddsi__transmit.h index aa2be7dc64..74513b7a2a 100644 --- a/src/core/ddsi/src/ddsi__transmit.h +++ b/src/core/ddsi/src/ddsi__transmit.h @@ -44,6 +44,8 @@ int ddsi_write_sample_gc_notk (struct ddsi_thread_state * const thrst, struct dd /** @component outgoing_rtps */ int ddsi_write_sample_nogc_notk (struct ddsi_thread_state * const thrst, struct ddsi_xpack *xp, struct ddsi_writer *wr, struct ddsi_serdata *serdata); +/** @component outgoing_rtps */ +int ddsi_write_and_fini_plist (struct ddsi_writer *wr, ddsi_plist_t *ps, bool alive); /* When calling the following functions, wr->lock must be held */ diff --git a/src/core/ddsi/src/ddsi__xevent.h b/src/core/ddsi/src/ddsi__xevent.h index 1fba889591..633cf81324 100644 --- a/src/core/ddsi/src/ddsi__xevent.h +++ b/src/core/ddsi/src/ddsi__xevent.h @@ -56,12 +56,6 @@ void ddsi_xeventq_stop (struct ddsi_xeventq *evq); /** @component timed_events */ void ddsi_qxev_msg (struct ddsi_xeventq *evq, struct ddsi_xmsg *msg); -/** @component timed_events */ -void ddsi_qxev_pwr_entityid (struct ddsi_proxy_writer * pwr, const ddsi_guid_t *guid); - -/** @component timed_events */ -void ddsi_qxev_prd_entityid (struct ddsi_proxy_reader * prd, const ddsi_guid_t *guid); - /** @component timed_events */ void ddsi_qxev_nt_callback (struct ddsi_xeventq *evq, void (*cb) (void *arg), void *arg); @@ -74,21 +68,14 @@ enum ddsi_qxev_msg_rexmit_result { /** @component timed_events */ enum ddsi_qxev_msg_rexmit_result ddsi_qxev_msg_rexmit_wrlock_held (struct ddsi_xeventq *evq, struct ddsi_xmsg *msg, int force); -/** @component timed_events */ -struct ddsi_xevent *ddsi_qxev_heartbeat (struct ddsi_xeventq *evq, ddsrt_mtime_t tsched, const ddsi_guid_t *wr_guid); - -/** @component timed_events */ -struct ddsi_xevent *ddsi_qxev_acknack (struct ddsi_xeventq *evq, ddsrt_mtime_t tsched, const ddsi_guid_t *pwr_guid, const ddsi_guid_t *rd_guid); - -/** @component timed_events */ -struct ddsi_xevent *ddsi_qxev_spdp (struct ddsi_xeventq *evq, ddsrt_mtime_t tsched, const ddsi_guid_t *pp_guid, const ddsi_guid_t *proxypp_guid); - -/** @component timed_events */ -struct ddsi_xevent *ddsi_qxev_pmd_update (struct ddsi_xeventq *evq, ddsrt_mtime_t tsched, const ddsi_guid_t *pp_guid); - -/** @component timed_events */ -struct ddsi_xevent *ddsi_qxev_delete_writer (struct ddsi_xeventq *evq, ddsrt_mtime_t tsched, const ddsi_guid_t *guid); - +#ifndef NDEBUG +/** + * @component timed_events + * @remark: locks EVQ for the duration of the operation + * @param ev the event + */ +bool ddsi_delete_xevent_pending (struct ddsi_xevent *ev); +#endif #if defined (__cplusplus) } diff --git a/src/core/ddsi/src/ddsi_acknack.c b/src/core/ddsi/src/ddsi_acknack.c index f2dcb3473d..1a3b83acf6 100644 --- a/src/core/ddsi/src/ddsi_acknack.c +++ b/src/core/ddsi/src/ddsi_acknack.c @@ -20,6 +20,7 @@ #include "ddsi__bitset.h" #include "ddsi__acknack.h" #include "ddsi__entity_index.h" +#include "ddsi__proxy_endpoint.h" #include "ddsi__endpoint_match.h" #include "ddsi__security_omg.h" #include "ddsi__xqos.h" @@ -369,7 +370,7 @@ void ddsi_sched_acknack_if_needed (struct ddsi_xevent *ev, struct ddsi_proxy_wri (void) ddsi_resched_xevent_if_earlier (ev, tnow); } -struct ddsi_xmsg *ddsi_make_and_resched_acknack (struct ddsi_xevent *ev, struct ddsi_proxy_writer *pwr, struct ddsi_pwr_rd_match *rwn, ddsrt_mtime_t tnow, bool avoid_suppressed_nack) +static struct ddsi_xmsg *make_and_resched_acknack (struct ddsi_xevent *ev, struct ddsi_proxy_writer *pwr, struct ddsi_pwr_rd_match *rwn, ddsrt_mtime_t tnow, bool avoid_suppressed_nack) { struct ddsi_domaingv * const gv = pwr->e.gv; struct ddsi_xmsg *msg; @@ -503,3 +504,120 @@ struct ddsi_xmsg *ddsi_make_and_resched_acknack (struct ddsi_xevent *ev, struct GVTRACE ("send acknack(rd "PGUIDFMT" -> pwr "PGUIDFMT")\n", PGUID (rwn->rd_guid), PGUID (pwr->e.guid)); return msg; } + +static dds_duration_t preemptive_acknack_interval (const struct ddsi_pwr_rd_match *rwn) +{ + if (rwn->t_last_ack.v < rwn->tcreate.v) + return 0; + else + { + const dds_duration_t age = rwn->t_last_ack.v - rwn->tcreate.v; + if (age <= DDS_SECS (10)) + return DDS_SECS (1); + else if (age <= DDS_SECS (60)) + return DDS_SECS (2); + else if (age <= DDS_SECS (120)) + return DDS_SECS (5); + else + return DDS_SECS (10); + } +} + +static struct ddsi_xmsg *make_preemptive_acknack (struct ddsi_xevent *ev, struct ddsi_proxy_writer *pwr, struct ddsi_pwr_rd_match *rwn, ddsrt_mtime_t tnow) +{ + const dds_duration_t old_intv = preemptive_acknack_interval (rwn); + if (tnow.v < ddsrt_mtime_add_duration (rwn->t_last_ack, old_intv).v) + { + (void) ddsi_resched_xevent_if_earlier (ev, ddsrt_mtime_add_duration (rwn->t_last_ack, old_intv)); + return NULL; + } + + struct ddsi_domaingv * const gv = pwr->e.gv; + struct ddsi_participant *pp = NULL; + if (ddsi_omg_proxy_participant_is_secure (pwr->c.proxypp)) + { + struct ddsi_reader *rd = ddsi_entidx_lookup_reader_guid (gv->entity_index, &rwn->rd_guid); + if (rd) + pp = rd->c.pp; + } + + struct ddsi_xmsg *msg; + if ((msg = ddsi_xmsg_new (gv->xmsgpool, &rwn->rd_guid, pp, DDSI_ACKNACK_SIZE_MAX, DDSI_XMSG_KIND_CONTROL)) == NULL) + { + // if out of memory, try again later + (void) ddsi_resched_xevent_if_earlier (ev, ddsrt_mtime_add_duration (tnow, old_intv)); + return NULL; + } + + ddsi_xmsg_setdst_pwr (msg, pwr); + struct ddsi_xmsg_marker sm_marker; + ddsi_rtps_acknack_t *an = ddsi_xmsg_append (msg, &sm_marker, DDSI_ACKNACK_SIZE (0)); + ddsi_xmsg_submsg_init (msg, sm_marker, DDSI_RTPS_SMID_ACKNACK); + an->readerId = ddsi_hton_entityid (rwn->rd_guid.entityid); + an->writerId = ddsi_hton_entityid (pwr->e.guid.entityid); + an->readerSNState.bitmap_base = ddsi_to_seqno (1); + an->readerSNState.numbits = 0; + ddsi_count_t * const countp = + (ddsi_count_t *) ((char *) an + offsetof (ddsi_rtps_acknack_t, bits) + DDSI_SEQUENCE_NUMBER_SET_BITS_SIZE (0)); + *countp = 0; + ddsi_xmsg_submsg_setnext (msg, sm_marker); + ddsi_security_encode_datareader_submsg (msg, sm_marker, pwr, &rwn->rd_guid); + + rwn->t_last_ack = tnow; + const dds_duration_t new_intv = preemptive_acknack_interval (rwn); + (void) ddsi_resched_xevent_if_earlier (ev, ddsrt_mtime_add_duration (rwn->t_last_ack, new_intv)); + + // numbits is always 0 here, so need to print the bitmap + ETRACE (pwr, "acknack "PGUIDFMT" -> "PGUIDFMT": #%"PRIu32":%"PRId64"/%"PRIu32":\n", + PGUID (rwn->rd_guid), PGUID (pwr->e.guid), *countp, + ddsi_from_seqno (an->readerSNState.bitmap_base), an->readerSNState.numbits); + return msg; +} + +void ddsi_acknack_xevent_cb (struct ddsi_domaingv *gv, struct ddsi_xevent *ev, struct ddsi_xpack *xp, void *varg, ddsrt_mtime_t tnow) +{ + /* FIXME: ought to keep track of which NACKs are being generated in + response to a Heartbeat. There is no point in having multiple + readers NACK the data. + + FIXME: ought to determine the set of missing samples (as it does + now), and then check which for of those fragments are available already. + A little snag is that the defragmenter can throw out partial samples in + favour of others, so MUST ensure that the defragmenter won't start + threshing and fail to make progress! */ + struct ddsi_acknack_xevent_cb_arg const * const arg = varg; + struct ddsi_proxy_writer *pwr; + struct ddsi_xmsg *msg; + struct ddsi_pwr_rd_match *rwn; + + if ((pwr = ddsi_entidx_lookup_proxy_writer_guid (gv->entity_index, &arg->pwr_guid)) == NULL) + { + return; + } + + ddsrt_mutex_lock (&pwr->e.lock); + if ((rwn = ddsrt_avl_lookup (&ddsi_pwr_readers_treedef, &pwr->readers, &arg->rd_guid)) == NULL) + { + ddsrt_mutex_unlock (&pwr->e.lock); + return; + } + + if (!pwr->have_seen_heartbeat) + msg = make_preemptive_acknack (ev, pwr, rwn, tnow); + else + msg = make_and_resched_acknack (ev, pwr, rwn, tnow, false); + ddsrt_mutex_unlock (&pwr->e.lock); + + /* ddsi_xpack_addmsg may sleep (for bandwidth-limited channels), so + must be outside the lock */ + if (msg) + { + // a possible result of trying to encode a submessage is that it is removed, + // in which case we may end up with an empty one. + // FIXME: change ddsi_security_encode_datareader_submsg so that it returns this and make it warn_unused_result + if (ddsi_xmsg_size (msg) == 0) + ddsi_xmsg_free (msg); + else + ddsi_xpack_addmsg (xp, msg, 0); + } +} diff --git a/src/core/ddsi/src/ddsi_config.c b/src/core/ddsi/src/ddsi_config.c index 64072b9c19..fb4fb25339 100644 --- a/src/core/ddsi/src/ddsi_config.c +++ b/src/core/ddsi/src/ddsi_config.c @@ -169,6 +169,7 @@ DU(duration_ms_1hr); DU(duration_ms_1s); DU(duration_us_1s); DU(duration_100ms_1hr); +DU(nop_duration_ms_1hr); PF(duration); DUPF(standards_conformance); DUPF(besmode); @@ -1395,6 +1396,12 @@ static enum update_result uf_duration_ms_1hr (struct ddsi_cfgst *cfgst, void *pa return uf_duration_gen (cfgst, parent, cfgelem, value, DDS_MSECS (1), 0, DDS_SECS (3600)); } +static enum update_result uf_nop_duration_ms_1hr (struct ddsi_cfgst *cfgst, UNUSED_ARG(void *parent), UNUSED_ARG(struct cfgelem const * const cfgelem), UNUSED_ARG (int first), const char *value) +{ + int64_t dummy; + return uf_natint64_unit (cfgst, &dummy, value, unittab_duration, DDS_MSECS (1), 0, DDS_SECS (3600)); +} + static enum update_result uf_duration_ms_1s (struct ddsi_cfgst *cfgst, void *parent, struct cfgelem const * const cfgelem, UNUSED_ARG (int first), const char *value) { return uf_duration_gen (cfgst, parent, cfgelem, value, DDS_MSECS (1), 0, DDS_SECS (1)); diff --git a/src/core/ddsi/src/ddsi_deadline.c b/src/core/ddsi/src/ddsi_deadline.c index ebf3c8081f..378266e20c 100644 --- a/src/core/ddsi/src/ddsi_deadline.c +++ b/src/core/ddsi/src/ddsi_deadline.c @@ -16,9 +16,16 @@ #include "dds/ddsi/ddsi_deadline.h" #include "ddsi__xevent.h" -static void instance_deadline_missed_cb (struct ddsi_xevent *xev, void *varg, ddsrt_mtime_t tnow) +struct instance_deadline_missed_cb_arg { + struct ddsi_deadline_adm *deadline_adm; +}; + +static void instance_deadline_missed_cb (struct ddsi_domaingv *gv, struct ddsi_xevent *xev, struct ddsi_xpack *xp, void *varg, ddsrt_mtime_t tnow) { - struct ddsi_deadline_adm * const deadline_adm = varg; + (void) gv; + (void) xp; + struct instance_deadline_missed_cb_arg * const arg = varg; + struct ddsi_deadline_adm * const deadline_adm = arg->deadline_adm; const ddsrt_mtime_t next = deadline_adm->deadline_missed_cb ((char *)deadline_adm - deadline_adm->list_offset, tnow); // Rate-limit repeated deadline miss notifications. The first deadline miss following an // update of the instance is always handled by ddsi_deadline_register_instance_real and @@ -80,7 +87,8 @@ ddsrt_mtime_t ddsi_deadline_next_missed_locked (struct ddsi_deadline_adm *deadli void ddsi_deadline_init (const struct ddsi_domaingv *gv, struct ddsi_deadline_adm *deadline_adm, size_t list_offset, size_t elem_offset, deadline_missed_cb_t deadline_missed_cb) { ddsrt_circlist_init (&deadline_adm->list); - deadline_adm->evt = ddsi_qxev_callback (gv->xevents, DDSRT_MTIME_NEVER, instance_deadline_missed_cb, deadline_adm); + struct instance_deadline_missed_cb_arg arg = { .deadline_adm = deadline_adm }; + deadline_adm->evt = ddsi_qxev_callback (gv->xevents, DDSRT_MTIME_NEVER, instance_deadline_missed_cb, &arg, sizeof (arg), true); deadline_adm->deadline_missed_cb = deadline_missed_cb; deadline_adm->list_offset = list_offset; deadline_adm->elem_offset = elem_offset; @@ -88,7 +96,7 @@ void ddsi_deadline_init (const struct ddsi_domaingv *gv, struct ddsi_deadline_ad void ddsi_deadline_stop (const struct ddsi_deadline_adm *deadline_adm) { - ddsi_delete_xevent_callback (deadline_adm->evt); + ddsi_delete_xevent (deadline_adm->evt); } void ddsi_deadline_clear (struct ddsi_deadline_adm *deadline_adm) diff --git a/src/core/ddsi/src/ddsi_discovery.c b/src/core/ddsi/src/ddsi_discovery.c index 531e916df4..fce8dac44d 100644 --- a/src/core/ddsi/src/ddsi_discovery.c +++ b/src/core/ddsi/src/ddsi_discovery.c @@ -30,1376 +30,49 @@ #include "ddsi__misc.h" #include "ddsi__xevent.h" #include "ddsi__discovery.h" -#include "ddsi__serdata_plist.h" -#include "ddsi__radmin.h" -#include "ddsi__entity_index.h" -#include "ddsi__entity.h" -#include "ddsi__participant.h" -#include "ddsi__xmsg.h" -#include "ddsi__transmit.h" -#include "ddsi__lease.h" -#include "ddsi__security_omg.h" -#include "ddsi__pmd.h" -#include "ddsi__endpoint.h" -#include "ddsi__plist.h" -#include "ddsi__proxy_endpoint.h" -#include "ddsi__proxy_participant.h" -#include "ddsi__topic.h" -#include "ddsi__tran.h" -#include "ddsi__typelib.h" -#include "ddsi__vendor.h" -#include "ddsi__xqos.h" -#include "ddsi__addrset.h" -#ifdef DDS_HAS_TYPE_DISCOVERY -#include "ddsi__typelookup.h" -#endif - -#ifdef DDS_HAS_SECURITY -#include "ddsi__security_exchange.h" -#endif - -typedef enum ddsi_sedp_kind { - SEDP_KIND_READER, - SEDP_KIND_WRITER, - SEDP_KIND_TOPIC -} ddsi_sedp_kind_t; - -static void allowmulticast_aware_add_to_addrset (const struct ddsi_domaingv *gv, uint32_t allow_multicast, struct ddsi_addrset *as, const ddsi_xlocator_t *loc) -{ -#ifdef DDS_HAS_SSM - if (ddsi_is_ssm_mcaddr (gv, &loc->c)) - { - if (!(allow_multicast & DDSI_AMC_SSM)) - return; - } - else if (ddsi_is_mcaddr (gv, &loc->c)) - { - if (!(allow_multicast & DDSI_AMC_ASM)) - return; - } -#else - if (ddsi_is_mcaddr (gv, &loc->c) && !(allow_multicast & DDSI_AMC_ASM)) - return; -#endif - ddsi_add_xlocator_to_addrset (gv, as, loc); -} - -typedef struct interface_set { - bool xs[MAX_XMIT_CONNS]; -} interface_set_t; - -static void interface_set_init (interface_set_t *intfs) -{ - for (size_t i = 0; i < sizeof (intfs->xs) / sizeof (intfs->xs[0]); i++) - intfs->xs[i] = false; -} - -static void addrset_from_locatorlists_add_one (struct ddsi_domaingv const * const gv, const ddsi_locator_t *loc, struct ddsi_addrset *as, interface_set_t *intfs, bool *direct) -{ - size_t interf_idx; - switch (ddsi_is_nearby_address (gv, loc, (size_t) gv->n_interfaces, gv->interfaces, &interf_idx)) - { - case DNAR_SELF: - case DNAR_LOCAL: - // if it matches an interface, use that one and record that this is a - // directly connected interface: those will then all be possibilities - // for transmitting multicasts (assuming capable, allowed, &c.) - assert (interf_idx < MAX_XMIT_CONNS); - ddsi_add_xlocator_to_addrset (gv, as, &(const ddsi_xlocator_t) { - .conn = gv->xmit_conns[interf_idx], - .c = *loc }); - intfs->xs[interf_idx] = true; - *direct = true; - break; - case DNAR_DISTANT: - // If DONT_ROUTE is set and there is no matching interface, then presumably - // one would not be able to reach this address. - if (!gv->config.dontRoute) - { - // Pick the first selected interface that isn't link-local or loopback - // (maybe it matters, maybe not, but it doesn't make sense to assign - // a transmit socket for a local interface to a distant host). If none - // exists, skip the address. - for (int i = 0; i < gv->n_interfaces; i++) - { - // do not use link-local or loopback interfaces transmit conn for distant nodes - if (gv->interfaces[i].link_local || gv->interfaces[i].loopback) - continue; - ddsi_add_xlocator_to_addrset (gv, as, &(const ddsi_xlocator_t) { - .conn = gv->xmit_conns[i], - .c = *loc }); - break; - } - } - break; - case DNAR_UNREACHABLE: - break; - } -} - -/** @brief Constructs a new address set from uni- and multicast locators received in SPDP or SEDP - * - * The construction process uses heuristics for determining which interfaces appear to be applicable for and uses - * this information to set (1) the transmit sockets and (2) choose the interfaces with which to associate multicast - * addresses. - * - * Loopback addresses are accepted if it can be determined that they originate on the same machine: - * - if all enabled interfaces are loopback interfaces, the peer must be on the same host (this ought to be cached) - * - if all advertised addresses are loopback addresses - * - if there is a non-unicast address that matches one of the (enabled) addresses of the host - * - * Unicast addresses are matched against interface addresses to determine whether the address is likely to be - * reachable without any routing. If so, the address is assigned to the interface and the interface is marked as - * "enabled" for the purposes of multicast handling. If not, it is associated with the first enabled non-loopback - * interface on the assumption that unicast routing works fine (but the interface is not "enabled" for multicast - * handling). - * - * Multicast addresses are added only for interfaces that are "enabled" based on unicast processing. If none are - * and the source locator matches an interface, it will enable that interface. - * - * @param[in] gv domain state, needed for interfaces, transports, tracing - * @param[in] uc list of advertised unicast locators - * @param[in] mc list of advertised multicast locators - * @param[in] srcloc source address for discovery packet, or "invalid" - * @param[in,out] inherited_intfs set of applicable interfaces, may be NULL - * - * @return new addrset, possibly empty */ -static struct ddsi_addrset *addrset_from_locatorlists (const struct ddsi_domaingv *gv, const ddsi_locators_t *uc, const ddsi_locators_t *mc, const ddsi_locator_t *srcloc, const interface_set_t *inherited_intfs) -{ - struct ddsi_addrset *as = ddsi_new_addrset (); - interface_set_t intfs; - interface_set_init (&intfs); - - // if all interfaces are loopback, or all locators in uc are loopback, we're cool with loopback addresses - bool allow_loopback; - { - bool a = true; - for (int i = 0; i < gv->n_interfaces && a; i++) - if (!gv->interfaces[i].loopback) - a = false; - bool b = true; - // FIXME: what about the cases where SEDP gives just a loopback address, but the proxypp is known to be on a remote node? - for (struct ddsi_locators_one *l = uc->first; l != NULL && b; l = l->next) - b = ddsi_is_loopbackaddr (gv, &l->loc); - allow_loopback = (a || b); - } - - // if any non-loopback address is identical to one of our own addresses (actual or advertised), - // assume it is the same machine, in which case loopback addresses may be picked up - for (struct ddsi_locators_one *l = uc->first; l != NULL && !allow_loopback; l = l->next) - { - if (ddsi_is_loopbackaddr (gv, &l->loc)) - continue; - allow_loopback = (ddsi_is_nearby_address (gv, &l->loc, (size_t) gv->n_interfaces, gv->interfaces, NULL) == DNAR_SELF); - } - //GVTRACE(" allow_loopback=%d\n", allow_loopback); - - bool direct = false; - for (struct ddsi_locators_one *l = uc->first; l != NULL; l = l->next) - { -#if 0 - { - char buf[DDSI_LOCSTRLEN]; - ddsi_locator_to_string_no_port (buf, sizeof (buf), &l->loc); - GVTRACE("%s: ignore %d loopback %d\n", buf, l->loc.tran->m_ignore, ddsi_is_loopbackaddr (gv, &l->loc)); - } -#endif - // skip unrecognized ones, as well as loopback ones if not on the same host - if (!allow_loopback && ddsi_is_loopbackaddr (gv, &l->loc)) - continue; - - ddsi_locator_t loc = l->loc; - - // if the advertised locator matches our own external locator, than presumably - // it is the same machine and should be addressed using the actual interface - // address - bool extloc_of_self = false; - for (int i = 0; i < gv->n_interfaces; i++) - { - if (loc.kind == gv->interfaces[i].loc.kind && memcmp (loc.address, gv->interfaces[i].extloc.address, sizeof (loc.address)) == 0) - { - memcpy (loc.address, gv->interfaces[i].loc.address, sizeof (loc.address)); - extloc_of_self = true; - break; - } - } - - if (!extloc_of_self && loc.kind == DDSI_LOCATOR_KIND_UDPv4 && gv->extmask.kind != DDSI_LOCATOR_KIND_INVALID) - { - /* If the examined locator is in the same subnet as our own - external IP address, this locator will be translated into one - in the same subnet as our own local ip and selected. */ - assert (gv->n_interfaces == 1); // gv->extmask: the hack is only supported if limited to a single interface - struct in_addr tmp4 = *((struct in_addr *) (loc.address + 12)); - const struct in_addr ownip = *((struct in_addr *) (gv->interfaces[0].loc.address + 12)); - const struct in_addr extip = *((struct in_addr *) (gv->interfaces[0].extloc.address + 12)); - const struct in_addr extmask = *((struct in_addr *) (gv->extmask.address + 12)); - - if ((tmp4.s_addr & extmask.s_addr) == (extip.s_addr & extmask.s_addr)) - { - /* translate network part of the IP address from the external - one to the internal one */ - tmp4.s_addr = (tmp4.s_addr & ~extmask.s_addr) | (ownip.s_addr & extmask.s_addr); - memcpy (loc.address + 12, &tmp4, 4); - } - } - - addrset_from_locatorlists_add_one (gv, &loc, as, &intfs, &direct); - } - - if (ddsi_addrset_empty (as) && !ddsi_is_unspec_locator (srcloc)) - { - //GVTRACE("add srcloc\n"); - // FIXME: conn_read should provide interface information in source address - //GVTRACE (" add-srcloc"); - addrset_from_locatorlists_add_one (gv, srcloc, as, &intfs, &direct); - } - - if (ddsi_addrset_empty (as) && inherited_intfs) - { - // implies no interfaces enabled in "intfs" yet -- just use whatever - // we inherited for the purposes of selecting multicast addresses - assert (!direct); - for (int i = 0; i < gv->n_interfaces; i++) - assert (!intfs.xs[i]); - //GVTRACE (" using-inherited-intfs"); - intfs = *inherited_intfs; - } - else if (!direct && gv->config.multicast_ttl > 1) - { - //GVTRACE("assuming multicast routing works\n"); - // if not directly connected but multicast TTL allows routing, - // assume any non-local interface will do - //GVTRACE (" enabling-non-loopback/link-local"); - for (int i = 0; i < gv->n_interfaces; i++) - { - assert (!intfs.xs[i]); - intfs.xs[i] = !(gv->interfaces[i].link_local || gv->interfaces[i].loopback); - } - } - -#if 0 - GVTRACE("enabled interfaces for multicast:"); - for (int i = 0; i < gv->n_interfaces; i++) - { - if (intfs[i]) - GVTRACE(" %s(%d)", gv->interfaces[i].name, gv->interfaces[i].mc_capable); - } - GVTRACE("\n"); -#endif - - for (struct ddsi_locators_one *l = mc->first; l != NULL; l = l->next) - { - for (int i = 0; i < gv->n_interfaces; i++) - { - if (intfs.xs[i] && gv->interfaces[i].mc_capable) - { - const ddsi_xlocator_t loc = { - .conn = gv->xmit_conns[i], - .c = l->loc - }; - if (ddsi_factory_supports (loc.conn->m_factory, loc.c.kind)) - allowmulticast_aware_add_to_addrset (gv, gv->config.allowMulticast, as, &loc); - } - } - } - return as; -} - - -/****************************************************************************** - *** - *** SPDP - *** - *****************************************************************************/ - -static void maybe_add_pp_as_meta_to_as_disc (struct ddsi_domaingv *gv, const struct ddsi_addrset *as_meta) -{ - if (ddsi_addrset_empty_mc (as_meta) || !(gv->config.allowMulticast & DDSI_AMC_SPDP)) - { - ddsi_xlocator_t loc; - if (ddsi_addrset_any_uc (as_meta, &loc)) - { - ddsi_add_xlocator_to_addrset (gv, gv->as_disc, &loc); - } - } -} - -struct locators_builder { - ddsi_locators_t *dst; - struct ddsi_locators_one *storage; - size_t storage_n; -}; - -static struct locators_builder locators_builder_init (ddsi_locators_t *dst, struct ddsi_locators_one *storage, size_t storage_n) -{ - dst->n = 0; - dst->first = NULL; - dst->last = NULL; - return (struct locators_builder) { - .dst = dst, - .storage = storage, - .storage_n = storage_n - }; -} - -static bool locators_add_one (struct locators_builder *b, const ddsi_locator_t *loc, uint32_t port_override) -{ - if (b->dst->n >= b->storage_n) - return false; - if (b->dst->n == 0) - b->dst->first = b->storage; - else - b->dst->last->next = &b->storage[b->dst->n]; - b->dst->last = &b->storage[b->dst->n++]; - b->dst->last->loc = *loc; - if (port_override != DDSI_LOCATOR_PORT_INVALID) - b->dst->last->loc.port = port_override; - b->dst->last->next = NULL; - return true; -} - -static bool include_multicast_locator_in_discovery (const struct ddsi_participant *pp) -{ -#ifdef DDS_HAS_SSM - /* Note that if the default multicast address is an SSM address, - we will simply advertise it. The recipients better understand - it means the writers will publish to address and the readers - favour SSM. */ - if (ddsi_is_ssm_mcaddr (pp->e.gv, &pp->e.gv->loc_default_mc)) - return (pp->e.gv->config.allowMulticast & DDSI_AMC_SSM) != 0; - else - return (pp->e.gv->config.allowMulticast & DDSI_AMC_ASM) != 0; -#else - return (pp->e.gv->config.allowMulticast & DDSI_AMC_ASM) != 0; -#endif -} - -void ddsi_get_participant_builtin_topic_data (const struct ddsi_participant *pp, ddsi_plist_t *dst, struct ddsi_participant_builtin_topic_data_locators *locs) -{ - size_t size; - char node[64]; - uint64_t qosdiff; - - ddsi_plist_init_empty (dst); - dst->present |= PP_PARTICIPANT_GUID | PP_BUILTIN_ENDPOINT_SET | - PP_PROTOCOL_VERSION | PP_VENDORID | PP_DOMAIN_ID; - dst->participant_guid = pp->e.guid; - dst->builtin_endpoint_set = pp->bes; - dst->protocol_version.major = DDSI_RTPS_MAJOR; - dst->protocol_version.minor = DDSI_RTPS_MINOR; - dst->vendorid = DDSI_VENDORID_ECLIPSE; - dst->domain_id = pp->e.gv->config.extDomainId.value; - /* Be sure not to send a DOMAIN_TAG when it is the default (an empty) - string: it is an "incompatible-if-unrecognized" parameter, and so - implementations that don't understand the parameter will refuse to - discover us, and so sending the default would break backwards - compatibility. */ - if (strcmp (pp->e.gv->config.domainTag, "") != 0) - { - dst->present |= PP_DOMAIN_TAG; - dst->aliased |= PP_DOMAIN_TAG; - dst->domain_tag = pp->e.gv->config.domainTag; - } - - // Construct unicast locator parameters - { - struct locators_builder def_uni = locators_builder_init (&dst->default_unicast_locators, locs->def_uni, MAX_XMIT_CONNS); - struct locators_builder meta_uni = locators_builder_init (&dst->metatraffic_unicast_locators, locs->meta_uni, MAX_XMIT_CONNS); - for (int i = 0; i < pp->e.gv->n_interfaces; i++) - { - if (!pp->e.gv->xmit_conns[i]->m_factory->m_enable_spdp) - { - // skip any interfaces where the address kind doesn't match the selected transport - // as a reasonablish way of not advertising iceoryx locators here - continue; - } -#ifndef NDEBUG - int32_t kind; -#endif - uint32_t data_port, meta_port; - if (pp->e.gv->config.many_sockets_mode != DDSI_MSM_MANY_UNICAST) - { -#ifndef NDEBUG - kind = pp->e.gv->loc_default_uc.kind; -#endif - assert (kind == pp->e.gv->loc_meta_uc.kind); - data_port = pp->e.gv->loc_default_uc.port; - meta_port = pp->e.gv->loc_meta_uc.port; - } - else - { -#ifndef NDEBUG - kind = pp->m_locator.kind; -#endif - data_port = meta_port = pp->m_locator.port; - } - assert (kind == pp->e.gv->interfaces[i].extloc.kind); - locators_add_one (&def_uni, &pp->e.gv->interfaces[i].extloc, data_port); - locators_add_one (&meta_uni, &pp->e.gv->interfaces[i].extloc, meta_port); - } - if (pp->e.gv->config.publish_uc_locators) - { - dst->present |= PP_DEFAULT_UNICAST_LOCATOR | PP_METATRAFFIC_UNICAST_LOCATOR; - dst->aliased |= PP_DEFAULT_UNICAST_LOCATOR | PP_METATRAFFIC_UNICAST_LOCATOR; - } - } - - if (include_multicast_locator_in_discovery (pp)) - { - dst->present |= PP_DEFAULT_MULTICAST_LOCATOR | PP_METATRAFFIC_MULTICAST_LOCATOR; - dst->aliased |= PP_DEFAULT_MULTICAST_LOCATOR | PP_METATRAFFIC_MULTICAST_LOCATOR; - struct locators_builder def_mc = locators_builder_init (&dst->default_multicast_locators, &locs->def_multi, 1); - struct locators_builder meta_mc = locators_builder_init (&dst->metatraffic_multicast_locators, &locs->meta_multi, 1); - locators_add_one (&def_mc, &pp->e.gv->loc_default_mc, DDSI_LOCATOR_PORT_INVALID); - locators_add_one (&meta_mc, &pp->e.gv->loc_meta_mc, DDSI_LOCATOR_PORT_INVALID); - } - - /* Add Adlink specific version information */ - { - dst->present |= PP_ADLINK_PARTICIPANT_VERSION_INFO; - memset (&dst->adlink_participant_version_info, 0, sizeof (dst->adlink_participant_version_info)); - dst->adlink_participant_version_info.version = 0; - dst->adlink_participant_version_info.flags = - DDSI_ADLINK_FL_DDSI2_PARTICIPANT_FLAG | - DDSI_ADLINK_FL_PTBES_FIXED_0 | - DDSI_ADLINK_FL_SUPPORTS_STATUSINFOX; - if (pp->e.gv->config.besmode == DDSI_BESMODE_MINIMAL) - dst->adlink_participant_version_info.flags |= DDSI_ADLINK_FL_MINIMAL_BES_MODE; - ddsrt_mutex_lock (&pp->e.gv->privileged_pp_lock); - if (pp->is_ddsi2_pp) - dst->adlink_participant_version_info.flags |= DDSI_ADLINK_FL_PARTICIPANT_IS_DDSI2; - ddsrt_mutex_unlock (&pp->e.gv->privileged_pp_lock); - -#if DDSRT_HAVE_GETHOSTNAME - if (ddsrt_gethostname(node, sizeof(node)-1) < 0) -#endif - (void) ddsrt_strlcpy (node, "unknown", sizeof (node)); - size = strlen(node) + strlen(DDS_VERSION) + strlen(DDS_HOST_NAME) + strlen(DDS_TARGET_NAME) + 4; /* + ///'\0' */ - dst->adlink_participant_version_info.internals = ddsrt_malloc(size); - (void) snprintf(dst->adlink_participant_version_info.internals, size, "%s/%s/%s/%s", node, DDS_VERSION, DDS_HOST_NAME, DDS_TARGET_NAME); - ETRACE (pp, "ddsi_spdp_write("PGUIDFMT") - internals: %s\n", PGUID (pp->e.guid), dst->adlink_participant_version_info.internals); - } - - /* Add Cyclone specific information */ - { - const uint32_t bufsz = ddsi_receive_buffer_size (pp->e.gv->m_factory); - if (bufsz > 0) - { - dst->present |= PP_CYCLONE_RECEIVE_BUFFER_SIZE; - dst->cyclone_receive_buffer_size = bufsz; - } - } - if (pp->e.gv->config.redundant_networking) - { - dst->present |= PP_CYCLONE_REDUNDANT_NETWORKING; - dst->cyclone_redundant_networking = true; - } - -#ifdef DDS_HAS_SECURITY - /* Add Security specific information. */ - if (ddsi_omg_get_participant_security_info (pp, &(dst->participant_security_info))) { - dst->present |= PP_PARTICIPANT_SECURITY_INFO; - dst->aliased |= PP_PARTICIPANT_SECURITY_INFO; - } -#endif - - /* Participant QoS's insofar as they are set, different from the default, and mapped to the SPDP data, rather than to the Adlink-specific CMParticipant endpoint. */ - qosdiff = ddsi_xqos_delta (&pp->plist->qos, &ddsi_default_qos_participant, DDSI_QP_USER_DATA | DDSI_QP_ENTITY_NAME | DDSI_QP_PROPERTY_LIST | DDSI_QP_LIVELINESS); - if (pp->e.gv->config.explicitly_publish_qos_set_to_default) - qosdiff |= ~(DDSI_QP_UNRECOGNIZED_INCOMPATIBLE_MASK | DDSI_QP_LIVELINESS); - - assert (dst->qos.present == 0); - ddsi_plist_mergein_missing (dst, pp->plist, 0, qosdiff); -#ifdef DDS_HAS_SECURITY - if (ddsi_omg_participant_is_secure(pp)) - ddsi_plist_mergein_missing (dst, pp->plist, PP_IDENTITY_TOKEN | PP_PERMISSIONS_TOKEN, 0); -#endif -} - -static int write_and_fini_plist (struct ddsi_writer *wr, ddsi_plist_t *ps, bool alive) -{ - struct ddsi_serdata *serdata = ddsi_serdata_from_sample (wr->type, alive ? SDK_DATA : SDK_KEY, ps); - ddsi_plist_fini (ps); - serdata->statusinfo = alive ? 0 : (DDSI_STATUSINFO_DISPOSE | DDSI_STATUSINFO_UNREGISTER); - serdata->timestamp = ddsrt_time_wallclock (); - return ddsi_write_sample_nogc_notk (ddsi_lookup_thread_state (), NULL, wr, serdata); -} - -int ddsi_spdp_write (struct ddsi_participant *pp) -{ - struct ddsi_writer *wr; - ddsi_plist_t ps; - struct ddsi_participant_builtin_topic_data_locators locs; - - if (pp->e.onlylocal) { - /* This topic is only locally available. */ - return 0; - } - - ETRACE (pp, "ddsi_spdp_write("PGUIDFMT")\n", PGUID (pp->e.guid)); - - if ((wr = ddsi_get_builtin_writer (pp, DDSI_ENTITYID_SPDP_BUILTIN_PARTICIPANT_WRITER)) == NULL) - { - ETRACE (pp, "ddsi_spdp_write("PGUIDFMT") - builtin participant writer not found\n", PGUID (pp->e.guid)); - return 0; - } - - ddsi_get_participant_builtin_topic_data (pp, &ps, &locs); - return write_and_fini_plist (wr, &ps, true); -} - -static int ddsi_spdp_dispose_unregister_with_wr (struct ddsi_participant *pp, unsigned entityid) -{ - ddsi_plist_t ps; - struct ddsi_writer *wr; - - if ((wr = ddsi_get_builtin_writer (pp, entityid)) == NULL) - { - ETRACE (pp, "ddsi_spdp_dispose_unregister("PGUIDFMT") - builtin participant %s writer not found\n", - PGUID (pp->e.guid), - entityid == DDSI_ENTITYID_SPDP_RELIABLE_BUILTIN_PARTICIPANT_SECURE_WRITER ? "secure" : ""); - return 0; - } - - ddsi_plist_init_empty (&ps); - ps.present |= PP_PARTICIPANT_GUID; - ps.participant_guid = pp->e.guid; - return write_and_fini_plist (wr, &ps, false); -} - -int ddsi_spdp_dispose_unregister (struct ddsi_participant *pp) -{ - /* - * When disposing a participant, it should be announced on both the - * non-secure and secure writers. - * The receiver will decide from which writer it accepts the dispose. - */ - int ret = ddsi_spdp_dispose_unregister_with_wr(pp, DDSI_ENTITYID_SPDP_BUILTIN_PARTICIPANT_WRITER); - if ((ret > 0) && ddsi_omg_participant_is_secure(pp)) - { - ret = ddsi_spdp_dispose_unregister_with_wr(pp, DDSI_ENTITYID_SPDP_RELIABLE_BUILTIN_PARTICIPANT_SECURE_WRITER); - } - return ret; -} - -static unsigned pseudo_random_delay (const ddsi_guid_t *x, const ddsi_guid_t *y, ddsrt_mtime_t tnow) -{ - /* You know, an ordinary random generator would be even better, but - the C library doesn't have a reentrant one and I don't feel like - integrating, say, the Mersenne Twister right now. */ - static const uint64_t cs[] = { - UINT64_C (15385148050874689571), - UINT64_C (17503036526311582379), - UINT64_C (11075621958654396447), - UINT64_C ( 9748227842331024047), - UINT64_C (14689485562394710107), - UINT64_C (17256284993973210745), - UINT64_C ( 9288286355086959209), - UINT64_C (17718429552426935775), - UINT64_C (10054290541876311021), - UINT64_C (13417933704571658407) - }; - uint32_t a = x->prefix.u[0], b = x->prefix.u[1], c = x->prefix.u[2], d = x->entityid.u; - uint32_t e = y->prefix.u[0], f = y->prefix.u[1], g = y->prefix.u[2], h = y->entityid.u; - uint32_t i = (uint32_t) ((uint64_t) tnow.v >> 32), j = (uint32_t) tnow.v; - uint64_t m = 0; - m += (a + cs[0]) * (b + cs[1]); - m += (c + cs[2]) * (d + cs[3]); - m += (e + cs[4]) * (f + cs[5]); - m += (g + cs[6]) * (h + cs[7]); - m += (i + cs[8]) * (j + cs[9]); - return (unsigned) (m >> 32); -} - -static void respond_to_spdp (const struct ddsi_domaingv *gv, const ddsi_guid_t *dest_proxypp_guid) -{ - struct ddsi_entity_enum_participant est; - struct ddsi_participant *pp; - ddsrt_mtime_t tnow = ddsrt_time_monotonic (); - ddsi_entidx_enum_participant_init (&est, gv->entity_index); - while ((pp = ddsi_entidx_enum_participant_next (&est)) != NULL) - { - /* delay_base has 32 bits, so delay_norm is approximately 1s max; - delay_max <= 1s by gv.config checks */ - unsigned delay_base = pseudo_random_delay (&pp->e.guid, dest_proxypp_guid, tnow); - unsigned delay_norm = delay_base >> 2; - int64_t delay_max_ms = gv->config.spdp_response_delay_max / 1000000; - int64_t delay = (int64_t) delay_norm * delay_max_ms / 1000; - ddsrt_mtime_t tsched = ddsrt_mtime_add_duration (tnow, delay); - GVTRACE (" %"PRId64, delay); - if (!pp->e.gv->config.unicast_response_to_spdp_messages) - /* pp can't reach gc_delete_participant => can safely reschedule */ - (void) ddsi_resched_xevent_if_earlier (pp->spdp_xevent, tsched); - else - ddsi_qxev_spdp (gv->xevents, tsched, &pp->e.guid, dest_proxypp_guid); - } - ddsi_entidx_enum_participant_fini (&est); -} - -static int handle_spdp_dead (const struct ddsi_receiver_state *rst, ddsi_entityid_t pwr_entityid, ddsrt_wctime_t timestamp, const ddsi_plist_t *datap, unsigned statusinfo) -{ - struct ddsi_domaingv * const gv = rst->gv; - ddsi_guid_t guid; - - GVLOGDISC ("SPDP ST%x", statusinfo); - - if (datap->present & PP_PARTICIPANT_GUID) - { - guid = datap->participant_guid; - GVLOGDISC (" %"PRIx32":%"PRIx32":%"PRIx32":%"PRIx32, PGUID (guid)); - assert (guid.entityid.u == DDSI_ENTITYID_PARTICIPANT); - if (ddsi_is_proxy_participant_deletion_allowed(gv, &guid, pwr_entityid)) - { - if (ddsi_delete_proxy_participant_by_guid (gv, &guid, timestamp, 0) < 0) - { - GVLOGDISC (" unknown"); - } - else - { - GVLOGDISC (" delete"); - } - } - else - { - GVLOGDISC (" not allowed"); - } - } - else - { - GVWARNING ("data (SPDP, vendor %u.%u): no/invalid payload\n", rst->vendor.id[0], rst->vendor.id[1]); - } - return 1; -} - -static struct ddsi_proxy_participant *find_ddsi2_proxy_participant (const struct ddsi_entity_index *entidx, const ddsi_guid_t *ppguid) -{ - struct ddsi_entity_enum_proxy_participant it; - struct ddsi_proxy_participant *pp; - ddsi_entidx_enum_proxy_participant_init (&it, entidx); - while ((pp = ddsi_entidx_enum_proxy_participant_next (&it)) != NULL) - { - if (ddsi_vendor_is_eclipse_or_opensplice (pp->vendor) && pp->e.guid.prefix.u[0] == ppguid->prefix.u[0] && pp->is_ddsi2_pp) - break; - } - ddsi_entidx_enum_proxy_participant_fini (&it); - return pp; -} - -static void make_participants_dependent_on_ddsi2 (struct ddsi_domaingv *gv, const ddsi_guid_t *ddsi2guid, ddsrt_wctime_t timestamp) -{ - struct ddsi_entity_enum_proxy_participant it; - struct ddsi_proxy_participant *pp, *d2pp; - if ((d2pp = ddsi_entidx_lookup_proxy_participant_guid (gv->entity_index, ddsi2guid)) == NULL) - return; - ddsi_entidx_enum_proxy_participant_init (&it, gv->entity_index); - while ((pp = ddsi_entidx_enum_proxy_participant_next (&it)) != NULL) - { - if (ddsi_vendor_is_eclipse_or_opensplice (pp->vendor) && pp->e.guid.prefix.u[0] == ddsi2guid->prefix.u[0] && !pp->is_ddsi2_pp) - { - GVTRACE ("proxy participant "PGUIDFMT" depends on ddsi2 "PGUIDFMT, PGUID (pp->e.guid), PGUID (*ddsi2guid)); - ddsrt_mutex_lock (&pp->e.lock); - pp->privileged_pp_guid = *ddsi2guid; - ddsrt_mutex_unlock (&pp->e.lock); - ddsi_proxy_participant_reassign_lease (pp, d2pp->lease); - GVTRACE ("\n"); - - if (ddsi_entidx_lookup_proxy_participant_guid (gv->entity_index, ddsi2guid) == NULL) - { - /* If DDSI2 has been deleted here (i.e., very soon after - having been created), we don't know whether pp will be - deleted */ - break; - } - } - } - ddsi_entidx_enum_proxy_participant_fini (&it); - - if (pp != NULL) - { - GVTRACE ("make_participants_dependent_on_ddsi2: ddsi2 "PGUIDFMT" is no more, delete "PGUIDFMT"\n", PGUID (*ddsi2guid), PGUID (pp->e.guid)); - ddsi_delete_proxy_participant_by_guid (gv, &pp->e.guid, timestamp, 1); - } -} - -static int handle_spdp_alive (const struct ddsi_receiver_state *rst, ddsi_seqno_t seq, ddsrt_wctime_t timestamp, const ddsi_plist_t *datap) -{ - struct ddsi_domaingv * const gv = rst->gv; - const unsigned bes_sedp_announcer_mask = - DDSI_DISC_BUILTIN_ENDPOINT_SUBSCRIPTION_ANNOUNCER | - DDSI_DISC_BUILTIN_ENDPOINT_PUBLICATION_ANNOUNCER; - struct ddsi_addrset *as_meta, *as_default; - uint32_t builtin_endpoint_set; - ddsi_guid_t privileged_pp_guid; - dds_duration_t lease_duration; - unsigned custom_flags = 0; - - /* If advertised domain id or domain tag doesn't match, ignore the message. Do this first to - minimize the impact such messages have. */ - { - const uint32_t domain_id = (datap->present & PP_DOMAIN_ID) ? datap->domain_id : gv->config.extDomainId.value; - const char *domain_tag = (datap->present & PP_DOMAIN_TAG) ? datap->domain_tag : ""; - if (domain_id != gv->config.extDomainId.value || strcmp (domain_tag, gv->config.domainTag) != 0) - { - GVTRACE ("ignore remote participant in mismatching domain %"PRIu32" tag \"%s\"\n", domain_id, domain_tag); - return 0; - } - } - - if (!(datap->present & PP_PARTICIPANT_GUID) || !(datap->present & PP_BUILTIN_ENDPOINT_SET)) - { - GVWARNING ("data (SPDP, vendor %u.%u): no/invalid payload\n", rst->vendor.id[0], rst->vendor.id[1]); - return 0; - } - - /* At some point the RTI implementation didn't mention - BUILTIN_ENDPOINT_DDSI_PARTICIPANT_MESSAGE_DATA_READER & ...WRITER, or - so it seemed; and yet they are necessary for correct operation, - so add them. */ - builtin_endpoint_set = datap->builtin_endpoint_set; - if (ddsi_vendor_is_rti (rst->vendor) && - ((builtin_endpoint_set & - (DDSI_BUILTIN_ENDPOINT_PARTICIPANT_MESSAGE_DATA_READER | - DDSI_BUILTIN_ENDPOINT_PARTICIPANT_MESSAGE_DATA_WRITER)) - != (DDSI_BUILTIN_ENDPOINT_PARTICIPANT_MESSAGE_DATA_READER | - DDSI_BUILTIN_ENDPOINT_PARTICIPANT_MESSAGE_DATA_WRITER)) && - gv->config.assume_rti_has_pmd_endpoints) - { - GVLOGDISC ("data (SPDP, vendor %u.%u): assuming unadvertised PMD endpoints do exist\n", - rst->vendor.id[0], rst->vendor.id[1]); - builtin_endpoint_set |= - DDSI_BUILTIN_ENDPOINT_PARTICIPANT_MESSAGE_DATA_READER | - DDSI_BUILTIN_ENDPOINT_PARTICIPANT_MESSAGE_DATA_WRITER; - } - - /* Do we know this GUID already? */ - { - struct ddsi_entity_common *existing_entity; - if ((existing_entity = ddsi_entidx_lookup_guid_untyped (gv->entity_index, &datap->participant_guid)) == NULL) - { - /* Local SPDP packets may be looped back, and that can include ones - for participants currently being deleted. The first thing that - happens when deleting a participant is removing it from the hash - table, and consequently the looped back packet may appear to be - from an unknown participant. So we handle that. */ - if (ddsi_is_deleted_participant_guid (gv->deleted_participants, &datap->participant_guid, DDSI_DELETED_PPGUID_REMOTE)) - { - RSTTRACE ("SPDP ST0 "PGUIDFMT" (recently deleted)", PGUID (datap->participant_guid)); - return 0; - } - } - else if (existing_entity->kind == DDSI_EK_PARTICIPANT) - { - RSTTRACE ("SPDP ST0 "PGUIDFMT" (local)", PGUID (datap->participant_guid)); - return 0; - } - else if (existing_entity->kind == DDSI_EK_PROXY_PARTICIPANT) - { - struct ddsi_proxy_participant *proxypp = (struct ddsi_proxy_participant *) existing_entity; - struct ddsi_lease *lease; - int interesting = 0; - RSTTRACE ("SPDP ST0 "PGUIDFMT" (known)", PGUID (datap->participant_guid)); - /* SPDP processing is so different from normal processing that we are - even skipping the automatic lease renewal. Note that proxy writers - that are not alive are not set alive here. This is done only when - data is received from a particular pwr (in handle_regular) */ - if ((lease = ddsrt_atomic_ldvoidp (&proxypp->minl_auto)) != NULL) - ddsi_lease_renew (lease, ddsrt_time_elapsed ()); - ddsrt_mutex_lock (&proxypp->e.lock); - if (proxypp->implicitly_created || seq > proxypp->seq) - { - interesting = 1; - if (!(gv->logconfig.c.mask & DDS_LC_TRACE)) - GVLOGDISC ("SPDP ST0 "PGUIDFMT, PGUID (datap->participant_guid)); - GVLOGDISC (proxypp->implicitly_created ? " (NEW was-implicitly-created)" : " (update)"); - proxypp->implicitly_created = 0; - ddsi_update_proxy_participant_plist_locked (proxypp, seq, datap, timestamp); - } - ddsrt_mutex_unlock (&proxypp->e.lock); - return interesting; - } - else - { - /* mismatch on entity kind: that should never have gotten past the - input validation */ - GVWARNING ("data (SPDP, vendor %u.%u): "PGUIDFMT" kind mismatch\n", rst->vendor.id[0], rst->vendor.id[1], PGUID (datap->participant_guid)); - return 0; - } - } - - const bool is_secure = ((datap->builtin_endpoint_set & DDSI_DISC_BUILTIN_ENDPOINT_PARTICIPANT_SECURE_ANNOUNCER) != 0 && - (datap->present & PP_IDENTITY_TOKEN)); - /* Make sure we don't create any security builtin endpoint when it's considered unsecure. */ - if (!is_secure) - builtin_endpoint_set &= DDSI_BES_MASK_NON_SECURITY; - GVLOGDISC ("SPDP ST0 "PGUIDFMT" bes %"PRIx32"%s NEW", PGUID (datap->participant_guid), builtin_endpoint_set, is_secure ? " (secure)" : ""); - - if (datap->present & PP_ADLINK_PARTICIPANT_VERSION_INFO) { - if ((datap->adlink_participant_version_info.flags & DDSI_ADLINK_FL_DDSI2_PARTICIPANT_FLAG) && - (datap->adlink_participant_version_info.flags & DDSI_ADLINK_FL_PARTICIPANT_IS_DDSI2)) - custom_flags |= DDSI_CF_PARTICIPANT_IS_DDSI2; - - GVLOGDISC (" (0x%08"PRIx32"-0x%08"PRIx32"-0x%08"PRIx32"-0x%08"PRIx32"-0x%08"PRIx32" %s)", - datap->adlink_participant_version_info.version, - datap->adlink_participant_version_info.flags, - datap->adlink_participant_version_info.unused[0], - datap->adlink_participant_version_info.unused[1], - datap->adlink_participant_version_info.unused[2], - datap->adlink_participant_version_info.internals); - } - - /* Can't do "mergein_missing" because of constness of *datap */ - if (datap->qos.present & DDSI_QP_LIVELINESS) - lease_duration = datap->qos.liveliness.lease_duration; - else - { - assert (ddsi_default_qos_participant.present & DDSI_QP_LIVELINESS); - lease_duration = ddsi_default_qos_participant.liveliness.lease_duration; - } - /* If any of the SEDP announcer are missing AND the guid prefix of - the SPDP writer differs from the guid prefix of the new participant, - we make it dependent on the writer's participant. See also the - lease expiration handling. Note that the entityid MUST be - DDSI_ENTITYID_PARTICIPANT or entidx_lookup will assert. So we only - zero the prefix. */ - privileged_pp_guid.prefix = rst->src_guid_prefix; - privileged_pp_guid.entityid.u = DDSI_ENTITYID_PARTICIPANT; - if ((builtin_endpoint_set & bes_sedp_announcer_mask) != bes_sedp_announcer_mask && - memcmp (&privileged_pp_guid, &datap->participant_guid, sizeof (ddsi_guid_t)) != 0) - { - GVLOGDISC (" (depends on "PGUIDFMT")", PGUID (privileged_pp_guid)); - /* never expire lease for this proxy: it won't actually expire - until the "privileged" one expires anyway */ - lease_duration = DDS_INFINITY; - } - else if (ddsi_vendor_is_eclipse_or_opensplice (rst->vendor) && !(custom_flags & DDSI_CF_PARTICIPANT_IS_DDSI2)) - { - /* Non-DDSI2 participants are made dependent on DDSI2 (but DDSI2 - itself need not be discovered yet) */ - struct ddsi_proxy_participant *ddsi2; - if ((ddsi2 = find_ddsi2_proxy_participant (gv->entity_index, &datap->participant_guid)) == NULL) - memset (&privileged_pp_guid.prefix, 0, sizeof (privileged_pp_guid.prefix)); - else - { - privileged_pp_guid.prefix = ddsi2->e.guid.prefix; - lease_duration = DDS_INFINITY; - GVLOGDISC (" (depends on "PGUIDFMT")", PGUID (privileged_pp_guid)); - } - } - else - { - memset (&privileged_pp_guid.prefix, 0, sizeof (privileged_pp_guid.prefix)); - } - - /* Choose locators */ - { - const ddsi_locators_t emptyset = { .n = 0, .first = NULL, .last = NULL }; - const ddsi_locators_t *uc; - const ddsi_locators_t *mc; - ddsi_locator_t srcloc; - interface_set_t intfs; - - srcloc = rst->srcloc; - uc = (datap->present & PP_DEFAULT_UNICAST_LOCATOR) ? &datap->default_unicast_locators : ∅ - mc = (datap->present & PP_DEFAULT_MULTICAST_LOCATOR) ? &datap->default_multicast_locators : ∅ - if (gv->config.tcp_use_peeraddr_for_unicast) - uc = ∅ // force use of source locator - else if (uc != &emptyset) - ddsi_set_unspec_locator (&srcloc); // can't always use the source address - - interface_set_init (&intfs); - as_default = addrset_from_locatorlists (gv, uc, mc, &srcloc, &intfs); - - srcloc = rst->srcloc; - uc = (datap->present & PP_METATRAFFIC_UNICAST_LOCATOR) ? &datap->metatraffic_unicast_locators : ∅ - mc = (datap->present & PP_METATRAFFIC_MULTICAST_LOCATOR) ? &datap->metatraffic_multicast_locators : ∅ - if (gv->config.tcp_use_peeraddr_for_unicast) - uc = ∅ // force use of source locator - else if (uc != &emptyset) - ddsi_set_unspec_locator (&srcloc); // can't always use the source address - interface_set_init (&intfs); - as_meta = addrset_from_locatorlists (gv, uc, mc, &srcloc, &intfs); - - ddsi_log_addrset (gv, DDS_LC_DISCOVERY, " (data", as_default); - ddsi_log_addrset (gv, DDS_LC_DISCOVERY, " meta", as_meta); - GVLOGDISC (")"); - } - - if (ddsi_addrset_empty_uc (as_default) || ddsi_addrset_empty_uc (as_meta)) - { - GVLOGDISC (" (no unicast address"); - ddsi_unref_addrset (as_default); - ddsi_unref_addrset (as_meta); - return 1; - } - - GVLOGDISC (" QOS={"); - ddsi_xqos_log (DDS_LC_DISCOVERY, &gv->logconfig, &datap->qos); - GVLOGDISC ("}\n"); - - maybe_add_pp_as_meta_to_as_disc (gv, as_meta); - - if (!ddsi_new_proxy_participant (gv, &datap->participant_guid, builtin_endpoint_set, &privileged_pp_guid, as_default, as_meta, datap, lease_duration, rst->vendor, custom_flags, timestamp, seq)) - { - /* If no proxy participant was created, don't respond */ - return 0; - } - else - { - /* Force transmission of SPDP messages - we're not very careful - in avoiding the processing of SPDP packets addressed to others - so filter here */ - int have_dst = (rst->dst_guid_prefix.u[0] != 0 || rst->dst_guid_prefix.u[1] != 0 || rst->dst_guid_prefix.u[2] != 0); - if (!have_dst) - { - GVLOGDISC ("broadcasted SPDP packet -> answering"); - respond_to_spdp (gv, &datap->participant_guid); - } - else - { - GVLOGDISC ("directed SPDP packet -> not responding\n"); - } - - if (custom_flags & DDSI_CF_PARTICIPANT_IS_DDSI2) - { - /* If we just discovered DDSI2, make sure any existing - participants served by it are made dependent on it */ - make_participants_dependent_on_ddsi2 (gv, &datap->participant_guid, timestamp); - } - else if (privileged_pp_guid.prefix.u[0] || privileged_pp_guid.prefix.u[1] || privileged_pp_guid.prefix.u[2]) - { - /* If we just created a participant dependent on DDSI2, make sure - DDSI2 still exists. There is a risk of racing the lease expiry - of DDSI2. */ - if (ddsi_entidx_lookup_proxy_participant_guid (gv->entity_index, &privileged_pp_guid) == NULL) - { - GVLOGDISC ("make_participants_dependent_on_ddsi2: ddsi2 "PGUIDFMT" is no more, delete "PGUIDFMT"\n", - PGUID (privileged_pp_guid), PGUID (datap->participant_guid)); - ddsi_delete_proxy_participant_by_guid (gv, &datap->participant_guid, timestamp, 1); - } - } - return 1; - } -} - -static void handle_spdp (const struct ddsi_receiver_state *rst, ddsi_entityid_t pwr_entityid, ddsi_seqno_t seq, const struct ddsi_serdata *serdata) -{ - struct ddsi_domaingv * const gv = rst->gv; - ddsi_plist_t decoded_data; - if (ddsi_serdata_to_sample (serdata, &decoded_data, NULL, NULL)) - { - int interesting = 0; - switch (serdata->statusinfo & (DDSI_STATUSINFO_DISPOSE | DDSI_STATUSINFO_UNREGISTER)) - { - case 0: - interesting = handle_spdp_alive (rst, seq, serdata->timestamp, &decoded_data); - break; - - case DDSI_STATUSINFO_DISPOSE: - case DDSI_STATUSINFO_UNREGISTER: - case (DDSI_STATUSINFO_DISPOSE | DDSI_STATUSINFO_UNREGISTER): - interesting = handle_spdp_dead (rst, pwr_entityid, serdata->timestamp, &decoded_data, serdata->statusinfo); - break; - } - - ddsi_plist_fini (&decoded_data); - GVLOG (interesting ? DDS_LC_DISCOVERY : DDS_LC_TRACE, "\n"); - } -} - -struct add_locator_to_ps_arg { - struct ddsi_domaingv *gv; - ddsi_plist_t *ps; -}; - -static void add_locator_to_ps (const ddsi_locator_t *loc, void *varg) -{ - struct add_locator_to_ps_arg *arg = varg; - struct ddsi_locators_one *elem = ddsrt_malloc (sizeof (struct ddsi_locators_one)); - struct ddsi_locators *locs; - unsigned present_flag; - - elem->loc = *loc; - elem->next = NULL; - - if (ddsi_is_mcaddr (arg->gv, loc)) { - locs = &arg->ps->multicast_locators; - present_flag = PP_MULTICAST_LOCATOR; - } else { - locs = &arg->ps->unicast_locators; - present_flag = PP_UNICAST_LOCATOR; - } - - if (!(arg->ps->present & present_flag)) - { - locs->n = 0; - locs->first = locs->last = NULL; - arg->ps->present |= present_flag; - } - locs->n++; - if (locs->first) - locs->last->next = elem; - else - locs->first = elem; - locs->last = elem; -} - -static void add_xlocator_to_ps (const ddsi_xlocator_t *loc, void *varg) -{ - add_locator_to_ps (&loc->c, varg); -} - -#ifdef DDS_HAS_SHM -static void add_iox_locator_to_ps(const ddsi_locator_t* loc, struct add_locator_to_ps_arg *arg) -{ - struct ddsi_locators_one* elem = ddsrt_malloc(sizeof(struct ddsi_locators_one)); - struct ddsi_locators* locs = &arg->ps->unicast_locators; - unsigned present_flag = PP_UNICAST_LOCATOR; - - elem->loc = *loc; - elem->next = NULL; - - if (!(arg->ps->present & present_flag)) - { - locs->n = 0; - locs->first = locs->last = NULL; - arg->ps->present |= present_flag; - } - - //add iceoryx to the FRONT of the list of addresses, to indicate its higher priority - if (locs->first) - elem->next = locs->first; - else - locs->last = elem; - locs->first = elem; - locs->n++; -} -#endif - -/****************************************************************************** - *** - *** SEDP - *** - *****************************************************************************/ - -static struct ddsi_writer *get_sedp_writer (const struct ddsi_participant *pp, unsigned entityid) -{ - struct ddsi_writer *sedp_wr = ddsi_get_builtin_writer (pp, entityid); - if (sedp_wr == NULL) - DDS_FATAL ("sedp_write_writer: no SEDP builtin writer %x for "PGUIDFMT"\n", entityid, PGUID (pp->e.guid)); - return sedp_wr; -} - -static int sedp_write_endpoint_impl -( - struct ddsi_writer *wr, int alive, const ddsi_guid_t *guid, - const struct ddsi_endpoint_common *epcommon, - const dds_qos_t *xqos, struct ddsi_addrset *as, ddsi_security_info_t *security -#ifdef DDS_HAS_TYPE_DISCOVERY - , const struct ddsi_sertype *sertype -#endif -) -{ - struct ddsi_domaingv * const gv = wr->e.gv; - const dds_qos_t *defqos = NULL; - if (ddsi_is_writer_entityid (guid->entityid)) - defqos = &ddsi_default_qos_writer; - else if (ddsi_is_reader_entityid (guid->entityid)) - defqos = &ddsi_default_qos_reader; - else - assert (false); - - uint64_t qosdiff; - ddsi_plist_t ps; - - ddsi_plist_init_empty (&ps); - ps.present |= PP_ENDPOINT_GUID; - ps.endpoint_guid = *guid; - -#ifdef DDS_HAS_SECURITY - if (security) - { - ps.present |= PP_ENDPOINT_SECURITY_INFO; - memcpy(&ps.endpoint_security_info, security, sizeof(ddsi_security_info_t)); - } -#else - (void)security; - assert(security == NULL); -#endif - - if (!alive) - { - assert (xqos == NULL); - assert (epcommon == NULL); - qosdiff = 0; - } - else - { - assert (xqos != NULL); - ps.present |= PP_PROTOCOL_VERSION | PP_VENDORID; - ps.protocol_version.major = DDSI_RTPS_MAJOR; - ps.protocol_version.minor = DDSI_RTPS_MINOR; - ps.vendorid = DDSI_VENDORID_ECLIPSE; - - assert (epcommon != NULL); - - if (epcommon->group_guid.entityid.u != 0) - { - ps.present |= PP_GROUP_GUID; - ps.group_guid = epcommon->group_guid; - } - - if (!ddsi_is_writer_entityid (guid->entityid)) - { - const struct ddsi_reader *rd = ddsi_entidx_lookup_reader_guid (gv->entity_index, guid); - assert (rd); - if (rd->request_keyhash) - { - ps.present |= PP_CYCLONE_REQUESTS_KEYHASH; - ps.cyclone_requests_keyhash = 1u; - } - } - -#ifdef DDS_HAS_SSM - /* A bit of a hack -- the easy alternative would be to make it yet - another parameter. We only set "reader favours SSM" if we - really do: no point in telling the world that everything is at - the default. */ - if (ddsi_is_reader_entityid (guid->entityid)) - { - const struct ddsi_reader *rd = ddsi_entidx_lookup_reader_guid (gv->entity_index, guid); - assert (rd); - if (rd->favours_ssm) - { - ps.present |= PP_READER_FAVOURS_SSM; - ps.reader_favours_ssm.state = 1u; - } - } -#endif - - qosdiff = ddsi_xqos_delta (xqos, defqos, ~(uint64_t)0); - if (gv->config.explicitly_publish_qos_set_to_default) - qosdiff |= ~DDSI_QP_UNRECOGNIZED_INCOMPATIBLE_MASK; - - struct add_locator_to_ps_arg arg; - arg.gv = gv; - arg.ps = &ps; - if (as) - ddsi_addrset_forall (as, add_xlocator_to_ps, &arg); - -#ifdef DDS_HAS_SHM - assert(wr->xqos->present & DDSI_QP_LOCATOR_MASK); - if (!(xqos->ignore_locator_type & DDSI_LOCATOR_KIND_SHEM)) - { - if (!(arg.ps->present & PP_UNICAST_LOCATOR) || 0 == arg.ps->unicast_locators.n) - { - if (epcommon->pp->e.gv->config.many_sockets_mode == DDSI_MSM_MANY_UNICAST) - add_locator_to_ps(&epcommon->pp->m_locator, &arg); - else - { - // FIXME: same as what SPDP uses, should be refactored, now more than ever - for (int i = 0; i < epcommon->pp->e.gv->n_interfaces; i++) - { - if (!epcommon->pp->e.gv->xmit_conns[i]->m_factory->m_enable_spdp) - { - // skip any interfaces where the address kind doesn't match the selected transport - // as a reasonablish way of not advertising iceoryx locators here - continue; - } - // FIXME: should have multiple loc_default_uc/loc_meta_uc or compute ports here - ddsi_locator_t loc = epcommon->pp->e.gv->interfaces[i].extloc; - loc.port = epcommon->pp->e.gv->loc_default_uc.port; - add_locator_to_ps(&loc, &arg); - } - } - } - - if (!(arg.ps->present & PP_MULTICAST_LOCATOR) || 0 == arg.ps->multicast_locators.n) - { - if (include_multicast_locator_in_discovery (epcommon->pp)) - add_locator_to_ps (&epcommon->pp->e.gv->loc_default_mc, &arg); - } - - add_iox_locator_to_ps(&gv->loc_iceoryx_addr, &arg); - } -#endif - -#ifdef DDS_HAS_TYPE_DISCOVERY - assert (sertype); - if ((ps.qos.type_information = ddsi_sertype_typeinfo (sertype))) - ps.qos.present |= DDSI_QP_TYPE_INFORMATION; -#endif - } - - if (xqos) - ddsi_xqos_mergein_missing (&ps.qos, xqos, qosdiff); - return write_and_fini_plist (wr, &ps, alive); -} - +#include "ddsi__discovery_addrset.h" +#include "ddsi__discovery_spdp.h" +#include "ddsi__discovery_endpoint.h" #ifdef DDS_HAS_TOPIC_DISCOVERY - -static int ddsi_sedp_write_topic_impl (struct ddsi_writer *wr, int alive, const ddsi_guid_t *guid, const dds_qos_t *xqos, ddsi_typeinfo_t *type_info) -{ - struct ddsi_domaingv * const gv = wr->e.gv; - const dds_qos_t *defqos = &ddsi_default_qos_topic; - - ddsi_plist_t ps; - ddsi_plist_init_empty (&ps); - ps.present |= PP_CYCLONE_TOPIC_GUID; - ps.topic_guid = *guid; - - assert (xqos != NULL); - ps.present |= PP_PROTOCOL_VERSION | PP_VENDORID; - ps.protocol_version.major = DDSI_RTPS_MAJOR; - ps.protocol_version.minor = DDSI_RTPS_MINOR; - ps.vendorid = DDSI_VENDORID_ECLIPSE; - - uint64_t qosdiff = ddsi_xqos_delta (xqos, defqos, ~(uint64_t)0); - if (gv->config.explicitly_publish_qos_set_to_default) - qosdiff |= ~DDSI_QP_UNRECOGNIZED_INCOMPATIBLE_MASK; - - if (type_info) - { - ps.qos.type_information = type_info; - ps.qos.present |= DDSI_QP_TYPE_INFORMATION; - } - if (xqos) - ddsi_xqos_mergein_missing (&ps.qos, xqos, qosdiff); - return write_and_fini_plist (wr, &ps, alive); -} - -int ddsi_sedp_write_topic (struct ddsi_topic *tp, bool alive) -{ - int res = 0; - if (!(tp->pp->bes & DDSI_DISC_BUILTIN_ENDPOINT_TOPICS_ANNOUNCER)) - return res; - if (!ddsi_is_builtin_entityid (tp->e.guid.entityid, DDSI_VENDORID_ECLIPSE) && !tp->e.onlylocal) - { - unsigned entityid = ddsi_determine_topic_writer (tp); - struct ddsi_writer *sedp_wr = get_sedp_writer (tp->pp, entityid); - ddsrt_mutex_lock (&tp->e.qos_lock); - // the allocation type info object is freed with the plist - res = ddsi_sedp_write_topic_impl (sedp_wr, alive, &tp->e.guid, tp->definition->xqos, ddsi_type_pair_complete_info (tp->e.gv, tp->definition->type_pair)); - ddsrt_mutex_unlock (&tp->e.qos_lock); - } - return res; -} - -#endif /* DDS_HAS_TOPIC_DISCOVERY */ - -int ddsi_sedp_write_writer (struct ddsi_writer *wr) -{ - if ((!ddsi_is_builtin_entityid(wr->e.guid.entityid, DDSI_VENDORID_ECLIPSE)) && (!wr->e.onlylocal)) - { - unsigned entityid = ddsi_determine_publication_writer(wr); - struct ddsi_writer *sedp_wr = get_sedp_writer (wr->c.pp, entityid); - ddsi_security_info_t *security = NULL; -#ifdef DDS_HAS_SSM - struct ddsi_addrset *as = wr->ssm_as; -#else - struct ddsi_addrset *as = NULL; -#endif -#ifdef DDS_HAS_SECURITY - ddsi_security_info_t tmp; - if (ddsi_omg_get_writer_security_info (wr, &tmp)) - { - security = &tmp; - } +#include "ddsi__discovery_topic.h" #endif +#include "ddsi__serdata_plist.h" +#include "ddsi__radmin.h" +#include "ddsi__entity_index.h" +#include "ddsi__entity.h" +#include "ddsi__participant.h" +#include "ddsi__xmsg.h" +#include "ddsi__transmit.h" +#include "ddsi__lease.h" +#include "ddsi__security_omg.h" +#include "ddsi__pmd.h" +#include "ddsi__endpoint.h" +#include "ddsi__plist.h" +#include "ddsi__proxy_endpoint.h" +#include "ddsi__proxy_participant.h" +#include "ddsi__topic.h" +#include "ddsi__tran.h" +#include "ddsi__typelib.h" +#include "ddsi__vendor.h" +#include "ddsi__xqos.h" +#include "ddsi__addrset.h" #ifdef DDS_HAS_TYPE_DISCOVERY - return sedp_write_endpoint_impl (sedp_wr, 1, &wr->e.guid, &wr->c, wr->xqos, as, security, wr->type); -#else - return sedp_write_endpoint_impl (sedp_wr, 1, &wr->e.guid, &wr->c, wr->xqos, as, security); +#include "ddsi__typelookup.h" #endif - } - return 0; -} - -int ddsi_sedp_write_reader (struct ddsi_reader *rd) -{ - if (ddsi_is_builtin_entityid (rd->e.guid.entityid, DDSI_VENDORID_ECLIPSE) || rd->e.onlylocal) - return 0; - unsigned entityid = ddsi_determine_subscription_writer(rd); - struct ddsi_writer *sedp_wr = get_sedp_writer (rd->c.pp, entityid); - ddsi_security_info_t *security = NULL; - struct ddsi_addrset *as = NULL; -#ifdef DDS_HAS_NETWORK_PARTITIONS - if (rd->uc_as != NULL || rd->mc_as != NULL) - { - // FIXME: do this without first creating a temporary addrset - as = ddsi_new_addrset (); - // use a placeholder connection to avoid exploding the multicast addreses to multiple - // interfaces - for (const struct ddsi_networkpartition_address *a = rd->uc_as; a != NULL; a = a->next) - ddsi_add_xlocator_to_addrset(rd->e.gv, as, &(const ddsi_xlocator_t) { - .c = a->loc, - .conn = rd->e.gv->xmit_conns[0] }); - for (const struct ddsi_networkpartition_address *a = rd->mc_as; a != NULL; a = a->next) - ddsi_add_xlocator_to_addrset(rd->e.gv, as, &(const ddsi_xlocator_t) { - .c = a->loc, - .conn = rd->e.gv->xmit_conns[0] }); - } -#endif #ifdef DDS_HAS_SECURITY - ddsi_security_info_t tmp; - if (ddsi_omg_get_reader_security_info (rd, &tmp)) - { - security = &tmp; - } -#endif -#ifdef DDS_HAS_TYPE_DISCOVERY - const int ret = sedp_write_endpoint_impl (sedp_wr, 1, &rd->e.guid, &rd->c, rd->xqos, as, security, rd->type); -#else - const int ret = sedp_write_endpoint_impl (sedp_wr, 1, &rd->e.guid, &rd->c, rd->xqos, as, security); -#endif - ddsi_unref_addrset (as); - return ret; -} - -int ddsi_sedp_dispose_unregister_writer (struct ddsi_writer *wr) -{ - if ((!ddsi_is_builtin_entityid(wr->e.guid.entityid, DDSI_VENDORID_ECLIPSE)) && (!wr->e.onlylocal)) - { - unsigned entityid = ddsi_determine_publication_writer(wr); - struct ddsi_writer *sedp_wr = get_sedp_writer (wr->c.pp, entityid); -#ifdef DDS_HAS_TYPE_DISCOVERY - return sedp_write_endpoint_impl (sedp_wr, 0, &wr->e.guid, NULL, NULL, NULL, NULL, NULL); -#else - return sedp_write_endpoint_impl (sedp_wr, 0, &wr->e.guid, NULL, NULL, NULL, NULL); -#endif - } - return 0; -} - -int ddsi_sedp_dispose_unregister_reader (struct ddsi_reader *rd) -{ - if ((!ddsi_is_builtin_entityid(rd->e.guid.entityid, DDSI_VENDORID_ECLIPSE)) && (!rd->e.onlylocal)) - { - unsigned entityid = ddsi_determine_subscription_writer(rd); - struct ddsi_writer *sedp_wr = get_sedp_writer (rd->c.pp, entityid); -#ifdef DDS_HAS_TYPE_DISCOVERY - return sedp_write_endpoint_impl (sedp_wr, 0, &rd->e.guid, NULL, NULL, NULL, NULL, NULL); -#else - return sedp_write_endpoint_impl (sedp_wr, 0, &rd->e.guid, NULL, NULL, NULL, NULL); +#include "ddsi__security_exchange.h" #endif - } - return 0; -} -static const char *durability_to_string (dds_durability_kind_t k) +struct ddsi_writer *ddsi_get_sedp_writer (const struct ddsi_participant *pp, unsigned entityid) { - switch (k) - { - case DDS_DURABILITY_VOLATILE: return "volatile"; - case DDS_DURABILITY_TRANSIENT_LOCAL: return "transient-local"; - case DDS_DURABILITY_TRANSIENT: return "transient"; - case DDS_DURABILITY_PERSISTENT: return "persistent"; - } - return "undefined-durability"; + struct ddsi_writer *sedp_wr = ddsi_get_builtin_writer (pp, entityid); + if (sedp_wr == NULL) + DDS_FATAL ("sedp_write_writer: no SEDP builtin writer %x for "PGUIDFMT"\n", entityid, PGUID (pp->e.guid)); + return sedp_wr; } -static struct ddsi_proxy_participant *implicitly_create_proxypp (struct ddsi_domaingv *gv, const ddsi_guid_t *ppguid, ddsi_plist_t *datap /* note: potentially modifies datap */, const ddsi_guid_prefix_t *src_guid_prefix, ddsi_vendorid_t vendorid, ddsrt_wctime_t timestamp, ddsi_seqno_t seq) +struct ddsi_proxy_participant *ddsi_implicitly_create_proxypp (struct ddsi_domaingv *gv, const ddsi_guid_t *ppguid, ddsi_plist_t *datap /* note: potentially modifies datap */, const ddsi_guid_prefix_t *src_guid_prefix, ddsi_vendorid_t vendorid, ddsrt_wctime_t timestamp, ddsi_seqno_t seq) { ddsi_guid_t privguid; ddsi_plist_t pp_plist; @@ -1479,7 +152,7 @@ static struct ddsi_proxy_participant *implicitly_create_proxypp (struct ddsi_dom return ddsi_entidx_lookup_proxy_participant_guid (gv->entity_index, ppguid); } -static bool check_sedp_kind_and_guid (ddsi_sedp_kind_t sedp_kind, const ddsi_guid_t *entity_guid) +bool ddsi_check_sedp_kind_and_guid (ddsi_sedp_kind_t sedp_kind, const ddsi_guid_t *entity_guid) { switch (sedp_kind) { @@ -1494,12 +167,12 @@ static bool check_sedp_kind_and_guid (ddsi_sedp_kind_t sedp_kind, const ddsi_gui return false; } -static bool handle_sedp_checks (struct ddsi_domaingv * const gv, ddsi_sedp_kind_t sedp_kind, ddsi_guid_t *entity_guid, ddsi_plist_t *datap, +bool ddsi_handle_sedp_checks (struct ddsi_domaingv * const gv, ddsi_sedp_kind_t sedp_kind, ddsi_guid_t *entity_guid, ddsi_plist_t *datap, const ddsi_guid_prefix_t *src_guid_prefix, ddsi_vendorid_t vendorid, ddsrt_wctime_t timestamp, struct ddsi_proxy_participant **proxypp, ddsi_guid_t *ppguid) { #define E(msg, lbl) do { GVLOGDISC (msg); return false; } while (0) - if (!check_sedp_kind_and_guid (sedp_kind, entity_guid)) + if (!ddsi_check_sedp_kind_and_guid (sedp_kind, entity_guid)) E (" SEDP topic/GUID entity kind mismatch\n", err); ppguid->prefix = entity_guid->prefix; ppguid->entityid.u = DDSI_ENTITYID_PARTICIPANT; @@ -1519,7 +192,7 @@ static bool handle_sedp_checks (struct ddsi_domaingv * const gv, ddsi_sedp_kind_ if ((*proxypp = ddsi_entidx_lookup_proxy_participant_guid (gv->entity_index, ppguid)) == NULL) { GVLOGDISC (" unknown-proxypp"); - if ((*proxypp = implicitly_create_proxypp (gv, ppguid, datap, src_guid_prefix, vendorid, timestamp, 0)) == NULL) + if ((*proxypp = ddsi_implicitly_create_proxypp (gv, ppguid, datap, src_guid_prefix, vendorid, timestamp, 0)) == NULL) E ("?\n", err); /* Repeat regular SEDP trace for convenience */ GVLOGDISC ("SEDP ST0 "PGUIDFMT" (cont)", PGUID (*entity_guid)); @@ -1528,319 +201,7 @@ static bool handle_sedp_checks (struct ddsi_domaingv * const gv, ddsi_sedp_kind_ #undef E } -struct ddsi_addrset_from_locatorlists_collect_interfaces_arg { - const struct ddsi_domaingv *gv; - interface_set_t *intfs; -}; - -/** @brief Figure out which interfaces are touched by (extended) locator @p loc - * - * Does this by looking up the connection in @p loc in the set of transmit connections. (There's plenty of room for optimisation here.) - * - * @param[in] loc locator - * @param[in] varg argument pointer, must point to a struct ddsi_addrset_from_locatorlists_collect_interfaces_arg - */ -static void addrset_from_locatorlists_collect_interfaces (const ddsi_xlocator_t *loc, void *varg) -{ - struct ddsi_addrset_from_locatorlists_collect_interfaces_arg *arg = varg; - struct ddsi_domaingv const * const gv = arg->gv; - for (int i = 0; i < gv->n_interfaces; i++) - { - //GVTRACE(" {%p,%p}", loc->conn, gv->xmit_conns[i]); - if (loc->conn == gv->xmit_conns[i]) - { - arg->intfs->xs[i] = true; - break; - } - } -} - -struct ddsi_addrset *ddsi_get_endpoint_addrset (const struct ddsi_domaingv *gv, const ddsi_plist_t *datap, struct ddsi_addrset *proxypp_as_default, const ddsi_locator_t *rst_srcloc) -{ - const ddsi_locators_t emptyset = { .n = 0, .first = NULL, .last = NULL }; - const ddsi_locators_t *uc = (datap->present & PP_UNICAST_LOCATOR) ? &datap->unicast_locators : ∅ - const ddsi_locators_t *mc = (datap->present & PP_MULTICAST_LOCATOR) ? &datap->multicast_locators : ∅ - ddsi_locator_t srcloc; - if (rst_srcloc == NULL) - ddsi_set_unspec_locator (&srcloc); - else // force use of source locator - { - uc = ∅ - srcloc = *rst_srcloc; - } - - // any interface that works for the participant is presumed ok - interface_set_t intfs; - interface_set_init (&intfs); - ddsi_addrset_forall (proxypp_as_default, addrset_from_locatorlists_collect_interfaces, &(struct ddsi_addrset_from_locatorlists_collect_interfaces_arg){ - .gv = gv, .intfs = &intfs - }); - //GVTRACE(" {%d%d%d%d}", intfs.xs[0], intfs.xs[1], intfs.xs[2], intfs.xs[3]); - struct ddsi_addrset *as = addrset_from_locatorlists (gv, uc, mc, &srcloc, &intfs); - // if SEDP gives: - // - no addresses, use ppant uni- and multicast addresses - // - only multicast, use those for multicast and use ppant address for unicast - // - only unicast, use only those (i.e., disable multicast for this reader) - // - both, use only those - // FIXME: then you can't do a specific unicast address + SSM ... oh well - if (ddsi_addrset_empty (as)) - ddsi_copy_addrset_into_addrset_mc (gv, as, proxypp_as_default); - if (ddsi_addrset_empty_uc (as)) - ddsi_copy_addrset_into_addrset_uc (gv, as, proxypp_as_default); - return as; -} - -static void handle_sedp_alive_endpoint (const struct ddsi_receiver_state *rst, ddsi_seqno_t seq, ddsi_plist_t *datap /* note: potentially modifies datap */, ddsi_sedp_kind_t sedp_kind, const ddsi_guid_prefix_t *src_guid_prefix, ddsi_vendorid_t vendorid, ddsrt_wctime_t timestamp) -{ -#define E(msg, lbl) do { GVLOGDISC (msg); goto lbl; } while (0) - struct ddsi_domaingv * const gv = rst->gv; - struct ddsi_proxy_participant *proxypp; - struct ddsi_proxy_writer * pwr = NULL; - struct ddsi_proxy_reader * prd = NULL; - ddsi_guid_t ppguid; - dds_qos_t *xqos; - int reliable; - struct ddsi_addrset *as; -#ifdef DDS_HAS_SSM - int ssm; -#endif - - assert (datap); - assert (datap->present & PP_ENDPOINT_GUID); - GVLOGDISC (" "PGUIDFMT, PGUID (datap->endpoint_guid)); - - if (!handle_sedp_checks (gv, sedp_kind, &datap->endpoint_guid, datap, src_guid_prefix, vendorid, timestamp, &proxypp, &ppguid)) - goto err; - - xqos = &datap->qos; - if (sedp_kind == SEDP_KIND_READER) - ddsi_xqos_mergein_missing (xqos, &ddsi_default_qos_reader, ~(uint64_t)0); - else if (sedp_kind == SEDP_KIND_WRITER) - { - ddsi_xqos_mergein_missing (xqos, &ddsi_default_qos_writer, ~(uint64_t)0); - if (!ddsi_vendor_is_eclipse_or_adlink (vendorid)) - { - // there is a difference in interpretation of autodispose between vendors - xqos->writer_data_lifecycle.autodispose_unregistered_instances = 0; - } - } - else - E (" invalid entity kind\n", err); - - /* After copy + merge, should have at least the ones present in the - input. Also verify reliability and durability are present, - because we explicitly read those. */ - assert ((xqos->present & datap->qos.present) == datap->qos.present); - assert (xqos->present & DDSI_QP_RELIABILITY); - assert (xqos->present & DDSI_QP_DURABILITY); - reliable = (xqos->reliability.kind == DDS_RELIABILITY_RELIABLE); - - GVLOGDISC (" %s %s %s %s: %s%s.%s/%s", - reliable ? "reliable" : "best-effort", - durability_to_string (xqos->durability.kind), - sedp_kind == SEDP_KIND_WRITER ? "writer" : "reader", - (xqos->present & DDSI_QP_ENTITY_NAME) ? xqos->entity_name : "unnamed", - ((!(xqos->present & DDSI_QP_PARTITION) || xqos->partition.n == 0 || *xqos->partition.strs[0] == '\0') - ? "(default)" : xqos->partition.strs[0]), - ((xqos->present & DDSI_QP_PARTITION) && xqos->partition.n > 1) ? "+" : "", - xqos->topic_name, xqos->type_name); - - if (sedp_kind == SEDP_KIND_READER && (datap->present & PP_EXPECTS_INLINE_QOS) && datap->expects_inline_qos) - E ("******* AARGH - it expects inline QoS ********\n", err); - - ddsi_omg_log_endpoint_protection (gv, datap); - if (ddsi_omg_is_endpoint_protected (datap) && !ddsi_omg_proxy_participant_is_secure (proxypp)) - E (" remote endpoint is protected while local federation is not secure\n", err); - - if (sedp_kind == SEDP_KIND_WRITER) - pwr = ddsi_entidx_lookup_proxy_writer_guid (gv->entity_index, &datap->endpoint_guid); - else - prd = ddsi_entidx_lookup_proxy_reader_guid (gv->entity_index, &datap->endpoint_guid); - if (pwr || prd) - { - /* Re-bind the proxy participant to the discovery service - and do this if it is currently - bound to another DS instance, because that other DS instance may have already failed and - with a new one taking over, without our noticing it. */ - GVLOGDISC (" known%s", ddsi_vendor_is_cloud (vendorid) ? "-DS" : ""); - if (ddsi_vendor_is_cloud (vendorid) && proxypp->implicitly_created && memcmp (&proxypp->privileged_pp_guid.prefix, src_guid_prefix, sizeof(proxypp->privileged_pp_guid.prefix)) != 0) - { - GVLOGDISC (" "PGUIDFMT" attach-to-DS "PGUIDFMT, PGUID(proxypp->e.guid), PGUIDPREFIX(*src_guid_prefix), proxypp->privileged_pp_guid.entityid.u); - ddsrt_mutex_lock (&proxypp->e.lock); - proxypp->privileged_pp_guid.prefix = *src_guid_prefix; - ddsi_lease_set_expiry (proxypp->lease, DDSRT_ETIME_NEVER); - ddsrt_mutex_unlock (&proxypp->e.lock); - } - GVLOGDISC ("\n"); - } - else - { - GVLOGDISC (" NEW"); - } - - as = ddsi_get_endpoint_addrset (gv, datap, proxypp->as_default, gv->config.tcp_use_peeraddr_for_unicast ? &rst->srcloc : NULL); - if (ddsi_addrset_empty (as)) - { - ddsi_unref_addrset (as); - E (" no address", err); - } - - ddsi_log_addrset(gv, DDS_LC_DISCOVERY, " (as", as); -#ifdef DDS_HAS_SSM - ssm = 0; - if (sedp_kind == SEDP_KIND_WRITER) - ssm = ddsi_addrset_contains_ssm (gv, as); - else if (datap->present & PP_READER_FAVOURS_SSM) - ssm = (datap->reader_favours_ssm.state != 0); - GVLOGDISC (" ssm=%u", ssm); -#endif - GVLOGDISC (") QOS={"); - ddsi_xqos_log (DDS_LC_DISCOVERY, &gv->logconfig, xqos); - GVLOGDISC ("}\n"); - - if ((datap->endpoint_guid.entityid.u & DDSI_ENTITYID_SOURCE_MASK) == DDSI_ENTITYID_SOURCE_VENDOR && !ddsi_vendor_is_eclipse_or_adlink (vendorid)) - { - GVLOGDISC ("ignoring vendor-specific endpoint "PGUIDFMT"\n", PGUID (datap->endpoint_guid)); - } - else - { - if (sedp_kind == SEDP_KIND_WRITER) - { - if (pwr) - ddsi_update_proxy_writer (pwr, seq, as, xqos, timestamp); - else - { - /* not supposed to get here for built-in ones, so can determine the channel based on the transport priority */ - assert (!ddsi_is_builtin_entityid (datap->endpoint_guid.entityid, vendorid)); - ddsi_new_proxy_writer (gv, &ppguid, &datap->endpoint_guid, as, datap, gv->user_dqueue, gv->xevents, timestamp, seq); - } - } - else - { - if (prd) - ddsi_update_proxy_reader (prd, seq, as, xqos, timestamp); - else - { -#ifdef DDS_HAS_SSM - ddsi_new_proxy_reader (gv, &ppguid, &datap->endpoint_guid, as, datap, timestamp, seq, ssm); -#else - ddsi_new_proxy_reader (gv, &ppguid, &datap->endpoint_guid, as, datap, timestamp, seq); -#endif - } - } - } - ddsi_unref_addrset (as); - -err: - return; -#undef E -} - -static void handle_sedp_dead_endpoint (const struct ddsi_receiver_state *rst, ddsi_plist_t *datap, ddsi_sedp_kind_t sedp_kind, ddsrt_wctime_t timestamp) -{ - struct ddsi_domaingv * const gv = rst->gv; - int res = -1; - assert (datap->present & PP_ENDPOINT_GUID); - GVLOGDISC (" "PGUIDFMT" ", PGUID (datap->endpoint_guid)); - if (!check_sedp_kind_and_guid (sedp_kind, &datap->endpoint_guid)) - return; - else if (sedp_kind == SEDP_KIND_WRITER) - res = ddsi_delete_proxy_writer (gv, &datap->endpoint_guid, timestamp, 0); - else - res = ddsi_delete_proxy_reader (gv, &datap->endpoint_guid, timestamp, 0); - GVLOGDISC (" %s\n", (res < 0) ? " unknown" : " delete"); -} - -#ifdef DDS_HAS_TOPIC_DISCOVERY - -static void handle_sedp_alive_topic (const struct ddsi_receiver_state *rst, ddsi_seqno_t seq, ddsi_plist_t *datap /* note: potentially modifies datap */, const ddsi_guid_prefix_t *src_guid_prefix, ddsi_vendorid_t vendorid, ddsrt_wctime_t timestamp) -{ - struct ddsi_domaingv * const gv = rst->gv; - struct ddsi_proxy_participant *proxypp; - ddsi_guid_t ppguid; - dds_qos_t *xqos; - int reliable; - const ddsi_typeid_t *type_id_minimal = NULL, *type_id_complete = NULL; - - assert (datap); - assert (datap->present & PP_CYCLONE_TOPIC_GUID); - GVLOGDISC (" "PGUIDFMT, PGUID (datap->topic_guid)); - - if (!handle_sedp_checks (gv, SEDP_KIND_TOPIC, &datap->topic_guid, datap, src_guid_prefix, vendorid, timestamp, &proxypp, &ppguid)) - return; - - xqos = &datap->qos; - ddsi_xqos_mergein_missing (xqos, &ddsi_default_qos_topic, ~(uint64_t)0); - /* After copy + merge, should have at least the ones present in the - input. Also verify reliability and durability are present, - because we explicitly read those. */ - assert ((xqos->present & datap->qos.present) == datap->qos.present); - assert (xqos->present & DDSI_QP_RELIABILITY); - assert (xqos->present & DDSI_QP_DURABILITY); - reliable = (xqos->reliability.kind == DDS_RELIABILITY_RELIABLE); - - GVLOGDISC (" %s %s %s: %s/%s", - reliable ? "reliable" : "best-effort", - durability_to_string (xqos->durability.kind), - "topic", xqos->topic_name, xqos->type_name); - if (xqos->present & DDSI_QP_TYPE_INFORMATION) - { - struct ddsi_typeid_str strm, strc; - type_id_minimal = ddsi_typeinfo_minimal_typeid (xqos->type_information); - type_id_complete = ddsi_typeinfo_complete_typeid (xqos->type_information); - GVLOGDISC (" tid %s/%s", ddsi_make_typeid_str(&strm, type_id_minimal), ddsi_make_typeid_str(&strc, type_id_complete)); - } - GVLOGDISC (" QOS={"); - ddsi_xqos_log (DDS_LC_DISCOVERY, &gv->logconfig, xqos); - GVLOGDISC ("}\n"); - - if ((datap->topic_guid.entityid.u & DDSI_ENTITYID_SOURCE_MASK) == DDSI_ENTITYID_SOURCE_VENDOR && !ddsi_vendor_is_eclipse_or_adlink (vendorid)) - { - GVLOGDISC ("ignoring vendor-specific topic "PGUIDFMT"\n", PGUID (datap->topic_guid)); - } - else - { - // FIXME: check compatibility with known topic definitions - struct ddsi_proxy_topic *ptp = ddsi_lookup_proxy_topic (proxypp, &datap->topic_guid); - if (ptp) - { - GVLOGDISC (" update known proxy-topic%s\n", ddsi_vendor_is_cloud (vendorid) ? "-DS" : ""); - ddsi_update_proxy_topic (proxypp, ptp, seq, xqos, timestamp); - } - else - { - GVLOGDISC (" NEW proxy-topic"); - if (ddsi_new_proxy_topic (proxypp, seq, &datap->topic_guid, type_id_minimal, type_id_complete, xqos, timestamp) != DDS_RETCODE_OK) - GVLOGDISC (" failed"); - } - } -} - -static void handle_sedp_dead_topic (const struct ddsi_receiver_state *rst, ddsi_plist_t *datap, ddsrt_wctime_t timestamp) -{ - struct ddsi_proxy_participant *proxypp; - struct ddsi_proxy_topic *proxytp; - struct ddsi_domaingv * const gv = rst->gv; - assert (datap->present & PP_CYCLONE_TOPIC_GUID); - GVLOGDISC (" "PGUIDFMT" ", PGUID (datap->topic_guid)); - if (!check_sedp_kind_and_guid (SEDP_KIND_TOPIC, &datap->topic_guid)) - return; - ddsi_guid_t ppguid = { .prefix = datap->topic_guid.prefix, .entityid.u = DDSI_ENTITYID_PARTICIPANT }; - if ((proxypp = ddsi_entidx_lookup_proxy_participant_guid (gv->entity_index, &ppguid)) == NULL) - GVLOGDISC (" unknown proxypp\n"); - else if ((proxytp = ddsi_lookup_proxy_topic (proxypp, &datap->topic_guid)) == NULL) - GVLOGDISC (" unknown proxy topic\n"); - else - { - ddsrt_mutex_lock (&proxypp->e.lock); - int res = ddsi_delete_proxy_topic_locked (proxypp, proxytp, timestamp); - GVLOGDISC (" %s\n", res == DDS_RETCODE_PRECONDITION_NOT_MET ? " already-deleting" : " delete"); - ddsrt_mutex_unlock (&proxypp->e.lock); - } -} - -#endif /* DDS_HAS_TOPIC_DISCOVERY */ - -static void handle_sedp (const struct ddsi_receiver_state *rst, ddsi_seqno_t seq, struct ddsi_serdata *serdata, ddsi_sedp_kind_t sedp_kind) +static void ddsi_handle_sedp (const struct ddsi_receiver_state *rst, ddsi_seqno_t seq, struct ddsi_serdata *serdata, ddsi_sedp_kind_t sedp_kind) { ddsi_plist_t decoded_data; if (ddsi_serdata_to_sample (serdata, &decoded_data, NULL, NULL)) @@ -1854,12 +215,12 @@ static void handle_sedp (const struct ddsi_receiver_state *rst, ddsi_seqno_t seq { case SEDP_KIND_TOPIC: #ifdef DDS_HAS_TOPIC_DISCOVERY - handle_sedp_alive_topic (rst, seq, &decoded_data, &rst->src_guid_prefix, rst->vendor, serdata->timestamp); + ddsi_handle_sedp_alive_topic (rst, seq, &decoded_data, &rst->src_guid_prefix, rst->vendor, serdata->timestamp); #endif break; case SEDP_KIND_READER: case SEDP_KIND_WRITER: - handle_sedp_alive_endpoint (rst, seq, &decoded_data, sedp_kind, &rst->src_guid_prefix, rst->vendor, serdata->timestamp); + ddsi_handle_sedp_alive_endpoint (rst, seq, &decoded_data, sedp_kind, &rst->src_guid_prefix, rst->vendor, serdata->timestamp); break; } break; @@ -1870,12 +231,12 @@ static void handle_sedp (const struct ddsi_receiver_state *rst, ddsi_seqno_t seq { case SEDP_KIND_TOPIC: #ifdef DDS_HAS_TOPIC_DISCOVERY - handle_sedp_dead_topic (rst, &decoded_data, serdata->timestamp); + ddsi_handle_sedp_dead_topic (rst, &decoded_data, serdata->timestamp); #endif break; case SEDP_KIND_READER: case SEDP_KIND_WRITER: - handle_sedp_dead_endpoint (rst, &decoded_data, sedp_kind, serdata->timestamp); + ddsi_handle_sedp_dead_endpoint (rst, &decoded_data, sedp_kind, serdata->timestamp); break; } break; @@ -1900,9 +261,6 @@ static void handle_typelookup (const struct ddsi_receiver_state *rst, ddsi_entit } #endif -/****************************************************************************** - *****************************************************************************/ - int ddsi_builtins_dqueue_handler (const struct ddsi_rsample_info *sampleinfo, const struct ddsi_rdata *fragchain, UNUSED_ARG (const ddsi_guid_t *rdguid), UNUSED_ARG (void *qarg)) { struct ddsi_domaingv * const gv = sampleinfo->rst->gv; @@ -2097,19 +455,21 @@ int ddsi_builtins_dqueue_handler (const struct ddsi_rsample_info *sampleinfo, co { case DDSI_ENTITYID_SPDP_BUILTIN_PARTICIPANT_WRITER: case DDSI_ENTITYID_SPDP_RELIABLE_BUILTIN_PARTICIPANT_SECURE_WRITER: - handle_spdp (sampleinfo->rst, srcguid.entityid, sampleinfo->seq, d); + ddsi_handle_spdp (sampleinfo->rst, srcguid.entityid, sampleinfo->seq, d); break; case DDSI_ENTITYID_SEDP_BUILTIN_PUBLICATIONS_WRITER: case DDSI_ENTITYID_SEDP_BUILTIN_PUBLICATIONS_SECURE_WRITER: - handle_sedp (sampleinfo->rst, sampleinfo->seq, d, SEDP_KIND_WRITER); + ddsi_handle_sedp (sampleinfo->rst, sampleinfo->seq, d, SEDP_KIND_WRITER); break; case DDSI_ENTITYID_SEDP_BUILTIN_SUBSCRIPTIONS_WRITER: case DDSI_ENTITYID_SEDP_BUILTIN_SUBSCRIPTIONS_SECURE_WRITER: - handle_sedp (sampleinfo->rst, sampleinfo->seq, d, SEDP_KIND_READER); + ddsi_handle_sedp (sampleinfo->rst, sampleinfo->seq, d, SEDP_KIND_READER); break; +#ifdef DDS_HAS_TOPIC_DISCOVERY case DDSI_ENTITYID_SEDP_BUILTIN_TOPIC_WRITER: - handle_sedp (sampleinfo->rst, sampleinfo->seq, d, SEDP_KIND_TOPIC); + ddsi_handle_sedp (sampleinfo->rst, sampleinfo->seq, d, SEDP_KIND_TOPIC); break; +#endif case DDSI_ENTITYID_P2P_BUILTIN_PARTICIPANT_MESSAGE_WRITER: case DDSI_ENTITYID_P2P_BUILTIN_PARTICIPANT_MESSAGE_SECURE_WRITER: ddsi_handle_pmd_message (sampleinfo->rst, d); diff --git a/src/core/ddsi/src/ddsi_discovery_addrset.c b/src/core/ddsi/src/ddsi_discovery_addrset.c new file mode 100644 index 0000000000..81af44a106 --- /dev/null +++ b/src/core/ddsi/src/ddsi_discovery_addrset.c @@ -0,0 +1,252 @@ +/* + * Copyright(c) 2023 ZettaScale Technology and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License + * v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ +#include +#include +#include +#include + +#include "dds/version.h" +#include "dds/ddsrt/heap.h" +#include "dds/ddsrt/log.h" +#include "dds/ddsi/ddsi_domaingv.h" +#include "ddsi__discovery_addrset.h" +#include "ddsi__participant.h" +#include "ddsi__tran.h" +#include "ddsi__addrset.h" + +void ddsi_interface_set_init (ddsi_interface_set_t *intfs) +{ + for (size_t i = 0; i < sizeof (intfs->xs) / sizeof (intfs->xs[0]); i++) + intfs->xs[i] = false; +} + +bool ddsi_include_multicast_locator_in_discovery (const struct ddsi_domaingv *gv) +{ +#ifdef DDS_HAS_SSM + /* Note that if the default multicast address is an SSM address, + we will simply advertise it. The recipients better understand + it means the writers will publish to address and the readers + favour SSM. */ + if (ddsi_is_ssm_mcaddr (gv, &gv->loc_default_mc)) + return (gv->config.allowMulticast & DDSI_AMC_SSM) != 0; + else + return (gv->config.allowMulticast & DDSI_AMC_ASM) != 0; +#else + return (gv->config.allowMulticast & DDSI_AMC_ASM) != 0; +#endif +} + +static void allowmulticast_aware_add_to_addrset (const struct ddsi_domaingv *gv, uint32_t allow_multicast, struct ddsi_addrset *as, const ddsi_xlocator_t *loc) +{ +#ifdef DDS_HAS_SSM + if (ddsi_is_ssm_mcaddr (gv, &loc->c)) + { + if (!(allow_multicast & DDSI_AMC_SSM)) + return; + } + else if (ddsi_is_mcaddr (gv, &loc->c)) + { + if (!(allow_multicast & DDSI_AMC_ASM)) + return; + } +#else + if (ddsi_is_mcaddr (gv, &loc->c) && !(allow_multicast & DDSI_AMC_ASM)) + return; +#endif + ddsi_add_xlocator_to_addrset (gv, as, loc); +} + +static void addrset_from_locatorlists_add_one (struct ddsi_domaingv const * const gv, const ddsi_locator_t *loc, struct ddsi_addrset *as, ddsi_interface_set_t *intfs, bool *direct) +{ + size_t interf_idx; + switch (ddsi_is_nearby_address (gv, loc, (size_t) gv->n_interfaces, gv->interfaces, &interf_idx)) + { + case DNAR_SELF: + case DNAR_LOCAL: + // if it matches an interface, use that one and record that this is a + // directly connected interface: those will then all be possibilities + // for transmitting multicasts (assuming capable, allowed, &c.) + assert (interf_idx < MAX_XMIT_CONNS); + ddsi_add_xlocator_to_addrset (gv, as, &(const ddsi_xlocator_t) { + .conn = gv->xmit_conns[interf_idx], + .c = *loc }); + intfs->xs[interf_idx] = true; + *direct = true; + break; + case DNAR_DISTANT: + // If DONT_ROUTE is set and there is no matching interface, then presumably + // one would not be able to reach this address. + if (!gv->config.dontRoute) + { + // Pick the first selected interface that isn't link-local or loopback + // (maybe it matters, maybe not, but it doesn't make sense to assign + // a transmit socket for a local interface to a distant host). If none + // exists, skip the address. + for (int i = 0; i < gv->n_interfaces; i++) + { + // do not use link-local or loopback interfaces transmit conn for distant nodes + if (gv->interfaces[i].link_local || gv->interfaces[i].loopback) + continue; + ddsi_add_xlocator_to_addrset (gv, as, &(const ddsi_xlocator_t) { + .conn = gv->xmit_conns[i], + .c = *loc }); + break; + } + } + break; + case DNAR_UNREACHABLE: + break; + } +} + +struct ddsi_addrset *ddsi_addrset_from_locatorlists (const struct ddsi_domaingv *gv, const ddsi_locators_t *uc, const ddsi_locators_t *mc, const ddsi_locator_t *srcloc, const ddsi_interface_set_t *inherited_intfs) +{ + struct ddsi_addrset *as = ddsi_new_addrset (); + ddsi_interface_set_t intfs; + ddsi_interface_set_init (&intfs); + + // if all interfaces are loopback, or all locators in uc are loopback, we're cool with loopback addresses + bool allow_loopback; + { + bool a = true; + for (int i = 0; i < gv->n_interfaces && a; i++) + if (!gv->interfaces[i].loopback) + a = false; + bool b = true; + // FIXME: what about the cases where SEDP gives just a loopback address, but the proxypp is known to be on a remote node? + for (struct ddsi_locators_one *l = uc->first; l != NULL && b; l = l->next) + b = ddsi_is_loopbackaddr (gv, &l->loc); + allow_loopback = (a || b); + } + + // if any non-loopback address is identical to one of our own addresses (actual or advertised), + // assume it is the same machine, in which case loopback addresses may be picked up + for (struct ddsi_locators_one *l = uc->first; l != NULL && !allow_loopback; l = l->next) + { + if (ddsi_is_loopbackaddr (gv, &l->loc)) + continue; + allow_loopback = (ddsi_is_nearby_address (gv, &l->loc, (size_t) gv->n_interfaces, gv->interfaces, NULL) == DNAR_SELF); + } + //GVTRACE(" allow_loopback=%d\n", allow_loopback); + + bool direct = false; + for (struct ddsi_locators_one *l = uc->first; l != NULL; l = l->next) + { +#if 0 + { + char buf[DDSI_LOCSTRLEN]; + ddsi_locator_to_string_no_port (buf, sizeof (buf), &l->loc); + GVTRACE("%s: ignore %d loopback %d\n", buf, l->loc.tran->m_ignore, ddsi_is_loopbackaddr (gv, &l->loc)); + } +#endif + // skip unrecognized ones, as well as loopback ones if not on the same host + if (!allow_loopback && ddsi_is_loopbackaddr (gv, &l->loc)) + continue; + + ddsi_locator_t loc = l->loc; + + // if the advertised locator matches our own external locator, than presumably + // it is the same machine and should be addressed using the actual interface + // address + bool extloc_of_self = false; + for (int i = 0; i < gv->n_interfaces; i++) + { + if (loc.kind == gv->interfaces[i].loc.kind && memcmp (loc.address, gv->interfaces[i].extloc.address, sizeof (loc.address)) == 0) + { + memcpy (loc.address, gv->interfaces[i].loc.address, sizeof (loc.address)); + extloc_of_self = true; + break; + } + } + + if (!extloc_of_self && loc.kind == DDSI_LOCATOR_KIND_UDPv4 && gv->extmask.kind != DDSI_LOCATOR_KIND_INVALID) + { + /* If the examined locator is in the same subnet as our own + external IP address, this locator will be translated into one + in the same subnet as our own local ip and selected. */ + assert (gv->n_interfaces == 1); // gv->extmask: the hack is only supported if limited to a single interface + struct in_addr tmp4 = *((struct in_addr *) (loc.address + 12)); + const struct in_addr ownip = *((struct in_addr *) (gv->interfaces[0].loc.address + 12)); + const struct in_addr extip = *((struct in_addr *) (gv->interfaces[0].extloc.address + 12)); + const struct in_addr extmask = *((struct in_addr *) (gv->extmask.address + 12)); + + if ((tmp4.s_addr & extmask.s_addr) == (extip.s_addr & extmask.s_addr)) + { + /* translate network part of the IP address from the external + one to the internal one */ + tmp4.s_addr = (tmp4.s_addr & ~extmask.s_addr) | (ownip.s_addr & extmask.s_addr); + memcpy (loc.address + 12, &tmp4, 4); + } + } + + addrset_from_locatorlists_add_one (gv, &loc, as, &intfs, &direct); + } + + if (ddsi_addrset_empty (as) && !ddsi_is_unspec_locator (srcloc)) + { + //GVTRACE("add srcloc\n"); + // FIXME: conn_read should provide interface information in source address + //GVTRACE (" add-srcloc"); + addrset_from_locatorlists_add_one (gv, srcloc, as, &intfs, &direct); + } + + if (ddsi_addrset_empty (as) && inherited_intfs) + { + // implies no interfaces enabled in "intfs" yet -- just use whatever + // we inherited for the purposes of selecting multicast addresses + assert (!direct); + for (int i = 0; i < gv->n_interfaces; i++) + assert (!intfs.xs[i]); + //GVTRACE (" using-inherited-intfs"); + intfs = *inherited_intfs; + } + else if (!direct && gv->config.multicast_ttl > 1) + { + //GVTRACE("assuming multicast routing works\n"); + // if not directly connected but multicast TTL allows routing, + // assume any non-local interface will do + //GVTRACE (" enabling-non-loopback/link-local"); + for (int i = 0; i < gv->n_interfaces; i++) + { + assert (!intfs.xs[i]); + intfs.xs[i] = !(gv->interfaces[i].link_local || gv->interfaces[i].loopback); + } + } + +#if 0 + GVTRACE("enabled interfaces for multicast:"); + for (int i = 0; i < gv->n_interfaces; i++) + { + if (intfs[i]) + GVTRACE(" %s(%d)", gv->interfaces[i].name, gv->interfaces[i].mc_capable); + } + GVTRACE("\n"); +#endif + + for (struct ddsi_locators_one *l = mc->first; l != NULL; l = l->next) + { + for (int i = 0; i < gv->n_interfaces; i++) + { + if (intfs.xs[i] && gv->interfaces[i].mc_capable) + { + const ddsi_xlocator_t loc = { + .conn = gv->xmit_conns[i], + .c = l->loc + }; + if (ddsi_factory_supports (loc.conn->m_factory, loc.c.kind)) + allowmulticast_aware_add_to_addrset (gv, gv->config.allowMulticast, as, &loc); + } + } + } + return as; +} + diff --git a/src/core/ddsi/src/ddsi_discovery_endpoint.c b/src/core/ddsi/src/ddsi_discovery_endpoint.c new file mode 100644 index 0000000000..97f43aec6c --- /dev/null +++ b/src/core/ddsi/src/ddsi_discovery_endpoint.c @@ -0,0 +1,587 @@ +/* + * Copyright(c) 2006 to 2022 ZettaScale Technology and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License + * v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ +//#include +#include +#include +#include +#include + +#include "dds/version.h" +#include "dds/ddsrt/heap.h" +#include "dds/ddsrt/log.h" +#include "dds/ddsi/ddsi_domaingv.h" +#include "ddsi__discovery.h" +#include "ddsi__discovery_addrset.h" +#include "ddsi__discovery_endpoint.h" +#include "ddsi__serdata_plist.h" +#include "ddsi__entity_index.h" +#include "ddsi__entity.h" +#include "ddsi__participant.h" +#include "ddsi__transmit.h" +#include "ddsi__lease.h" +#include "ddsi__security_omg.h" +#include "ddsi__endpoint.h" +#include "ddsi__plist.h" +#include "ddsi__proxy_endpoint.h" +#include "ddsi__tran.h" +#include "ddsi__vendor.h" +#include "ddsi__xqos.h" +#include "ddsi__addrset.h" + +struct add_locator_to_ps_arg { + struct ddsi_domaingv *gv; + ddsi_plist_t *ps; +}; + +static void add_locator_to_ps (const ddsi_locator_t *loc, void *varg) +{ + struct add_locator_to_ps_arg *arg = varg; + struct ddsi_locators_one *elem = ddsrt_malloc (sizeof (struct ddsi_locators_one)); + struct ddsi_locators *locs; + unsigned present_flag; + + elem->loc = *loc; + elem->next = NULL; + + if (ddsi_is_mcaddr (arg->gv, loc)) { + locs = &arg->ps->multicast_locators; + present_flag = PP_MULTICAST_LOCATOR; + } else { + locs = &arg->ps->unicast_locators; + present_flag = PP_UNICAST_LOCATOR; + } + + if (!(arg->ps->present & present_flag)) + { + locs->n = 0; + locs->first = locs->last = NULL; + arg->ps->present |= present_flag; + } + locs->n++; + if (locs->first) + locs->last->next = elem; + else + locs->first = elem; + locs->last = elem; +} + +static void add_xlocator_to_ps (const ddsi_xlocator_t *loc, void *varg) +{ + add_locator_to_ps (&loc->c, varg); +} + +#ifdef DDS_HAS_SHM +static void add_iox_locator_to_ps(const ddsi_locator_t* loc, struct add_locator_to_ps_arg *arg) +{ + struct ddsi_locators_one* elem = ddsrt_malloc(sizeof(struct ddsi_locators_one)); + struct ddsi_locators* locs = &arg->ps->unicast_locators; + unsigned present_flag = PP_UNICAST_LOCATOR; + + elem->loc = *loc; + elem->next = NULL; + + if (!(arg->ps->present & present_flag)) + { + locs->n = 0; + locs->first = locs->last = NULL; + arg->ps->present |= present_flag; + } + + //add iceoryx to the FRONT of the list of addresses, to indicate its higher priority + if (locs->first) + elem->next = locs->first; + else + locs->last = elem; + locs->first = elem; + locs->n++; +} +#endif + +static int sedp_write_endpoint_impl +( + struct ddsi_writer *wr, int alive, const ddsi_guid_t *guid, + const struct ddsi_endpoint_common *epcommon, + const dds_qos_t *xqos, struct ddsi_addrset *as, ddsi_security_info_t *security +#ifdef DDS_HAS_TYPE_DISCOVERY + , const struct ddsi_sertype *sertype +#endif +) +{ + struct ddsi_domaingv * const gv = wr->e.gv; + const dds_qos_t *defqos = NULL; + if (ddsi_is_writer_entityid (guid->entityid)) + defqos = &ddsi_default_qos_writer; + else if (ddsi_is_reader_entityid (guid->entityid)) + defqos = &ddsi_default_qos_reader; + else + assert (false); + + uint64_t qosdiff; + ddsi_plist_t ps; + + ddsi_plist_init_empty (&ps); + ps.present |= PP_ENDPOINT_GUID; + ps.endpoint_guid = *guid; + +#ifdef DDS_HAS_SECURITY + if (security) + { + ps.present |= PP_ENDPOINT_SECURITY_INFO; + memcpy(&ps.endpoint_security_info, security, sizeof(ddsi_security_info_t)); + } +#else + (void)security; + assert(security == NULL); +#endif + + if (!alive) + { + assert (xqos == NULL); + assert (epcommon == NULL); + qosdiff = 0; + } + else + { + assert (xqos != NULL); + ps.present |= PP_PROTOCOL_VERSION | PP_VENDORID; + ps.protocol_version.major = DDSI_RTPS_MAJOR; + ps.protocol_version.minor = DDSI_RTPS_MINOR; + ps.vendorid = DDSI_VENDORID_ECLIPSE; + + assert (epcommon != NULL); + + if (epcommon->group_guid.entityid.u != 0) + { + ps.present |= PP_GROUP_GUID; + ps.group_guid = epcommon->group_guid; + } + + if (!ddsi_is_writer_entityid (guid->entityid)) + { + const struct ddsi_reader *rd = ddsi_entidx_lookup_reader_guid (gv->entity_index, guid); + assert (rd); + if (rd->request_keyhash) + { + ps.present |= PP_CYCLONE_REQUESTS_KEYHASH; + ps.cyclone_requests_keyhash = 1u; + } + } + +#ifdef DDS_HAS_SSM + /* A bit of a hack -- the easy alternative would be to make it yet + another parameter. We only set "reader favours SSM" if we + really do: no point in telling the world that everything is at + the default. */ + if (ddsi_is_reader_entityid (guid->entityid)) + { + const struct ddsi_reader *rd = ddsi_entidx_lookup_reader_guid (gv->entity_index, guid); + assert (rd); + if (rd->favours_ssm) + { + ps.present |= PP_READER_FAVOURS_SSM; + ps.reader_favours_ssm.state = 1u; + } + } +#endif + + qosdiff = ddsi_xqos_delta (xqos, defqos, ~(uint64_t)0); + if (gv->config.explicitly_publish_qos_set_to_default) + qosdiff |= ~DDSI_QP_UNRECOGNIZED_INCOMPATIBLE_MASK; + + struct add_locator_to_ps_arg arg; + arg.gv = gv; + arg.ps = &ps; + if (as) + ddsi_addrset_forall (as, add_xlocator_to_ps, &arg); + +#ifdef DDS_HAS_SHM + assert(wr->xqos->present & DDSI_QP_LOCATOR_MASK); + if (!(xqos->ignore_locator_type & DDSI_LOCATOR_KIND_SHEM)) + { + if (!(arg.ps->present & PP_UNICAST_LOCATOR) || 0 == arg.ps->unicast_locators.n) + { + if (epcommon->pp->e.gv->config.many_sockets_mode == DDSI_MSM_MANY_UNICAST) + add_locator_to_ps(&epcommon->pp->m_locator, &arg); + else + { + // FIXME: same as what SPDP uses, should be refactored, now more than ever + for (int i = 0; i < epcommon->pp->e.gv->n_interfaces; i++) + { + if (!epcommon->pp->e.gv->xmit_conns[i]->m_factory->m_enable_spdp) + { + // skip any interfaces where the address kind doesn't match the selected transport + // as a reasonablish way of not advertising iceoryx locators here + continue; + } + // FIXME: should have multiple loc_default_uc/loc_meta_uc or compute ports here + ddsi_locator_t loc = epcommon->pp->e.gv->interfaces[i].extloc; + loc.port = epcommon->pp->e.gv->loc_default_uc.port; + add_locator_to_ps(&loc, &arg); + } + } + } + + if (!(arg.ps->present & PP_MULTICAST_LOCATOR) || 0 == arg.ps->multicast_locators.n) + { + if (ddsi_include_multicast_locator_in_discovery (epcommon->pp->e.gv)) + add_locator_to_ps (&epcommon->pp->e.gv->loc_default_mc, &arg); + } + + add_iox_locator_to_ps(&gv->loc_iceoryx_addr, &arg); + } +#endif + +#ifdef DDS_HAS_TYPE_DISCOVERY + assert (sertype); + if ((ps.qos.type_information = ddsi_sertype_typeinfo (sertype))) + ps.qos.present |= DDSI_QP_TYPE_INFORMATION; +#endif + } + + if (xqos) + ddsi_xqos_mergein_missing (&ps.qos, xqos, qosdiff); + return ddsi_write_and_fini_plist (wr, &ps, alive); +} + +int ddsi_sedp_write_writer (struct ddsi_writer *wr) +{ + if ((!ddsi_is_builtin_entityid(wr->e.guid.entityid, DDSI_VENDORID_ECLIPSE)) && (!wr->e.onlylocal)) + { + unsigned entityid = ddsi_determine_publication_writer(wr); + struct ddsi_writer *sedp_wr = ddsi_get_sedp_writer (wr->c.pp, entityid); + ddsi_security_info_t *security = NULL; +#ifdef DDS_HAS_SSM + struct ddsi_addrset *as = wr->ssm_as; +#else + struct ddsi_addrset *as = NULL; +#endif +#ifdef DDS_HAS_SECURITY + ddsi_security_info_t tmp; + if (ddsi_omg_get_writer_security_info (wr, &tmp)) + { + security = &tmp; + } +#endif +#ifdef DDS_HAS_TYPE_DISCOVERY + return sedp_write_endpoint_impl (sedp_wr, 1, &wr->e.guid, &wr->c, wr->xqos, as, security, wr->type); +#else + return sedp_write_endpoint_impl (sedp_wr, 1, &wr->e.guid, &wr->c, wr->xqos, as, security); +#endif + } + return 0; +} + +int ddsi_sedp_write_reader (struct ddsi_reader *rd) +{ + if (ddsi_is_builtin_entityid (rd->e.guid.entityid, DDSI_VENDORID_ECLIPSE) || rd->e.onlylocal) + return 0; + + unsigned entityid = ddsi_determine_subscription_writer(rd); + struct ddsi_writer *sedp_wr = ddsi_get_sedp_writer (rd->c.pp, entityid); + ddsi_security_info_t *security = NULL; + struct ddsi_addrset *as = NULL; +#ifdef DDS_HAS_NETWORK_PARTITIONS + if (rd->uc_as != NULL || rd->mc_as != NULL) + { + // FIXME: do this without first creating a temporary addrset + as = ddsi_new_addrset (); + // use a placeholder connection to avoid exploding the multicast addreses to multiple + // interfaces + for (const struct ddsi_networkpartition_address *a = rd->uc_as; a != NULL; a = a->next) + ddsi_add_xlocator_to_addrset(rd->e.gv, as, &(const ddsi_xlocator_t) { + .c = a->loc, + .conn = rd->e.gv->xmit_conns[0] }); + for (const struct ddsi_networkpartition_address *a = rd->mc_as; a != NULL; a = a->next) + ddsi_add_xlocator_to_addrset(rd->e.gv, as, &(const ddsi_xlocator_t) { + .c = a->loc, + .conn = rd->e.gv->xmit_conns[0] }); + } +#endif +#ifdef DDS_HAS_SECURITY + ddsi_security_info_t tmp; + if (ddsi_omg_get_reader_security_info (rd, &tmp)) + { + security = &tmp; + } +#endif +#ifdef DDS_HAS_TYPE_DISCOVERY + const int ret = sedp_write_endpoint_impl (sedp_wr, 1, &rd->e.guid, &rd->c, rd->xqos, as, security, rd->type); +#else + const int ret = sedp_write_endpoint_impl (sedp_wr, 1, &rd->e.guid, &rd->c, rd->xqos, as, security); +#endif + ddsi_unref_addrset (as); + return ret; +} + +int ddsi_sedp_dispose_unregister_writer (struct ddsi_writer *wr) +{ + if ((!ddsi_is_builtin_entityid(wr->e.guid.entityid, DDSI_VENDORID_ECLIPSE)) && (!wr->e.onlylocal)) + { + unsigned entityid = ddsi_determine_publication_writer(wr); + struct ddsi_writer *sedp_wr = ddsi_get_sedp_writer (wr->c.pp, entityid); +#ifdef DDS_HAS_TYPE_DISCOVERY + return sedp_write_endpoint_impl (sedp_wr, 0, &wr->e.guid, NULL, NULL, NULL, NULL, NULL); +#else + return sedp_write_endpoint_impl (sedp_wr, 0, &wr->e.guid, NULL, NULL, NULL, NULL); +#endif + } + return 0; +} + +int ddsi_sedp_dispose_unregister_reader (struct ddsi_reader *rd) +{ + if ((!ddsi_is_builtin_entityid(rd->e.guid.entityid, DDSI_VENDORID_ECLIPSE)) && (!rd->e.onlylocal)) + { + unsigned entityid = ddsi_determine_subscription_writer(rd); + struct ddsi_writer *sedp_wr = ddsi_get_sedp_writer (rd->c.pp, entityid); +#ifdef DDS_HAS_TYPE_DISCOVERY + return sedp_write_endpoint_impl (sedp_wr, 0, &rd->e.guid, NULL, NULL, NULL, NULL, NULL); +#else + return sedp_write_endpoint_impl (sedp_wr, 0, &rd->e.guid, NULL, NULL, NULL, NULL); +#endif + } + return 0; +} + +static const char *durability_to_string (dds_durability_kind_t k) +{ + switch (k) + { + case DDS_DURABILITY_VOLATILE: return "volatile"; + case DDS_DURABILITY_TRANSIENT_LOCAL: return "transient-local"; + case DDS_DURABILITY_TRANSIENT: return "transient"; + case DDS_DURABILITY_PERSISTENT: return "persistent"; + } + return "undefined-durability"; +} + +struct ddsi_addrset_from_locatorlists_collect_interfaces_arg { + const struct ddsi_domaingv *gv; + ddsi_interface_set_t *intfs; +}; + +/** @brief Figure out which interfaces are touched by (extended) locator @p loc + * + * Does this by looking up the connection in @p loc in the set of transmit connections. (There's plenty of room for optimisation here.) + * + * @param[in] loc locator + * @param[in] varg argument pointer, must point to a struct ddsi_addrset_from_locatorlists_collect_interfaces_arg + */ +static void addrset_from_locatorlists_collect_interfaces (const ddsi_xlocator_t *loc, void *varg) +{ + struct ddsi_addrset_from_locatorlists_collect_interfaces_arg *arg = varg; + struct ddsi_domaingv const * const gv = arg->gv; + for (int i = 0; i < gv->n_interfaces; i++) + { + //GVTRACE(" {%p,%p}", loc->conn, gv->xmit_conns[i]); + if (loc->conn == gv->xmit_conns[i]) + { + arg->intfs->xs[i] = true; + break; + } + } +} + +struct ddsi_addrset *ddsi_get_endpoint_addrset (const struct ddsi_domaingv *gv, const ddsi_plist_t *datap, struct ddsi_addrset *proxypp_as_default, const ddsi_locator_t *rst_srcloc) +{ + const ddsi_locators_t emptyset = { .n = 0, .first = NULL, .last = NULL }; + const ddsi_locators_t *uc = (datap->present & PP_UNICAST_LOCATOR) ? &datap->unicast_locators : ∅ + const ddsi_locators_t *mc = (datap->present & PP_MULTICAST_LOCATOR) ? &datap->multicast_locators : ∅ + ddsi_locator_t srcloc; + if (rst_srcloc == NULL) + ddsi_set_unspec_locator (&srcloc); + else // force use of source locator + { + uc = ∅ + srcloc = *rst_srcloc; + } + + // any interface that works for the participant is presumed ok + ddsi_interface_set_t intfs; + ddsi_interface_set_init (&intfs); + ddsi_addrset_forall (proxypp_as_default, addrset_from_locatorlists_collect_interfaces, &(struct ddsi_addrset_from_locatorlists_collect_interfaces_arg){ + .gv = gv, .intfs = &intfs + }); + //GVTRACE(" {%d%d%d%d}", intfs.xs[0], intfs.xs[1], intfs.xs[2], intfs.xs[3]); + struct ddsi_addrset *as = ddsi_addrset_from_locatorlists (gv, uc, mc, &srcloc, &intfs); + // if SEDP gives: + // - no addresses, use ppant uni- and multicast addresses + // - only multicast, use those for multicast and use ppant address for unicast + // - only unicast, use only those (i.e., disable multicast for this reader) + // - both, use only those + // FIXME: then you can't do a specific unicast address + SSM ... oh well + if (ddsi_addrset_empty (as)) + ddsi_copy_addrset_into_addrset_mc (gv, as, proxypp_as_default); + if (ddsi_addrset_empty_uc (as)) + ddsi_copy_addrset_into_addrset_uc (gv, as, proxypp_as_default); + return as; +} + +void ddsi_handle_sedp_alive_endpoint (const struct ddsi_receiver_state *rst, ddsi_seqno_t seq, ddsi_plist_t *datap /* note: potentially modifies datap */, ddsi_sedp_kind_t sedp_kind, const ddsi_guid_prefix_t *src_guid_prefix, ddsi_vendorid_t vendorid, ddsrt_wctime_t timestamp) +{ +#define E(msg, lbl) do { GVLOGDISC (msg); goto lbl; } while (0) + struct ddsi_domaingv * const gv = rst->gv; + struct ddsi_proxy_participant *proxypp; + struct ddsi_proxy_writer * pwr = NULL; + struct ddsi_proxy_reader * prd = NULL; + ddsi_guid_t ppguid; + dds_qos_t *xqos; + int reliable; + struct ddsi_addrset *as; +#ifdef DDS_HAS_SSM + int ssm; +#endif + + assert (datap); + assert (datap->present & PP_ENDPOINT_GUID); + GVLOGDISC (" "PGUIDFMT, PGUID (datap->endpoint_guid)); + + if (!ddsi_handle_sedp_checks (gv, sedp_kind, &datap->endpoint_guid, datap, src_guid_prefix, vendorid, timestamp, &proxypp, &ppguid)) + goto err; + + xqos = &datap->qos; + if (sedp_kind == SEDP_KIND_READER) + ddsi_xqos_mergein_missing (xqos, &ddsi_default_qos_reader, ~(uint64_t)0); + else if (sedp_kind == SEDP_KIND_WRITER) + { + ddsi_xqos_mergein_missing (xqos, &ddsi_default_qos_writer, ~(uint64_t)0); + if (!ddsi_vendor_is_eclipse_or_adlink (vendorid)) + { + // there is a difference in interpretation of autodispose between vendors + xqos->writer_data_lifecycle.autodispose_unregistered_instances = 0; + } + } + else + E (" invalid entity kind\n", err); + + /* After copy + merge, should have at least the ones present in the + input. Also verify reliability and durability are present, + because we explicitly read those. */ + assert ((xqos->present & datap->qos.present) == datap->qos.present); + assert (xqos->present & DDSI_QP_RELIABILITY); + assert (xqos->present & DDSI_QP_DURABILITY); + reliable = (xqos->reliability.kind == DDS_RELIABILITY_RELIABLE); + + GVLOGDISC (" %s %s %s %s: %s%s.%s/%s", + reliable ? "reliable" : "best-effort", + durability_to_string (xqos->durability.kind), + sedp_kind == SEDP_KIND_WRITER ? "writer" : "reader", + (xqos->present & DDSI_QP_ENTITY_NAME) ? xqos->entity_name : "unnamed", + ((!(xqos->present & DDSI_QP_PARTITION) || xqos->partition.n == 0 || *xqos->partition.strs[0] == '\0') + ? "(default)" : xqos->partition.strs[0]), + ((xqos->present & DDSI_QP_PARTITION) && xqos->partition.n > 1) ? "+" : "", + xqos->topic_name, xqos->type_name); + + if (sedp_kind == SEDP_KIND_READER && (datap->present & PP_EXPECTS_INLINE_QOS) && datap->expects_inline_qos) + E ("******* AARGH - it expects inline QoS ********\n", err); + + ddsi_omg_log_endpoint_protection (gv, datap); + if (ddsi_omg_is_endpoint_protected (datap) && !ddsi_omg_proxy_participant_is_secure (proxypp)) + E (" remote endpoint is protected while local federation is not secure\n", err); + + if (sedp_kind == SEDP_KIND_WRITER) + pwr = ddsi_entidx_lookup_proxy_writer_guid (gv->entity_index, &datap->endpoint_guid); + else + prd = ddsi_entidx_lookup_proxy_reader_guid (gv->entity_index, &datap->endpoint_guid); + if (pwr || prd) + { + /* Re-bind the proxy participant to the discovery service - and do this if it is currently + bound to another DS instance, because that other DS instance may have already failed and + with a new one taking over, without our noticing it. */ + GVLOGDISC (" known%s", ddsi_vendor_is_cloud (vendorid) ? "-DS" : ""); + if (ddsi_vendor_is_cloud (vendorid) && proxypp->implicitly_created && memcmp (&proxypp->privileged_pp_guid.prefix, src_guid_prefix, sizeof(proxypp->privileged_pp_guid.prefix)) != 0) + { + GVLOGDISC (" "PGUIDFMT" attach-to-DS "PGUIDFMT, PGUID(proxypp->e.guid), PGUIDPREFIX(*src_guid_prefix), proxypp->privileged_pp_guid.entityid.u); + ddsrt_mutex_lock (&proxypp->e.lock); + proxypp->privileged_pp_guid.prefix = *src_guid_prefix; + ddsi_lease_set_expiry (proxypp->lease, DDSRT_ETIME_NEVER); + ddsrt_mutex_unlock (&proxypp->e.lock); + } + GVLOGDISC ("\n"); + } + else + { + GVLOGDISC (" NEW"); + } + + as = ddsi_get_endpoint_addrset (gv, datap, proxypp->as_default, gv->config.tcp_use_peeraddr_for_unicast ? &rst->srcloc : NULL); + if (ddsi_addrset_empty (as)) + { + ddsi_unref_addrset (as); + E (" no address", err); + } + + ddsi_log_addrset(gv, DDS_LC_DISCOVERY, " (as", as); +#ifdef DDS_HAS_SSM + ssm = 0; + if (sedp_kind == SEDP_KIND_WRITER) + ssm = ddsi_addrset_contains_ssm (gv, as); + else if (datap->present & PP_READER_FAVOURS_SSM) + ssm = (datap->reader_favours_ssm.state != 0); + GVLOGDISC (" ssm=%u", ssm); +#endif + GVLOGDISC (") QOS={"); + ddsi_xqos_log (DDS_LC_DISCOVERY, &gv->logconfig, xqos); + GVLOGDISC ("}\n"); + + if ((datap->endpoint_guid.entityid.u & DDSI_ENTITYID_SOURCE_MASK) == DDSI_ENTITYID_SOURCE_VENDOR && !ddsi_vendor_is_eclipse_or_adlink (vendorid)) + { + GVLOGDISC ("ignoring vendor-specific endpoint "PGUIDFMT"\n", PGUID (datap->endpoint_guid)); + } + else + { + if (sedp_kind == SEDP_KIND_WRITER) + { + if (pwr) + ddsi_update_proxy_writer (pwr, seq, as, xqos, timestamp); + else + { + /* not supposed to get here for built-in ones, so can determine the channel based on the transport priority */ + assert (!ddsi_is_builtin_entityid (datap->endpoint_guid.entityid, vendorid)); + ddsi_new_proxy_writer (gv, &ppguid, &datap->endpoint_guid, as, datap, gv->user_dqueue, gv->xevents, timestamp, seq); + } + } + else + { + if (prd) + ddsi_update_proxy_reader (prd, seq, as, xqos, timestamp); + else + { +#ifdef DDS_HAS_SSM + ddsi_new_proxy_reader (gv, &ppguid, &datap->endpoint_guid, as, datap, timestamp, seq, ssm); +#else + ddsi_new_proxy_reader (gv, &ppguid, &datap->endpoint_guid, as, datap, timestamp, seq); +#endif + } + } + } + ddsi_unref_addrset (as); + +err: + return; +#undef E +} + +void ddsi_handle_sedp_dead_endpoint (const struct ddsi_receiver_state *rst, ddsi_plist_t *datap, ddsi_sedp_kind_t sedp_kind, ddsrt_wctime_t timestamp) +{ + struct ddsi_domaingv * const gv = rst->gv; + int res = -1; + assert (datap->present & PP_ENDPOINT_GUID); + GVLOGDISC (" "PGUIDFMT" ", PGUID (datap->endpoint_guid)); + if (!ddsi_check_sedp_kind_and_guid (sedp_kind, &datap->endpoint_guid)) + return; + else if (sedp_kind == SEDP_KIND_WRITER) + res = ddsi_delete_proxy_writer (gv, &datap->endpoint_guid, timestamp, 0); + else + res = ddsi_delete_proxy_reader (gv, &datap->endpoint_guid, timestamp, 0); + GVLOGDISC (" %s\n", (res < 0) ? " unknown" : " delete"); +} diff --git a/src/core/ddsi/src/ddsi_discovery_spdp.c b/src/core/ddsi/src/ddsi_discovery_spdp.c new file mode 100644 index 0000000000..210f52c324 --- /dev/null +++ b/src/core/ddsi/src/ddsi_discovery_spdp.c @@ -0,0 +1,858 @@ +/* + * Copyright(c) 2006 to 2022 ZettaScale Technology and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License + * v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ +#include +#include +#include +#include + +#include "dds/version.h" +#include "dds/ddsrt/heap.h" +#include "dds/ddsrt/string.h" +#include "dds/ddsi/ddsi_domaingv.h" +#include "ddsi__discovery_spdp.h" +#include "ddsi__discovery_addrset.h" +#include "ddsi__discovery_endpoint.h" +#include "ddsi__serdata_plist.h" +#include "ddsi__entity_index.h" +#include "ddsi__entity.h" +#include "ddsi__security_omg.h" +#include "ddsi__endpoint.h" +#include "ddsi__plist.h" +#include "ddsi__proxy_participant.h" +#include "ddsi__topic.h" +#include "ddsi__vendor.h" +#include "ddsi__xevent.h" +#include "ddsi__transmit.h" +#include "ddsi__lease.h" +#include "ddsi__xqos.h" + +static void maybe_add_pp_as_meta_to_as_disc (struct ddsi_domaingv *gv, const struct ddsi_addrset *as_meta) +{ + if (ddsi_addrset_empty_mc (as_meta) || !(gv->config.allowMulticast & DDSI_AMC_SPDP)) + { + ddsi_xlocator_t loc; + if (ddsi_addrset_any_uc (as_meta, &loc)) + { + ddsi_add_xlocator_to_addrset (gv, gv->as_disc, &loc); + } + } +} + +struct locators_builder { + ddsi_locators_t *dst; + struct ddsi_locators_one *storage; + size_t storage_n; +}; + +static struct locators_builder locators_builder_init (ddsi_locators_t *dst, struct ddsi_locators_one *storage, size_t storage_n) +{ + dst->n = 0; + dst->first = NULL; + dst->last = NULL; + return (struct locators_builder) { + .dst = dst, + .storage = storage, + .storage_n = storage_n + }; +} + +static bool locators_add_one (struct locators_builder *b, const ddsi_locator_t *loc, uint32_t port_override) +{ + if (b->dst->n >= b->storage_n) + return false; + if (b->dst->n == 0) + b->dst->first = b->storage; + else + b->dst->last->next = &b->storage[b->dst->n]; + b->dst->last = &b->storage[b->dst->n++]; + b->dst->last->loc = *loc; + if (port_override != DDSI_LOCATOR_PORT_INVALID) + b->dst->last->loc.port = port_override; + b->dst->last->next = NULL; + return true; +} + +void ddsi_get_participant_builtin_topic_data (const struct ddsi_participant *pp, ddsi_plist_t *dst, struct ddsi_participant_builtin_topic_data_locators *locs) +{ + struct ddsi_domaingv * const gv = pp->e.gv; + size_t size; + char node[64]; + uint64_t qosdiff; + + ddsi_plist_init_empty (dst); + dst->present |= PP_PARTICIPANT_GUID | PP_BUILTIN_ENDPOINT_SET | + PP_PROTOCOL_VERSION | PP_VENDORID | PP_DOMAIN_ID; + dst->participant_guid = pp->e.guid; + dst->builtin_endpoint_set = pp->bes; + dst->protocol_version.major = DDSI_RTPS_MAJOR; + dst->protocol_version.minor = DDSI_RTPS_MINOR; + dst->vendorid = DDSI_VENDORID_ECLIPSE; + dst->domain_id = gv->config.extDomainId.value; + /* Be sure not to send a DOMAIN_TAG when it is the default (an empty) + string: it is an "incompatible-if-unrecognized" parameter, and so + implementations that don't understand the parameter will refuse to + discover us, and so sending the default would break backwards + compatibility. */ + if (strcmp (gv->config.domainTag, "") != 0) + { + dst->present |= PP_DOMAIN_TAG; + dst->aliased |= PP_DOMAIN_TAG; + dst->domain_tag = gv->config.domainTag; + } + + // Construct unicast locator parameters + { + struct locators_builder def_uni = locators_builder_init (&dst->default_unicast_locators, locs->def_uni, MAX_XMIT_CONNS); + struct locators_builder meta_uni = locators_builder_init (&dst->metatraffic_unicast_locators, locs->meta_uni, MAX_XMIT_CONNS); + for (int i = 0; i < gv->n_interfaces; i++) + { + if (!gv->xmit_conns[i]->m_factory->m_enable_spdp) + { + // skip any interfaces where the address kind doesn't match the selected transport + // as a reasonablish way of not advertising iceoryx locators here + continue; + } +#ifndef NDEBUG + int32_t kind; +#endif + uint32_t data_port, meta_port; + if (gv->config.many_sockets_mode != DDSI_MSM_MANY_UNICAST) + { +#ifndef NDEBUG + kind = gv->loc_default_uc.kind; +#endif + assert (kind == gv->loc_meta_uc.kind); + data_port = gv->loc_default_uc.port; + meta_port = gv->loc_meta_uc.port; + } + else + { +#ifndef NDEBUG + kind = pp->m_locator.kind; +#endif + data_port = meta_port = pp->m_locator.port; + } + assert (kind == gv->interfaces[i].extloc.kind); + locators_add_one (&def_uni, &gv->interfaces[i].extloc, data_port); + locators_add_one (&meta_uni, &gv->interfaces[i].extloc, meta_port); + } + if (gv->config.publish_uc_locators) + { + dst->present |= PP_DEFAULT_UNICAST_LOCATOR | PP_METATRAFFIC_UNICAST_LOCATOR; + dst->aliased |= PP_DEFAULT_UNICAST_LOCATOR | PP_METATRAFFIC_UNICAST_LOCATOR; + } + } + + if (ddsi_include_multicast_locator_in_discovery (gv)) + { + dst->present |= PP_DEFAULT_MULTICAST_LOCATOR | PP_METATRAFFIC_MULTICAST_LOCATOR; + dst->aliased |= PP_DEFAULT_MULTICAST_LOCATOR | PP_METATRAFFIC_MULTICAST_LOCATOR; + struct locators_builder def_mc = locators_builder_init (&dst->default_multicast_locators, &locs->def_multi, 1); + struct locators_builder meta_mc = locators_builder_init (&dst->metatraffic_multicast_locators, &locs->meta_multi, 1); + locators_add_one (&def_mc, &gv->loc_default_mc, DDSI_LOCATOR_PORT_INVALID); + locators_add_one (&meta_mc, &gv->loc_meta_mc, DDSI_LOCATOR_PORT_INVALID); + } + + /* Add Adlink specific version information */ + { + dst->present |= PP_ADLINK_PARTICIPANT_VERSION_INFO; + memset (&dst->adlink_participant_version_info, 0, sizeof (dst->adlink_participant_version_info)); + dst->adlink_participant_version_info.version = 0; + dst->adlink_participant_version_info.flags = + DDSI_ADLINK_FL_DDSI2_PARTICIPANT_FLAG | + DDSI_ADLINK_FL_PTBES_FIXED_0 | + DDSI_ADLINK_FL_SUPPORTS_STATUSINFOX; + if (gv->config.besmode == DDSI_BESMODE_MINIMAL) + dst->adlink_participant_version_info.flags |= DDSI_ADLINK_FL_MINIMAL_BES_MODE; + ddsrt_mutex_lock (&gv->privileged_pp_lock); + if (pp->is_ddsi2_pp) + dst->adlink_participant_version_info.flags |= DDSI_ADLINK_FL_PARTICIPANT_IS_DDSI2; + ddsrt_mutex_unlock (&gv->privileged_pp_lock); + +#if DDSRT_HAVE_GETHOSTNAME + if (ddsrt_gethostname(node, sizeof(node)-1) < 0) +#endif + (void) ddsrt_strlcpy (node, "unknown", sizeof (node)); + size = strlen(node) + strlen(DDS_VERSION) + strlen(DDS_HOST_NAME) + strlen(DDS_TARGET_NAME) + 4; /* + ///'\0' */ + dst->adlink_participant_version_info.internals = ddsrt_malloc(size); + (void) snprintf(dst->adlink_participant_version_info.internals, size, "%s/%s/%s/%s", node, DDS_VERSION, DDS_HOST_NAME, DDS_TARGET_NAME); + ETRACE (pp, "ddsi_spdp_write("PGUIDFMT") - internals: %s\n", PGUID (pp->e.guid), dst->adlink_participant_version_info.internals); + } + + /* Add Cyclone specific information */ + { + const uint32_t bufsz = ddsi_receive_buffer_size (gv->m_factory); + if (bufsz > 0) + { + dst->present |= PP_CYCLONE_RECEIVE_BUFFER_SIZE; + dst->cyclone_receive_buffer_size = bufsz; + } + } + if (gv->config.redundant_networking) + { + dst->present |= PP_CYCLONE_REDUNDANT_NETWORKING; + dst->cyclone_redundant_networking = true; + } + +#ifdef DDS_HAS_SECURITY + /* Add Security specific information. */ + if (ddsi_omg_get_participant_security_info (pp, &(dst->participant_security_info))) { + dst->present |= PP_PARTICIPANT_SECURITY_INFO; + dst->aliased |= PP_PARTICIPANT_SECURITY_INFO; + } +#endif + + /* Participant QoS's insofar as they are set, different from the default, and mapped to the SPDP data, rather than to the Adlink-specific CMParticipant endpoint. */ + qosdiff = ddsi_xqos_delta (&pp->plist->qos, &ddsi_default_qos_participant, DDSI_QP_USER_DATA | DDSI_QP_ENTITY_NAME | DDSI_QP_PROPERTY_LIST | DDSI_QP_LIVELINESS); + if (gv->config.explicitly_publish_qos_set_to_default) + qosdiff |= ~(DDSI_QP_UNRECOGNIZED_INCOMPATIBLE_MASK | DDSI_QP_LIVELINESS); + + assert (dst->qos.present == 0); + ddsi_plist_mergein_missing (dst, pp->plist, 0, qosdiff); +#ifdef DDS_HAS_SECURITY + if (ddsi_omg_participant_is_secure(pp)) + ddsi_plist_mergein_missing (dst, pp->plist, PP_IDENTITY_TOKEN | PP_PERMISSIONS_TOKEN, 0); +#endif +} + +int ddsi_spdp_write (struct ddsi_participant *pp) +{ + struct ddsi_writer *wr; + ddsi_plist_t ps; + struct ddsi_participant_builtin_topic_data_locators locs; + + if (pp->e.onlylocal) { + /* This topic is only locally available. */ + return 0; + } + + ETRACE (pp, "ddsi_spdp_write("PGUIDFMT")\n", PGUID (pp->e.guid)); + + if ((wr = ddsi_get_builtin_writer (pp, DDSI_ENTITYID_SPDP_BUILTIN_PARTICIPANT_WRITER)) == NULL) + { + ETRACE (pp, "ddsi_spdp_write("PGUIDFMT") - builtin participant writer not found\n", PGUID (pp->e.guid)); + return 0; + } + + ddsi_get_participant_builtin_topic_data (pp, &ps, &locs); + return ddsi_write_and_fini_plist (wr, &ps, true); +} + +static int ddsi_spdp_dispose_unregister_with_wr (struct ddsi_participant *pp, unsigned entityid) +{ + ddsi_plist_t ps; + struct ddsi_writer *wr; + + if ((wr = ddsi_get_builtin_writer (pp, entityid)) == NULL) + { + ETRACE (pp, "ddsi_spdp_dispose_unregister("PGUIDFMT") - builtin participant %s writer not found\n", + PGUID (pp->e.guid), + entityid == DDSI_ENTITYID_SPDP_RELIABLE_BUILTIN_PARTICIPANT_SECURE_WRITER ? "secure" : ""); + return 0; + } + + ddsi_plist_init_empty (&ps); + ps.present |= PP_PARTICIPANT_GUID; + ps.participant_guid = pp->e.guid; + return ddsi_write_and_fini_plist (wr, &ps, false); +} + +int ddsi_spdp_dispose_unregister (struct ddsi_participant *pp) +{ + /* + * When disposing a participant, it should be announced on both the + * non-secure and secure writers. + * The receiver will decide from which writer it accepts the dispose. + */ + int ret = ddsi_spdp_dispose_unregister_with_wr(pp, DDSI_ENTITYID_SPDP_BUILTIN_PARTICIPANT_WRITER); + if ((ret > 0) && ddsi_omg_participant_is_secure(pp)) + { + ret = ddsi_spdp_dispose_unregister_with_wr(pp, DDSI_ENTITYID_SPDP_RELIABLE_BUILTIN_PARTICIPANT_SECURE_WRITER); + } + return ret; +} + +struct ddsi_spdp_directed_xevent_cb_arg { + ddsi_guid_t pp_guid; + int nrepeats; + ddsi_guid_prefix_t dest_proxypp_guid_prefix; +}; + +static bool resend_spdp_sample_by_guid_key (struct ddsi_writer *wr, const ddsi_guid_t *guid, struct ddsi_proxy_reader *prd) +{ + /* Look up data in (transient-local) WHC by key value -- FIXME: clearly + a slightly more efficient and elegant way of looking up the key value + is to be preferred */ + struct ddsi_domaingv *gv = wr->e.gv; + bool sample_found; + ddsi_plist_t ps; + ddsi_plist_init_empty (&ps); + ps.present |= PP_PARTICIPANT_GUID; + ps.participant_guid = *guid; + struct ddsi_serdata *sd = ddsi_serdata_from_sample (gv->spdp_type, SDK_KEY, &ps); + ddsi_plist_fini (&ps); + struct ddsi_whc_borrowed_sample sample; + + ddsrt_mutex_lock (&wr->e.lock); + sample_found = ddsi_whc_borrow_sample_key (wr->whc, sd, &sample); + if (sample_found) + { + /* Claiming it is new rather than a retransmit so that the rexmit + limiting won't kick in. It is best-effort and therefore the + updating of the last transmitted sequence number won't take + place anyway. Nor is it necessary to fiddle with heartbeat + control stuff. */ + ddsi_enqueue_spdp_sample_wrlock_held (wr, sample.seq, sample.serdata, prd); + ddsi_whc_return_sample(wr->whc, &sample, false); + } + ddsrt_mutex_unlock (&wr->e.lock); + ddsi_serdata_unref (sd); + return sample_found; +} + +static bool get_pp_and_spdp_wr (struct ddsi_domaingv *gv, const ddsi_guid_t *pp_guid, struct ddsi_participant **pp, struct ddsi_writer **spdp_wr) +{ + if ((*pp = ddsi_entidx_lookup_participant_guid (gv->entity_index, pp_guid)) == NULL) + { + GVTRACE ("handle_xevk_spdp "PGUIDFMT" - unknown guid\n", PGUID (*pp_guid)); + return false; + } + if ((*spdp_wr = ddsi_get_builtin_writer (*pp, DDSI_ENTITYID_SPDP_BUILTIN_PARTICIPANT_WRITER)) == NULL) + { + GVTRACE ("handle_xevk_spdp "PGUIDFMT" - spdp writer of participant not found\n", PGUID (*pp_guid)); + return false; + } + return true; +} + +static void ddsi_spdp_directed_xevent_cb (struct ddsi_domaingv *gv, struct ddsi_xevent *ev, UNUSED_ARG (struct ddsi_xpack *xp), void *varg, ddsrt_mtime_t tnow) +{ + struct ddsi_spdp_directed_xevent_cb_arg * const arg = varg; + struct ddsi_participant *pp; + struct ddsi_writer *spdp_wr; + + if (!get_pp_and_spdp_wr (gv, &arg->pp_guid, &pp, &spdp_wr)) + { + ddsi_delete_xevent (ev); + return; + } + + const ddsi_guid_t guid = { .prefix = arg->dest_proxypp_guid_prefix, .entityid = { .u = DDSI_ENTITYID_SPDP_BUILTIN_PARTICIPANT_READER } }; + struct ddsi_proxy_reader *prd; + if ((prd = ddsi_entidx_lookup_proxy_reader_guid (gv->entity_index, &guid)) == NULL) + GVTRACE ("xmit spdp: no proxy reader "PGUIDFMT"\n", PGUID (guid)); + else if (!resend_spdp_sample_by_guid_key (spdp_wr, &arg->pp_guid, prd)) + GVTRACE ("xmit spdp: suppressing early spdp response from "PGUIDFMT" to %"PRIx32":%"PRIx32":%"PRIx32":%x\n", + PGUID (pp->e.guid), PGUIDPREFIX (arg->dest_proxypp_guid_prefix), DDSI_ENTITYID_PARTICIPANT); + + // Directed events are used to send SPDP packets to newly discovered peers, and used just once + if (--arg->nrepeats == 0 || gv->config.spdp_interval < DDS_SECS (1) || pp->plist->qos.liveliness.lease_duration < DDS_SECS (1)) + ddsi_delete_xevent (ev); + else + { + ddsrt_mtime_t tnext = ddsrt_mtime_add_duration (tnow, DDS_SECS (1)); + GVTRACE ("xmit spdp "PGUIDFMT" to %"PRIx32":%"PRIx32":%"PRIx32":%x (resched %gs)\n", + PGUID (pp->e.guid), + PGUIDPREFIX (arg->dest_proxypp_guid_prefix), DDSI_ENTITYID_SPDP_BUILTIN_PARTICIPANT_READER, + (double)(tnext.v - tnow.v) / 1e9); + (void) ddsi_resched_xevent_if_earlier (ev, tnext); + } +} + +static void resched_spdp_broadcast (struct ddsi_xevent *ev, struct ddsi_participant *pp, ddsrt_mtime_t tnow) +{ + /* schedule next when 80% of the interval has elapsed, or 2s + before the lease ends, whichever comes first (similar to PMD), + but never wait longer than spdp_interval */ + struct ddsi_domaingv * const gv = pp->e.gv; + const dds_duration_t mindelta = DDS_MSECS (10); + const dds_duration_t ldur = pp->plist->qos.liveliness.lease_duration; + ddsrt_mtime_t tnext; + int64_t intv; + + if (ldur < 5 * mindelta / 4) + intv = mindelta; + else if (ldur < DDS_SECS (10)) + intv = 4 * ldur / 5; + else + intv = ldur - DDS_SECS (2); + if (intv > gv->config.spdp_interval) + intv = gv->config.spdp_interval; + + tnext = ddsrt_mtime_add_duration (tnow, intv); + GVTRACE ("xmit spdp "PGUIDFMT" to %"PRIx32":%"PRIx32":%"PRIx32":%x (resched %gs)\n", + PGUID (pp->e.guid), + 0,0,0, DDSI_ENTITYID_SPDP_BUILTIN_PARTICIPANT_READER, + (double)(tnext.v - tnow.v) / 1e9); + (void) ddsi_resched_xevent_if_earlier (ev, tnext); +} + +void ddsi_spdp_broadcast_xevent_cb (struct ddsi_domaingv *gv, struct ddsi_xevent *ev, UNUSED_ARG (struct ddsi_xpack *xp), void *varg, ddsrt_mtime_t tnow) +{ + /* Like the writer pointer in the heartbeat event, the participant pointer in the spdp event is assumed valid. */ + struct ddsi_spdp_broadcast_xevent_cb_arg * const arg = varg; + struct ddsi_participant *pp; + struct ddsi_writer *spdp_wr; + + if (!get_pp_and_spdp_wr (gv, &arg->pp_guid, &pp, &spdp_wr)) + return; + + if (!resend_spdp_sample_by_guid_key (spdp_wr, &arg->pp_guid, NULL)) + { +#ifndef NDEBUG + /* If undirected, it is pp->spdp_xevent, and that one must never + run into an empty WHC unless it is already marked for deletion. + + If directed, it may happen in response to an SPDP packet during + creation of the participant. This is because pp is inserted in + the hash table quite early on, which, in turn, is because it + needs to be visible for creating its builtin endpoints. But in + this case, the initial broadcast of the SPDP packet of pp will + happen shortly. */ + ddsrt_mutex_lock (&pp->e.lock); + assert (ddsi_delete_xevent_pending (ev)); + ddsrt_mutex_unlock (&pp->e.lock); +#endif + } + + resched_spdp_broadcast (ev, pp, tnow); +} + +static unsigned pseudo_random_delay (const ddsi_guid_t *x, const ddsi_guid_t *y, ddsrt_mtime_t tnow) +{ + /* You know, an ordinary random generator would be even better, but + the C library doesn't have a reentrant one and I don't feel like + integrating, say, the Mersenne Twister right now. */ + static const uint64_t cs[] = { + UINT64_C (15385148050874689571), + UINT64_C (17503036526311582379), + UINT64_C (11075621958654396447), + UINT64_C ( 9748227842331024047), + UINT64_C (14689485562394710107), + UINT64_C (17256284993973210745), + UINT64_C ( 9288286355086959209), + UINT64_C (17718429552426935775), + UINT64_C (10054290541876311021), + UINT64_C (13417933704571658407) + }; + uint32_t a = x->prefix.u[0], b = x->prefix.u[1], c = x->prefix.u[2], d = x->entityid.u; + uint32_t e = y->prefix.u[0], f = y->prefix.u[1], g = y->prefix.u[2], h = y->entityid.u; + uint32_t i = (uint32_t) ((uint64_t) tnow.v >> 32), j = (uint32_t) tnow.v; + uint64_t m = 0; + m += (a + cs[0]) * (b + cs[1]); + m += (c + cs[2]) * (d + cs[3]); + m += (e + cs[4]) * (f + cs[5]); + m += (g + cs[6]) * (h + cs[7]); + m += (i + cs[8]) * (j + cs[9]); + return (unsigned) (m >> 32); +} + +static void respond_to_spdp (const struct ddsi_domaingv *gv, const ddsi_guid_t *dest_proxypp_guid) +{ + struct ddsi_entity_enum_participant est; + struct ddsi_participant *pp; + ddsrt_mtime_t tnow = ddsrt_time_monotonic (); + ddsi_entidx_enum_participant_init (&est, gv->entity_index); + while ((pp = ddsi_entidx_enum_participant_next (&est)) != NULL) + { + /* delay_base has 32 bits, so delay_norm is approximately 1s max; + delay_max <= 1s by gv.config checks */ + unsigned delay_base = pseudo_random_delay (&pp->e.guid, dest_proxypp_guid, tnow); + unsigned delay_norm = delay_base >> 2; + int64_t delay_max_ms = gv->config.spdp_response_delay_max / 1000000; + int64_t delay = (int64_t) delay_norm * delay_max_ms / 1000; + ddsrt_mtime_t tsched = ddsrt_mtime_add_duration (tnow, delay); + GVTRACE (" %"PRId64, delay); + if (!pp->e.gv->config.unicast_response_to_spdp_messages) + /* pp can't reach gc_delete_participant => can safely reschedule */ + (void) ddsi_resched_xevent_if_earlier (pp->spdp_xevent, tsched); + else + { + struct ddsi_spdp_directed_xevent_cb_arg arg = { + .pp_guid = pp->e.guid, + .nrepeats = 4, .dest_proxypp_guid_prefix = dest_proxypp_guid->prefix + }; + ddsi_qxev_callback (gv->xevents, tsched, ddsi_spdp_directed_xevent_cb, &arg, sizeof (arg), false); + } + } + ddsi_entidx_enum_participant_fini (&est); +} + +static int handle_spdp_dead (const struct ddsi_receiver_state *rst, ddsi_entityid_t pwr_entityid, ddsrt_wctime_t timestamp, const ddsi_plist_t *datap, unsigned statusinfo) +{ + struct ddsi_domaingv * const gv = rst->gv; + ddsi_guid_t guid; + + GVLOGDISC ("SPDP ST%x", statusinfo); + + if (datap->present & PP_PARTICIPANT_GUID) + { + guid = datap->participant_guid; + GVLOGDISC (" %"PRIx32":%"PRIx32":%"PRIx32":%"PRIx32, PGUID (guid)); + assert (guid.entityid.u == DDSI_ENTITYID_PARTICIPANT); + if (ddsi_is_proxy_participant_deletion_allowed(gv, &guid, pwr_entityid)) + { + if (ddsi_delete_proxy_participant_by_guid (gv, &guid, timestamp, 0) < 0) + { + GVLOGDISC (" unknown"); + } + else + { + GVLOGDISC (" delete"); + } + } + else + { + GVLOGDISC (" not allowed"); + } + } + else + { + GVWARNING ("data (SPDP, vendor %u.%u): no/invalid payload\n", rst->vendor.id[0], rst->vendor.id[1]); + } + return 1; +} + +static struct ddsi_proxy_participant *find_ddsi2_proxy_participant (const struct ddsi_entity_index *entidx, const ddsi_guid_t *ppguid) +{ + struct ddsi_entity_enum_proxy_participant it; + struct ddsi_proxy_participant *pp; + ddsi_entidx_enum_proxy_participant_init (&it, entidx); + while ((pp = ddsi_entidx_enum_proxy_participant_next (&it)) != NULL) + { + if (ddsi_vendor_is_eclipse_or_opensplice (pp->vendor) && pp->e.guid.prefix.u[0] == ppguid->prefix.u[0] && pp->is_ddsi2_pp) + break; + } + ddsi_entidx_enum_proxy_participant_fini (&it); + return pp; +} + +static void make_participants_dependent_on_ddsi2 (struct ddsi_domaingv *gv, const ddsi_guid_t *ddsi2guid, ddsrt_wctime_t timestamp) +{ + struct ddsi_entity_enum_proxy_participant it; + struct ddsi_proxy_participant *pp, *d2pp; + if ((d2pp = ddsi_entidx_lookup_proxy_participant_guid (gv->entity_index, ddsi2guid)) == NULL) + return; + ddsi_entidx_enum_proxy_participant_init (&it, gv->entity_index); + while ((pp = ddsi_entidx_enum_proxy_participant_next (&it)) != NULL) + { + if (ddsi_vendor_is_eclipse_or_opensplice (pp->vendor) && pp->e.guid.prefix.u[0] == ddsi2guid->prefix.u[0] && !pp->is_ddsi2_pp) + { + GVTRACE ("proxy participant "PGUIDFMT" depends on ddsi2 "PGUIDFMT, PGUID (pp->e.guid), PGUID (*ddsi2guid)); + ddsrt_mutex_lock (&pp->e.lock); + pp->privileged_pp_guid = *ddsi2guid; + ddsrt_mutex_unlock (&pp->e.lock); + ddsi_proxy_participant_reassign_lease (pp, d2pp->lease); + GVTRACE ("\n"); + + if (ddsi_entidx_lookup_proxy_participant_guid (gv->entity_index, ddsi2guid) == NULL) + { + /* If DDSI2 has been deleted here (i.e., very soon after + having been created), we don't know whether pp will be + deleted */ + break; + } + } + } + ddsi_entidx_enum_proxy_participant_fini (&it); + + if (pp != NULL) + { + GVTRACE ("make_participants_dependent_on_ddsi2: ddsi2 "PGUIDFMT" is no more, delete "PGUIDFMT"\n", PGUID (*ddsi2guid), PGUID (pp->e.guid)); + ddsi_delete_proxy_participant_by_guid (gv, &pp->e.guid, timestamp, 1); + } +} + +static int handle_spdp_alive (const struct ddsi_receiver_state *rst, ddsi_seqno_t seq, ddsrt_wctime_t timestamp, const ddsi_plist_t *datap) +{ + struct ddsi_domaingv * const gv = rst->gv; + const unsigned bes_sedp_announcer_mask = + DDSI_DISC_BUILTIN_ENDPOINT_SUBSCRIPTION_ANNOUNCER | + DDSI_DISC_BUILTIN_ENDPOINT_PUBLICATION_ANNOUNCER; + struct ddsi_addrset *as_meta, *as_default; + uint32_t builtin_endpoint_set; + ddsi_guid_t privileged_pp_guid; + dds_duration_t lease_duration; + unsigned custom_flags = 0; + + /* If advertised domain id or domain tag doesn't match, ignore the message. Do this first to + minimize the impact such messages have. */ + { + const uint32_t domain_id = (datap->present & PP_DOMAIN_ID) ? datap->domain_id : gv->config.extDomainId.value; + const char *domain_tag = (datap->present & PP_DOMAIN_TAG) ? datap->domain_tag : ""; + if (domain_id != gv->config.extDomainId.value || strcmp (domain_tag, gv->config.domainTag) != 0) + { + GVTRACE ("ignore remote participant in mismatching domain %"PRIu32" tag \"%s\"\n", domain_id, domain_tag); + return 0; + } + } + + if (!(datap->present & PP_PARTICIPANT_GUID) || !(datap->present & PP_BUILTIN_ENDPOINT_SET)) + { + GVWARNING ("data (SPDP, vendor %u.%u): no/invalid payload\n", rst->vendor.id[0], rst->vendor.id[1]); + return 0; + } + + /* At some point the RTI implementation didn't mention + BUILTIN_ENDPOINT_DDSI_PARTICIPANT_MESSAGE_DATA_READER & ...WRITER, or + so it seemed; and yet they are necessary for correct operation, + so add them. */ + builtin_endpoint_set = datap->builtin_endpoint_set; + if (ddsi_vendor_is_rti (rst->vendor) && + ((builtin_endpoint_set & + (DDSI_BUILTIN_ENDPOINT_PARTICIPANT_MESSAGE_DATA_READER | + DDSI_BUILTIN_ENDPOINT_PARTICIPANT_MESSAGE_DATA_WRITER)) + != (DDSI_BUILTIN_ENDPOINT_PARTICIPANT_MESSAGE_DATA_READER | + DDSI_BUILTIN_ENDPOINT_PARTICIPANT_MESSAGE_DATA_WRITER)) && + gv->config.assume_rti_has_pmd_endpoints) + { + GVLOGDISC ("data (SPDP, vendor %u.%u): assuming unadvertised PMD endpoints do exist\n", + rst->vendor.id[0], rst->vendor.id[1]); + builtin_endpoint_set |= + DDSI_BUILTIN_ENDPOINT_PARTICIPANT_MESSAGE_DATA_READER | + DDSI_BUILTIN_ENDPOINT_PARTICIPANT_MESSAGE_DATA_WRITER; + } + + /* Do we know this GUID already? */ + { + struct ddsi_entity_common *existing_entity; + if ((existing_entity = ddsi_entidx_lookup_guid_untyped (gv->entity_index, &datap->participant_guid)) == NULL) + { + /* Local SPDP packets may be looped back, and that can include ones + for participants currently being deleted. The first thing that + happens when deleting a participant is removing it from the hash + table, and consequently the looped back packet may appear to be + from an unknown participant. So we handle that. */ + if (ddsi_is_deleted_participant_guid (gv->deleted_participants, &datap->participant_guid, DDSI_DELETED_PPGUID_REMOTE)) + { + RSTTRACE ("SPDP ST0 "PGUIDFMT" (recently deleted)", PGUID (datap->participant_guid)); + return 0; + } + } + else if (existing_entity->kind == DDSI_EK_PARTICIPANT) + { + RSTTRACE ("SPDP ST0 "PGUIDFMT" (local)", PGUID (datap->participant_guid)); + return 0; + } + else if (existing_entity->kind == DDSI_EK_PROXY_PARTICIPANT) + { + struct ddsi_proxy_participant *proxypp = (struct ddsi_proxy_participant *) existing_entity; + struct ddsi_lease *lease; + int interesting = 0; + RSTTRACE ("SPDP ST0 "PGUIDFMT" (known)", PGUID (datap->participant_guid)); + /* SPDP processing is so different from normal processing that we are + even skipping the automatic lease renewal. Note that proxy writers + that are not alive are not set alive here. This is done only when + data is received from a particular pwr (in handle_regular) */ + if ((lease = ddsrt_atomic_ldvoidp (&proxypp->minl_auto)) != NULL) + ddsi_lease_renew (lease, ddsrt_time_elapsed ()); + ddsrt_mutex_lock (&proxypp->e.lock); + if (proxypp->implicitly_created || seq > proxypp->seq) + { + interesting = 1; + if (!(gv->logconfig.c.mask & DDS_LC_TRACE)) + GVLOGDISC ("SPDP ST0 "PGUIDFMT, PGUID (datap->participant_guid)); + GVLOGDISC (proxypp->implicitly_created ? " (NEW was-implicitly-created)" : " (update)"); + proxypp->implicitly_created = 0; + ddsi_update_proxy_participant_plist_locked (proxypp, seq, datap, timestamp); + } + ddsrt_mutex_unlock (&proxypp->e.lock); + return interesting; + } + else + { + /* mismatch on entity kind: that should never have gotten past the + input validation */ + GVWARNING ("data (SPDP, vendor %u.%u): "PGUIDFMT" kind mismatch\n", rst->vendor.id[0], rst->vendor.id[1], PGUID (datap->participant_guid)); + return 0; + } + } + + const bool is_secure = ((datap->builtin_endpoint_set & DDSI_DISC_BUILTIN_ENDPOINT_PARTICIPANT_SECURE_ANNOUNCER) != 0 && + (datap->present & PP_IDENTITY_TOKEN)); + /* Make sure we don't create any security builtin endpoint when it's considered unsecure. */ + if (!is_secure) + builtin_endpoint_set &= DDSI_BES_MASK_NON_SECURITY; + GVLOGDISC ("SPDP ST0 "PGUIDFMT" bes %"PRIx32"%s NEW", PGUID (datap->participant_guid), builtin_endpoint_set, is_secure ? " (secure)" : ""); + + if (datap->present & PP_ADLINK_PARTICIPANT_VERSION_INFO) { + if ((datap->adlink_participant_version_info.flags & DDSI_ADLINK_FL_DDSI2_PARTICIPANT_FLAG) && + (datap->adlink_participant_version_info.flags & DDSI_ADLINK_FL_PARTICIPANT_IS_DDSI2)) + custom_flags |= DDSI_CF_PARTICIPANT_IS_DDSI2; + + GVLOGDISC (" (0x%08"PRIx32"-0x%08"PRIx32"-0x%08"PRIx32"-0x%08"PRIx32"-0x%08"PRIx32" %s)", + datap->adlink_participant_version_info.version, + datap->adlink_participant_version_info.flags, + datap->adlink_participant_version_info.unused[0], + datap->adlink_participant_version_info.unused[1], + datap->adlink_participant_version_info.unused[2], + datap->adlink_participant_version_info.internals); + } + + /* Can't do "mergein_missing" because of constness of *datap */ + if (datap->qos.present & DDSI_QP_LIVELINESS) + lease_duration = datap->qos.liveliness.lease_duration; + else + { + assert (ddsi_default_qos_participant.present & DDSI_QP_LIVELINESS); + lease_duration = ddsi_default_qos_participant.liveliness.lease_duration; + } + /* If any of the SEDP announcer are missing AND the guid prefix of + the SPDP writer differs from the guid prefix of the new participant, + we make it dependent on the writer's participant. See also the + lease expiration handling. Note that the entityid MUST be + DDSI_ENTITYID_PARTICIPANT or entidx_lookup will assert. So we only + zero the prefix. */ + privileged_pp_guid.prefix = rst->src_guid_prefix; + privileged_pp_guid.entityid.u = DDSI_ENTITYID_PARTICIPANT; + if ((builtin_endpoint_set & bes_sedp_announcer_mask) != bes_sedp_announcer_mask && + memcmp (&privileged_pp_guid, &datap->participant_guid, sizeof (ddsi_guid_t)) != 0) + { + GVLOGDISC (" (depends on "PGUIDFMT")", PGUID (privileged_pp_guid)); + /* never expire lease for this proxy: it won't actually expire + until the "privileged" one expires anyway */ + lease_duration = DDS_INFINITY; + } + else if (ddsi_vendor_is_eclipse_or_opensplice (rst->vendor) && !(custom_flags & DDSI_CF_PARTICIPANT_IS_DDSI2)) + { + /* Non-DDSI2 participants are made dependent on DDSI2 (but DDSI2 + itself need not be discovered yet) */ + struct ddsi_proxy_participant *ddsi2; + if ((ddsi2 = find_ddsi2_proxy_participant (gv->entity_index, &datap->participant_guid)) == NULL) + memset (&privileged_pp_guid.prefix, 0, sizeof (privileged_pp_guid.prefix)); + else + { + privileged_pp_guid.prefix = ddsi2->e.guid.prefix; + lease_duration = DDS_INFINITY; + GVLOGDISC (" (depends on "PGUIDFMT")", PGUID (privileged_pp_guid)); + } + } + else + { + memset (&privileged_pp_guid.prefix, 0, sizeof (privileged_pp_guid.prefix)); + } + + /* Choose locators */ + { + const ddsi_locators_t emptyset = { .n = 0, .first = NULL, .last = NULL }; + const ddsi_locators_t *uc; + const ddsi_locators_t *mc; + ddsi_locator_t srcloc; + ddsi_interface_set_t intfs; + + srcloc = rst->srcloc; + uc = (datap->present & PP_DEFAULT_UNICAST_LOCATOR) ? &datap->default_unicast_locators : ∅ + mc = (datap->present & PP_DEFAULT_MULTICAST_LOCATOR) ? &datap->default_multicast_locators : ∅ + if (gv->config.tcp_use_peeraddr_for_unicast) + uc = ∅ // force use of source locator + else if (uc != &emptyset) + ddsi_set_unspec_locator (&srcloc); // can't always use the source address + + ddsi_interface_set_init (&intfs); + as_default = ddsi_addrset_from_locatorlists (gv, uc, mc, &srcloc, &intfs); + + srcloc = rst->srcloc; + uc = (datap->present & PP_METATRAFFIC_UNICAST_LOCATOR) ? &datap->metatraffic_unicast_locators : ∅ + mc = (datap->present & PP_METATRAFFIC_MULTICAST_LOCATOR) ? &datap->metatraffic_multicast_locators : ∅ + if (gv->config.tcp_use_peeraddr_for_unicast) + uc = ∅ // force use of source locator + else if (uc != &emptyset) + ddsi_set_unspec_locator (&srcloc); // can't always use the source address + ddsi_interface_set_init (&intfs); + as_meta = ddsi_addrset_from_locatorlists (gv, uc, mc, &srcloc, &intfs); + + ddsi_log_addrset (gv, DDS_LC_DISCOVERY, " (data", as_default); + ddsi_log_addrset (gv, DDS_LC_DISCOVERY, " meta", as_meta); + GVLOGDISC (")"); + } + + if (ddsi_addrset_empty_uc (as_default) || ddsi_addrset_empty_uc (as_meta)) + { + GVLOGDISC (" (no unicast address"); + ddsi_unref_addrset (as_default); + ddsi_unref_addrset (as_meta); + return 1; + } + + GVLOGDISC (" QOS={"); + ddsi_xqos_log (DDS_LC_DISCOVERY, &gv->logconfig, &datap->qos); + GVLOGDISC ("}\n"); + + maybe_add_pp_as_meta_to_as_disc (gv, as_meta); + + if (!ddsi_new_proxy_participant (gv, &datap->participant_guid, builtin_endpoint_set, &privileged_pp_guid, as_default, as_meta, datap, lease_duration, rst->vendor, custom_flags, timestamp, seq)) + { + /* If no proxy participant was created, don't respond */ + return 0; + } + else + { + /* Force transmission of SPDP messages - we're not very careful + in avoiding the processing of SPDP packets addressed to others + so filter here */ + int have_dst = (rst->dst_guid_prefix.u[0] != 0 || rst->dst_guid_prefix.u[1] != 0 || rst->dst_guid_prefix.u[2] != 0); + if (!have_dst) + { + GVLOGDISC ("broadcasted SPDP packet -> answering"); + respond_to_spdp (gv, &datap->participant_guid); + } + else + { + GVLOGDISC ("directed SPDP packet -> not responding\n"); + } + + if (custom_flags & DDSI_CF_PARTICIPANT_IS_DDSI2) + { + /* If we just discovered DDSI2, make sure any existing + participants served by it are made dependent on it */ + make_participants_dependent_on_ddsi2 (gv, &datap->participant_guid, timestamp); + } + else if (privileged_pp_guid.prefix.u[0] || privileged_pp_guid.prefix.u[1] || privileged_pp_guid.prefix.u[2]) + { + /* If we just created a participant dependent on DDSI2, make sure + DDSI2 still exists. There is a risk of racing the lease expiry + of DDSI2. */ + if (ddsi_entidx_lookup_proxy_participant_guid (gv->entity_index, &privileged_pp_guid) == NULL) + { + GVLOGDISC ("make_participants_dependent_on_ddsi2: ddsi2 "PGUIDFMT" is no more, delete "PGUIDFMT"\n", + PGUID (privileged_pp_guid), PGUID (datap->participant_guid)); + ddsi_delete_proxy_participant_by_guid (gv, &datap->participant_guid, timestamp, 1); + } + } + return 1; + } +} + +void ddsi_handle_spdp (const struct ddsi_receiver_state *rst, ddsi_entityid_t pwr_entityid, ddsi_seqno_t seq, const struct ddsi_serdata *serdata) +{ + struct ddsi_domaingv * const gv = rst->gv; + ddsi_plist_t decoded_data; + if (ddsi_serdata_to_sample (serdata, &decoded_data, NULL, NULL)) + { + int interesting = 0; + switch (serdata->statusinfo & (DDSI_STATUSINFO_DISPOSE | DDSI_STATUSINFO_UNREGISTER)) + { + case 0: + interesting = handle_spdp_alive (rst, seq, serdata->timestamp, &decoded_data); + break; + + case DDSI_STATUSINFO_DISPOSE: + case DDSI_STATUSINFO_UNREGISTER: + case (DDSI_STATUSINFO_DISPOSE | DDSI_STATUSINFO_UNREGISTER): + interesting = handle_spdp_dead (rst, pwr_entityid, serdata->timestamp, &decoded_data, serdata->statusinfo); + break; + } + + ddsi_plist_fini (&decoded_data); + GVLOG (interesting ? DDS_LC_DISCOVERY : DDS_LC_TRACE, "\n"); + } +} diff --git a/src/core/ddsi/src/ddsi_discovery_topic.c b/src/core/ddsi/src/ddsi_discovery_topic.c new file mode 100644 index 0000000000..79fb2c0d06 --- /dev/null +++ b/src/core/ddsi/src/ddsi_discovery_topic.c @@ -0,0 +1,177 @@ +/* + * Copyright(c) 2006 to 2022 ZettaScale Technology and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License + * v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ +#include +#include +#include +#include + +#include "dds/ddsrt/heap.h" +#include "dds/ddsrt/log.h" +#include "dds/ddsi/ddsi_domaingv.h" +#include "ddsi__discovery.h" +#include "ddsi__discovery_topic.h" +#include "ddsi__entity_index.h" +#include "ddsi__entity.h" +#include "ddsi__participant.h" +#include "ddsi__transmit.h" +#include "ddsi__security_omg.h" +#include "ddsi__endpoint.h" +#include "ddsi__plist.h" +#include "ddsi__topic.h" +#include "ddsi__vendor.h" +#include "ddsi__xqos.h" +#include "ddsi__typelib.h" + +static int ddsi_sedp_write_topic_impl (struct ddsi_writer *wr, int alive, const ddsi_guid_t *guid, const dds_qos_t *xqos, ddsi_typeinfo_t *type_info) +{ + struct ddsi_domaingv * const gv = wr->e.gv; + const dds_qos_t *defqos = &ddsi_default_qos_topic; + + ddsi_plist_t ps; + ddsi_plist_init_empty (&ps); + ps.present |= PP_CYCLONE_TOPIC_GUID; + ps.topic_guid = *guid; + + assert (xqos != NULL); + ps.present |= PP_PROTOCOL_VERSION | PP_VENDORID; + ps.protocol_version.major = DDSI_RTPS_MAJOR; + ps.protocol_version.minor = DDSI_RTPS_MINOR; + ps.vendorid = DDSI_VENDORID_ECLIPSE; + + uint64_t qosdiff = ddsi_xqos_delta (xqos, defqos, ~(uint64_t)0); + if (gv->config.explicitly_publish_qos_set_to_default) + qosdiff |= ~DDSI_QP_UNRECOGNIZED_INCOMPATIBLE_MASK; + + if (type_info) + { + ps.qos.type_information = type_info; + ps.qos.present |= DDSI_QP_TYPE_INFORMATION; + } + if (xqos) + ddsi_xqos_mergein_missing (&ps.qos, xqos, qosdiff); + return ddsi_write_and_fini_plist (wr, &ps, alive); +} + +int ddsi_sedp_write_topic (struct ddsi_topic *tp, bool alive) +{ + int res = 0; + if (!(tp->pp->bes & DDSI_DISC_BUILTIN_ENDPOINT_TOPICS_ANNOUNCER)) + return res; + if (!ddsi_is_builtin_entityid (tp->e.guid.entityid, DDSI_VENDORID_ECLIPSE) && !tp->e.onlylocal) + { + unsigned entityid = ddsi_determine_topic_writer (tp); + struct ddsi_writer *sedp_wr = ddsi_get_sedp_writer (tp->pp, entityid); + ddsrt_mutex_lock (&tp->e.qos_lock); + // the allocation type info object is freed with the plist + res = ddsi_sedp_write_topic_impl (sedp_wr, alive, &tp->e.guid, tp->definition->xqos, ddsi_type_pair_complete_info (tp->e.gv, tp->definition->type_pair)); + ddsrt_mutex_unlock (&tp->e.qos_lock); + } + return res; +} + +static const char *durability_to_string (dds_durability_kind_t k) +{ + switch (k) + { + case DDS_DURABILITY_VOLATILE: return "volatile"; + case DDS_DURABILITY_TRANSIENT_LOCAL: return "transient-local"; + case DDS_DURABILITY_TRANSIENT: return "transient"; + case DDS_DURABILITY_PERSISTENT: return "persistent"; + } + return "undefined-durability"; +} + +void ddsi_handle_sedp_alive_topic (const struct ddsi_receiver_state *rst, ddsi_seqno_t seq, ddsi_plist_t *datap /* note: potentially modifies datap */, const ddsi_guid_prefix_t *src_guid_prefix, ddsi_vendorid_t vendorid, ddsrt_wctime_t timestamp) +{ + struct ddsi_domaingv * const gv = rst->gv; + struct ddsi_proxy_participant *proxypp; + ddsi_guid_t ppguid; + dds_qos_t *xqos; + int reliable; + const ddsi_typeid_t *type_id_minimal = NULL, *type_id_complete = NULL; + + assert (datap); + assert (datap->present & PP_CYCLONE_TOPIC_GUID); + GVLOGDISC (" "PGUIDFMT, PGUID (datap->topic_guid)); + + if (!ddsi_handle_sedp_checks (gv, SEDP_KIND_TOPIC, &datap->topic_guid, datap, src_guid_prefix, vendorid, timestamp, &proxypp, &ppguid)) + return; + + xqos = &datap->qos; + ddsi_xqos_mergein_missing (xqos, &ddsi_default_qos_topic, ~(uint64_t)0); + /* After copy + merge, should have at least the ones present in the + input. Also verify reliability and durability are present, + because we explicitly read those. */ + assert ((xqos->present & datap->qos.present) == datap->qos.present); + assert (xqos->present & DDSI_QP_RELIABILITY); + assert (xqos->present & DDSI_QP_DURABILITY); + reliable = (xqos->reliability.kind == DDS_RELIABILITY_RELIABLE); + + GVLOGDISC (" %s %s %s: %s/%s", + reliable ? "reliable" : "best-effort", + durability_to_string (xqos->durability.kind), + "topic", xqos->topic_name, xqos->type_name); + if (xqos->present & DDSI_QP_TYPE_INFORMATION) + { + struct ddsi_typeid_str strm, strc; + type_id_minimal = ddsi_typeinfo_minimal_typeid (xqos->type_information); + type_id_complete = ddsi_typeinfo_complete_typeid (xqos->type_information); + GVLOGDISC (" tid %s/%s", ddsi_make_typeid_str(&strm, type_id_minimal), ddsi_make_typeid_str(&strc, type_id_complete)); + } + GVLOGDISC (" QOS={"); + ddsi_xqos_log (DDS_LC_DISCOVERY, &gv->logconfig, xqos); + GVLOGDISC ("}\n"); + + if ((datap->topic_guid.entityid.u & DDSI_ENTITYID_SOURCE_MASK) == DDSI_ENTITYID_SOURCE_VENDOR && !ddsi_vendor_is_eclipse_or_adlink (vendorid)) + { + GVLOGDISC ("ignoring vendor-specific topic "PGUIDFMT"\n", PGUID (datap->topic_guid)); + } + else + { + // FIXME: check compatibility with known topic definitions + struct ddsi_proxy_topic *ptp = ddsi_lookup_proxy_topic (proxypp, &datap->topic_guid); + if (ptp) + { + GVLOGDISC (" update known proxy-topic%s\n", ddsi_vendor_is_cloud (vendorid) ? "-DS" : ""); + ddsi_update_proxy_topic (proxypp, ptp, seq, xqos, timestamp); + } + else + { + GVLOGDISC (" NEW proxy-topic"); + if (ddsi_new_proxy_topic (proxypp, seq, &datap->topic_guid, type_id_minimal, type_id_complete, xqos, timestamp) != DDS_RETCODE_OK) + GVLOGDISC (" failed"); + } + } +} + +void ddsi_handle_sedp_dead_topic (const struct ddsi_receiver_state *rst, ddsi_plist_t *datap, ddsrt_wctime_t timestamp) +{ + struct ddsi_proxy_participant *proxypp; + struct ddsi_proxy_topic *proxytp; + struct ddsi_domaingv * const gv = rst->gv; + assert (datap->present & PP_CYCLONE_TOPIC_GUID); + GVLOGDISC (" "PGUIDFMT" ", PGUID (datap->topic_guid)); + if (!ddsi_check_sedp_kind_and_guid (SEDP_KIND_TOPIC, &datap->topic_guid)) + return; + ddsi_guid_t ppguid = { .prefix = datap->topic_guid.prefix, .entityid.u = DDSI_ENTITYID_PARTICIPANT }; + if ((proxypp = ddsi_entidx_lookup_proxy_participant_guid (gv->entity_index, &ppguid)) == NULL) + GVLOGDISC (" unknown proxypp\n"); + else if ((proxytp = ddsi_lookup_proxy_topic (proxypp, &datap->topic_guid)) == NULL) + GVLOGDISC (" unknown proxy topic\n"); + else + { + ddsrt_mutex_lock (&proxypp->e.lock); + int res = ddsi_delete_proxy_topic_locked (proxypp, proxytp, timestamp); + GVLOGDISC (" %s\n", res == DDS_RETCODE_PRECONDITION_NOT_MET ? " already-deleting" : " delete"); + ddsrt_mutex_unlock (&proxypp->e.lock); + } +} diff --git a/src/core/ddsi/src/ddsi_endpoint.c b/src/core/ddsi/src/ddsi_endpoint.c index e79096f879..4eb715318d 100644 --- a/src/core/ddsi/src/ddsi_endpoint.c +++ b/src/core/ddsi/src/ddsi_endpoint.c @@ -12,6 +12,7 @@ #include #include #include +#include #include "dds/ddsrt/avl.h" #include "dds/ddsrt/heap.h" @@ -30,7 +31,7 @@ #include "ddsi__udp.h" #include "ddsi__wraddrset.h" #include "ddsi__security_omg.h" -#include "ddsi__discovery.h" +#include "ddsi__discovery_endpoint.h" #include "ddsi__whc.h" #include "ddsi__xevent.h" #include "ddsi__addrset.h" @@ -863,10 +864,13 @@ static void ddsi_new_writer_guid_common_init (struct ddsi_writer *wr, const char writer for it in the hash table. NEVER => won't ever be scheduled, and this can only change by writing data, which won't happen until after it becomes visible. */ - if (wr->reliable) - wr->heartbeat_xevent = ddsi_qxev_heartbeat (wr->evq, DDSRT_MTIME_NEVER, &wr->e.guid); - else + if (!wr->reliable) wr->heartbeat_xevent = NULL; + else + { + struct ddsi_heartbeat_xevent_cb_arg arg = {.wr_guid = wr->e.guid }; + wr->heartbeat_xevent = ddsi_qxev_callback (wr->evq, DDSRT_MTIME_NEVER, ddsi_heartbeat_xevent_cb, &arg, sizeof (arg), false); + } assert (wr->xqos->present & DDSI_QP_LIVELINESS); if (wr->xqos->liveliness.lease_duration != DDS_INFINITY) @@ -1263,6 +1267,19 @@ void ddsi_delete_local_orphan_writer (struct ddsi_local_orphan_writer *lowr) ddsrt_mutex_unlock (&lowr->wr.e.lock); } +struct ddsi_delete_writer_xevent_cb_arg { + ddsi_guid_t wr_guid; +}; + +static void ddsi_delete_writer_xevent_cb (struct ddsi_domaingv *gv, struct ddsi_xevent *ev, UNUSED_ARG (struct ddsi_xpack *xp), void *varg, UNUSED_ARG (ddsrt_mtime_t tnow)) +{ + struct ddsi_delete_writer_xevent_cb_arg const * const arg = varg; + /* don't worry if the writer is already gone by the time we get here, delete_writer_nolinger checks for that. */ + GVTRACE ("handle_xevk_delete_writer: "PGUIDFMT"\n", PGUID (arg->wr_guid)); + ddsi_delete_writer_nolinger (gv, &arg->wr_guid); + ddsi_delete_xevent (ev); +} + dds_return_t ddsi_delete_writer (struct ddsi_domaingv *gv, const struct ddsi_guid *guid) { struct ddsi_writer *wr; @@ -1296,7 +1313,9 @@ dds_return_t ddsi_delete_writer (struct ddsi_domaingv *gv, const struct ddsi_gui ddsrt_mtime_to_sec_usec (&tsec, &tusec, tsched); GVLOGDISC ("delete_writer(guid "PGUIDFMT") - unack'ed samples, will delete when ack'd or at t = %"PRId32".%06"PRId32"\n", PGUID (*guid), tsec, tusec); - ddsi_qxev_delete_writer (gv->xevents, tsched, &wr->e.guid); + + struct ddsi_delete_writer_xevent_cb_arg arg = { .wr_guid = wr->e.guid }; + ddsi_qxev_callback (gv->xevents, tsched, ddsi_delete_writer_xevent_cb, &arg, sizeof (arg), false); } return 0; } diff --git a/src/core/ddsi/src/ddsi_endpoint_match.c b/src/core/ddsi/src/ddsi_endpoint_match.c index 91e747baeb..a8f4db21ed 100644 --- a/src/core/ddsi/src/ddsi_endpoint_match.c +++ b/src/core/ddsi/src/ddsi_endpoint_match.c @@ -33,6 +33,7 @@ #include "ddsi__typelib.h" #include "ddsi__vendor.h" #include "ddsi__lat_estim.h" +#include "ddsi__acknack.h" #ifdef DDS_HAS_TYPE_DISCOVERY #include "ddsi__typelookup.h" #endif @@ -1076,7 +1077,10 @@ void ddsi_proxy_writer_add_connection (struct ddsi_proxy_writer *pwr, struct dds } const ddsrt_mtime_t tsched = use_iceoryx ? DDSRT_MTIME_NEVER : ddsrt_mtime_add_duration (tnow, pwr->e.gv->config.preemptive_ack_delay); - m->acknack_xevent = ddsi_qxev_acknack (pwr->evq, tsched, &pwr->e.guid, &rd->e.guid); + { + struct ddsi_acknack_xevent_cb_arg arg = { .pwr_guid = pwr->e.guid, .rd_guid = rd->e.guid }; + m->acknack_xevent = ddsi_qxev_callback (pwr->evq, tsched, ddsi_acknack_xevent_cb, &arg, sizeof (arg), false); + } m->u.not_in_sync.reorder = ddsi_reorder_new (&pwr->e.gv->logconfig, DDSI_REORDER_MODE_NORMAL, secondary_reorder_maxsamples, pwr->e.gv->config.late_ack_mode); pwr->n_reliable_readers++; @@ -1098,7 +1102,7 @@ void ddsi_proxy_writer_add_connection (struct ddsi_proxy_writer *pwr, struct dds #endif ddsrt_mutex_unlock (&pwr->e.lock); - ddsi_qxev_pwr_entityid (pwr, &rd->e.guid); + ddsi_send_entityid_to_pwr (pwr, &rd->e.guid); ELOGDISC (pwr, "\n"); return; @@ -1137,7 +1141,7 @@ void ddsi_proxy_reader_add_connection (struct ddsi_proxy_reader *prd, struct dds PGUID (wr->e.guid), PGUID (prd->e.guid)); ddsrt_avl_insert_ipath (&ddsi_prd_writers_treedef, &prd->writers, m, &path); ddsrt_mutex_unlock (&prd->e.lock); - ddsi_qxev_prd_entityid (prd, &wr->e.guid); + ddsi_send_entityid_to_prd (prd, &wr->e.guid); } } diff --git a/src/core/ddsi/src/ddsi_hbcontrol.c b/src/core/ddsi/src/ddsi_hbcontrol.c new file mode 100644 index 0000000000..e2d4b5cd79 --- /dev/null +++ b/src/core/ddsi/src/ddsi_hbcontrol.c @@ -0,0 +1,615 @@ +/* + * Copyright(c) 2006 to 2023 ZettaScale Technology and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License + * v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ +#include +#include +#include + +#include "dds/ddsrt/sync.h" +#include "dds/ddsrt/avl.h" +#include "dds/ddsi/ddsi_domaingv.h" +#include "dds/ddsi/ddsi_unused.h" +#include "ddsi__entity_index.h" +#include "ddsi__xmsg.h" +#include "ddsi__misc.h" +#include "ddsi__xevent.h" +#include "ddsi__transmit.h" +#include "ddsi__hbcontrol.h" +#include "ddsi__security_omg.h" +#include "ddsi__sysdeps.h" +#include "ddsi__endpoint.h" +#include "ddsi__endpoint_match.h" +#include "ddsi__protocol.h" + +static const struct ddsi_wr_prd_match *root_rdmatch (const struct ddsi_writer *wr) +{ + return ddsrt_avl_root (&ddsi_wr_readers_treedef, &wr->readers); +} + +void ddsi_writer_hbcontrol_init (struct ddsi_hbcontrol *hbc) +{ + hbc->t_of_last_write.v = 0; + hbc->t_of_last_hb.v = 0; + hbc->t_of_last_ackhb.v = 0; + hbc->tsched = DDSRT_MTIME_NEVER; + hbc->hbs_since_last_write = 0; + hbc->last_packetid = 0; +} + +static void writer_hbcontrol_note_hb (struct ddsi_writer *wr, ddsrt_mtime_t tnow, int ansreq) +{ + struct ddsi_hbcontrol * const hbc = &wr->hbcontrol; + + if (ansreq) + hbc->t_of_last_ackhb = tnow; + hbc->t_of_last_hb = tnow; + + /* Count number of heartbeats since last write, used to lower the + heartbeat rate. Overflow doesn't matter, it'll just revert to a + highish rate for a short while. */ + hbc->hbs_since_last_write++; +} + +int64_t ddsi_writer_hbcontrol_intv (const struct ddsi_writer *wr, const struct ddsi_whc_state *whcst, UNUSED_ARG (ddsrt_mtime_t tnow)) +{ + struct ddsi_domaingv const * const gv = wr->e.gv; + struct ddsi_hbcontrol const * const hbc = &wr->hbcontrol; + int64_t ret = gv->config.const_hb_intv_sched; + size_t n_unacked; + + if (hbc->hbs_since_last_write > 5) + { + unsigned cnt = (hbc->hbs_since_last_write - 5) / 2; + while (cnt-- != 0 && 2 * ret < gv->config.const_hb_intv_sched_max) + ret *= 2; + } + + n_unacked = whcst->unacked_bytes; + if (n_unacked >= wr->whc_low + 3 * (wr->whc_high - wr->whc_low) / 4) + ret /= 2; + if (n_unacked >= wr->whc_low + (wr->whc_high - wr->whc_low) / 2) + ret /= 2; + if (wr->throttling) + ret /= 2; + if (ret < gv->config.const_hb_intv_sched_min) + ret = gv->config.const_hb_intv_sched_min; + return ret; +} + +void ddsi_writer_hbcontrol_note_asyncwrite (struct ddsi_writer *wr, ddsrt_mtime_t tnow) +{ + struct ddsi_domaingv const * const gv = wr->e.gv; + struct ddsi_hbcontrol * const hbc = &wr->hbcontrol; + ddsrt_mtime_t tnext; + + /* Reset number of heartbeats since last write: that means the + heartbeat rate will go back up to the default */ + hbc->hbs_since_last_write = 0; + + /* We know this is new data, so we want a heartbeat event after one + base interval */ + tnext.v = tnow.v + gv->config.const_hb_intv_sched; + if (tnext.v < hbc->tsched.v) + { + /* Insertion of a message with WHC locked => must now have at + least one unacked msg if there are reliable readers, so must + have a heartbeat scheduled. Do so now */ + hbc->tsched = tnext; + (void) ddsi_resched_xevent_if_earlier (wr->heartbeat_xevent, tnext); + } +} + +int ddsi_writer_hbcontrol_must_send (const struct ddsi_writer *wr, const struct ddsi_whc_state *whcst, ddsrt_mtime_t tnow /* monotonic */) +{ + struct ddsi_hbcontrol const * const hbc = &wr->hbcontrol; + return (tnow.v >= hbc->t_of_last_hb.v + ddsi_writer_hbcontrol_intv (wr, whcst, tnow)); +} + +struct ddsi_xmsg *ddsi_writer_hbcontrol_create_heartbeat (struct ddsi_writer *wr, const struct ddsi_whc_state *whcst, ddsrt_mtime_t tnow, int hbansreq, int issync) +{ + struct ddsi_domaingv const * const gv = wr->e.gv; + struct ddsi_xmsg *msg; + const ddsi_guid_t *prd_guid; + + ASSERT_MUTEX_HELD (&wr->e.lock); + assert (wr->reliable); + assert (hbansreq >= 0); + + if ((msg = ddsi_xmsg_new (gv->xmsgpool, &wr->e.guid, wr->c.pp, sizeof (ddsi_rtps_info_ts_t) + sizeof (ddsi_rtps_heartbeat_t), DDSI_XMSG_KIND_CONTROL)) == NULL) + /* out of memory at worst slows down traffic */ + return NULL; + + if (ddsrt_avl_is_empty (&wr->readers) || wr->num_reliable_readers == 0) + { + /* Not really supposed to come here, at least not for the first + case. Secondly, there really seems to be little use for + optimising reliable writers with only best-effort readers. And + in any case, it is always legal to multicast a heartbeat from a + reliable writer. */ + prd_guid = NULL; + } + else if (wr->seq != root_rdmatch (wr)->max_seq) + { + /* If the writer is ahead of its readers, multicast. Couldn't care + less about the pessimal cases such as multicasting when there + is one reliable reader & multiple best-effort readers. See + comment above. */ + prd_guid = NULL; + } + else + { + const uint32_t n_unacked = wr->num_reliable_readers - root_rdmatch (wr)->num_reliable_readers_where_seq_equals_max; + if (n_unacked == 0) + prd_guid = NULL; + else + { + assert (root_rdmatch (wr)->arbitrary_unacked_reader.entityid.u != DDSI_ENTITYID_UNKNOWN); + if (n_unacked > 1) + prd_guid = NULL; + else + prd_guid = &(root_rdmatch (wr)->arbitrary_unacked_reader); + } + } + + ETRACE (wr, "writer_hbcontrol: wr "PGUIDFMT" ", PGUID (wr->e.guid)); + if (prd_guid == NULL) + ETRACE (wr, "multicasting "); + else + ETRACE (wr, "unicasting to prd "PGUIDFMT" ", PGUID (*prd_guid)); + if (ddsrt_avl_is_empty (&wr->readers)) + { + ETRACE (wr, "(rel-prd %"PRId32" seq-eq-max [none] seq %"PRId64" maxseq [none])\n", + wr->num_reliable_readers, wr->seq); + } + else + { + ETRACE (wr, "(rel-prd %"PRId32" seq-eq-max %"PRId32" seq %"PRIu64" maxseq %"PRIu64")\n", + wr->num_reliable_readers, + (int32_t) root_rdmatch (wr)->num_reliable_readers_where_seq_equals_max, + wr->seq, + root_rdmatch (wr)->max_seq); + } + + if (prd_guid == NULL) + { + ddsi_xmsg_setdst_addrset (msg, wr->as); + ddsi_add_heartbeat (msg, wr, whcst, hbansreq, 0, ddsi_to_entityid (DDSI_ENTITYID_UNKNOWN), issync); + } + else + { + struct ddsi_proxy_reader *prd; + if ((prd = ddsi_entidx_lookup_proxy_reader_guid (gv->entity_index, prd_guid)) == NULL) + { + ETRACE (wr, "writer_hbcontrol: wr "PGUIDFMT" unknown prd "PGUIDFMT"\n", PGUID (wr->e.guid), PGUID (*prd_guid)); + ddsi_xmsg_free (msg); + return NULL; + } + /* set the destination explicitly to the unicast destination and the fourth + param of ddsi_add_heartbeat needs to be the guid of the reader */ + ddsi_xmsg_setdst_prd (msg, prd); + // send to all readers in the participant: whether or not the entityid is set affects + // the retransmit requests + ddsi_add_heartbeat (msg, wr, whcst, hbansreq, 0, ddsi_to_entityid (DDSI_ENTITYID_UNKNOWN), issync); + } + + /* It is possible that the encoding removed the submessage(s). */ + if (ddsi_xmsg_size(msg) == 0) + { + ddsi_xmsg_free (msg); + msg = NULL; + } + + writer_hbcontrol_note_hb (wr, tnow, hbansreq); + return msg; +} + +static int writer_hbcontrol_ack_required_generic (const struct ddsi_writer *wr, const struct ddsi_whc_state *whcst, ddsrt_mtime_t tlast, ddsrt_mtime_t tnow, int piggyback) +{ + struct ddsi_domaingv const * const gv = wr->e.gv; + struct ddsi_hbcontrol const * const hbc = &wr->hbcontrol; + const int64_t hb_intv_ack = gv->config.const_hb_intv_sched; + assert(wr->heartbeat_xevent != NULL && whcst != NULL); + + if (piggyback) + { + /* If it is likely that a heartbeat requiring an ack will go out + shortly after the sample was written, it is better to piggyback + it onto the sample. The current idea is that a write shortly + before the next heartbeat will go out should have one + piggybacked onto it, so that the scheduled heartbeat can be + suppressed. */ + if (tnow.v >= tlast.v + 4 * hb_intv_ack / 5) + return 2; + } + else + { + /* For heartbeat events use a slightly longer interval */ + if (tnow.v >= tlast.v + hb_intv_ack) + return 2; + } + + if (whcst->unacked_bytes >= wr->whc_low + (wr->whc_high - wr->whc_low) / 2) + { + if (tnow.v >= hbc->t_of_last_ackhb.v + gv->config.const_hb_intv_sched_min) + return 2; + else if (tnow.v >= hbc->t_of_last_ackhb.v + gv->config.const_hb_intv_min) + return 1; + } + + return 0; +} + +int ddsi_writer_hbcontrol_ack_required (const struct ddsi_writer *wr, const struct ddsi_whc_state *whcst, ddsrt_mtime_t tnow) +{ + struct ddsi_hbcontrol const * const hbc = &wr->hbcontrol; + return writer_hbcontrol_ack_required_generic (wr, whcst, hbc->t_of_last_write, tnow, 0); +} + +struct ddsi_xmsg *ddsi_writer_hbcontrol_piggyback (struct ddsi_writer *wr, const struct ddsi_whc_state *whcst, ddsrt_mtime_t tnow, uint32_t packetid, int *hbansreq) +{ + struct ddsi_hbcontrol * const hbc = &wr->hbcontrol; + uint32_t last_packetid; + ddsrt_mtime_t tlast; + ddsrt_mtime_t t_of_last_hb; + struct ddsi_xmsg *msg; + + tlast = hbc->t_of_last_write; + last_packetid = hbc->last_packetid; + t_of_last_hb = hbc->t_of_last_hb; + + hbc->t_of_last_write = tnow; + hbc->last_packetid = packetid; + + /* Update statistics, intervals, scheduling of heartbeat event, + &c. -- there's no real difference between async and sync so we + reuse the async version. */ + ddsi_writer_hbcontrol_note_asyncwrite (wr, tnow); + + *hbansreq = writer_hbcontrol_ack_required_generic (wr, whcst, tlast, tnow, 1); + if (*hbansreq >= 2) { + /* So we force a heartbeat in - but we also rely on our caller to + send the packet out */ + msg = ddsi_writer_hbcontrol_create_heartbeat (wr, whcst, tnow, *hbansreq, 1); + } else if (last_packetid != packetid && tnow.v - t_of_last_hb.v > DDS_USECS (100)) { + /* If we crossed a packet boundary since the previous write, + piggyback a heartbeat, with *hbansreq determining whether or + not an ACK is needed. We don't force the packet out either: + this is just to ensure a regular flow of ACKs for cleaning up + the WHC & for allowing readers to NACK missing samples. + + Still rate-limit: if there are new readers that haven't sent an + an ACK yet, the FINAL flag will be cleared and so we get an ACK + storm if writing at a high rate without batching which eats up + a *large* amount of time because there are out-of-order readers + present. */ + msg = ddsi_writer_hbcontrol_create_heartbeat (wr, whcst, tnow, *hbansreq, 1); + } else { + *hbansreq = 0; + msg = NULL; + } + + if (msg) + { + if (ddsrt_avl_is_empty (&wr->readers)) + { + ETRACE (wr, "heartbeat(wr "PGUIDFMT"%s) piggybacked, resched in %g s (min-ack [none], avail-seq %"PRIu64", xmit %"PRIu64")\n", + PGUID (wr->e.guid), + *hbansreq ? "" : " final", + (hbc->tsched.v == DDS_NEVER) ? INFINITY : (double) (hbc->tsched.v - tnow.v) / 1e9, + whcst->max_seq, ddsi_writer_read_seq_xmit(wr)); + } + else + { + ETRACE (wr, "heartbeat(wr "PGUIDFMT"%s) piggybacked, resched in %g s (min-ack %"PRIu64"%s, avail-seq %"PRIu64", xmit %"PRIu64")\n", + PGUID (wr->e.guid), + *hbansreq ? "" : " final", + (hbc->tsched.v == DDS_NEVER) ? INFINITY : (double) (hbc->tsched.v - tnow.v) / 1e9, + root_rdmatch (wr)->min_seq, + root_rdmatch (wr)->all_have_replied_to_hb ? "" : "!", + whcst->max_seq, ddsi_writer_read_seq_xmit(wr)); + } + } + + return msg; +} + +#ifdef DDS_HAS_SECURITY +struct ddsi_xmsg *ddsi_writer_hbcontrol_p2p(struct ddsi_writer *wr, const struct ddsi_whc_state *whcst, int hbansreq, struct ddsi_proxy_reader *prd) +{ + struct ddsi_domaingv const * const gv = wr->e.gv; + struct ddsi_xmsg *msg; + + ASSERT_MUTEX_HELD (&wr->e.lock); + assert (wr->reliable); + + if ((msg = ddsi_xmsg_new (gv->xmsgpool, &wr->e.guid, wr->c.pp, sizeof (ddsi_rtps_info_ts_t) + sizeof (ddsi_rtps_heartbeat_t), DDSI_XMSG_KIND_CONTROL)) == NULL) + return NULL; + + ETRACE (wr, "writer_hbcontrol_p2p: wr "PGUIDFMT" unicasting to prd "PGUIDFMT" ", PGUID (wr->e.guid), PGUID (prd->e.guid)); + if (ddsrt_avl_is_empty (&wr->readers)) + { + ETRACE (wr, "(rel-prd %d seq-eq-max [none] seq %"PRIu64")\n", wr->num_reliable_readers, wr->seq); + } + else + { + ETRACE (wr, "(rel-prd %d seq-eq-max %d seq %"PRIu64" maxseq %"PRIu64")\n", + wr->num_reliable_readers, + (int32_t) root_rdmatch (wr)->num_reliable_readers_where_seq_equals_max, + wr->seq, + root_rdmatch (wr)->max_seq); + } + + /* set the destination explicitly to the unicast destination and the fourth + param of ddsi_add_heartbeat needs to be the guid of the reader */ + ddsi_xmsg_setdst_prd (msg, prd); + ddsi_add_heartbeat (msg, wr, whcst, hbansreq, 0, prd->e.guid.entityid, 1); + + if (ddsi_xmsg_size(msg) == 0) + { + ddsi_xmsg_free (msg); + msg = NULL; + } + + return msg; +} +#endif + +void ddsi_add_heartbeat (struct ddsi_xmsg *msg, struct ddsi_writer *wr, const struct ddsi_whc_state *whcst, int hbansreq, int hbliveliness, ddsi_entityid_t dst, int issync) +{ + struct ddsi_domaingv const * const gv = wr->e.gv; + struct ddsi_xmsg_marker sm_marker; + ddsi_rtps_heartbeat_t * hb; + ddsi_seqno_t max, min; + + ASSERT_MUTEX_HELD (&wr->e.lock); + + assert (wr->reliable); + assert (hbansreq >= 0); + assert (hbliveliness >= 0); + + if (gv->config.meas_hb_to_ack_latency) + { + /* If configured to measure heartbeat-to-ack latency, we must add + a timestamp. No big deal if it fails. */ + ddsi_xmsg_add_timestamp (msg, ddsrt_time_wallclock ()); + } + + hb = ddsi_xmsg_append (msg, &sm_marker, sizeof (ddsi_rtps_heartbeat_t)); + ddsi_xmsg_submsg_init (msg, sm_marker, DDSI_RTPS_SMID_HEARTBEAT); + + if (!hbansreq) + hb->smhdr.flags |= DDSI_HEARTBEAT_FLAG_FINAL; + if (hbliveliness) + hb->smhdr.flags |= DDSI_HEARTBEAT_FLAG_LIVELINESS; + + hb->readerId = ddsi_hton_entityid (dst); + hb->writerId = ddsi_hton_entityid (wr->e.guid.entityid); + if (DDSI_WHCST_ISEMPTY(whcst)) + { + max = wr->seq; + min = max + 1; + } + else + { + /* If data present in WHC, wr->seq > 0, but xmit_seq possibly still 0 */ + min = whcst->min_seq; + max = wr->seq; + const ddsi_seqno_t seq_xmit = ddsi_writer_read_seq_xmit (wr); + assert (min <= max); + /* Informing readers of samples that haven't even been transmitted makes little sense, + but for transient-local data, we let the first heartbeat determine the time at which + we trigger wait_for_historical_data, so it had better be correct */ + if (!issync && seq_xmit < max && !wr->handle_as_transient_local) + { + /* When: queue data ; queue heartbeat ; transmit data ; update + seq_xmit, max may be < min. But we must never advertise the + minimum available sequence number incorrectly! */ + if (seq_xmit >= min) { + /* Advertise some but not all data */ + max = seq_xmit; + } else { + /* Advertise no data yet */ + max = min - 1; + } + } + } + hb->firstSN = ddsi_to_seqno (min); + hb->lastSN = ddsi_to_seqno (max); + + hb->count = wr->hbcount++; + + ddsi_xmsg_submsg_setnext (msg, sm_marker); + ddsi_security_encode_datawriter_submsg(msg, sm_marker, wr); +} + +#ifdef DDS_HAS_SECURITY +static int send_heartbeat_to_all_readers_check_and_sched (struct ddsi_xevent *ev, struct ddsi_writer *wr, const struct ddsi_whc_state *whcst, ddsrt_mtime_t tnow, ddsrt_mtime_t *t_next) +{ + int send; + if (!ddsi_writer_must_have_hb_scheduled (wr, whcst)) + { + wr->hbcontrol.tsched = DDSRT_MTIME_NEVER; + send = -1; + } + else if (!ddsi_writer_hbcontrol_must_send (wr, whcst, tnow)) + { + wr->hbcontrol.tsched = ddsrt_mtime_add_duration (tnow, ddsi_writer_hbcontrol_intv (wr, whcst, tnow)); + send = -1; + } + else + { + const int hbansreq = ddsi_writer_hbcontrol_ack_required (wr, whcst, tnow); + wr->hbcontrol.tsched = ddsrt_mtime_add_duration (tnow, ddsi_writer_hbcontrol_intv (wr, whcst, tnow)); + send = hbansreq; + } + + ddsi_resched_xevent_if_earlier (ev, wr->hbcontrol.tsched); + *t_next = wr->hbcontrol.tsched; + return send; +} + +static void send_heartbeat_to_all_readers (struct ddsi_xpack *xp, struct ddsi_xevent *ev, struct ddsi_writer *wr, ddsrt_mtime_t tnow) +{ + struct ddsi_whc_state whcst; + ddsrt_mtime_t t_next; + unsigned count = 0; + + ddsrt_mutex_lock (&wr->e.lock); + + ddsi_whc_get_state(wr->whc, &whcst); + const int hbansreq = send_heartbeat_to_all_readers_check_and_sched (ev, wr, &whcst, tnow, &t_next); + if (hbansreq >= 0) + { + struct ddsi_wr_prd_match *m; + struct ddsi_guid last_guid = { .prefix = {.u = {0,0,0}}, .entityid = {0} }; + + while ((m = ddsrt_avl_lookup_succ (&ddsi_wr_readers_treedef, &wr->readers, &last_guid)) != NULL) + { + last_guid = m->prd_guid; + if (m->seq < m->last_seq) + { + struct ddsi_proxy_reader *prd; + + prd = ddsi_entidx_lookup_proxy_reader_guid (wr->e.gv->entity_index, &m->prd_guid); + if (prd) + { + ETRACE (wr, " heartbeat(wr "PGUIDFMT" rd "PGUIDFMT" %s) send, resched in %g s (min-ack %"PRIu64", avail-seq %"PRIu64")\n", + PGUID (wr->e.guid), + PGUID (m->prd_guid), + hbansreq ? "" : " final", + (double)(t_next.v - tnow.v) / 1e9, + m->seq, + m->last_seq); + + struct ddsi_xmsg *msg = ddsi_writer_hbcontrol_p2p(wr, &whcst, hbansreq, prd); + if (msg != NULL) + { + ddsrt_mutex_unlock (&wr->e.lock); + ddsi_xpack_addmsg (xp, msg, 0); + ddsrt_mutex_lock (&wr->e.lock); + } + count++; + } + } + } + } + + if (count == 0) + { + if (ddsrt_avl_is_empty (&wr->readers)) + { + ETRACE (wr, "heartbeat(wr "PGUIDFMT") suppressed, resched in %g s (min-ack [none], avail-seq %"PRIu64", xmit %"PRIu64")\n", + PGUID (wr->e.guid), + (t_next.v == DDS_NEVER) ? INFINITY : (double)(t_next.v - tnow.v) / 1e9, + whcst.max_seq, + ddsi_writer_read_seq_xmit(wr)); + } + else + { + ETRACE (wr, "heartbeat(wr "PGUIDFMT") suppressed, resched in %g s (min-ack %"PRIu64"%s, avail-seq %"PRIu64", xmit %"PRIu64")\n", + PGUID (wr->e.guid), + (t_next.v == DDS_NEVER) ? INFINITY : (double)(t_next.v - tnow.v) / 1e9, + ((struct ddsi_wr_prd_match *) ddsrt_avl_root (&ddsi_wr_readers_treedef, &wr->readers))->min_seq, + ((struct ddsi_wr_prd_match *) ddsrt_avl_root (&ddsi_wr_readers_treedef, &wr->readers))->all_have_replied_to_hb ? "" : "!", + whcst.max_seq, + ddsi_writer_read_seq_xmit(wr)); + } + } + + ddsrt_mutex_unlock (&wr->e.lock); +} +#endif + +void ddsi_heartbeat_xevent_cb (struct ddsi_domaingv *gv, struct ddsi_xevent *ev, struct ddsi_xpack *xp, void *varg, ddsrt_mtime_t tnow) +{ + struct ddsi_heartbeat_xevent_cb_arg const * const arg = varg; + struct ddsi_writer *wr; + if ((wr = ddsi_entidx_lookup_writer_guid (gv->entity_index, &arg->wr_guid)) == NULL) + { + GVTRACE("heartbeat(wr "PGUIDFMT") writer gone\n", PGUID (arg->wr_guid)); + return; + } + + struct ddsi_xmsg *msg; + ddsrt_mtime_t t_next; + int hbansreq = 0; + struct ddsi_whc_state whcst; + +#ifdef DDS_HAS_SECURITY + if (wr->e.guid.entityid.u == DDSI_ENTITYID_P2P_BUILTIN_PARTICIPANT_VOLATILE_SECURE_WRITER) + { + send_heartbeat_to_all_readers(xp, ev, wr, tnow); + return; + } +#endif + + ddsrt_mutex_lock (&wr->e.lock); + assert (wr->reliable); + ddsi_whc_get_state(wr->whc, &whcst); + if (!ddsi_writer_must_have_hb_scheduled (wr, &whcst)) + { + hbansreq = 1; /* just for trace */ + msg = NULL; /* Need not send it now, and no need to schedule it for the future */ + t_next.v = DDS_NEVER; + } + else if (!ddsi_writer_hbcontrol_must_send (wr, &whcst, tnow)) + { + hbansreq = 1; /* just for trace */ + msg = NULL; + t_next.v = tnow.v + ddsi_writer_hbcontrol_intv (wr, &whcst, tnow); + } + else + { + hbansreq = ddsi_writer_hbcontrol_ack_required (wr, &whcst, tnow); + msg = ddsi_writer_hbcontrol_create_heartbeat (wr, &whcst, tnow, hbansreq, 0); + t_next.v = tnow.v + ddsi_writer_hbcontrol_intv (wr, &whcst, tnow); + } + + if (ddsrt_avl_is_empty (&wr->readers)) + { + GVTRACE ("heartbeat(wr "PGUIDFMT"%s) %s, resched in %g s (min-ack [none], avail-seq %"PRIu64", xmit %"PRIu64")\n", + PGUID (wr->e.guid), + hbansreq ? "" : " final", + msg ? "sent" : "suppressed", + (t_next.v == DDS_NEVER) ? INFINITY : (double)(t_next.v - tnow.v) / 1e9, + whcst.max_seq, ddsi_writer_read_seq_xmit (wr)); + } + else + { + GVTRACE ("heartbeat(wr "PGUIDFMT"%s) %s, resched in %g s (min-ack %"PRId64"%s, avail-seq %"PRIu64", xmit %"PRIu64")\n", + PGUID (wr->e.guid), + hbansreq ? "" : " final", + msg ? "sent" : "suppressed", + (t_next.v == DDS_NEVER) ? INFINITY : (double)(t_next.v - tnow.v) / 1e9, + ((struct ddsi_wr_prd_match *) ddsrt_avl_root_non_empty (&ddsi_wr_readers_treedef, &wr->readers))->min_seq, + ((struct ddsi_wr_prd_match *) ddsrt_avl_root_non_empty (&ddsi_wr_readers_treedef, &wr->readers))->all_have_replied_to_hb ? "" : "!", + whcst.max_seq, ddsi_writer_read_seq_xmit (wr)); + } + (void) ddsi_resched_xevent_if_earlier (ev, t_next); + wr->hbcontrol.tsched = t_next; + ddsrt_mutex_unlock (&wr->e.lock); + + /* Can't transmit synchronously with writer lock held: trying to add + the heartbeat to the xp may cause xp to be sent out, which may + require updating wr->seq_xmit for other messages already in xp. + Besides, ddsi_xpack_addmsg may sleep for bandwidth-limited channels + and we certainly don't want to hold the lock during that time. */ + if (msg) + { + if (!wr->test_suppress_heartbeat) + ddsi_xpack_addmsg (xp, msg, 0); + else + { + GVTRACE ("test_suppress_heartbeat\n"); + ddsi_xmsg_free (msg); + } + } +} diff --git a/src/core/ddsi/src/ddsi_init.c b/src/core/ddsi/src/ddsi_init.c index c91cd65cfa..74fc8dd5e1 100644 --- a/src/core/ddsi/src/ddsi_init.c +++ b/src/core/ddsi/src/ddsi_init.c @@ -677,26 +677,24 @@ static void ddsi_term_prep (struct ddsi_domaingv *gv) } struct wait_for_receive_threads_helper_arg { - struct ddsi_domaingv *gv; unsigned count; }; -static void wait_for_receive_threads_helper (struct ddsi_xevent *xev, void *varg, ddsrt_mtime_t tnow) +static void wait_for_receive_threads_helper (struct ddsi_domaingv *gv, struct ddsi_xevent *xev, struct ddsi_xpack *xp, void *varg, ddsrt_mtime_t tnow) { struct wait_for_receive_threads_helper_arg * const arg = varg; - if (arg->count++ == arg->gv->config.recv_thread_stop_maxretries) + (void) xp; + if (arg->count++ == gv->config.recv_thread_stop_maxretries) abort (); - ddsi_trigger_recv_threads (arg->gv); + ddsi_trigger_recv_threads (gv); (void) ddsi_resched_xevent_if_earlier (xev, ddsrt_mtime_add_duration (tnow, DDS_SECS (1))); } static void wait_for_receive_threads (struct ddsi_domaingv *gv) { struct ddsi_xevent *trigev; - struct wait_for_receive_threads_helper_arg cbarg; - cbarg.gv = gv; - cbarg.count = 0; - if ((trigev = ddsi_qxev_callback (gv->xevents, ddsrt_mtime_add_duration (ddsrt_time_monotonic (), DDS_SECS (1)), wait_for_receive_threads_helper, &cbarg)) == NULL) + struct wait_for_receive_threads_helper_arg cbarg = { .count = 0 }; + if ((trigev = ddsi_qxev_callback (gv->xevents, ddsrt_mtime_add_duration (ddsrt_time_monotonic (), DDS_SECS (1)), wait_for_receive_threads_helper, &cbarg, sizeof (cbarg), true)) == NULL) { /* retrying is to deal a packet geting lost because the socket buffer is full or because the macOS firewall (and perhaps others) likes to ask if the process is allowed to receive data, @@ -714,7 +712,7 @@ static void wait_for_receive_threads (struct ddsi_domaingv *gv) } if (trigev) { - ddsi_delete_xevent_callback (trigev); + ddsi_delete_xevent (trigev); } } @@ -962,9 +960,8 @@ static uint32_t topic_definition_hash_wrap (const void *tpd) } #endif /* DDS_HAS_TYPE_DISCOVERY */ -static void reset_deaf_mute (struct ddsi_xevent *xev, void *varg, UNUSED_ARG (ddsrt_mtime_t tnow)) +static void reset_deaf_mute (struct ddsi_domaingv *gv, struct ddsi_xevent *xev, UNUSED_ARG (struct ddsi_xpack *xp), UNUSED_ARG (void *varg), UNUSED_ARG (ddsrt_mtime_t tnow)) { - struct ddsi_domaingv *gv = varg; gv->deaf = 0; gv->mute = 0; GVLOGDISC ("DEAFMUTE auto-reset to [deaf, mute]=[%d, %d]\n", gv->deaf, gv->mute); @@ -980,7 +977,7 @@ void ddsi_set_deafmute (struct ddsi_domaingv *gv, bool deaf, bool mute, int64_t { ddsrt_mtime_t when = ddsrt_mtime_add_duration (ddsrt_time_monotonic (), reset_after); GVTRACE (" reset after %"PRId64".%09u ns", reset_after / DDS_NSECS_IN_SEC, (unsigned) (reset_after % DDS_NSECS_IN_SEC)); - ddsi_qxev_callback (gv->xevents, when, reset_deaf_mute, gv); + ddsi_qxev_callback (gv->xevents, when, reset_deaf_mute, NULL, 0, true); } GVLOGDISC ("\n"); } @@ -1605,7 +1602,7 @@ int ddsi_init (struct ddsi_domaingv *gv) gv->user_dqueue = ddsi_dqueue_new ("user", gv, gv->config.delivery_queue_maxsamples, ddsi_user_dqueue_handler, NULL); if (reset_deaf_mute_time.v < DDS_NEVER) - ddsi_qxev_callback (gv->xevents, reset_deaf_mute_time, reset_deaf_mute, gv); + ddsi_qxev_callback (gv->xevents, reset_deaf_mute_time, reset_deaf_mute, NULL, 0, true); return 0; #if 0 diff --git a/src/core/ddsi/src/ddsi_lifespan.c b/src/core/ddsi/src/ddsi_lifespan.c index 3548dd6255..ac0a09d867 100644 --- a/src/core/ddsi/src/ddsi_lifespan.c +++ b/src/core/ddsi/src/ddsi_lifespan.c @@ -25,9 +25,16 @@ static int compare_lifespan_texp (const void *va, const void *vb) const ddsrt_fibheap_def_t lifespan_fhdef = DDSRT_FIBHEAPDEF_INITIALIZER(offsetof (struct ddsi_lifespan_fhnode, heapnode), compare_lifespan_texp); -static void lifespan_rhc_node_exp (struct ddsi_xevent *xev, void *varg, ddsrt_mtime_t tnow) +struct lifespan_rhc_node_exp_arg { + struct ddsi_lifespan_adm *lifespan_adm; +}; + +static void lifespan_rhc_node_exp (struct ddsi_domaingv *gv, struct ddsi_xevent *xev, struct ddsi_xpack *xp, void *varg, ddsrt_mtime_t tnow) { - struct ddsi_lifespan_adm * const lifespan_adm = varg; + struct lifespan_rhc_node_exp_arg * const arg = varg; + struct ddsi_lifespan_adm * const lifespan_adm = arg->lifespan_adm; + (void) gv; + (void) xp; ddsrt_mtime_t next_valid = lifespan_adm->sample_expired_cb((char *)lifespan_adm - lifespan_adm->fh_offset, tnow); ddsi_resched_xevent_if_earlier (xev, next_valid); } @@ -51,7 +58,8 @@ ddsrt_mtime_t ddsi_lifespan_next_expired_locked (const struct ddsi_lifespan_adm void ddsi_lifespan_init (const struct ddsi_domaingv *gv, struct ddsi_lifespan_adm *lifespan_adm, size_t fh_offset, size_t fh_node_offset, ddsi_sample_expired_cb_t sample_expired_cb) { ddsrt_fibheap_init (&lifespan_fhdef, &lifespan_adm->ls_exp_heap); - lifespan_adm->evt = ddsi_qxev_callback (gv->xevents, DDSRT_MTIME_NEVER, lifespan_rhc_node_exp, lifespan_adm); + struct lifespan_rhc_node_exp_arg arg = { .lifespan_adm = lifespan_adm }; + lifespan_adm->evt = ddsi_qxev_callback (gv->xevents, DDSRT_MTIME_NEVER, lifespan_rhc_node_exp, &arg, sizeof (arg), true); lifespan_adm->sample_expired_cb = sample_expired_cb; lifespan_adm->fh_offset = fh_offset; lifespan_adm->fhn_offset = fh_node_offset; @@ -60,7 +68,7 @@ void ddsi_lifespan_init (const struct ddsi_domaingv *gv, struct ddsi_lifespan_ad void ddsi_lifespan_fini (const struct ddsi_lifespan_adm *lifespan_adm) { assert (ddsrt_fibheap_min (&lifespan_fhdef, &lifespan_adm->ls_exp_heap) == NULL); - ddsi_delete_xevent_callback (lifespan_adm->evt); + ddsi_delete_xevent (lifespan_adm->evt); } extern inline void ddsi_lifespan_register_sample_locked (struct ddsi_lifespan_adm *lifespan_adm, struct ddsi_lifespan_fhnode *node); diff --git a/src/core/ddsi/src/ddsi_participant.c b/src/core/ddsi/src/ddsi_participant.c index c475116ba9..7f7f9c9792 100644 --- a/src/core/ddsi/src/ddsi_participant.c +++ b/src/core/ddsi/src/ddsi_participant.c @@ -23,7 +23,7 @@ #include "ddsi__entity_index.h" #include "ddsi__security_omg.h" #include "ddsi__handshake.h" -#include "ddsi__discovery.h" +#include "ddsi__discovery_spdp.h" #include "ddsi__xevent.h" #include "ddsi__lease.h" #include "ddsi__receive.h" @@ -32,6 +32,7 @@ #include "ddsi__endpoint_match.h" #include "ddsi__gc.h" #include "ddsi__plist.h" +#include "ddsi__pmd.h" #include "ddsi__protocol.h" #include "ddsi__tran.h" #include "ddsi__vendor.h" @@ -979,13 +980,14 @@ static dds_return_t new_participant_guid (ddsi_guid_t *ppguid, struct ddsi_domai fire before the calls return. If the initial sample wasn't accepted, all is lost, but we continue nonetheless, even though the participant won't be able to discover or be discovered. */ - pp->spdp_xevent = ddsi_qxev_spdp (gv->xevents, ddsrt_mtime_add_duration (ddsrt_time_monotonic (), DDS_MSECS (100)), &pp->e.guid, NULL); + struct ddsi_spdp_broadcast_xevent_cb_arg arg = { .pp_guid = pp->e.guid }; + pp->spdp_xevent = ddsi_qxev_callback (gv->xevents, ddsrt_mtime_add_duration (ddsrt_time_monotonic (), DDS_MSECS (100)), ddsi_spdp_broadcast_xevent_cb, &arg, sizeof (arg), false); } { - ddsrt_mtime_t tsched; - tsched = (pp->plist->qos.liveliness.lease_duration == DDS_INFINITY) ? DDSRT_MTIME_NEVER : (ddsrt_mtime_t){0}; - pp->pmd_update_xevent = ddsi_qxev_pmd_update (gv->xevents, tsched, &pp->e.guid); + ddsrt_mtime_t tsched = (pp->plist->qos.liveliness.lease_duration == DDS_INFINITY) ? DDSRT_MTIME_NEVER : (ddsrt_mtime_t){0}; + struct ddsi_write_pmd_message_xevent_cb_arg arg = { .pp_guid = pp->e.guid }; + pp->pmd_update_xevent = ddsi_qxev_callback (gv->xevents, tsched, ddsi_write_pmd_message_xevent_cb, &arg, sizeof (arg), false); } #ifdef DDS_HAS_SECURITY diff --git a/src/core/ddsi/src/ddsi_pmd.c b/src/core/ddsi/src/ddsi_pmd.c index 528ca6030f..b8d1c8110d 100644 --- a/src/core/ddsi/src/ddsi_pmd.c +++ b/src/core/ddsi/src/ddsi_pmd.c @@ -27,7 +27,7 @@ #include "ddsi__transmit.h" #include "ddsi__xmsg.h" #include "ddsi__proxy_participant.h" -#include "ddsi__sysdeps.h" +#include "ddsi__xevent.h" /* note: treating guid prefix + kind as if it were a GUID because that matches the octet-sequence/sequence-of-uint32 distinction between the specified wire @@ -124,3 +124,39 @@ void ddsi_handle_pmd_message (const struct ddsi_receiver_state *rst, struct ddsi } RSTTRACE ("\n"); } + +void ddsi_write_pmd_message_xevent_cb (struct ddsi_domaingv *gv, struct ddsi_xevent *ev, struct ddsi_xpack *xp, void *varg, ddsrt_mtime_t tnow) +{ + struct ddsi_thread_state * const thrst = ddsi_lookup_thread_state (); + struct ddsi_write_pmd_message_xevent_cb_arg const * const arg = varg; + struct ddsi_participant *pp; + dds_duration_t intv; + ddsrt_mtime_t tnext; + + if ((pp = ddsi_entidx_lookup_participant_guid (gv->entity_index, &arg->pp_guid)) == NULL) + { + return; + } + + ddsi_write_pmd_message (thrst, xp, pp, DDSI_PARTICIPANT_MESSAGE_DATA_KIND_AUTOMATIC_LIVELINESS_UPDATE); + + intv = ddsi_participant_get_pmd_interval (pp); + if (intv == DDS_INFINITY) + { + tnext.v = DDS_NEVER; + GVTRACE ("resched pmd("PGUIDFMT"): never\n", PGUID (pp->e.guid)); + } + else + { + /* schedule next when 80% of the interval has elapsed, or 2s + before the lease ends, whichever comes first */ + if (intv >= DDS_SECS (10)) + tnext.v = tnow.v + intv - DDS_SECS (2); + else + tnext.v = tnow.v + 4 * intv / 5; + GVTRACE ("resched pmd("PGUIDFMT"): %gs\n", PGUID (pp->e.guid), (double)(tnext.v - tnow.v) / 1e9); + } + + (void) ddsi_resched_xevent_if_earlier (ev, tnext); +} + diff --git a/src/core/ddsi/src/ddsi_proxy_endpoint.c b/src/core/ddsi/src/ddsi_proxy_endpoint.c index a60d0552d8..c5afaa7ccd 100644 --- a/src/core/ddsi/src/ddsi_proxy_endpoint.c +++ b/src/core/ddsi/src/ddsi_proxy_endpoint.c @@ -170,6 +170,34 @@ bool ddsi_is_proxy_endpoint (const struct ddsi_entity_common *e) } #endif /* DDS_HAS_TYPE_DISCOVERY */ +void ddsi_send_entityid_to_prd (struct ddsi_proxy_reader *prd, const ddsi_guid_t *guid) +{ + /* For connected transports, may need to establish and identify connection */ + struct ddsi_domaingv * const gv = prd->e.gv; + if (!gv->m_factory->m_connless) + { + GVTRACE (" ddsi_send_entityid_to_prd (%"PRIx32":%"PRIx32":%"PRIx32")\n", PGUIDPREFIX (guid->prefix)); + struct ddsi_xmsg *msg = ddsi_xmsg_new (gv->xmsgpool, guid, NULL, sizeof (ddsi_rtps_entityid_t), DDSI_XMSG_KIND_CONTROL); + ddsi_xmsg_setdst_prd (msg, prd); + ddsi_xmsg_add_entityid (msg); + ddsi_qxev_msg (gv->xevents, msg); + } +} + +void ddsi_send_entityid_to_pwr (struct ddsi_proxy_writer *pwr, const ddsi_guid_t *guid) +{ + /* For connected transports, may need to establish and identify connection */ + struct ddsi_domaingv * const gv = pwr->e.gv; + if (!gv->m_factory->m_connless) + { + GVTRACE (" ddsi_send_entityid_to_pwr (%"PRIx32":%"PRIx32":%"PRIx32")\n", PGUIDPREFIX (guid->prefix)); + struct ddsi_xmsg *msg = ddsi_xmsg_new (gv->xmsgpool, guid, NULL, sizeof (ddsi_rtps_entityid_t), DDSI_XMSG_KIND_CONTROL); + ddsi_xmsg_setdst_pwr (msg, pwr); + ddsi_xmsg_add_entityid (msg); + ddsi_qxev_msg (gv->xevents, msg); + } +} + /* PROXY-WRITER ----------------------------------------------------- */ static enum ddsi_reorder_mode get_proxy_writer_reorder_mode(const ddsi_entityid_t pwr_entityid, int isreliable) @@ -338,7 +366,7 @@ void ddsi_update_proxy_writer (struct ddsi_proxy_writer *pwr, ddsi_seqno_t seq, rd = ddsi_entidx_lookup_reader_guid (pwr->e.gv->entity_index, &m->rd_guid); if (rd) { - ddsi_qxev_pwr_entityid (pwr, &rd->e.guid); + ddsi_send_entityid_to_pwr (pwr, &rd->e.guid); } m = ddsrt_avl_iter_next (&iter); } @@ -638,7 +666,7 @@ void ddsi_update_proxy_reader (struct ddsi_proxy_reader *prd, ddsi_seqno_t seq, ddsrt_mutex_lock (&wr->e.lock); ddsi_rebuild_writer_addrset (wr); ddsrt_mutex_unlock (&wr->e.lock); - ddsi_qxev_prd_entityid (prd, &wr->e.guid); + ddsi_send_entityid_to_prd (prd, &wr->e.guid); } wrguid = guid_next; ddsrt_mutex_lock (&prd->e.lock); diff --git a/src/core/ddsi/src/ddsi_security_exchange.c b/src/core/ddsi/src/ddsi_security_exchange.c index 2b4a81f9f0..5020d027e3 100644 --- a/src/core/ddsi/src/ddsi_security_exchange.c +++ b/src/core/ddsi/src/ddsi_security_exchange.c @@ -25,7 +25,7 @@ #include "ddsi__security_omg.h" #include "ddsi__handshake.h" #include "ddsi__serdata_pserop.h" -#include "ddsi__discovery.h" +#include "ddsi__discovery_spdp.h" #include "ddsi__xmsg.h" #include "ddsi__transmit.h" #include "ddsi__entity.h" diff --git a/src/core/ddsi/src/ddsi_security_omg.c b/src/core/ddsi/src/ddsi_security_omg.c index 67678e29ef..2a9aee0f9b 100644 --- a/src/core/ddsi/src/ddsi_security_omg.c +++ b/src/core/ddsi/src/ddsi_security_omg.c @@ -334,8 +334,6 @@ static void free_pending_match(struct pending_match *match) } } -static void pending_match_expiry_cb(struct ddsi_xevent *xev, void *varg, ddsrt_mtime_t tnow); - static struct pending_match * find_or_create_pending_entity_match(struct pending_match_index *index, enum ddsi_entity_kind kind, const ddsi_guid_t *remote_guid, const ddsi_guid_t *local_guid, int64_t crypto_handle, DDS_Security_ParticipantCryptoTokenSeq *tokens) { struct guid_pair guids = { .remote_guid = *remote_guid, .local_guid = *local_guid}; @@ -410,9 +408,16 @@ static void delete_pending_match(struct pending_match_index *index, struct pendi ddsrt_mutex_unlock(&index->lock); } -static void pending_match_expiry_cb(struct ddsi_xevent *xev, void *varg, ddsrt_mtime_t tnow) +struct pending_match_expiry_cb_arg { + struct pending_match_index *index; +}; + +static void pending_match_expiry_cb(struct ddsi_domaingv *gv, struct ddsi_xevent *xev, struct ddsi_xpack *xp, void *varg, ddsrt_mtime_t tnow) { - struct pending_match_index *index = varg; + struct pending_match_expiry_cb_arg * const arg = varg; + struct pending_match_index *index = arg->index; + (void) gv; + (void) xp; ddsrt_mutex_lock(&index->lock); struct pending_match *match = ddsrt_fibheap_min(&pending_match_expiry_fhdef, &index->expiry_timers); @@ -475,12 +480,13 @@ static void pending_match_index_init(const struct ddsi_domaingv *gv, struct pend ddsrt_avl_init(&pending_match_index_treedef, &index->pending_matches); ddsrt_fibheap_init(&pending_match_expiry_fhdef, &index->expiry_timers); index->gv = gv; - index->evt = ddsi_qxev_callback(gv->xevents, DDSRT_MTIME_NEVER, pending_match_expiry_cb, index);; + struct pending_match_expiry_cb_arg arg = { .index = index }; + index->evt = ddsi_qxev_callback(gv->xevents, DDSRT_MTIME_NEVER, pending_match_expiry_cb, &arg, sizeof (arg), true); } static void pending_match_index_deinit(struct pending_match_index *index) { - ddsi_delete_xevent_callback(index->evt); + ddsi_delete_xevent(index->evt); ddsrt_mutex_destroy(&index->lock); assert(ddsrt_avl_is_empty(&index->pending_matches)); ddsrt_avl_free(&pending_match_index_treedef, &index->pending_matches, 0); diff --git a/src/core/ddsi/src/ddsi_topic.c b/src/core/ddsi/src/ddsi_topic.c index f544ef4c45..bfdd23cc94 100644 --- a/src/core/ddsi/src/ddsi_topic.c +++ b/src/core/ddsi/src/ddsi_topic.c @@ -23,6 +23,9 @@ #include "ddsi__topic.h" #include "ddsi__entity_index.h" #include "ddsi__discovery.h" +#ifdef DDS_HAS_TOPIC_DISCOVERY +#include "ddsi__discovery_topic.h" +#endif #include "ddsi__xmsg.h" #include "ddsi__misc.h" #include "ddsi__gc.h" diff --git a/src/core/ddsi/src/ddsi_transmit.c b/src/core/ddsi/src/ddsi_transmit.c index 6e1cd21dd1..d4a1bca5b9 100644 --- a/src/core/ddsi/src/ddsi_transmit.c +++ b/src/core/ddsi/src/ddsi_transmit.c @@ -55,402 +55,6 @@ static int have_reliable_subs (const struct ddsi_writer *wr) return 1; } -void ddsi_writer_hbcontrol_init (struct ddsi_hbcontrol *hbc) -{ - hbc->t_of_last_write.v = 0; - hbc->t_of_last_hb.v = 0; - hbc->t_of_last_ackhb.v = 0; - hbc->tsched = DDSRT_MTIME_NEVER; - hbc->hbs_since_last_write = 0; - hbc->last_packetid = 0; -} - -static void writer_hbcontrol_note_hb (struct ddsi_writer *wr, ddsrt_mtime_t tnow, int ansreq) -{ - struct ddsi_hbcontrol * const hbc = &wr->hbcontrol; - - if (ansreq) - hbc->t_of_last_ackhb = tnow; - hbc->t_of_last_hb = tnow; - - /* Count number of heartbeats since last write, used to lower the - heartbeat rate. Overflow doesn't matter, it'll just revert to a - highish rate for a short while. */ - hbc->hbs_since_last_write++; -} - -int64_t ddsi_writer_hbcontrol_intv (const struct ddsi_writer *wr, const struct ddsi_whc_state *whcst, UNUSED_ARG (ddsrt_mtime_t tnow)) -{ - struct ddsi_domaingv const * const gv = wr->e.gv; - struct ddsi_hbcontrol const * const hbc = &wr->hbcontrol; - int64_t ret = gv->config.const_hb_intv_sched; - size_t n_unacked; - - if (hbc->hbs_since_last_write > 5) - { - unsigned cnt = (hbc->hbs_since_last_write - 5) / 2; - while (cnt-- != 0 && 2 * ret < gv->config.const_hb_intv_sched_max) - ret *= 2; - } - - n_unacked = whcst->unacked_bytes; - if (n_unacked >= wr->whc_low + 3 * (wr->whc_high - wr->whc_low) / 4) - ret /= 2; - if (n_unacked >= wr->whc_low + (wr->whc_high - wr->whc_low) / 2) - ret /= 2; - if (wr->throttling) - ret /= 2; - if (ret < gv->config.const_hb_intv_sched_min) - ret = gv->config.const_hb_intv_sched_min; - return ret; -} - -void ddsi_writer_hbcontrol_note_asyncwrite (struct ddsi_writer *wr, ddsrt_mtime_t tnow) -{ - struct ddsi_domaingv const * const gv = wr->e.gv; - struct ddsi_hbcontrol * const hbc = &wr->hbcontrol; - ddsrt_mtime_t tnext; - - /* Reset number of heartbeats since last write: that means the - heartbeat rate will go back up to the default */ - hbc->hbs_since_last_write = 0; - - /* We know this is new data, so we want a heartbeat event after one - base interval */ - tnext.v = tnow.v + gv->config.const_hb_intv_sched; - if (tnext.v < hbc->tsched.v) - { - /* Insertion of a message with WHC locked => must now have at - least one unacked msg if there are reliable readers, so must - have a heartbeat scheduled. Do so now */ - hbc->tsched = tnext; - (void) ddsi_resched_xevent_if_earlier (wr->heartbeat_xevent, tnext); - } -} - -int ddsi_writer_hbcontrol_must_send (const struct ddsi_writer *wr, const struct ddsi_whc_state *whcst, ddsrt_mtime_t tnow /* monotonic */) -{ - struct ddsi_hbcontrol const * const hbc = &wr->hbcontrol; - return (tnow.v >= hbc->t_of_last_hb.v + ddsi_writer_hbcontrol_intv (wr, whcst, tnow)); -} - -struct ddsi_xmsg *ddsi_writer_hbcontrol_create_heartbeat (struct ddsi_writer *wr, const struct ddsi_whc_state *whcst, ddsrt_mtime_t tnow, int hbansreq, int issync) -{ - struct ddsi_domaingv const * const gv = wr->e.gv; - struct ddsi_xmsg *msg; - const ddsi_guid_t *prd_guid; - - ASSERT_MUTEX_HELD (&wr->e.lock); - assert (wr->reliable); - assert (hbansreq >= 0); - - if ((msg = ddsi_xmsg_new (gv->xmsgpool, &wr->e.guid, wr->c.pp, sizeof (ddsi_rtps_info_ts_t) + sizeof (ddsi_rtps_heartbeat_t), DDSI_XMSG_KIND_CONTROL)) == NULL) - /* out of memory at worst slows down traffic */ - return NULL; - - if (ddsrt_avl_is_empty (&wr->readers) || wr->num_reliable_readers == 0) - { - /* Not really supposed to come here, at least not for the first - case. Secondly, there really seems to be little use for - optimising reliable writers with only best-effort readers. And - in any case, it is always legal to multicast a heartbeat from a - reliable writer. */ - prd_guid = NULL; - } - else if (wr->seq != root_rdmatch (wr)->max_seq) - { - /* If the writer is ahead of its readers, multicast. Couldn't care - less about the pessimal cases such as multicasting when there - is one reliable reader & multiple best-effort readers. See - comment above. */ - prd_guid = NULL; - } - else - { - const uint32_t n_unacked = wr->num_reliable_readers - root_rdmatch (wr)->num_reliable_readers_where_seq_equals_max; - if (n_unacked == 0) - prd_guid = NULL; - else - { - assert (root_rdmatch (wr)->arbitrary_unacked_reader.entityid.u != DDSI_ENTITYID_UNKNOWN); - if (n_unacked > 1) - prd_guid = NULL; - else - prd_guid = &(root_rdmatch (wr)->arbitrary_unacked_reader); - } - } - - ETRACE (wr, "writer_hbcontrol: wr "PGUIDFMT" ", PGUID (wr->e.guid)); - if (prd_guid == NULL) - ETRACE (wr, "multicasting "); - else - ETRACE (wr, "unicasting to prd "PGUIDFMT" ", PGUID (*prd_guid)); - if (ddsrt_avl_is_empty (&wr->readers)) - { - ETRACE (wr, "(rel-prd %"PRId32" seq-eq-max [none] seq %"PRId64" maxseq [none])\n", - wr->num_reliable_readers, wr->seq); - } - else - { - ETRACE (wr, "(rel-prd %"PRId32" seq-eq-max %"PRId32" seq %"PRIu64" maxseq %"PRIu64")\n", - wr->num_reliable_readers, - (int32_t) root_rdmatch (wr)->num_reliable_readers_where_seq_equals_max, - wr->seq, - root_rdmatch (wr)->max_seq); - } - - if (prd_guid == NULL) - { - ddsi_xmsg_setdst_addrset (msg, wr->as); - ddsi_add_heartbeat (msg, wr, whcst, hbansreq, 0, ddsi_to_entityid (DDSI_ENTITYID_UNKNOWN), issync); - } - else - { - struct ddsi_proxy_reader *prd; - if ((prd = ddsi_entidx_lookup_proxy_reader_guid (gv->entity_index, prd_guid)) == NULL) - { - ETRACE (wr, "writer_hbcontrol: wr "PGUIDFMT" unknown prd "PGUIDFMT"\n", PGUID (wr->e.guid), PGUID (*prd_guid)); - ddsi_xmsg_free (msg); - return NULL; - } - /* set the destination explicitly to the unicast destination and the fourth - param of ddsi_add_heartbeat needs to be the guid of the reader */ - ddsi_xmsg_setdst_prd (msg, prd); - // send to all readers in the participant: whether or not the entityid is set affects - // the retransmit requests - ddsi_add_heartbeat (msg, wr, whcst, hbansreq, 0, ddsi_to_entityid (DDSI_ENTITYID_UNKNOWN), issync); - } - - /* It is possible that the encoding removed the submessage(s). */ - if (ddsi_xmsg_size(msg) == 0) - { - ddsi_xmsg_free (msg); - msg = NULL; - } - - writer_hbcontrol_note_hb (wr, tnow, hbansreq); - return msg; -} - -static int writer_hbcontrol_ack_required_generic (const struct ddsi_writer *wr, const struct ddsi_whc_state *whcst, ddsrt_mtime_t tlast, ddsrt_mtime_t tnow, int piggyback) -{ - struct ddsi_domaingv const * const gv = wr->e.gv; - struct ddsi_hbcontrol const * const hbc = &wr->hbcontrol; - const int64_t hb_intv_ack = gv->config.const_hb_intv_sched; - assert(wr->heartbeat_xevent != NULL && whcst != NULL); - - if (piggyback) - { - /* If it is likely that a heartbeat requiring an ack will go out - shortly after the sample was written, it is better to piggyback - it onto the sample. The current idea is that a write shortly - before the next heartbeat will go out should have one - piggybacked onto it, so that the scheduled heartbeat can be - suppressed. */ - if (tnow.v >= tlast.v + 4 * hb_intv_ack / 5) - return 2; - } - else - { - /* For heartbeat events use a slightly longer interval */ - if (tnow.v >= tlast.v + hb_intv_ack) - return 2; - } - - if (whcst->unacked_bytes >= wr->whc_low + (wr->whc_high - wr->whc_low) / 2) - { - if (tnow.v >= hbc->t_of_last_ackhb.v + gv->config.const_hb_intv_sched_min) - return 2; - else if (tnow.v >= hbc->t_of_last_ackhb.v + gv->config.const_hb_intv_min) - return 1; - } - - return 0; -} - -int ddsi_writer_hbcontrol_ack_required (const struct ddsi_writer *wr, const struct ddsi_whc_state *whcst, ddsrt_mtime_t tnow) -{ - struct ddsi_hbcontrol const * const hbc = &wr->hbcontrol; - return writer_hbcontrol_ack_required_generic (wr, whcst, hbc->t_of_last_write, tnow, 0); -} - -struct ddsi_xmsg *ddsi_writer_hbcontrol_piggyback (struct ddsi_writer *wr, const struct ddsi_whc_state *whcst, ddsrt_mtime_t tnow, uint32_t packetid, int *hbansreq) -{ - struct ddsi_hbcontrol * const hbc = &wr->hbcontrol; - uint32_t last_packetid; - ddsrt_mtime_t tlast; - ddsrt_mtime_t t_of_last_hb; - struct ddsi_xmsg *msg; - - tlast = hbc->t_of_last_write; - last_packetid = hbc->last_packetid; - t_of_last_hb = hbc->t_of_last_hb; - - hbc->t_of_last_write = tnow; - hbc->last_packetid = packetid; - - /* Update statistics, intervals, scheduling of heartbeat event, - &c. -- there's no real difference between async and sync so we - reuse the async version. */ - ddsi_writer_hbcontrol_note_asyncwrite (wr, tnow); - - *hbansreq = writer_hbcontrol_ack_required_generic (wr, whcst, tlast, tnow, 1); - if (*hbansreq >= 2) { - /* So we force a heartbeat in - but we also rely on our caller to - send the packet out */ - msg = ddsi_writer_hbcontrol_create_heartbeat (wr, whcst, tnow, *hbansreq, 1); - } else if (last_packetid != packetid && tnow.v - t_of_last_hb.v > DDS_USECS (100)) { - /* If we crossed a packet boundary since the previous write, - piggyback a heartbeat, with *hbansreq determining whether or - not an ACK is needed. We don't force the packet out either: - this is just to ensure a regular flow of ACKs for cleaning up - the WHC & for allowing readers to NACK missing samples. - - Still rate-limit: if there are new readers that haven't sent an - an ACK yet, the FINAL flag will be cleared and so we get an ACK - storm if writing at a high rate without batching which eats up - a *large* amount of time because there are out-of-order readers - present. */ - msg = ddsi_writer_hbcontrol_create_heartbeat (wr, whcst, tnow, *hbansreq, 1); - } else { - *hbansreq = 0; - msg = NULL; - } - - if (msg) - { - if (ddsrt_avl_is_empty (&wr->readers)) - { - ETRACE (wr, "heartbeat(wr "PGUIDFMT"%s) piggybacked, resched in %g s (min-ack [none], avail-seq %"PRIu64", xmit %"PRIu64")\n", - PGUID (wr->e.guid), - *hbansreq ? "" : " final", - (hbc->tsched.v == DDS_NEVER) ? INFINITY : (double) (hbc->tsched.v - tnow.v) / 1e9, - whcst->max_seq, ddsi_writer_read_seq_xmit(wr)); - } - else - { - ETRACE (wr, "heartbeat(wr "PGUIDFMT"%s) piggybacked, resched in %g s (min-ack %"PRIu64"%s, avail-seq %"PRIu64", xmit %"PRIu64")\n", - PGUID (wr->e.guid), - *hbansreq ? "" : " final", - (hbc->tsched.v == DDS_NEVER) ? INFINITY : (double) (hbc->tsched.v - tnow.v) / 1e9, - root_rdmatch (wr)->min_seq, - root_rdmatch (wr)->all_have_replied_to_hb ? "" : "!", - whcst->max_seq, ddsi_writer_read_seq_xmit(wr)); - } - } - - return msg; -} - -#ifdef DDS_HAS_SECURITY -struct ddsi_xmsg *ddsi_writer_hbcontrol_p2p(struct ddsi_writer *wr, const struct ddsi_whc_state *whcst, int hbansreq, struct ddsi_proxy_reader *prd) -{ - struct ddsi_domaingv const * const gv = wr->e.gv; - struct ddsi_xmsg *msg; - - ASSERT_MUTEX_HELD (&wr->e.lock); - assert (wr->reliable); - - if ((msg = ddsi_xmsg_new (gv->xmsgpool, &wr->e.guid, wr->c.pp, sizeof (ddsi_rtps_info_ts_t) + sizeof (ddsi_rtps_heartbeat_t), DDSI_XMSG_KIND_CONTROL)) == NULL) - return NULL; - - ETRACE (wr, "writer_hbcontrol_p2p: wr "PGUIDFMT" unicasting to prd "PGUIDFMT" ", PGUID (wr->e.guid), PGUID (prd->e.guid)); - if (ddsrt_avl_is_empty (&wr->readers)) - { - ETRACE (wr, "(rel-prd %d seq-eq-max [none] seq %"PRIu64")\n", wr->num_reliable_readers, wr->seq); - } - else - { - ETRACE (wr, "(rel-prd %d seq-eq-max %d seq %"PRIu64" maxseq %"PRIu64")\n", - wr->num_reliable_readers, - (int32_t) root_rdmatch (wr)->num_reliable_readers_where_seq_equals_max, - wr->seq, - root_rdmatch (wr)->max_seq); - } - - /* set the destination explicitly to the unicast destination and the fourth - param of ddsi_add_heartbeat needs to be the guid of the reader */ - ddsi_xmsg_setdst_prd (msg, prd); - ddsi_add_heartbeat (msg, wr, whcst, hbansreq, 0, prd->e.guid.entityid, 1); - - if (ddsi_xmsg_size(msg) == 0) - { - ddsi_xmsg_free (msg); - msg = NULL; - } - - return msg; -} -#endif - -void ddsi_add_heartbeat (struct ddsi_xmsg *msg, struct ddsi_writer *wr, const struct ddsi_whc_state *whcst, int hbansreq, int hbliveliness, ddsi_entityid_t dst, int issync) -{ - struct ddsi_domaingv const * const gv = wr->e.gv; - struct ddsi_xmsg_marker sm_marker; - ddsi_rtps_heartbeat_t * hb; - ddsi_seqno_t max, min; - - ASSERT_MUTEX_HELD (&wr->e.lock); - - assert (wr->reliable); - assert (hbansreq >= 0); - assert (hbliveliness >= 0); - - if (gv->config.meas_hb_to_ack_latency) - { - /* If configured to measure heartbeat-to-ack latency, we must add - a timestamp. No big deal if it fails. */ - ddsi_xmsg_add_timestamp (msg, ddsrt_time_wallclock ()); - } - - hb = ddsi_xmsg_append (msg, &sm_marker, sizeof (ddsi_rtps_heartbeat_t)); - ddsi_xmsg_submsg_init (msg, sm_marker, DDSI_RTPS_SMID_HEARTBEAT); - - if (!hbansreq) - hb->smhdr.flags |= DDSI_HEARTBEAT_FLAG_FINAL; - if (hbliveliness) - hb->smhdr.flags |= DDSI_HEARTBEAT_FLAG_LIVELINESS; - - hb->readerId = ddsi_hton_entityid (dst); - hb->writerId = ddsi_hton_entityid (wr->e.guid.entityid); - if (DDSI_WHCST_ISEMPTY(whcst)) - { - max = wr->seq; - min = max + 1; - } - else - { - /* If data present in WHC, wr->seq > 0, but xmit_seq possibly still 0 */ - min = whcst->min_seq; - max = wr->seq; - const ddsi_seqno_t seq_xmit = ddsi_writer_read_seq_xmit (wr); - assert (min <= max); - /* Informing readers of samples that haven't even been transmitted makes little sense, - but for transient-local data, we let the first heartbeat determine the time at which - we trigger wait_for_historical_data, so it had better be correct */ - if (!issync && seq_xmit < max && !wr->handle_as_transient_local) - { - /* When: queue data ; queue heartbeat ; transmit data ; update - seq_xmit, max may be < min. But we must never advertise the - minimum available sequence number incorrectly! */ - if (seq_xmit >= min) { - /* Advertise some but not all data */ - max = seq_xmit; - } else { - /* Advertise no data yet */ - max = min - 1; - } - } - } - hb->firstSN = ddsi_to_seqno (min); - hb->lastSN = ddsi_to_seqno (max); - - hb->count = wr->hbcount++; - - ddsi_xmsg_submsg_setnext (msg, sm_marker); - ddsi_security_encode_datawriter_submsg(msg, sm_marker, wr); -} - static dds_return_t ddsi_create_fragment_message_simple (struct ddsi_writer *wr, ddsi_seqno_t seq, struct ddsi_serdata *serdata, struct ddsi_xmsg **pmsg) { #define TEST_KEYHASH 0 @@ -1361,3 +965,12 @@ int ddsi_write_sample_nogc_notk (struct ddsi_thread_state * const thrst, struct ddsi_tkmap_instance_unref (wr->e.gv->m_tkmap, tk); return res; } + +int ddsi_write_and_fini_plist (struct ddsi_writer *wr, ddsi_plist_t *ps, bool alive) +{ + struct ddsi_serdata *serdata = ddsi_serdata_from_sample (wr->type, alive ? SDK_DATA : SDK_KEY, ps); + ddsi_plist_fini (ps); + serdata->statusinfo = alive ? 0 : (DDSI_STATUSINFO_DISPOSE | DDSI_STATUSINFO_UNREGISTER); + serdata->timestamp = ddsrt_time_wallclock (); + return ddsi_write_sample_nogc_notk (ddsi_lookup_thread_state (), NULL, wr, serdata); +} diff --git a/src/core/ddsi/src/ddsi_xevent.c b/src/core/ddsi/src/ddsi_xevent.c index f9c9f33244..31f60bc0cc 100644 --- a/src/core/ddsi/src/ddsi_xevent.c +++ b/src/core/ddsi/src/ddsi_xevent.c @@ -10,43 +10,22 @@ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ #include +#include #include #include "dds/ddsrt/atomics.h" #include "dds/ddsrt/heap.h" #include "dds/ddsrt/sync.h" -#include "dds/ddsrt/avl.h" #include "dds/ddsrt/fibheap.h" #include "dds/ddsi/ddsi_unused.h" #include "dds/ddsi/ddsi_domaingv.h" -#include "dds/ddsi/ddsi_serdata.h" -#include "dds/ddsi/ddsi_tkmap.h" #include "ddsi__log.h" -#include "ddsi__addrset.h" -#include "ddsi__xmsg.h" #include "ddsi__xevent.h" #include "ddsi__thread.h" -#include "ddsi__entity_index.h" #include "ddsi__transmit.h" -#include "ddsi__entity.h" -#include "ddsi__participant.h" -#include "ddsi__misc.h" -#include "ddsi__radmin.h" -#include "ddsi__bitset.h" -#include "ddsi__lease.h" #include "ddsi__xmsg.h" -#include "ddsi__security_omg.h" -#include "ddsi__pmd.h" -#include "ddsi__acknack.h" -#include "ddsi__endpoint.h" -#include "ddsi__endpoint_match.h" -#include "ddsi__plist.h" -#include "ddsi__proxy_endpoint.h" #include "ddsi__tran.h" -#include "ddsi__hbcontrol.h" #include "ddsi__sysdeps.h" -#include "dds__whc.h" - #define EVQTRACE(...) DDS_CTRACE (&evq->gv->logconfig, __VA_ARGS__) @@ -54,14 +33,10 @@ != 0 -- and note that it had better be 2's complement machine! */ #define TSCHED_DELETE ((int64_t) ((uint64_t) 1 << 63)) -enum ddsi_xeventkind -{ - XEVK_HEARTBEAT, - XEVK_ACKNACK, - XEVK_SPDP, - XEVK_PMD_UPDATE, - XEVK_DELETE_WRITER, - XEVK_CALLBACK +enum cb_sync_on_delete_state { + CSODS_NO_SYNC_NEEDED, + CSODS_SCHEDULED, + CSODS_EXECUTING }; struct ddsi_xevent @@ -69,36 +44,16 @@ struct ddsi_xevent ddsrt_fibheap_node_t heapnode; struct ddsi_xeventq *evq; ddsrt_mtime_t tsched; - enum ddsi_xeventkind kind; + + enum cb_sync_on_delete_state sync_state; union { - struct { - ddsi_guid_t wr_guid; - } heartbeat; - struct { - ddsi_guid_t pwr_guid; - ddsi_guid_t rd_guid; - } acknack; - struct { - ddsi_guid_t pp_guid; - ddsi_guid_prefix_t dest_proxypp_guid_prefix; /* only if "directed" */ - int directed; /* if 0, undirected; if > 0, number of directed ones to send in reasonably short succession */ - } spdp; - struct { - ddsi_guid_t pp_guid; - } pmd_update; -#if 0 - struct { - } info; -#endif - struct { - ddsi_guid_t guid; - } delete_writer; - struct { - void (*cb) (struct ddsi_xevent *ev, void *arg, ddsrt_mtime_t tnow); - void *arg; - bool executing; - } callback; - } u; + ddsi_xevent_cb_t cb; + // ensure alignment of arg is good + void *p; + uint64_t u64; + double d; + } cb; + char arg[]; }; enum ddsi_xeventkind_nt @@ -106,7 +61,6 @@ enum ddsi_xeventkind_nt XEVK_MSG, XEVK_MSG_REXMIT, XEVK_MSG_REXMIT_NOMERGE, - XEVK_ENTITYID, XEVK_NT_CALLBACK }; @@ -130,10 +84,6 @@ struct ddsi_xevent_nt size_t queued_rexmit_bytes; ddsrt_avl_node_t msg_avlnode; } msg_rexmit; /* and msg_rexmit_nomerge */ - struct { - /* xmsg is self-contained / relies on reference counts */ - struct ddsi_xmsg *msg; - } entityid; struct { void (*cb) (void *arg); void *arg; @@ -146,6 +96,7 @@ struct ddsi_xeventq { ddsrt_avl_tree_t msg_xevents; struct ddsi_xevent_nt *non_timed_xmit_list_oldest; struct ddsi_xevent_nt *non_timed_xmit_list_newest; /* undefined if ..._oldest == NULL */ + size_t non_timed_xmit_list_length; size_t queued_rexmit_bytes; size_t queued_rexmit_msgs; size_t max_queued_rexmit_bytes; @@ -163,7 +114,7 @@ static uint32_t xevent_thread (struct ddsi_xeventq *xevq); static ddsrt_mtime_t earliest_in_xeventq (struct ddsi_xeventq *evq); static int msg_xevents_cmp (const void *a, const void *b); static int compare_xevent_tsched (const void *va, const void *vb); -static void handle_nontimed_xevent (struct ddsi_xevent_nt *xev, struct ddsi_xpack *xp); +static void handle_nontimed_xevent (struct ddsi_xeventq *evq, struct ddsi_xevent_nt *xev, struct ddsi_xpack *xp); static const ddsrt_avl_treedef_t msg_xevents_treedef = DDSRT_AVL_TREEDEF_INITIALIZER_INDKEY (offsetof (struct ddsi_xevent_nt, u.msg_rexmit.msg_avlnode), offsetof (struct ddsi_xevent_nt, u.msg_rexmit.msg), msg_xevents_cmp, 0); @@ -176,17 +127,13 @@ static int compare_xevent_tsched (const void *va, const void *vb) return (a->tsched.v == b->tsched.v) ? 0 : (a->tsched.v < b->tsched.v) ? -1 : 1; } -static void update_rexmit_counts (struct ddsi_xeventq *evq, struct ddsi_xevent_nt *ev) +static void update_rexmit_counts (struct ddsi_xeventq *evq, size_t msg_rexmit_queued_rexmit_bytes) { -#if 0 - EVQTRACE ("ZZZ(%p,%"PRIuSIZE")", (void *) ev, ev->u.msg_rexmit.queued_rexmit_bytes); -#endif - assert (ev->kind == XEVK_MSG_REXMIT || ev->kind == XEVK_MSG_REXMIT_NOMERGE); - assert (ev->u.msg_rexmit.queued_rexmit_bytes <= evq->queued_rexmit_bytes); + assert (msg_rexmit_queued_rexmit_bytes <= evq->queued_rexmit_bytes); assert (evq->queued_rexmit_msgs > 0); - evq->queued_rexmit_bytes -= ev->u.msg_rexmit.queued_rexmit_bytes; + evq->queued_rexmit_bytes -= msg_rexmit_queued_rexmit_bytes; evq->queued_rexmit_msgs--; - evq->cum_rexmit_bytes += ev->u.msg_rexmit.queued_rexmit_bytes; + evq->cum_rexmit_bytes += msg_rexmit_queued_rexmit_bytes; } #if 0 @@ -249,6 +196,7 @@ static void add_to_non_timed_xmit_list (struct ddsi_xeventq *evq, struct ddsi_xe evq->non_timed_xmit_list_newest->listnode.next = ev; } evq->non_timed_xmit_list_newest = ev; + evq->non_timed_xmit_list_length++; if (ev->kind == XEVK_MSG_REXMIT) remember_msg (evq, ev); @@ -263,6 +211,7 @@ static struct ddsi_xevent_nt *getnext_from_non_timed_xmit_list (struct ddsi_xev struct ddsi_xevent_nt *ev = evq->non_timed_xmit_list_oldest; if (ev != NULL) { + evq->non_timed_xmit_list_length--; evq->non_timed_xmit_list_oldest = ev->listnode.next; if (ev->kind == XEVK_MSG_REXMIT) @@ -280,22 +229,6 @@ static int non_timed_xmit_list_is_empty (struct ddsi_xeventq *evq) return (evq->non_timed_xmit_list_oldest == NULL); } -static int compute_non_timed_xmit_list_size (struct ddsi_xeventq *evq) -{ - /* returns how many "non-timed" xevents are pending by counting the - number of events in the list -- it'd be easy to compute the - length incrementally in the add_... and next_... functions, but - it isn't really being used anywhere, so why bother? */ - struct ddsi_xevent_nt *current = evq->non_timed_xmit_list_oldest; - int i = 0; - while (current) - { - current = current->listnode.next; - i++; - } - return i; -} - #ifndef NDEBUG static int nontimed_xevent_in_queue (struct ddsi_xeventq *evq, struct ddsi_xevent_nt *ev) { @@ -319,27 +252,14 @@ static int nontimed_xevent_in_queue (struct ddsi_xeventq *evq, struct ddsi_xeven static void free_xevent (struct ddsi_xeventq *evq, struct ddsi_xevent *ev) { (void) evq; - if (ev->tsched.v != TSCHED_DELETE) - { - switch (ev->kind) - { - case XEVK_HEARTBEAT: - case XEVK_ACKNACK: - case XEVK_SPDP: - case XEVK_PMD_UPDATE: - case XEVK_DELETE_WRITER: - case XEVK_CALLBACK: - break; - } - } ddsrt_free (ev); } -void ddsi_delete_xevent (struct ddsi_xevent *ev) +static void ddsi_delete_xevent_nosync (struct ddsi_xevent *ev) { struct ddsi_xeventq *evq = ev->evq; ddsrt_mutex_lock (&evq->lock); - assert (ev->kind != XEVK_CALLBACK || ev->u.callback.executing); + assert (ev->sync_state != CSODS_EXECUTING); /* Can delete it only once, no matter how we implement it internally */ assert (ev->tsched.v != TSCHED_DELETE); assert (TSCHED_DELETE < ev->tsched.v); @@ -359,13 +279,12 @@ void ddsi_delete_xevent (struct ddsi_xevent *ev) ddsrt_mutex_unlock (&evq->lock); } -void ddsi_delete_xevent_callback (struct ddsi_xevent *ev) +static void ddsi_delete_xevent_sync (struct ddsi_xevent *ev) { struct ddsi_xeventq *evq = ev->evq; - assert (ev->kind == XEVK_CALLBACK); ddsrt_mutex_lock (&evq->lock); /* wait until neither scheduled nor executing; loop in case the callback reschedules the event */ - while (ev->tsched.v != DDS_NEVER || ev->u.callback.executing) + while (ev->tsched.v != DDS_NEVER || ev->sync_state == CSODS_EXECUTING) { if (ev->tsched.v != DDS_NEVER) { @@ -373,7 +292,7 @@ void ddsi_delete_xevent_callback (struct ddsi_xevent *ev) ddsrt_fibheap_delete (&evq_xevents_fhdef, &evq->xevents, ev); ev->tsched.v = DDS_NEVER; } - if (ev->u.callback.executing) + if (ev->sync_state == CSODS_EXECUTING) { ddsrt_cond_wait (&evq->cond, &evq->lock); } @@ -382,6 +301,14 @@ void ddsi_delete_xevent_callback (struct ddsi_xevent *ev) free_xevent (evq, ev); } +void ddsi_delete_xevent (struct ddsi_xevent *ev) +{ + if (ev->sync_state == CSODS_NO_SYNC_NEEDED) + ddsi_delete_xevent_nosync (ev); + else + ddsi_delete_xevent_sync (ev); +} + int ddsi_resched_xevent_if_earlier (struct ddsi_xevent *ev, ddsrt_mtime_t tsched) { struct ddsi_xeventq *evq = ev->evq; @@ -417,46 +344,16 @@ int ddsi_resched_xevent_if_earlier (struct ddsi_xevent *ev, ddsrt_mtime_t tsched return is_resched; } -static ddsrt_mtime_t mtime_round_up (ddsrt_mtime_t t, int64_t round) -{ - /* This function rounds up t to the nearest next multiple of round. - t is nanoseconds, round is milliseconds. Avoid functions from - maths libraries to keep code portable */ - assert (t.v >= 0 && round >= 0); - if (round == 0 || t.v == DDS_INFINITY) - return t; - else - { - int64_t remainder = t.v % round; - if (remainder == 0) - return t; - else - return (ddsrt_mtime_t) { t.v + round - remainder }; - } -} - -static struct ddsi_xevent *qxev_common (struct ddsi_xeventq *evq, ddsrt_mtime_t tsched, enum ddsi_xeventkind kind) +#ifndef NDEBUG +bool ddsi_delete_xevent_pending (struct ddsi_xevent *ev) { - /* qxev_common is the route by which all timed xevents are - created. */ - struct ddsi_xevent *ev = ddsrt_malloc (sizeof (*ev)); - - assert (tsched.v != TSCHED_DELETE); - ASSERT_MUTEX_HELD (&evq->lock); - - /* round up the scheduled time if required */ - if (tsched.v != DDS_NEVER && evq->gv->config.schedule_time_rounding != 0) - { - ddsrt_mtime_t tsched_rounded = mtime_round_up (tsched, evq->gv->config.schedule_time_rounding); - EVQTRACE ("rounded event scheduled for %"PRId64" to %"PRId64"\n", tsched.v, tsched_rounded.v); - tsched = tsched_rounded; - } - - ev->evq = evq; - ev->tsched = tsched; - ev->kind = kind; - return ev; + struct ddsi_xeventq *evq = ev->evq; + ddsrt_mutex_lock (&evq->lock); + const bool is_pending = (ev->tsched.v == TSCHED_DELETE); + ddsrt_mutex_unlock (&evq->lock); + return is_pending; } +#endif static struct ddsi_xevent_nt *qxev_common_nt (struct ddsi_xeventq *evq, enum ddsi_xeventkind_nt kind) { @@ -495,7 +392,7 @@ static void qxev_insert_nt (struct ddsi_xevent_nt *ev) struct ddsi_xeventq *evq = ev->evq; ASSERT_MUTEX_HELD (&evq->lock); add_to_non_timed_xmit_list (evq, ev); - EVQTRACE ("non-timed queue now has %d items\n", compute_non_timed_xmit_list_size (evq)); + EVQTRACE (" (%"PRIuSIZE" in queue)\n", evq->non_timed_xmit_list_length); } static int msg_xevents_cmp (const void *a, const void *b) @@ -513,6 +410,7 @@ struct ddsi_xeventq * ddsi_xeventq_new (struct ddsi_domaingv *gv, size_t max_que ddsrt_avl_init (&msg_xevents_treedef, &evq->msg_xevents); evq->non_timed_xmit_list_oldest = NULL; evq->non_timed_xmit_list_newest = NULL; + evq->non_timed_xmit_list_length = 0; evq->terminate = 0; evq->thrst = NULL; evq->max_queued_rexmit_bytes = max_queued_rexmit_bytes; @@ -575,7 +473,7 @@ void ddsi_xeventq_free (struct ddsi_xeventq *evq) while (!non_timed_xmit_list_is_empty (evq)) { ddsi_thread_state_awake_to_awake_no_nest (ddsi_lookup_thread_state ()); - handle_nontimed_xevent (getnext_from_non_timed_xmit_list (evq), xp); + handle_nontimed_xevent (evq, getnext_from_non_timed_xmit_list (evq), xp); } ddsrt_mutex_unlock (&evq->lock); ddsi_xpack_send (xp, false); @@ -591,621 +489,68 @@ void ddsi_xeventq_free (struct ddsi_xeventq *evq) /* EVENT QUEUE EVENT HANDLERS ******************************************************/ -static void handle_xevk_msg (struct ddsi_xpack *xp, struct ddsi_xevent_nt *ev) -{ - assert (!nontimed_xevent_in_queue (ev->evq, ev)); - ddsi_xpack_addmsg (xp, ev->u.msg.msg, 0); -} - -static void handle_xevk_msg_rexmit (struct ddsi_xpack *xp, struct ddsi_xevent_nt *ev) -{ - struct ddsi_xeventq *evq = ev->evq; - - assert (!nontimed_xevent_in_queue (ev->evq, ev)); - - ddsi_xpack_addmsg (xp, ev->u.msg_rexmit.msg, 0); - - /* FIXME: less than happy about having to relock the queue for a - little while here */ - ddsrt_mutex_lock (&evq->lock); - update_rexmit_counts (evq, ev); - ddsrt_mutex_unlock (&evq->lock); -} - -static void handle_xevk_entityid (struct ddsi_xpack *xp, struct ddsi_xevent_nt *ev) -{ - assert (!nontimed_xevent_in_queue (ev->evq, ev)); - ddsi_xpack_addmsg (xp, ev->u.entityid.msg, 0); -} - -#ifdef DDS_HAS_SECURITY -static int send_heartbeat_to_all_readers_check_and_sched (struct ddsi_xevent *ev, struct ddsi_writer *wr, const struct ddsi_whc_state *whcst, ddsrt_mtime_t tnow, ddsrt_mtime_t *t_next) -{ - int send; - if (!ddsi_writer_must_have_hb_scheduled (wr, whcst)) - { - wr->hbcontrol.tsched = DDSRT_MTIME_NEVER; - send = -1; - } - else if (!ddsi_writer_hbcontrol_must_send (wr, whcst, tnow)) - { - wr->hbcontrol.tsched = ddsrt_mtime_add_duration (tnow, ddsi_writer_hbcontrol_intv (wr, whcst, tnow)); - send = -1; - } - else - { - const int hbansreq = ddsi_writer_hbcontrol_ack_required (wr, whcst, tnow); - wr->hbcontrol.tsched = ddsrt_mtime_add_duration (tnow, ddsi_writer_hbcontrol_intv (wr, whcst, tnow)); - send = hbansreq; - } - - ddsi_resched_xevent_if_earlier (ev, wr->hbcontrol.tsched); - *t_next = wr->hbcontrol.tsched; - return send; -} - -static void send_heartbeat_to_all_readers (struct ddsi_xpack *xp, struct ddsi_xevent *ev, struct ddsi_writer *wr, ddsrt_mtime_t tnow) -{ - struct ddsi_whc_state whcst; - ddsrt_mtime_t t_next; - unsigned count = 0; - - ddsrt_mutex_lock (&wr->e.lock); - - ddsi_whc_get_state(wr->whc, &whcst); - const int hbansreq = send_heartbeat_to_all_readers_check_and_sched (ev, wr, &whcst, tnow, &t_next); - if (hbansreq >= 0) - { - struct ddsi_wr_prd_match *m; - struct ddsi_guid last_guid = { .prefix = {.u = {0,0,0}}, .entityid = {0} }; - - while ((m = ddsrt_avl_lookup_succ (&ddsi_wr_readers_treedef, &wr->readers, &last_guid)) != NULL) - { - last_guid = m->prd_guid; - if (m->seq < m->last_seq) - { - struct ddsi_proxy_reader *prd; - - prd = ddsi_entidx_lookup_proxy_reader_guid (wr->e.gv->entity_index, &m->prd_guid); - if (prd) - { - ETRACE (wr, " heartbeat(wr "PGUIDFMT" rd "PGUIDFMT" %s) send, resched in %g s (min-ack %"PRIu64", avail-seq %"PRIu64")\n", - PGUID (wr->e.guid), - PGUID (m->prd_guid), - hbansreq ? "" : " final", - (double)(t_next.v - tnow.v) / 1e9, - m->seq, - m->last_seq); - - struct ddsi_xmsg *msg = ddsi_writer_hbcontrol_p2p(wr, &whcst, hbansreq, prd); - if (msg != NULL) - { - ddsrt_mutex_unlock (&wr->e.lock); - ddsi_xpack_addmsg (xp, msg, 0); - ddsrt_mutex_lock (&wr->e.lock); - } - count++; - } - } - } - } - - if (count == 0) - { - if (ddsrt_avl_is_empty (&wr->readers)) - { - ETRACE (wr, "heartbeat(wr "PGUIDFMT") suppressed, resched in %g s (min-ack [none], avail-seq %"PRIu64", xmit %"PRIu64")\n", - PGUID (wr->e.guid), - (t_next.v == DDS_NEVER) ? INFINITY : (double)(t_next.v - tnow.v) / 1e9, - whcst.max_seq, - ddsi_writer_read_seq_xmit(wr)); - } - else - { - ETRACE (wr, "heartbeat(wr "PGUIDFMT") suppressed, resched in %g s (min-ack %"PRIu64"%s, avail-seq %"PRIu64", xmit %"PRIu64")\n", - PGUID (wr->e.guid), - (t_next.v == DDS_NEVER) ? INFINITY : (double)(t_next.v - tnow.v) / 1e9, - ((struct ddsi_wr_prd_match *) ddsrt_avl_root (&ddsi_wr_readers_treedef, &wr->readers))->min_seq, - ((struct ddsi_wr_prd_match *) ddsrt_avl_root (&ddsi_wr_readers_treedef, &wr->readers))->all_have_replied_to_hb ? "" : "!", - whcst.max_seq, - ddsi_writer_read_seq_xmit(wr)); - } - } - - ddsrt_mutex_unlock (&wr->e.lock); -} -#endif - -static void handle_xevk_heartbeat (struct ddsi_xpack *xp, struct ddsi_xevent *ev, ddsrt_mtime_t tnow) -{ - struct ddsi_domaingv const * const gv = ev->evq->gv; - struct ddsi_xmsg *msg; - struct ddsi_writer *wr; - ddsrt_mtime_t t_next; - int hbansreq = 0; - struct ddsi_whc_state whcst; - - if ((wr = ddsi_entidx_lookup_writer_guid (gv->entity_index, &ev->u.heartbeat.wr_guid)) == NULL) - { - GVTRACE("heartbeat(wr "PGUIDFMT") writer gone\n", PGUID (ev->u.heartbeat.wr_guid)); - return; - } - -#ifdef DDS_HAS_SECURITY - if (wr->e.guid.entityid.u == DDSI_ENTITYID_P2P_BUILTIN_PARTICIPANT_VOLATILE_SECURE_WRITER) - { - send_heartbeat_to_all_readers(xp, ev, wr, tnow); - return; - } -#endif - - ddsrt_mutex_lock (&wr->e.lock); - assert (wr->reliable); - ddsi_whc_get_state(wr->whc, &whcst); - if (!ddsi_writer_must_have_hb_scheduled (wr, &whcst)) - { - hbansreq = 1; /* just for trace */ - msg = NULL; /* Need not send it now, and no need to schedule it for the future */ - t_next.v = DDS_NEVER; - } - else if (!ddsi_writer_hbcontrol_must_send (wr, &whcst, tnow)) - { - hbansreq = 1; /* just for trace */ - msg = NULL; - t_next.v = tnow.v + ddsi_writer_hbcontrol_intv (wr, &whcst, tnow); - } - else - { - hbansreq = ddsi_writer_hbcontrol_ack_required (wr, &whcst, tnow); - msg = ddsi_writer_hbcontrol_create_heartbeat (wr, &whcst, tnow, hbansreq, 0); - t_next.v = tnow.v + ddsi_writer_hbcontrol_intv (wr, &whcst, tnow); - } - - if (ddsrt_avl_is_empty (&wr->readers)) - { - GVTRACE ("heartbeat(wr "PGUIDFMT"%s) %s, resched in %g s (min-ack [none], avail-seq %"PRIu64", xmit %"PRIu64")\n", - PGUID (wr->e.guid), - hbansreq ? "" : " final", - msg ? "sent" : "suppressed", - (t_next.v == DDS_NEVER) ? INFINITY : (double)(t_next.v - tnow.v) / 1e9, - whcst.max_seq, ddsi_writer_read_seq_xmit (wr)); - } - else - { - GVTRACE ("heartbeat(wr "PGUIDFMT"%s) %s, resched in %g s (min-ack %"PRId64"%s, avail-seq %"PRIu64", xmit %"PRIu64")\n", - PGUID (wr->e.guid), - hbansreq ? "" : " final", - msg ? "sent" : "suppressed", - (t_next.v == DDS_NEVER) ? INFINITY : (double)(t_next.v - tnow.v) / 1e9, - ((struct ddsi_wr_prd_match *) ddsrt_avl_root_non_empty (&ddsi_wr_readers_treedef, &wr->readers))->min_seq, - ((struct ddsi_wr_prd_match *) ddsrt_avl_root_non_empty (&ddsi_wr_readers_treedef, &wr->readers))->all_have_replied_to_hb ? "" : "!", - whcst.max_seq, ddsi_writer_read_seq_xmit (wr)); - } - (void) ddsi_resched_xevent_if_earlier (ev, t_next); - wr->hbcontrol.tsched = t_next; - ddsrt_mutex_unlock (&wr->e.lock); - - /* Can't transmit synchronously with writer lock held: trying to add - the heartbeat to the xp may cause xp to be sent out, which may - require updating wr->seq_xmit for other messages already in xp. - Besides, ddsi_xpack_addmsg may sleep for bandwidth-limited channels - and we certainly don't want to hold the lock during that time. */ - if (msg) - { - if (!wr->test_suppress_heartbeat) - ddsi_xpack_addmsg (xp, msg, 0); - else - { - GVTRACE ("test_suppress_heartbeat\n"); - ddsi_xmsg_free (msg); - } - } -} - -static dds_duration_t preemptive_acknack_interval (const struct ddsi_pwr_rd_match *rwn) -{ - if (rwn->t_last_ack.v < rwn->tcreate.v) - return 0; - else - { - const dds_duration_t age = rwn->t_last_ack.v - rwn->tcreate.v; - if (age <= DDS_SECS (10)) - return DDS_SECS (1); - else if (age <= DDS_SECS (60)) - return DDS_SECS (2); - else if (age <= DDS_SECS (120)) - return DDS_SECS (5); - else - return DDS_SECS (10); - } -} - -static struct ddsi_xmsg *make_preemptive_acknack (struct ddsi_xevent *ev, struct ddsi_proxy_writer *pwr, struct ddsi_pwr_rd_match *rwn, ddsrt_mtime_t tnow) +static void handle_timed_xevent (struct ddsi_xeventq *evq, struct ddsi_xevent *xev, struct ddsi_xpack *xp, ddsrt_mtime_t tnow) { - const dds_duration_t old_intv = preemptive_acknack_interval (rwn); - if (tnow.v < ddsrt_mtime_add_duration (rwn->t_last_ack, old_intv).v) - { - (void) ddsi_resched_xevent_if_earlier (ev, ddsrt_mtime_add_duration (rwn->t_last_ack, old_intv)); - return NULL; - } - - struct ddsi_domaingv * const gv = pwr->e.gv; - struct ddsi_participant *pp = NULL; - if (ddsi_omg_proxy_participant_is_secure (pwr->c.proxypp)) - { - struct ddsi_reader *rd = ddsi_entidx_lookup_reader_guid (gv->entity_index, &rwn->rd_guid); - if (rd) - pp = rd->c.pp; - } + /* event rescheduling functions look at xev->tsched to + determine whether it is currently on the heap or not (i.e., + scheduled or not), so set to TSCHED_NEVER to indicate it + currently isn't. */ + xev->tsched.v = DDS_NEVER; - struct ddsi_xmsg *msg; - if ((msg = ddsi_xmsg_new (gv->xmsgpool, &rwn->rd_guid, pp, DDSI_ACKNACK_SIZE_MAX, DDSI_XMSG_KIND_CONTROL)) == NULL) + /* We relinquish the lock while processing the event. */ + if (xev->sync_state == CSODS_NO_SYNC_NEEDED) { - // if out of memory, try again later - (void) ddsi_resched_xevent_if_earlier (ev, ddsrt_mtime_add_duration (tnow, old_intv)); - return NULL; - } - - ddsi_xmsg_setdst_pwr (msg, pwr); - struct ddsi_xmsg_marker sm_marker; - ddsi_rtps_acknack_t *an = ddsi_xmsg_append (msg, &sm_marker, DDSI_ACKNACK_SIZE (0)); - ddsi_xmsg_submsg_init (msg, sm_marker, DDSI_RTPS_SMID_ACKNACK); - an->readerId = ddsi_hton_entityid (rwn->rd_guid.entityid); - an->writerId = ddsi_hton_entityid (pwr->e.guid.entityid); - an->readerSNState.bitmap_base = ddsi_to_seqno (1); - an->readerSNState.numbits = 0; - ddsi_count_t * const countp = - (ddsi_count_t *) ((char *) an + offsetof (ddsi_rtps_acknack_t, bits) + DDSI_SEQUENCE_NUMBER_SET_BITS_SIZE (0)); - *countp = 0; - ddsi_xmsg_submsg_setnext (msg, sm_marker); - ddsi_security_encode_datareader_submsg (msg, sm_marker, pwr, &rwn->rd_guid); - - rwn->t_last_ack = tnow; - const dds_duration_t new_intv = preemptive_acknack_interval (rwn); - (void) ddsi_resched_xevent_if_earlier (ev, ddsrt_mtime_add_duration (rwn->t_last_ack, new_intv)); - - // numbits is always 0 here, so need to print the bitmap - ETRACE (pwr, "acknack "PGUIDFMT" -> "PGUIDFMT": #%"PRIu32":%"PRId64"/%"PRIu32":\n", - PGUID (rwn->rd_guid), PGUID (pwr->e.guid), *countp, - ddsi_from_seqno (an->readerSNState.bitmap_base), an->readerSNState.numbits); - return msg; -} - -static void handle_xevk_acknack (struct ddsi_xpack *xp, struct ddsi_xevent *ev, ddsrt_mtime_t tnow) -{ - /* FIXME: ought to keep track of which NACKs are being generated in - response to a Heartbeat. There is no point in having multiple - readers NACK the data. - - FIXME: ought to determine the set of missing samples (as it does - now), and then check which for of those fragments are available already. - A little snag is that the defragmenter can throw out partial samples in - favour of others, so MUST ensure that the defragmenter won't start - threshing and fail to make progress! */ - struct ddsi_domaingv *gv = ev->evq->gv; - struct ddsi_proxy_writer *pwr; - struct ddsi_xmsg *msg; - struct ddsi_pwr_rd_match *rwn; - - if ((pwr = ddsi_entidx_lookup_proxy_writer_guid (gv->entity_index, &ev->u.acknack.pwr_guid)) == NULL) - { - return; - } - - ddsrt_mutex_lock (&pwr->e.lock); - if ((rwn = ddsrt_avl_lookup (&ddsi_pwr_readers_treedef, &pwr->readers, &ev->u.acknack.rd_guid)) == NULL) - { - ddsrt_mutex_unlock (&pwr->e.lock); - return; - } - - if (!pwr->have_seen_heartbeat) - msg = make_preemptive_acknack (ev, pwr, rwn, tnow); - else - msg = ddsi_make_and_resched_acknack (ev, pwr, rwn, tnow, false); - ddsrt_mutex_unlock (&pwr->e.lock); - - /* ddsi_xpack_addmsg may sleep (for bandwidth-limited channels), so - must be outside the lock */ - if (msg) - { - // a possible result of trying to encode a submessage is that it is removed, - // in which case we may end up with an empty one. - // FIXME: change ddsi_security_encode_datareader_submsg so that it returns this and make it warn_unused_result - if (ddsi_xmsg_size (msg) == 0) - ddsi_xmsg_free (msg); - else - ddsi_xpack_addmsg (xp, msg, 0); - } -} - -static bool resend_spdp_sample_by_guid_key (struct ddsi_writer *wr, const ddsi_guid_t *guid, struct ddsi_proxy_reader *prd) -{ - /* Look up data in (transient-local) WHC by key value -- FIXME: clearly - a slightly more efficient and elegant way of looking up the key value - is to be preferred */ - struct ddsi_domaingv *gv = wr->e.gv; - bool sample_found; - ddsi_plist_t ps; - ddsi_plist_init_empty (&ps); - ps.present |= PP_PARTICIPANT_GUID; - ps.participant_guid = *guid; - struct ddsi_serdata *sd = ddsi_serdata_from_sample (gv->spdp_type, SDK_KEY, &ps); - ddsi_plist_fini (&ps); - struct ddsi_whc_borrowed_sample sample; - - ddsrt_mutex_lock (&wr->e.lock); - sample_found = ddsi_whc_borrow_sample_key (wr->whc, sd, &sample); - if (sample_found) - { - /* Claiming it is new rather than a retransmit so that the rexmit - limiting won't kick in. It is best-effort and therefore the - updating of the last transmitted sequence number won't take - place anyway. Nor is it necessary to fiddle with heartbeat - control stuff. */ - ddsi_enqueue_spdp_sample_wrlock_held (wr, sample.seq, sample.serdata, prd); - ddsi_whc_return_sample(wr->whc, &sample, false); - } - ddsrt_mutex_unlock (&wr->e.lock); - ddsi_serdata_unref (sd); - return sample_found; -} - -static void handle_xevk_spdp (UNUSED_ARG (struct ddsi_xpack *xp), struct ddsi_xevent *ev, ddsrt_mtime_t tnow) -{ - /* Like the writer pointer in the heartbeat event, the participant pointer in the spdp event is assumed valid. */ - struct ddsi_domaingv *gv = ev->evq->gv; - struct ddsi_participant *pp; - struct ddsi_proxy_reader *prd; - struct ddsi_writer *spdp_wr; - bool do_write; - - if ((pp = ddsi_entidx_lookup_participant_guid (gv->entity_index, &ev->u.spdp.pp_guid)) == NULL) - { - GVTRACE ("handle_xevk_spdp "PGUIDFMT" - unknown guid\n", PGUID (ev->u.spdp.pp_guid)); - if (ev->u.spdp.directed) - ddsi_delete_xevent (ev); - return; - } - - if ((spdp_wr = ddsi_get_builtin_writer (pp, DDSI_ENTITYID_SPDP_BUILTIN_PARTICIPANT_WRITER)) == NULL) - { - GVTRACE ("handle_xevk_spdp "PGUIDFMT" - spdp writer of participant not found\n", PGUID (ev->u.spdp.pp_guid)); - if (ev->u.spdp.directed) - ddsi_delete_xevent (ev); - return; - } - - if (!ev->u.spdp.directed) - { - /* memset is for tracing output */ - memset (&ev->u.spdp.dest_proxypp_guid_prefix, 0, sizeof (ev->u.spdp.dest_proxypp_guid_prefix)); - prd = NULL; - do_write = true; - } - else - { - ddsi_guid_t guid; - guid.prefix = ev->u.spdp.dest_proxypp_guid_prefix; - guid.entityid.u = DDSI_ENTITYID_SPDP_BUILTIN_PARTICIPANT_READER; - prd = ddsi_entidx_lookup_proxy_reader_guid (gv->entity_index, &guid); - do_write = (prd != NULL); - if (!do_write) - GVTRACE ("xmit spdp: no proxy reader "PGUIDFMT"\n", PGUID (guid)); - } - - if (do_write && !resend_spdp_sample_by_guid_key (spdp_wr, &ev->u.spdp.pp_guid, prd)) - { -#ifndef NDEBUG - /* If undirected, it is pp->spdp_xevent, and that one must never - run into an empty WHC unless it is already marked for deletion. - - If directed, it may happen in response to an SPDP packet during - creation of the participant. This is because pp is inserted in - the hash table quite early on, which, in turn, is because it - needs to be visible for creating its builtin endpoints. But in - this case, the initial broadcast of the SPDP packet of pp will - happen shortly. */ - if (!ev->u.spdp.directed) - { - ddsrt_mutex_lock (&pp->e.lock); - ddsrt_mutex_lock (&ev->evq->lock); - assert (ev->tsched.v == TSCHED_DELETE); - ddsrt_mutex_unlock (&ev->evq->lock); - ddsrt_mutex_unlock (&pp->e.lock); - } - else - { - GVTRACE ("xmit spdp: suppressing early spdp response from "PGUIDFMT" to %"PRIx32":%"PRIx32":%"PRIx32":%x\n", - PGUID (pp->e.guid), PGUIDPREFIX (ev->u.spdp.dest_proxypp_guid_prefix), DDSI_ENTITYID_PARTICIPANT); - } -#endif - } - - if (ev->u.spdp.directed) - { - /* Directed events are used to send SPDP packets to newly - discovered peers, and used just once. */ - if (--ev->u.spdp.directed == 0 || gv->config.spdp_interval < DDS_SECS (1) || pp->plist->qos.liveliness.lease_duration < DDS_SECS (1)) - ddsi_delete_xevent (ev); - else - { - ddsrt_mtime_t tnext = ddsrt_mtime_add_duration (tnow, DDS_SECS (1)); - GVTRACE ("xmit spdp "PGUIDFMT" to %"PRIx32":%"PRIx32":%"PRIx32":%x (resched %gs)\n", - PGUID (pp->e.guid), - PGUIDPREFIX (ev->u.spdp.dest_proxypp_guid_prefix), DDSI_ENTITYID_SPDP_BUILTIN_PARTICIPANT_READER, - (double)(tnext.v - tnow.v) / 1e9); - (void) ddsi_resched_xevent_if_earlier (ev, tnext); - } - } - else - { - /* schedule next when 80% of the interval has elapsed, or 2s - before the lease ends, whichever comes first (similar to PMD), - but never wait longer than spdp_interval */ - const dds_duration_t mindelta = DDS_MSECS (10); - const dds_duration_t ldur = pp->plist->qos.liveliness.lease_duration; - ddsrt_mtime_t tnext; - int64_t intv; - - if (ldur < 5 * mindelta / 4) - intv = mindelta; - else if (ldur < DDS_SECS (10)) - intv = 4 * ldur / 5; - else - intv = ldur - DDS_SECS (2); - if (intv > gv->config.spdp_interval) - intv = gv->config.spdp_interval; - - tnext = ddsrt_mtime_add_duration (tnow, intv); - GVTRACE ("xmit spdp "PGUIDFMT" to %"PRIx32":%"PRIx32":%"PRIx32":%x (resched %gs)\n", - PGUID (pp->e.guid), - PGUIDPREFIX (ev->u.spdp.dest_proxypp_guid_prefix), DDSI_ENTITYID_SPDP_BUILTIN_PARTICIPANT_READER, - (double)(tnext.v - tnow.v) / 1e9); - (void) ddsi_resched_xevent_if_earlier (ev, tnext); - } -} - -static void handle_xevk_pmd_update (struct ddsi_thread_state * const thrst, struct ddsi_xpack *xp, struct ddsi_xevent *ev, ddsrt_mtime_t tnow) -{ - struct ddsi_domaingv * const gv = ev->evq->gv; - struct ddsi_participant *pp; - dds_duration_t intv; - ddsrt_mtime_t tnext; - - if ((pp = ddsi_entidx_lookup_participant_guid (gv->entity_index, &ev->u.pmd_update.pp_guid)) == NULL) - { - return; - } - - ddsi_write_pmd_message (thrst, xp, pp, DDSI_PARTICIPANT_MESSAGE_DATA_KIND_AUTOMATIC_LIVELINESS_UPDATE); - - intv = ddsi_participant_get_pmd_interval (pp); - if (intv == DDS_INFINITY) - { - tnext.v = DDS_NEVER; - GVTRACE ("resched pmd("PGUIDFMT"): never\n", PGUID (pp->e.guid)); + ddsrt_mutex_unlock (&evq->lock); + xev->cb.cb (evq->gv, xev, xp, xev->arg, tnow); + ddsrt_mutex_lock (&evq->lock); } else { - /* schedule next when 80% of the interval has elapsed, or 2s - before the lease ends, whichever comes first */ - if (intv >= DDS_SECS (10)) - tnext.v = tnow.v + intv - DDS_SECS (2); - else - tnext.v = tnow.v + 4 * intv / 5; - GVTRACE ("resched pmd("PGUIDFMT"): %gs\n", PGUID (pp->e.guid), (double)(tnext.v - tnow.v) / 1e9); + xev->sync_state = CSODS_EXECUTING; + ddsrt_mutex_unlock (&evq->lock); + xev->cb.cb (evq->gv, xev, xp, xev->arg, tnow); + ddsrt_mutex_lock (&evq->lock); + xev->sync_state = CSODS_SCHEDULED; + ddsrt_cond_broadcast (&evq->cond); } - - (void) ddsi_resched_xevent_if_earlier (ev, tnext); } -static void handle_xevk_delete_writer (UNUSED_ARG (struct ddsi_xpack *xp), struct ddsi_xevent *ev, UNUSED_ARG (ddsrt_mtime_t tnow)) +static void handle_nontimed_xevent (struct ddsi_xeventq *evq, struct ddsi_xevent_nt *xev, struct ddsi_xpack *xp) { - /* don't worry if the writer is already gone by the time we get here, delete_writer_nolinger checks for that. */ - struct ddsi_domaingv * const gv = ev->evq->gv; - GVTRACE ("handle_xevk_delete_writer: "PGUIDFMT"\n", PGUID (ev->u.delete_writer.guid)); - ddsi_delete_writer_nolinger (gv, &ev->u.delete_writer.guid); - ddsi_delete_xevent (ev); -} + /* This function handles the individual xevent irrespective of + whether it is a "timed" or "non-timed" xevent */ + size_t msg_rexmit_queued_rexmit_bytes = SIZE_MAX; -static void handle_individual_xevent (struct ddsi_thread_state * const thrst, struct ddsi_xevent *xev, struct ddsi_xpack *xp, ddsrt_mtime_t tnow) -{ - struct ddsi_xeventq *xevq = xev->evq; /* We relinquish the lock while processing the event, but require it held for administrative work. */ - ASSERT_MUTEX_HELD (&xevq->lock); - if (xev->kind == XEVK_CALLBACK) - { - xev->u.callback.executing = true; - ddsrt_mutex_unlock (&xevq->lock); - xev->u.callback.cb (xev, xev->u.callback.arg, tnow); - ddsrt_mutex_lock (&xevq->lock); - xev->u.callback.executing = false; - ddsrt_cond_broadcast (&xevq->cond); - } - else - { - ddsrt_mutex_unlock (&xevq->lock); - switch (xev->kind) - { - case XEVK_HEARTBEAT: - handle_xevk_heartbeat (xp, xev, tnow); - break; - case XEVK_ACKNACK: - handle_xevk_acknack (xp, xev, tnow); - break; - case XEVK_SPDP: - handle_xevk_spdp (xp, xev, tnow); - break; - case XEVK_PMD_UPDATE: - handle_xevk_pmd_update (thrst, xp, xev, tnow); - break; - case XEVK_DELETE_WRITER: - handle_xevk_delete_writer (xp, xev, tnow); - break; - case XEVK_CALLBACK: - assert (0); - break; - } - ddsrt_mutex_lock (&xevq->lock); - } - ASSERT_MUTEX_HELD (&xevq->lock); -} - -static void handle_individual_xevent_nt (struct ddsi_xevent_nt *xev, struct ddsi_xpack *xp) -{ + ASSERT_MUTEX_HELD (&evq->lock); + ddsrt_mutex_unlock (&evq->lock); switch (xev->kind) { case XEVK_MSG: - handle_xevk_msg (xp, xev); + assert (!nontimed_xevent_in_queue (evq, xev)); + ddsi_xpack_addmsg (xp, xev->u.msg.msg, 0); break; case XEVK_MSG_REXMIT: case XEVK_MSG_REXMIT_NOMERGE: - handle_xevk_msg_rexmit (xp, xev); - break; - case XEVK_ENTITYID: - handle_xevk_entityid (xp, xev); + assert (!nontimed_xevent_in_queue (evq, xev)); + ddsi_xpack_addmsg (xp, xev->u.msg_rexmit.msg, 0); + msg_rexmit_queued_rexmit_bytes = xev->u.msg_rexmit.queued_rexmit_bytes; + assert (msg_rexmit_queued_rexmit_bytes < SIZE_MAX); break; case XEVK_NT_CALLBACK: xev->u.callback.cb (xev->u.callback.arg); break; } ddsrt_free (xev); + ddsrt_mutex_lock (&evq->lock); + if (msg_rexmit_queued_rexmit_bytes < SIZE_MAX) { + update_rexmit_counts (evq, msg_rexmit_queued_rexmit_bytes); + } } -static void handle_timed_xevent (struct ddsi_thread_state * const thrst, struct ddsi_xevent *xev, struct ddsi_xpack *xp, ddsrt_mtime_t tnow /* monotonic */) -{ - /* This function handles the individual xevent irrespective of - whether it is a "timed" or "non-timed" xevent */ - assert (xev->tsched.v != TSCHED_DELETE); - handle_individual_xevent (thrst, xev, xp, tnow /* monotonic */); -} - -static void handle_nontimed_xevent (struct ddsi_xevent_nt *xev, struct ddsi_xpack *xp) -{ - /* This function handles the individual xevent irrespective of - whether it is a "timed" or "non-timed" xevent */ - struct ddsi_xeventq *xevq = xev->evq; - - /* We relinquish the lock while processing the event, but require it - held for administrative work. */ - ASSERT_MUTEX_HELD (&xevq->lock); - - assert (xev->evq == xevq); - - ddsrt_mutex_unlock (&xevq->lock); - handle_individual_xevent_nt (xev, xp); - /* non-timed xevents are freed by the handlers */ - ddsrt_mutex_lock (&xevq->lock); - - ASSERT_MUTEX_HELD (&xevq->lock); -} - -static void handle_xevents (struct ddsi_thread_state * const thrst, struct ddsi_xeventq *xevq, struct ddsi_xpack *xp, ddsrt_mtime_t tnow /* monotonic */) +static void handle_xevents (struct ddsi_thread_state * const thrst, struct ddsi_xeventq *xevq, struct ddsi_xpack *xp, ddsrt_mtime_t tnow) { - int xeventsToProcess = 1; - ASSERT_MUTEX_HELD (&xevq->lock); assert (ddsi_thread_is_awake ()); @@ -1217,55 +562,41 @@ static void handle_xevents (struct ddsi_thread_state * const thrst, struct ddsi_ clock and continue the loop, i.e. test again to see whether any "timed" events are now due. */ - while (xeventsToProcess) - { - while (earliest_in_xeventq(xevq).v <= tnow.v) + bool cont; + do { + cont = false; + while (earliest_in_xeventq (xevq).v <= tnow.v) { struct ddsi_xevent *xev = ddsrt_fibheap_extract_min (&evq_xevents_fhdef, &xevq->xevents); if (xev->tsched.v == TSCHED_DELETE) - { free_xevent (xevq, xev); - } else { - /* event rescheduling functions look at xev->tsched to - determine whether it is currently on the heap or not (i.e., - scheduled or not), so set to TSCHED_NEVER to indicate it - currently isn't. */ - xev->tsched.v = DDS_NEVER; ddsi_thread_state_awake_to_awake_no_nest (thrst); - handle_timed_xevent (thrst, xev, xp, tnow); + handle_timed_xevent (xevq, xev, xp, tnow); + cont = true; } - - /* Limited-bandwidth channels means events can take a LONG time - to process. So read the clock more often. */ - tnow = ddsrt_time_monotonic (); } if (!non_timed_xmit_list_is_empty (xevq)) { struct ddsi_xevent_nt *xev = getnext_from_non_timed_xmit_list (xevq); ddsi_thread_state_awake_to_awake_no_nest (thrst); - handle_nontimed_xevent (xev, xp); - tnow = ddsrt_time_monotonic (); - } - else - { - xeventsToProcess = 0; + handle_nontimed_xevent (xevq, xev, xp); + cont = true; } - } + tnow = ddsrt_time_monotonic (); + } while (cont); ASSERT_MUTEX_HELD (&xevq->lock); } static uint32_t xevent_thread (struct ddsi_xeventq * xevq) { struct ddsi_thread_state * const thrst = ddsi_lookup_thread_state (); - struct ddsi_xpack *xp; ddsrt_mtime_t next_thread_cputime = { 0 }; - xp = ddsi_xpack_new (xevq->gv, false); - + struct ddsi_xpack * const xp = ddsi_xpack_new (xevq->gv, false); ddsrt_mutex_lock (&xevq->lock); while (!xevq->terminate) { @@ -1295,14 +626,11 @@ static uint32_t xevent_thread (struct ddsi_xeventq * xevq) } else { - /* Although we assumed instantaneous handling of events, we - don't want to sleep much longer than we have to. With - os_condTimedWait requiring a relative time, we don't have - much choice but to read the clock now */ + /* "Wrong" time-base here ... should make cond_waitfor time-base aware */ tnow = ddsrt_time_monotonic (); if (twakeup.v > tnow.v) { - twakeup.v -= tnow.v; /* ddsrt_cond_waitfor: relative timeout */ + twakeup.v -= tnow.v; ddsrt_cond_waitfor (&xevq->cond, &xevq->lock, twakeup.v); } } @@ -1338,50 +666,6 @@ void ddsi_qxev_nt_callback (struct ddsi_xeventq *evq, void (*cb) (void *arg), vo ddsrt_mutex_unlock (&evq->lock); } -void ddsi_qxev_prd_entityid (struct ddsi_proxy_reader *prd, const ddsi_guid_t *guid) -{ - struct ddsi_domaingv * const gv = prd->e.gv; - struct ddsi_xmsg *msg; - struct ddsi_xevent_nt *ev; - - /* For connected transports, may need to establish and identify connection */ - - if (! gv->m_factory->m_connless) - { - msg = ddsi_xmsg_new (gv->xmsgpool, guid, NULL, sizeof (ddsi_rtps_entityid_t), DDSI_XMSG_KIND_CONTROL); - ddsi_xmsg_setdst_prd (msg, prd); - GVTRACE (" ddsi_qxev_prd_entityid (%"PRIx32":%"PRIx32":%"PRIx32")\n", PGUIDPREFIX (guid->prefix)); - ddsi_xmsg_add_entityid (msg); - ddsrt_mutex_lock (&gv->xevents->lock); - ev = qxev_common_nt (gv->xevents, XEVK_ENTITYID); - ev->u.entityid.msg = msg; - qxev_insert_nt (ev); - ddsrt_mutex_unlock (&gv->xevents->lock); - } -} - -void ddsi_qxev_pwr_entityid (struct ddsi_proxy_writer *pwr, const ddsi_guid_t *guid) -{ - struct ddsi_domaingv * const gv = pwr->e.gv; - struct ddsi_xmsg *msg; - struct ddsi_xevent_nt *ev; - - /* For connected transports, may need to establish and identify connection */ - - if (! gv->m_factory->m_connless) - { - msg = ddsi_xmsg_new (gv->xmsgpool, guid, NULL, sizeof (ddsi_rtps_entityid_t), DDSI_XMSG_KIND_CONTROL); - ddsi_xmsg_setdst_pwr (msg, pwr); - GVTRACE (" ddsi_qxev_pwr_entityid (%"PRIx32":%"PRIx32":%"PRIx32")\n", PGUIDPREFIX (guid->prefix)); - ddsi_xmsg_add_entityid (msg); - ddsrt_mutex_lock (&pwr->evq->lock); - ev = qxev_common_nt (pwr->evq, XEVK_ENTITYID); - ev->u.entityid.msg = msg; - qxev_insert_nt (ev); - ddsrt_mutex_unlock (&pwr->evq->lock); - } -} - enum ddsi_qxev_msg_rexmit_result ddsi_qxev_msg_rexmit_wrlock_held (struct ddsi_xeventq *evq, struct ddsi_xmsg *msg, int force) { struct ddsi_domaingv * const gv = evq->gv; @@ -1443,82 +727,17 @@ enum ddsi_qxev_msg_rexmit_result ddsi_qxev_msg_rexmit_wrlock_held (struct ddsi_x } } -struct ddsi_xevent *ddsi_qxev_heartbeat (struct ddsi_xeventq *evq, ddsrt_mtime_t tsched, const ddsi_guid_t *wr_guid) +struct ddsi_xevent *ddsi_qxev_callback (struct ddsi_xeventq *evq, ddsrt_mtime_t tsched, ddsi_xevent_cb_t cb, const void *arg, size_t arg_size, bool sync_on_delete) { - /* Event _must_ be deleted before enough of the writer is freed to - cause trouble. Currently used exclusively for - wr->heartbeat_xevent. */ - struct ddsi_xevent *ev; - assert(evq); - ddsrt_mutex_lock (&evq->lock); - ev = qxev_common (evq, tsched, XEVK_HEARTBEAT); - ev->u.heartbeat.wr_guid = *wr_guid; - qxev_insert (ev); - ddsrt_mutex_unlock (&evq->lock); - return ev; -} - -struct ddsi_xevent *ddsi_qxev_acknack (struct ddsi_xeventq *evq, ddsrt_mtime_t tsched, const ddsi_guid_t *pwr_guid, const ddsi_guid_t *rd_guid) -{ - struct ddsi_xevent *ev; - assert(evq); - ddsrt_mutex_lock (&evq->lock); - ev = qxev_common (evq, tsched, XEVK_ACKNACK); - ev->u.acknack.pwr_guid = *pwr_guid; - ev->u.acknack.rd_guid = *rd_guid; - qxev_insert (ev); - ddsrt_mutex_unlock (&evq->lock); - return ev; -} - -struct ddsi_xevent *ddsi_qxev_spdp (struct ddsi_xeventq *evq, ddsrt_mtime_t tsched, const ddsi_guid_t *pp_guid, const ddsi_guid_t *dest_proxypp_guid) -{ - struct ddsi_xevent *ev; - ddsrt_mutex_lock (&evq->lock); - ev = qxev_common (evq, tsched, XEVK_SPDP); - ev->u.spdp.pp_guid = *pp_guid; - if (dest_proxypp_guid == NULL) - ev->u.spdp.directed = 0; - else - { - ev->u.spdp.dest_proxypp_guid_prefix = dest_proxypp_guid->prefix; - ev->u.spdp.directed = 4; - } - qxev_insert (ev); - ddsrt_mutex_unlock (&evq->lock); - return ev; -} - -struct ddsi_xevent *ddsi_qxev_pmd_update (struct ddsi_xeventq *evq, ddsrt_mtime_t tsched, const ddsi_guid_t *pp_guid) -{ - struct ddsi_xevent *ev; - ddsrt_mutex_lock (&evq->lock); - ev = qxev_common (evq, tsched, XEVK_PMD_UPDATE); - ev->u.pmd_update.pp_guid = *pp_guid; - qxev_insert (ev); - ddsrt_mutex_unlock (&evq->lock); - return ev; -} - -struct ddsi_xevent *ddsi_qxev_delete_writer (struct ddsi_xeventq *evq, ddsrt_mtime_t tsched, const ddsi_guid_t *guid) -{ - struct ddsi_xevent *ev; - ddsrt_mutex_lock (&evq->lock); - ev = qxev_common (evq, tsched, XEVK_DELETE_WRITER); - ev->u.delete_writer.guid = *guid; - qxev_insert (ev); - ddsrt_mutex_unlock (&evq->lock); - return ev; -} - -struct ddsi_xevent *ddsi_qxev_callback (struct ddsi_xeventq *evq, ddsrt_mtime_t tsched, void (*cb) (struct ddsi_xevent *ev, void *arg, ddsrt_mtime_t tnow), void *arg) -{ - struct ddsi_xevent *ev; + assert (tsched.v != TSCHED_DELETE); + struct ddsi_xevent *ev = ddsrt_malloc (sizeof (*ev) + arg_size); + ev->evq = evq; + ev->tsched = tsched; + ev->cb.cb = cb; + ev->sync_state = sync_on_delete ? CSODS_SCHEDULED : CSODS_NO_SYNC_NEEDED; + if (arg_size) // so arg = NULL, arg_size = 0 is allowed + memcpy (ev->arg, arg, arg_size); ddsrt_mutex_lock (&evq->lock); - ev = qxev_common (evq, tsched, XEVK_CALLBACK); - ev->u.callback.cb = cb; - ev->u.callback.arg = arg; - ev->u.callback.executing = false; qxev_insert (ev); ddsrt_mutex_unlock (&evq->lock); return ev; diff --git a/src/security/core/src/dds_security_timed_cb.c b/src/security/core/src/dds_security_timed_cb.c index d4e951a801..eaf5d439b8 100644 --- a/src/security/core/src/dds_security_timed_cb.c +++ b/src/security/core/src/dds_security_timed_cb.c @@ -96,9 +96,16 @@ static ddsrt_mtime_t calc_tsched (const struct dds_security_timed_event *ev, dds } } -static void timed_event_cb (struct ddsi_xevent *xev, void *arg, ddsrt_mtime_t tnow) +struct timed_event_cb_arg { + struct dds_security_timed_dispatcher *dispatcher; +}; + +static void timed_event_cb (struct ddsi_domaingv *gv, struct ddsi_xevent *xev, struct ddsi_xpack *xp, void *varg, ddsrt_mtime_t tnow) { - struct dds_security_timed_dispatcher * const dispatcher = arg; + struct timed_event_cb_arg * const arg = varg; + struct dds_security_timed_dispatcher * const dispatcher = arg->dispatcher; + (void) gv; + (void) xp; (void) tnow; ddsrt_mutex_lock (&dispatcher->lock); @@ -142,7 +149,8 @@ void dds_security_timed_dispatcher_enable (struct dds_security_timed_dispatcher if (d->evt == NULL) { struct dds_security_timed_event const * const ev = ddsrt_fibheap_min (&timed_cb_queue_fhdef, &d->timers); - d->evt = ddsi_qxev_callback (d->evq, calc_tsched (ev, dds_time ()), timed_event_cb, d); + struct timed_event_cb_arg arg = { .dispatcher = d }; + d->evt = ddsi_qxev_callback (d->evq, calc_tsched (ev, dds_time ()), timed_event_cb, &arg, sizeof (arg), true); } ddsrt_mutex_unlock (&d->lock); } @@ -182,7 +190,7 @@ bool dds_security_timed_dispatcher_disable (struct dds_security_timed_dispatcher ddsrt_mutex_unlock (&d->lock); if (evt != NULL) - ddsi_delete_xevent_callback (evt); + ddsi_delete_xevent (evt); return (evt != NULL); }