@@ -134,7 +134,7 @@ TEST_F(WrapperSystemTest, shouldRemovePendingAsyncPublicationUponError)
134134 AgentInvoker<ClientConductor> &invoker = aeron->conductorAgentInvoker ();
135135 invoker.start ();
136136
137- auto channel = " aeron:udp?control =localhost:99999" ;
137+ auto channel = " aeron:udp?endpoint =localhost:99999" ;
138138 int stream_id = 1000 ;
139139 int64_t registration_id = aeron->addPublication (channel, stream_id);
140140
@@ -161,6 +161,65 @@ TEST_F(WrapperSystemTest, shouldRemovePendingAsyncPublicationUponError)
161161 }
162162}
163163
164+ TEST_F (WrapperSystemTest, shouldRemovePendingAsyncPublicationUponSuccess)
165+ {
166+ Context ctx;
167+ ctx.useConductorAgentInvoker (true );
168+
169+ std::shared_ptr<Aeron> aeron = Aeron::connect (ctx);
170+ AgentInvoker<ClientConductor> &invoker = aeron->conductorAgentInvoker ();
171+ invoker.start ();
172+ auto channel = " aeron:udp?endpoint=localhost:5555" ;
173+ int stream_id = 1000 ;
174+ int64_t registration_id = aeron->addPublication (channel, stream_id);
175+ POLL_FOR_NON_NULL (publication, aeron->findPublication (registration_id), invoker);
176+
177+ try
178+ {
179+ POLL_FOR_NON_NULL (publication2, aeron->findPublication (registration_id), invoker);
180+ FAIL ();
181+ }
182+ catch ( const IllegalArgumentException& e )
183+ {
184+ auto errorMsg = std::string (e.what ());
185+ EXPECT_NE (std::string::npos, errorMsg.find (std::string (" Unknown registration id: " ).append (std::to_string (registration_id)), 0 ));
186+ }
187+ }
188+
189+ TEST_F (WrapperSystemTest, shouldManuallyFreeAsyncPublicationIfNotPolled)
190+ {
191+ Context ctx;
192+ ctx.useConductorAgentInvoker (true );
193+
194+ std::shared_ptr<Aeron> aeron = Aeron::connect (ctx);
195+ AgentInvoker<ClientConductor> &invoker = aeron->conductorAgentInvoker ();
196+ invoker.start ();
197+
198+ auto channel = " aeron:udp?endpoint=localhost:5555" ;
199+ int stream_id = 1000 ;
200+ auto async = aeron->addPublicationAsync (channel, stream_id);
201+ aeron_async_cmd_free (async); // safe since client conductor is not running concurrently
202+ }
203+
204+ TEST_F (WrapperSystemTest, shouldManuallyFreeAsyncPublicationIfNotPolledConductor)
205+ {
206+ Context ctx;
207+ ctx.useConductorAgentInvoker (false );
208+
209+ std::shared_ptr<Aeron> aeron = Aeron::connect (ctx);
210+
211+ auto channel = " aeron:udp?endpoint=localhost:5555" ;
212+ int stream_id = 1000 ;
213+ auto async = aeron->addPublicationAsync (channel, stream_id);
214+ EXPECT_NE (nullptr , async);
215+
216+ // wait for addPublicationAsync to complete
217+ int64_t counterId = aeron->addCounter (1000 , nullptr , 0 , " test" );
218+ WAIT_FOR_NON_NULL (counter, aeron->findCounter (counterId));
219+
220+ aeron_async_cmd_free (async); // it is safe to delete now since client conductor processed this command
221+ }
222+
164223TEST_F (WrapperSystemTest, shouldRemovePendingAsyncExclusivePublicationUponError)
165224{
166225 Context ctx;
@@ -299,6 +358,31 @@ TEST_F(WrapperSystemTest, asyncSubscriptionMustBeManuallyFreedAfterUsage)
299358 delete async;
300359}
301360
361+ TEST_F (WrapperSystemTest, asyncSubscriptionMustBeManuallyFreedAfterUsageConductor)
362+ {
363+ Context ctx;
364+ ctx.useConductorAgentInvoker (false );
365+
366+ std::shared_ptr<Aeron> aeron = Aeron::connect (ctx);
367+
368+ auto channel = " aeron:udp?endpoint=localhost:99999" ;
369+ int stream_id = 1000 ;
370+ auto async = aeron->addSubscriptionAsync (channel, stream_id);
371+
372+ try
373+ {
374+ WAIT_FOR_NON_NULL (subscription, aeron->findSubscription (async));
375+ FAIL ();
376+ }
377+ catch ( const AeronException& e )
378+ {
379+ auto errorMsg = std::string (e.what ());
380+ EXPECT_NE (std::string::npos, errorMsg.find (" port out of range: 99999" , 0 ));
381+ }
382+
383+ delete async;
384+ }
385+
302386TEST_F (WrapperSystemTest, nonPolledAsyncSubscriptionMustBeManuallyFreedAfterUsage)
303387{
304388 Context ctx;
@@ -437,3 +521,108 @@ TEST_F(WrapperSystemTest, polledSubscriptionShouldCloseAllAllocatedResourcesAfte
437521
438522 EXPECT_EQ (registration_id, subscription->registrationId ());
439523}
524+
525+ TEST_F (WrapperSystemTest, nonPolledPendingAsyncDestinationsAreAutomaticallyFreedSubscription)
526+ {
527+ Context ctx;
528+ ctx.useConductorAgentInvoker (false );
529+
530+ std::shared_ptr<Aeron> aeron = Aeron::connect (ctx);
531+
532+ auto channel = " aeron:udp?control-mode=manual" ;
533+ auto dest1Uri = " aeron:udp?endpoint=localhost:4554" ;
534+ auto dest2Uri = " aeron:udp?endpoint=localhost:7777" ;
535+ auto dest3Uri = " aeron:udp?endpoint=localhost:10000" ;
536+ int stream_id = 1000 ;
537+
538+ int64_t registration_id = aeron->addSubscription (channel, stream_id);
539+ WAIT_FOR_NON_NULL (subscription, aeron->findSubscription (registration_id));
540+
541+ int64_t addDest1RegistrationId = subscription->addDestination (dest1Uri);
542+ auto addDest3Async = subscription->addDestinationAsync (dest3Uri);
543+ int64_t addDest2RegistrationId = subscription->addDestination (dest2Uri);
544+
545+ auto removeDest1Async = subscription->removeDestinationAsync (dest1Uri);
546+ int64_t removeDest2RegistrationId = subscription->removeDestination (dest2Uri);
547+ int64_t removeDest3RegistrationId = subscription->removeDestination (dest3Uri);
548+
549+ EXPECT_GT (removeDest2RegistrationId, addDest1RegistrationId);
550+ EXPECT_GT (removeDest3RegistrationId, removeDest2RegistrationId);
551+
552+ WAIT_FOR (subscription->findDestinationResponse (addDest2RegistrationId));
553+ WAIT_FOR (subscription->findDestinationResponse (removeDest3RegistrationId));
554+
555+ // safe to delete after commands were processed by the client conductor thread
556+ aeron_async_cmd_free (addDest3Async);
557+ aeron_async_cmd_free (removeDest1Async);
558+ }
559+
560+ TEST_F (WrapperSystemTest, nonPolledPendingAsyncDestinationsAreAutomaticallyFreedPublication)
561+ {
562+ Context ctx;
563+ ctx.useConductorAgentInvoker (false );
564+
565+ std::shared_ptr<Aeron> aeron = Aeron::connect (ctx);
566+
567+ auto channel = " aeron:udp?control-mode=manual" ;
568+ auto dest1Uri = " aeron:udp?endpoint=localhost:4554" ;
569+ auto dest2Uri = " aeron:udp?endpoint=localhost:7777" ;
570+ auto dest3Uri = " aeron:udp?endpoint=localhost:10000" ;
571+ int stream_id = 1000 ;
572+
573+ int64_t registration_id = aeron->addPublication (channel, stream_id);
574+ WAIT_FOR_NON_NULL (publication, aeron->findPublication (registration_id));
575+
576+ int64_t addDest1RegistrationId = publication->addDestination (dest1Uri);
577+ auto addDest3Async = publication->addDestinationAsync (dest3Uri);
578+ int64_t addDest2RegistrationId = publication->addDestination (dest2Uri);
579+
580+ auto removeDest1Async = publication->removeDestinationAsync (dest1Uri);
581+ int64_t removeDest2RegistrationId = publication->removeDestination (dest2Uri);
582+ int64_t removeDest3RegistrationId = publication->removeDestination (dest3Uri);
583+
584+ EXPECT_GT (removeDest2RegistrationId, addDest1RegistrationId);
585+ EXPECT_GT (removeDest3RegistrationId, removeDest2RegistrationId);
586+
587+ WAIT_FOR (publication->findDestinationResponse (addDest2RegistrationId));
588+ WAIT_FOR (publication->findDestinationResponse (removeDest3RegistrationId));
589+
590+ // safe to delete after commands were processed by the client conductor thread
591+ aeron_async_cmd_free (addDest3Async);
592+ aeron_async_cmd_free (removeDest1Async);
593+ }
594+
595+ TEST_F (WrapperSystemTest, nonPolledPendingAsyncDestinationsAreAutomaticallyFreedExclusivePublication)
596+ {
597+ Context ctx;
598+ ctx.useConductorAgentInvoker (false );
599+
600+ std::shared_ptr<Aeron> aeron = Aeron::connect (ctx);
601+
602+ auto channel = " aeron:udp?control-mode=manual" ;
603+ auto dest1Uri = " aeron:udp?endpoint=localhost:4554" ;
604+ auto dest2Uri = " aeron:udp?endpoint=localhost:7777" ;
605+ auto dest3Uri = " aeron:udp?endpoint=localhost:10000" ;
606+ int stream_id = 1000 ;
607+
608+ int64_t registration_id = aeron->addExclusivePublication (channel, stream_id);
609+ WAIT_FOR_NON_NULL (publication, aeron->findExclusivePublication (registration_id));
610+
611+ int64_t addDest1RegistrationId = publication->addDestination (dest1Uri);
612+ auto addDest3Async = publication->addDestinationAsync (dest3Uri);
613+ int64_t addDest2RegistrationId = publication->addDestination (dest2Uri);
614+
615+ auto removeDest1Async = publication->removeDestinationAsync (dest1Uri);
616+ int64_t removeDest2RegistrationId = publication->removeDestination (dest2Uri);
617+ int64_t removeDest3RegistrationId = publication->removeDestination (dest3Uri);
618+
619+ EXPECT_GT (removeDest2RegistrationId, addDest1RegistrationId);
620+ EXPECT_GT (removeDest3RegistrationId, removeDest2RegistrationId);
621+
622+ WAIT_FOR (publication->findDestinationResponse (addDest2RegistrationId));
623+ WAIT_FOR (publication->findDestinationResponse (removeDest3RegistrationId));
624+
625+ // safe to delete after commands were processed by the client conductor thread
626+ aeron_async_cmd_free (addDest3Async);
627+ aeron_async_cmd_free (removeDest1Async);
628+ }
0 commit comments