12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811 |
- /*
- * Copyright (c) Contributors to the Open 3D Engine Project.
- * For complete copyright and license terms please see the LICENSE at the root of this distribution.
- *
- * SPDX-License-Identifier: Apache-2.0 OR MIT
- *
- */
- #include <AzCore/EBus/EBus.h>
- #include <AzCore/EBus/Results.h>
- #include <AzCore/std/sort.h>
- #include <AzCore/std/chrono/chrono.h>
- #include <AzCore/std/parallel/mutex.h>
- #include <AzCore/std/parallel/thread.h>
- #include <AzCore/std/smart_ptr/unique_ptr.h>
- #include <AzCore/Jobs/JobManager.h>
- #include <AzCore/Jobs/JobContext.h>
- #include <AzCore/Jobs/JobCompletion.h>
- #include <AzCore/Jobs/JobFunction.h>
- #include <AzCore/Math/Random.h>
- #include <AzCore/UnitTest/TestTypes.h>
- #include <Tests/AZTestShared/Utils/Utils.h>
- #include <gtest/gtest.h>
- // For GetTypeName<T>()
- #include <gtest/internal/gtest-type-util.h>
- using namespace AZ;
- // TestBus implementation details
- namespace BusImplementation
- {
- // Interface for the benchmark bus
- class Interface
- {
- public:
- virtual ~Interface() = default;
- virtual int OnEvent() = 0;
- virtual void OnWait() = 0;
- virtual void Release() = 0;
- virtual bool Compare(const Interface* other) const = 0;
- };
- // Traits for the benchmark bus
- template <AZ::EBusAddressPolicy addressPolicy, AZ::EBusHandlerPolicy handlerPolicy, bool locklessDispatch = false>
- class Traits
- : public AZ::EBusTraits
- {
- public:
- static const AZ::EBusAddressPolicy AddressPolicy = addressPolicy;
- static const AZ::EBusHandlerPolicy HandlerPolicy = handlerPolicy;
- static const bool LocklessDispatch = locklessDispatch;
- // Allow queuing
- static const bool EnableEventQueue = true;
- // Force locking
- using MutexType = AZStd::recursive_mutex;
- // Only specialize BusIdType if not single address
- using BusIdType = AZStd::conditional_t<AddressPolicy == AZ::EBusAddressPolicy::Single, AZ::NullBusId, int>;
- // Only specialize BusIdOrderCompare if addresses are multiple and ordered
- using BusIdOrderCompare = AZStd::conditional_t<AddressPolicy != EBusAddressPolicy::ByIdAndOrdered, AZ::NullBusIdCompare, AZStd::less<int>>;
- };
- template <typename Bus>
- class HandlerCommon
- : public Bus::Handler
- {
- public:
- AZ_CLASS_ALLOCATOR(HandlerCommon, AZ::SystemAllocator);
- unsigned int m_eventCalls = 0;
- unsigned int m_expectedOrder = 0;
- unsigned int m_executedOrder = 0;
- HandlerCommon()
- {
- AZ::BetterPseudoRandom random;
- random.GetRandom(m_expectedOrder);
- }
- HandlerCommon(uint32_t handlerOrder)
- {
- m_expectedOrder = handlerOrder;
- }
- ~HandlerCommon() override
- {
- Bus::Handler::BusDisconnect();
- }
- bool Compare(const Interface* other) const override
- {
- return m_expectedOrder < reinterpret_cast<const HandlerCommon*>(other)->m_expectedOrder;
- }
- int OnEvent() override
- {
- ++m_eventCalls;
- m_executedOrder = s_nextExecution++;
- return 0;
- }
- void OnWait() override
- {
- AZStd::this_thread::yield();
- }
- void Release() override
- {
- delete this;
- }
- private:
- static int s_nextExecution;
- };
- template <typename Bus>
- int HandlerCommon<Bus>::s_nextExecution = 0;
- template <typename Bus>
- class MultiHandlerCommon
- : public Bus::MultiHandler
- {
- public:
- AZ_CLASS_ALLOCATOR(MultiHandlerCommon, AZ::SystemAllocator);
- MultiHandlerCommon() = default;
- ~MultiHandlerCommon() override
- {
- Bus::MultiHandler::BusDisconnect();
- }
- int OnEvent() override
- {
- ++m_eventCalls;
- return 0;
- }
- void OnWait() override
- {
- AZStd::this_thread::yield();
- }
- void Release() override
- {
- delete this;
- }
- bool Compare(const Interface* other) const override
- {
- return m_expectedOrder < static_cast<const MultiHandlerCommon*>(other)->m_expectedOrder;
- }
- private:
- uint32_t m_eventCalls{};
- uint32_t m_expectedOrder{};
- };
- class InterfaceWithMutex
- : public AZ::EBusTraits
- {
- public:
- static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
- static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
- using BusIdType = uint32_t;
- // Setting the MutexType to a value other than NullMutex
- // signals to the EBus system that the Ebus is able to be used in multiple threads
- // and therefore the EBus must be disconnected prior to the EBus internal handler destructor being invoked
- using MutexType = AZStd::recursive_mutex;
- virtual void OnEvent() = 0;
- };
- using InterfaceWithMutexBus = AZ::EBus<InterfaceWithMutex>;
- }
- class MutexBusHandler
- : public BusImplementation::InterfaceWithMutexBus::Handler
- {
- void OnEvent() override
- {
- ++m_eventCalls;
- }
- private:
- uint32_t m_eventCalls{};
- };
- // Definition of the benchmark bus, depending on supplied policies
- template <AZ::EBusAddressPolicy addressPolicy, AZ::EBusHandlerPolicy handlerPolicy, bool locklessDispatch = false>
- using TestBus = AZ::EBus<BusImplementation::Interface, BusImplementation::Traits<addressPolicy, handlerPolicy, locklessDispatch>>;
- #define EBUS_TEST_ALIAS(BusType, AddressPolicy, HandlerPolicy) \
- using BusType = TestBus<AZ::EBusAddressPolicy::AddressPolicy, AZ::EBusHandlerPolicy::HandlerPolicy>; \
- namespace testing { namespace internal { template<> std::string GetTypeName<BusType>() { return #BusType; } } }
- // Predefined benchmark bus instantiations
- // Single
- EBUS_TEST_ALIAS(OneToOne, Single, Single)
- EBUS_TEST_ALIAS(OneToMany, Single, Multiple)
- EBUS_TEST_ALIAS(OneToManyOrdered, Single, MultipleAndOrdered)
- // ById
- EBUS_TEST_ALIAS(ManyToOne, ById, Single)
- EBUS_TEST_ALIAS(ManyToMany, ById, Multiple)
- EBUS_TEST_ALIAS(ManyToManyOrdered, ById, MultipleAndOrdered)
- // ByIdAndOrdered
- EBUS_TEST_ALIAS(ManyOrderedToOne, ByIdAndOrdered, Single)
- EBUS_TEST_ALIAS(ManyOrderedToMany, ByIdAndOrdered, Multiple)
- EBUS_TEST_ALIAS(ManyOrderedToManyOrdered, ByIdAndOrdered, MultipleAndOrdered)
- // Handler for multi-address buses
- template <typename Bus, AZ::EBusAddressPolicy addressPolicy = Bus::Traits::AddressPolicy>
- class Handler
- : public BusImplementation::HandlerCommon<Bus>
- {
- public:
- AZ_CLASS_ALLOCATOR(Handler, AZ::SystemAllocator);
- Handler(int id, bool connectOnConstruct)
- {
- m_busId = id;
- if (connectOnConstruct)
- {
- EXPECT_FALSE(this->BusIsConnected());
- Connect();
- EXPECT_TRUE(this->BusIsConnected());
- }
- }
- Handler(int id, int handlerOrder, bool connectOnConstruct)
- : BusImplementation::HandlerCommon<Bus>(handlerOrder)
- {
- m_busId = id;
- if (connectOnConstruct)
- {
- EXPECT_FALSE(this->BusIsConnected());
- Connect();
- EXPECT_TRUE(this->BusIsConnected());
- }
- }
- ~Handler() override = default;
- // Helper function for connecting without specifying an id
- void Connect()
- {
- this->BusConnect(m_busId);
- }
- void Disconnect()
- {
- this->BusDisconnect(m_busId);
- }
- int OnEvent() override
- {
- BusImplementation::HandlerCommon<Bus>::OnEvent();
- return 1;
- }
- private:
- int m_busId = 0;
- };
- // Special handler for single address buses
- template <typename Bus>
- class Handler<Bus, AZ::EBusAddressPolicy::Single>
- : public BusImplementation::HandlerCommon<Bus>
- {
- public:
- AZ_CLASS_ALLOCATOR(Handler, AZ::SystemAllocator);
- Handler(int, bool connectOnConstruct)
- {
- if (connectOnConstruct)
- {
- EXPECT_FALSE(this->BusIsConnected());
- Connect();
- EXPECT_TRUE(this->BusIsConnected());
- }
- }
- Handler(int, int handlerOrder, bool connectOnConstruct)
- : BusImplementation::HandlerCommon<Bus>(handlerOrder)
- {
- if (connectOnConstruct)
- {
- EXPECT_FALSE(this->BusIsConnected());
- Connect();
- EXPECT_TRUE(this->BusIsConnected());
- }
- }
- // Helper function for connecting without specifying an id
- void Connect()
- {
- this->BusConnect();
- }
- void Disconnect()
- {
- this->BusDisconnect();
- }
- int OnEvent() override
- {
- BusImplementation::HandlerCommon<Bus>::OnEvent();
- return 2;
- }
- };
- // Handler for multi-address buses
- template <typename Bus>
- class MultiHandlerById
- : public BusImplementation::MultiHandlerCommon<Bus>
- {
- public:
- AZ_CLASS_ALLOCATOR(MultiHandlerById, AZ::SystemAllocator);
- MultiHandlerById(std::initializer_list<int> busIdList)
- {
- // We will bind at construction time to the bus. Disconnect is automatic when the object is
- // destroyed or we can call BusDisconnect()
- EXPECT_FALSE(this->BusIsConnected());
- Connect(busIdList);
- EXPECT_TRUE(this->BusIsConnected());
- }
- // Helper function for connecting on multiple ids
- void Connect(std::initializer_list<int> busIdList)
- {
- for (int busId : busIdList)
- {
- this->BusConnect(busId);
- }
- }
- void Disconnect(std::initializer_list<int> busIdList)
- {
- for (int busId : busIdList)
- {
- this->BusDisconnect(busId);
- }
- }
- int OnEvent() override
- {
- return BusImplementation::MultiHandlerCommon<Bus>::OnEvent();
- }
- };
- namespace UnitTest
- {
- using BusTypesId = ::testing::Types<
- ManyToOne, ManyToMany, ManyToManyOrdered,
- ManyOrderedToOne, ManyOrderedToMany, ManyOrderedToManyOrdered>;
- using BusTypesAll = ::testing::Types<
- OneToOne, OneToMany, OneToManyOrdered,
- ManyToOne, ManyToMany, ManyToManyOrdered,
- ManyOrderedToOne, ManyOrderedToMany, ManyOrderedToManyOrdered>;
- template <typename Bus>
- class EBusTestAll
- : public LeakDetectionFixture
- {
- public:
- using BusHandler = Handler<Bus>;
- using BusMultiHandlerById = MultiHandlerById<Bus>;
- EBusTestAll()
- {
- Bus::GetOrCreateContext();
- }
- void TearDown() override
- {
- DestroyHandlers();
- LeakDetectionFixture::TearDown();
- }
- //////////////////////////////////////////////////////////////////////////
- // Handler Helpers
- // Create an appropriate number of handlers for testing
- void CreateHandlers()
- {
- int numAddresses = HasMultipleAddresses() ? 3 : 1;
- int numHandlersPerAddress = HasMultipleHandlersPerAddress() ? 3 : 1;
- constexpr bool connectOnConstruct{ true };
- for (int address = 0; address < numAddresses; ++address)
- {
- for (int handler = 0; handler < numHandlersPerAddress; ++handler)
- {
- m_handlers[address].emplace_back(aznew BusHandler(address, connectOnConstruct));
- ++m_numHandlers;
- }
- }
- ValidateCalls(0);
- }
- // Gets the total number of handlers active
- int GetNumHandlers()
- {
- return m_numHandlers;
- }
- // Clears the handlers list without deleting them (useful for Release tests)
- void ClearHandlers()
- {
- m_handlers.clear();
- m_handlers.rehash(0);
- m_numHandlers = 0;
- }
- // Destroy all handlers
- void DestroyHandlers()
- {
- for (const auto& handlerPair : m_handlers)
- {
- for (BusHandler* handler : handlerPair.second)
- {
- delete handler;
- }
- }
- ClearHandlers();
- EXPECT_FALSE(Bus::HasHandlers());
- }
- // Ensure that all active handlers have the expected call count, in the correct order
- // This should only be called after Broadcast()
- void ValidateCalls(int expected, bool isForward = true)
- {
- for (const auto& handlerPair : m_handlers)
- {
- ValidateCalls(expected, handlerPair.first, isForward);
- }
- // Validate address execution order
- if (AddressesAreOrdered())
- {
- // Collect the first handler from each address
- using PairType = AZStd::pair<int, BusHandler*>;
- AZStd::vector<PairType> sortedHandlers;
- for (const auto& handlerPair : m_handlers)
- {
- PairType pair(handlerPair.first, handlerPair.second.front());
- auto insertPos = AZStd::lower_bound(
- sortedHandlers.begin(), sortedHandlers.end(),
- pair,
- [](const PairType& lhs, const PairType& rhs)
- {
- return lhs.first < rhs.first;
- }
- );
- sortedHandlers.emplace(insertPos, pair);
- }
- // Iterate over the list, and validate that they were called in the correct order
- unsigned int lastExecuted = 0;
- for (const PairType& pair : sortedHandlers)
- {
- if (lastExecuted > 0)
- {
- if (isForward)
- {
- EXPECT_LT(lastExecuted, pair.second->m_executedOrder);
- }
- else
- {
- EXPECT_GT(lastExecuted, pair.second->m_executedOrder);
- }
- }
- lastExecuted = pair.second->m_executedOrder;
- }
- }
- }
- // Ensure that all active handlers have the expected call count, and were called in the correct order
- void ValidateCalls(int expected, int id, bool isForward = true)
- {
- auto& handlers = m_handlers[id];
- for (BusHandler* handler : handlers)
- {
- EXPECT_EQ(expected, handler->m_eventCalls);
- }
- // Validate handler execution order
- if (HandlersAreOrdered())
- {
- // Sort the handlers the same way we expect the bus to sort them
- auto sortedHandlers = handlers;
- AZStd::sort(sortedHandlers.begin(), sortedHandlers.end(), AZStd::bind(&BusHandler::Compare, AZStd::placeholders::_1, AZStd::placeholders::_2));
- // Iterate over the list, and validate that they were called in the correct order
- unsigned int lastExecuted = 0;
- for (const BusHandler* handler : sortedHandlers)
- {
- if (lastExecuted > 0)
- {
- if (isForward)
- {
- EXPECT_LT(lastExecuted, handler->m_executedOrder);
- }
- else
- {
- EXPECT_GT(lastExecuted, handler->m_executedOrder);
- }
- }
- lastExecuted = handler->m_executedOrder;
- }
- }
- }
- //////////////////////////////////////////////////////////////////////////
- // Metadata helpers
- bool HasMultipleAddresses()
- {
- return Bus::HasId;
- }
- bool AddressesAreOrdered()
- {
- return Bus::Traits::AddressPolicy == EBusAddressPolicy::ByIdAndOrdered;
- }
- bool HasMultipleHandlersPerAddress()
- {
- return Bus::Traits::HandlerPolicy != EBusHandlerPolicy::Single;
- }
- bool HandlersAreOrdered()
- {
- return Bus::Traits::HandlerPolicy == EBusHandlerPolicy::MultipleAndOrdered;
- }
- protected:
- AZStd::unordered_map<int, AZStd::vector<BusHandler*>> m_handlers;
- int m_numHandlers = 0;
- };
- TYPED_TEST_SUITE(EBusTestAll, BusTypesAll);
- template <typename Bus>
- class EBusTestId
- : public EBusTestAll<Bus>
- {
- };
- TYPED_TEST_SUITE(EBusTestId, BusTypesId);
- using BusTypesIdMultiHandlers = ::testing::Types<
- ManyToMany, ManyToManyOrdered,
- ManyOrderedToMany, ManyOrderedToManyOrdered>;
- template <typename Bus>
- class EBusTestIdMultiHandlers
- : public EBusTestAll<Bus>
- {
- };
- TYPED_TEST_SUITE(EBusTestIdMultiHandlers, BusTypesIdMultiHandlers);
- //////////////////////////////////////////////////////////////////////////
- // Non-event functions
- TYPED_TEST(EBusTestAll, ConnectDisconnect)
- {
- using Bus = TypeParam;
- using Handler = typename EBusTestAll<Bus>::BusHandler;
- constexpr bool connectOnConstruct{ true };
- Handler meh(0, connectOnConstruct);
- EXPECT_EQ(0, meh.m_eventCalls);
- EXPECT_TRUE(Bus::HasHandlers());
- Bus::Broadcast(&Bus::Events::OnEvent);
- EXPECT_EQ(1, meh.m_eventCalls);
- EXPECT_TRUE(meh.BusIsConnected());
- meh.BusDisconnect(); // we disconnect from receiving events.
- EXPECT_FALSE(meh.BusIsConnected());
- EXPECT_FALSE(Bus::HasHandlers());
- // this signal will NOT trigger any calls.
- Bus::Broadcast(&Bus::Events::OnEvent);
- EXPECT_EQ(1, meh.m_eventCalls);
- }
- TYPED_TEST(EBusTestIdMultiHandlers, EnumerateHandlers_MultiHandler)
- {
- using Bus = TypeParam;
- using BusMultiHandlerById = typename EBusTestAll<Bus>::BusMultiHandlerById;
- BusMultiHandlerById sourceMultiHandler{ 0, 1, 2 };
- BusMultiHandlerById multiHandlerWithOverlappingIds{ 1, 3, 5 };
- // Test handlers' enumeration functionality
- Bus::EnumerateHandlers([](typename BusMultiHandlerById::Interface* interfaceInst) -> bool
- {
- interfaceInst->OnEvent();
- return true;
- });
- }
- TYPED_TEST(EBusTestId, FindFirstHandler)
- {
- using Bus = TypeParam;
- using Handler = typename EBusTestAll<Bus>::BusHandler;
- constexpr bool connectOnConstruct{ true };
- Handler meh0(0, connectOnConstruct); /// <-- Bind to bus 0
- Handler meh1(1, connectOnConstruct); /// <-- Bind to bus 1
- // Test handlers' enumeration functionality
- EXPECT_EQ(&meh0, Bus::FindFirstHandler(0));
- EXPECT_EQ(&meh1, Bus::FindFirstHandler(1));
- EXPECT_EQ(nullptr, Bus::FindFirstHandler(3));
- }
- //////////////////////////////////////////////////////////////////////////
- // Immediate calls
- TYPED_TEST(EBusTestAll, Broadcast)
- {
- using Bus = TypeParam;
- this->CreateHandlers();
- Bus::Broadcast(&Bus::Events::OnEvent);
- this->ValidateCalls(1);
- Bus::Broadcast(&Bus::Events::OnEvent);
- this->ValidateCalls(2);
- }
- TYPED_TEST(EBusTestAll, Broadcast_Release)
- {
- using Bus = TypeParam;
- this->CreateHandlers();
- EXPECT_EQ(this->GetNumHandlers(), Bus::GetTotalNumOfEventHandlers());
- Bus::Broadcast(&Bus::Events::Release);
- EXPECT_FALSE(Bus::HasHandlers());
- this->ClearHandlers();
- }
- TYPED_TEST(EBusTestAll, BroadcastReverse)
- {
- using Bus = TypeParam;
- this->CreateHandlers();
- Bus::BroadcastReverse(&Bus::Events::OnEvent);
- this->ValidateCalls(1, false);
- Bus::BroadcastReverse(&Bus::Events::OnEvent);
- this->ValidateCalls(2, false);
- }
- TYPED_TEST(EBusTestAll, BroadcastReverse_Release)
- {
- using Bus = TypeParam;
- this->CreateHandlers();
- EXPECT_EQ(this->GetNumHandlers(), Bus::GetTotalNumOfEventHandlers());
- Bus::BroadcastReverse(&Bus::Events::Release);
- EXPECT_FALSE(Bus::HasHandlers());
- this->ClearHandlers();
- }
- TYPED_TEST(EBusTestAll, BroadcastResult)
- {
- using Bus = TypeParam;
- this->CreateHandlers();
- int result = -1;
- Bus::BroadcastResult(result, &Bus::Events::OnEvent);
- EXPECT_LT(0, result);
- this->ValidateCalls(1);
- result = -1;
- Bus::BroadcastResult(result, &Bus::Events::OnEvent);
- EXPECT_LT(0, result);
- this->ValidateCalls(2);
- this->DestroyHandlers();
- result = -1;
- Bus::BroadcastResult(result, &Bus::Events::OnEvent);
- EXPECT_EQ(-1, result);
- }
- TYPED_TEST(EBusTestAll, BroadcastResultReverse)
- {
- using Bus = TypeParam;
- this->CreateHandlers();
- int result = -1;
- Bus::BroadcastResultReverse(result, &Bus::Events::OnEvent);
- EXPECT_LT(0, result);
- this->ValidateCalls(1, false);
- result = -1;
- Bus::BroadcastResultReverse(result, &Bus::Events::OnEvent);
- EXPECT_LT(0, result);
- this->ValidateCalls(2, false);
- this->DestroyHandlers();
- result = -1;
- Bus::BroadcastResultReverse(result, &Bus::Events::OnEvent);
- EXPECT_EQ(-1, result);
- }
- // Test sending events on an address
- TYPED_TEST(EBusTestId, Event)
- {
- using Bus = TypeParam;
- this->CreateHandlers();
- // Signal OnEvent event on bus 1
- Bus::Event(1, &Bus::Events::OnEvent);
- // Validate bus 1 has 2 calls, all others have 1
- this->ValidateCalls(1, 1);
- this->ValidateCalls(0, 2);
- this->ValidateCalls(0, 3);
- }
- // Test sending events on an address
- TYPED_TEST(EBusTestId, Event_Release)
- {
- using Bus = TypeParam;
- this->CreateHandlers();
- for (const auto& handlerPair : this->m_handlers)
- {
- Bus::Event(handlerPair.first, &Bus::Events::Release);
- }
- EXPECT_FALSE(Bus::HasHandlers());
- this->ClearHandlers();
- }
- // Test sending events on an address
- TYPED_TEST(EBusTestId, EventReverse)
- {
- using Bus = TypeParam;
- this->CreateHandlers();
- // Signal OnEvent event on bus 1
- Bus::EventReverse(1, &Bus::Events::OnEvent);
- // Validate bus 1 has 2 calls, all others have 1
- this->ValidateCalls(1, 1, false);
- this->ValidateCalls(0, 2, false);
- this->ValidateCalls(0, 3, false);
- }
- // Test sending events (that delete this) on an address, backwards
- TYPED_TEST(EBusTestId, EventReverse_Release)
- {
- using Bus = TypeParam;
- this->CreateHandlers();
- for (const auto& handlerPair : this->m_handlers)
- {
- Bus::EventReverse(handlerPair.first, &Bus::Events::Release);
- }
- EXPECT_FALSE(Bus::HasHandlers());
- this->ClearHandlers();
- }
- // Test sending events with results on an address
- TYPED_TEST(EBusTestId, EventResult)
- {
- using Bus = TypeParam;
- this->CreateHandlers();
- // Signal OnEvent event on bus 1
- int result = -1;
- Bus::EventResult(result, 1, &Bus::Events::OnEvent);
- EXPECT_LT(0, result);
- // Validate bus 1 has 2 calls, all others have 1
- this->ValidateCalls(1, 1);
- this->ValidateCalls(0, 2);
- this->ValidateCalls(0, 3);
- this->DestroyHandlers();
- result = -1;
- Bus::EventResult(result, 1, &Bus::Events::OnEvent);
- EXPECT_EQ(-1, result);
- }
- // Test sending events with results on an address
- TYPED_TEST(EBusTestId, EventResultReverse)
- {
- using Bus = TypeParam;
- this->CreateHandlers();
- // Signal OnEvent event on bus 1
- int result = -1;
- Bus::EventResultReverse(result, 1, &Bus::Events::OnEvent);
- EXPECT_LT(0, result);
- // Validate bus 1 has 2 calls, all others have 1
- this->ValidateCalls(1, 1, false);
- this->ValidateCalls(0, 2, false);
- this->ValidateCalls(0, 3, false);
- this->DestroyHandlers();
- result = -1;
- Bus::EventResultReverse(result, 1, &Bus::Events::OnEvent);
- EXPECT_EQ(-1, result);
- }
- // Test sending events on a bound bus ptr
- TYPED_TEST(EBusTestId, BindEvent)
- {
- using Bus = TypeParam;
- this->CreateHandlers();
- typename Bus::BusPtr ptr;
- Bus::Bind(ptr, 1);
- EXPECT_NE(nullptr, ptr);
- // Signal OnEvent event on bus 1
- Bus::Event(ptr, &Bus::Events::OnEvent);
- // Validate bus 1 has 2 calls, all others have 1
- this->ValidateCalls(1, 1);
- this->ValidateCalls(0, 2);
- this->ValidateCalls(0, 3);
- this->DestroyHandlers();
- // Validate that broadcasting/eventing after binding disconnecting all doesn't crash
- Bus::Broadcast(&Bus::Events::OnEvent);
- Bus::Event(ptr, &Bus::Events::OnEvent);
- Bus::Event(1, &Bus::Events::OnEvent);
- }
- // Test sending events (that delete this) on a bound bus ptr
- TYPED_TEST(EBusTestId, BindEvent_Release)
- {
- using Bus = TypeParam;
- this->CreateHandlers();
- typename Bus::BusPtr ptr;
- for (const auto& handlerPair : this->m_handlers)
- {
- Bus::Bind(ptr, handlerPair.first);
- EXPECT_NE(nullptr, ptr);
- Bus::Event(ptr, &Bus::Events::Release);
- }
- EXPECT_FALSE(Bus::HasHandlers());
- this->ClearHandlers();
- }
- // Test sending events on a bound bus ptr, backwards
- TYPED_TEST(EBusTestId, BindEventReverse)
- {
- using Bus = TypeParam;
- this->CreateHandlers();
- typename Bus::BusPtr ptr;
- Bus::Bind(ptr, 1);
- EXPECT_NE(nullptr, ptr);
- // Signal OnEvent event on bus 1
- Bus::EventReverse(ptr, &Bus::Events::OnEvent);
- // Validate bus 1 has 2 calls, all others have 1
- this->ValidateCalls(1, 1, false);
- this->ValidateCalls(0, 2, false);
- this->ValidateCalls(0, 3, false);
- this->DestroyHandlers();
- // Validate that broadcasting/eventing after binding disconnecting all doesn't crash
- Bus::BroadcastReverse(&Bus::Events::OnEvent);
- Bus::EventReverse(ptr, &Bus::Events::OnEvent);
- Bus::EventReverse(1, &Bus::Events::OnEvent);
- }
- // Test sending events (that delete this) on a bound bus ptr, backwards
- TYPED_TEST(EBusTestId, BindEventReverse_Release)
- {
- using Bus = TypeParam;
- this->CreateHandlers();
- typename Bus::BusPtr ptr;
- for (const auto& handlerPair : this->m_handlers)
- {
- Bus::Bind(ptr, handlerPair.first);
- EXPECT_NE(nullptr, ptr);
- Bus::EventReverse(ptr, &Bus::Events::Release);
- }
- EXPECT_FALSE(Bus::HasHandlers());
- this->ClearHandlers();
- }
- // Test sending events on a bound bus ptr
- TYPED_TEST(EBusTestId, BindEventResult)
- {
- using Bus = TypeParam;
- this->CreateHandlers();
- typename Bus::BusPtr ptr;
- Bus::Bind(ptr, 1);
- EXPECT_NE(nullptr, ptr);
- // Signal OnEvent event on bus 1
- int result = -1;
- Bus::EventResult(result, ptr, &Bus::Events::OnEvent);
- EXPECT_LT(0, result);
- // Validate bus 1 has 2 calls, all others have 1
- this->ValidateCalls(1, 1);
- this->ValidateCalls(0, 2);
- this->ValidateCalls(0, 3);
- this->DestroyHandlers();
- // Validate that broadcasting/eventing after binding disconnecting all doesn't crash
- Bus::Broadcast(&Bus::Events::OnEvent);
- Bus::Event(ptr, &Bus::Events::OnEvent);
- Bus::Event(1, &Bus::Events::OnEvent);
- }
- // Test sending events on a bound bus ptr, backwards
- TYPED_TEST(EBusTestId, BindEventResultReverse)
- {
- using Bus = TypeParam;
- this->CreateHandlers();
- typename Bus::BusPtr ptr;
- Bus::Bind(ptr, 1);
- EXPECT_NE(nullptr, ptr);
- // Signal OnEvent event on bus 1
- int result = -1;
- Bus::EventResultReverse(result, ptr, &Bus::Events::OnEvent);
- EXPECT_LT(0, result);
- // Validate bus 1 has 2 calls, all others have 1
- this->ValidateCalls(1, 1, false);
- this->ValidateCalls(0, 2, false);
- this->ValidateCalls(0, 3, false);
- this->DestroyHandlers();
- // Validate that broadcasting/eventing after binding disconnecting all doesn't crash
- Bus::BroadcastReverse(&Bus::Events::OnEvent);
- Bus::EventReverse(ptr, &Bus::Events::OnEvent);
- Bus::EventReverse(1, &Bus::Events::OnEvent);
- }
- //////////////////////////////////////////////////////////////////////////
- // Queued calls
- TYPED_TEST(EBusTestAll, QueueBroadcast)
- {
- using Bus = TypeParam;
- this->CreateHandlers();
- Bus::QueueBroadcast(&Bus::Events::OnEvent);
- this->ValidateCalls(0);
- Bus::ExecuteQueuedEvents();
- this->ValidateCalls(1);
- Bus::QueueBroadcast(&Bus::Events::OnEvent);
- this->ValidateCalls(1);
- Bus::ExecuteQueuedEvents();
- this->ValidateCalls(2);
- }
- TYPED_TEST(EBusTestAll, QueueBroadcast_Release)
- {
- using Bus = TypeParam;
- this->CreateHandlers();
- EXPECT_EQ(this->GetNumHandlers(), Bus::GetTotalNumOfEventHandlers());
- Bus::QueueBroadcast(&Bus::Events::Release);
- EXPECT_EQ(this->GetNumHandlers(), Bus::GetTotalNumOfEventHandlers());
- Bus::ExecuteQueuedEvents();
- EXPECT_FALSE(Bus::HasHandlers());
- this->ClearHandlers();
- }
- TYPED_TEST(EBusTestAll, QueueBroadcastReverse)
- {
- using Bus = TypeParam;
- this->CreateHandlers();
- Bus::QueueBroadcastReverse(&Bus::Events::OnEvent);
- this->ValidateCalls(0, false);
- Bus::ExecuteQueuedEvents();
- this->ValidateCalls(1, false);
- Bus::QueueBroadcastReverse(&Bus::Events::OnEvent);
- this->ValidateCalls(1, false);
- Bus::ExecuteQueuedEvents();
- this->ValidateCalls(2, false);
- }
- TYPED_TEST(EBusTestAll, QueueBroadcastReverse_Release)
- {
- using Bus = TypeParam;
- this->CreateHandlers();
- EXPECT_EQ(this->GetNumHandlers(), Bus::GetTotalNumOfEventHandlers());
- Bus::QueueBroadcastReverse(&Bus::Events::Release);
- EXPECT_EQ(this->GetNumHandlers(), Bus::GetTotalNumOfEventHandlers());
- Bus::ExecuteQueuedEvents();
- EXPECT_FALSE(Bus::HasHandlers());
- this->ClearHandlers();
- }
- // Test sending events on an address
- TYPED_TEST(EBusTestId, QueueEvent)
- {
- using Bus = TypeParam;
- this->CreateHandlers();
- // Signal OnEvent event on bus 1
- Bus::QueueEvent(1, &Bus::Events::OnEvent);
- this->ValidateCalls(0);
- Bus::ExecuteQueuedEvents();
- // Validate bus 1 has 1 calls, all others have 0
- this->ValidateCalls(1, 1);
- this->ValidateCalls(0, 2);
- this->ValidateCalls(0, 3);
- }
- // Test sending events on an address
- TYPED_TEST(EBusTestId, QueueEvent_Release)
- {
- using Bus = TypeParam;
- this->CreateHandlers();
- for (const auto& handlerPair : this->m_handlers)
- {
- Bus::QueueEvent(handlerPair.first, &Bus::Events::Release);
- }
- EXPECT_EQ(this->GetNumHandlers(), Bus::GetTotalNumOfEventHandlers());
- Bus::ExecuteQueuedEvents();
- EXPECT_FALSE(Bus::HasHandlers());
- this->ClearHandlers();
- }
- // Test sending events on an address
- TYPED_TEST(EBusTestId, QueueEventReverse)
- {
- using Bus = TypeParam;
- this->CreateHandlers();
- // Signal OnEvent event on bus 1
- Bus::QueueEventReverse(1, &Bus::Events::OnEvent);
- this->ValidateCalls(0);
- Bus::ExecuteQueuedEvents();
- // Validate bus 1 has 2 calls, all others have 1
- this->ValidateCalls(1, 1, false);
- this->ValidateCalls(0, 2, false);
- this->ValidateCalls(0, 3, false);
- }
- // Test sending events (that delete this) on an address, backwards
- TYPED_TEST(EBusTestId, QueueEventReverse_Release)
- {
- using Bus = TypeParam;
- this->CreateHandlers();
- for (const auto& handlerPair : this->m_handlers)
- {
- Bus::QueueEventReverse(handlerPair.first, &Bus::Events::Release);
- }
- EXPECT_EQ(this->GetNumHandlers(), Bus::GetTotalNumOfEventHandlers());
- Bus::ExecuteQueuedEvents();
- EXPECT_FALSE(Bus::HasHandlers());
- this->ClearHandlers();
- }
- // Test sending events on a bound bus ptr
- TYPED_TEST(EBusTestId, QueueBindEvent)
- {
- using Bus = TypeParam;
- this->CreateHandlers();
- typename Bus::BusPtr ptr;
- Bus::Bind(ptr, 1);
- EXPECT_NE(nullptr, ptr);
- // Signal OnEvent event on bus 1
- Bus::QueueEvent(ptr, &Bus::Events::OnEvent);
- this->ValidateCalls(0);
- Bus::ExecuteQueuedEvents();
- // Validate bus 1 has 2 calls, all others have 1
- this->ValidateCalls(1, 1);
- this->ValidateCalls(0, 2);
- this->ValidateCalls(0, 3);
- }
- // Test sending events (that delete this) on a bound bus ptr
- TYPED_TEST(EBusTestId, QueueBindEvent_Release)
- {
- using Bus = TypeParam;
- this->CreateHandlers();
- typename Bus::BusPtr ptr;
- for (const auto& handlerPair : this->m_handlers)
- {
- Bus::Bind(ptr, handlerPair.first);
- EXPECT_NE(nullptr, ptr);
- Bus::QueueEvent(ptr, &Bus::Events::Release);
- }
- EXPECT_EQ(this->GetNumHandlers(), Bus::GetTotalNumOfEventHandlers());
- Bus::ExecuteQueuedEvents();
- EXPECT_FALSE(Bus::HasHandlers());
- this->ClearHandlers();
- }
- // Test sending events on a bound bus ptr, backwards
- TYPED_TEST(EBusTestId, QueueBindEventReverse)
- {
- using Bus = TypeParam;
- this->CreateHandlers();
- typename Bus::BusPtr ptr;
- Bus::Bind(ptr, 1);
- EXPECT_NE(nullptr, ptr);
- // Signal OnEvent event on bus 1
- Bus::QueueEventReverse(ptr, &Bus::Events::OnEvent);
- this->ValidateCalls(0);
- Bus::ExecuteQueuedEvents();
- // Validate bus 1 has 2 calls, all others have 1
- this->ValidateCalls(1, 1, false);
- this->ValidateCalls(0, 2, false);
- this->ValidateCalls(0, 3, false);
- }
- // Test sending events (that delete this) on a bound bus ptr, backwards
- TYPED_TEST(EBusTestId, QueueBindEventReverse_Release)
- {
- using Bus = TypeParam;
- this->CreateHandlers();
- typename Bus::BusPtr ptr;
- for (const auto& handlerPair : this->m_handlers)
- {
- Bus::Bind(ptr, handlerPair.first);
- EXPECT_NE(nullptr, ptr);
- Bus::QueueEventReverse(ptr, &Bus::Events::Release);
- }
- EXPECT_EQ(this->GetNumHandlers(), Bus::GetTotalNumOfEventHandlers());
- Bus::ExecuteQueuedEvents();
- EXPECT_FALSE(Bus::HasHandlers());
- this->ClearHandlers();
- }
- //////////////////////////////////////////////////////////////////////////
- // GetCurrentBusId calls
- TYPED_TEST(EBusTestId, Event_GetCurrentBusId_ReturnsNonNullptr)
- {
- using Bus = TypeParam;
- this->CreateHandlers();
- auto busCallback = [](typename Bus::InterfaceType*)
- {
- const int* busId = Bus::GetCurrentBusId();
- ASSERT_NE(nullptr, busId);
- EXPECT_EQ(1, *busId);
- };
- Bus::Event(1, busCallback);
- this->DestroyHandlers();
- }
- TYPED_TEST(EBusTestId, EventResult_GetCurrentBusId_ReturnsNonNullptr)
- {
- using Bus = TypeParam;
- this->CreateHandlers();
- auto busCallback = [](typename Bus::InterfaceType*) -> const char*
- {
- const int* busId = Bus::GetCurrentBusId();
- EXPECT_NE(nullptr, busId);
- if (busId)
- {
- EXPECT_EQ(1, *busId);
- }
- return "BusType";
- };
- const char* result{};
- Bus::EventResult(result, 1, busCallback);
- EXPECT_STREQ("BusType", result);
- this->DestroyHandlers();
- }
- TYPED_TEST(EBusTestId, EventReverse_GetCurrentBusId_ReturnsNonNullptr)
- {
- using Bus = TypeParam;
- this->CreateHandlers();
- auto busCallback = [](typename Bus::InterfaceType*)
- {
- const int* busId = Bus::GetCurrentBusId();
- ASSERT_NE(nullptr, busId);
- EXPECT_EQ(1, *busId);
- };
- Bus::EventReverse(1, busCallback);
- this->DestroyHandlers();
- }
- TYPED_TEST(EBusTestId, EventResultReverse_GetCurrentBusId_ReturnsNonNullptr)
- {
- using Bus = TypeParam;
- this->CreateHandlers();
- auto busCallback = [](typename Bus::InterfaceType*)
- {
- const int* busId = Bus::GetCurrentBusId();
- EXPECT_NE(nullptr, busId);
- if (busId)
- {
- EXPECT_EQ(1, *busId);
- }
- return 7;
- };
- int32_t result{};
- Bus::EventResultReverse(result, 1, busCallback);
- EXPECT_EQ(7, result);
- this->DestroyHandlers();
- }
- TYPED_TEST(EBusTestId, BindEvent_GetCurrentBusId_ReturnsNonNullptr)
- {
- using Bus = TypeParam;
- this->CreateHandlers();
- typename Bus::BusPtr ptr;
- auto busCallback = [](typename Bus::InterfaceType*)
- {
- EXPECT_NE(nullptr, Bus::GetCurrentBusId());
- };
- for (const auto& handlerPair : this->m_handlers)
- {
- Bus::Bind(ptr, handlerPair.first);
- EXPECT_NE(nullptr, ptr);
- Bus::Event(ptr, busCallback);
- }
- this->DestroyHandlers();
- }
- TYPED_TEST(EBusTestId, BindEventResult_GetCurrentBusId_ReturnsNonNullptr)
- {
- using Bus = TypeParam;
- this->CreateHandlers();
- typename Bus::BusPtr ptr;
- auto busCallback = [](typename Bus::InterfaceType*)
- {
- EXPECT_NE(nullptr, Bus::GetCurrentBusId());
- return true;
- };
- for (const auto& handlerPair : this->m_handlers)
- {
- Bus::Bind(ptr, handlerPair.first);
- EXPECT_NE(nullptr, ptr);
- bool result{};
- Bus::EventResult(result, ptr, busCallback);
- EXPECT_TRUE(result);
- }
- this->DestroyHandlers();
- }
- TYPED_TEST(EBusTestId, BindEventReverse_GetCurrentBusId_ReturnsNonNullptr)
- {
- using Bus = TypeParam;
- this->CreateHandlers();
- typename Bus::BusPtr ptr;
- auto busCallback = [](typename Bus::InterfaceType*)
- {
- EXPECT_NE(nullptr, Bus::GetCurrentBusId());
- };
- for (const auto& handlerPair : this->m_handlers)
- {
- Bus::Bind(ptr, handlerPair.first);
- EXPECT_NE(nullptr, ptr);
- Bus::EventReverse(ptr, busCallback);
- }
- this->DestroyHandlers();
- }
- TYPED_TEST(EBusTestId, BindEventResultReverse_GetCurrentBusId_ReturnsNonNullptr)
- {
- using Bus = TypeParam;
- this->CreateHandlers();
- typename Bus::BusPtr ptr;
- auto busCallback = [](typename Bus::InterfaceType*)
- {
- EXPECT_NE(nullptr, Bus::GetCurrentBusId());
- return true;
- };
- for (const auto& handlerPair : this->m_handlers)
- {
- Bus::Bind(ptr, handlerPair.first);
- EXPECT_NE(nullptr, ptr);
- bool result{};
- Bus::EventResultReverse(result, ptr, busCallback);
- EXPECT_TRUE(result);
- }
- this->DestroyHandlers();
- }
- TYPED_TEST(EBusTestId, Broadcast_GetCurrentBusId_ReturnsNonNullptr)
- {
- using Bus = TypeParam;
- this->CreateHandlers();
- auto busCallback = [](typename Bus::InterfaceType*)
- {
- const int* busId = Bus::GetCurrentBusId();
- EXPECT_NE(nullptr, busId);
- };
- Bus::Broadcast(busCallback);
- this->DestroyHandlers();
- }
- TYPED_TEST(EBusTestId, BroadcastResult_GetCurrentBusId_ReturnsNonNullptr)
- {
- using Bus = TypeParam;
- this->CreateHandlers();
- auto busCallback = [](typename Bus::InterfaceType*)
- {
- const int* busId = Bus::GetCurrentBusId();
- EXPECT_NE(nullptr, busId);
- return 16.0f;
- };
- float result{};
- Bus::BroadcastResult(result, busCallback);
- EXPECT_FLOAT_EQ(16.0f, result);
- this->DestroyHandlers();
- }
- TYPED_TEST(EBusTestId, BroadcastReverse_GetCurrentBusId_ReturnsNonNullptr)
- {
- using Bus = TypeParam;
- this->CreateHandlers();
- auto busCallback = [](typename Bus::InterfaceType*)
- {
- const int* busId = Bus::GetCurrentBusId();
- EXPECT_NE(nullptr, busId);
- };
- Bus::BroadcastReverse(busCallback);
- this->DestroyHandlers();
- }
- TYPED_TEST(EBusTestId, BroadcastResultReverse_GetCurrentBusId_ReturnsNonNullptr)
- {
- using Bus = TypeParam;
- this->CreateHandlers();
- auto busCallback = [](typename Bus::InterfaceType*)
- {
- const int* busId = Bus::GetCurrentBusId();
- EXPECT_NE(nullptr, busId);
- return 8.0;
- };
- double result{};
- Bus::BroadcastResultReverse(result, busCallback);
- EXPECT_DOUBLE_EQ(8.0, result);
- this->DestroyHandlers();
- }
- TYPED_TEST(EBusTestId, EnumerateHandlers_GetCurrentBusId_ReturnsNonNullptr)
- {
- using Bus = TypeParam;
- this->CreateHandlers();
- auto busCallback = [](typename Bus::InterfaceType*)
- {
- const int* busId = Bus::GetCurrentBusId();
- EXPECT_NE(nullptr, busId);
- return true;
- };
- Bus::EnumerateHandlers(busCallback);
- this->DestroyHandlers();
- }
- TYPED_TEST(EBusTestId, EnumerateHandlersId_GetCurrentBusId_ReturnsNonNullptr)
- {
- using Bus = TypeParam;
- this->CreateHandlers();
- auto busCallback = [](typename Bus::InterfaceType*)
- {
- const int* busId = Bus::GetCurrentBusId();
- EXPECT_NE(nullptr, busId);
- if (busId)
- {
- EXPECT_EQ(1, *busId);
- }
- return true;
- };
- Bus::EnumerateHandlersId(1, busCallback);
- this->DestroyHandlers();
- }
- TYPED_TEST(EBusTestId, EnumerateHandlersPtr_GetCurrentBusId_ReturnsNonNullptr)
- {
- using Bus = TypeParam;
- this->CreateHandlers();
- auto busCallback = [](typename Bus::InterfaceType*)
- {
- const int* busId = Bus::GetCurrentBusId();
- EXPECT_NE(nullptr, busId);
- if(busId)
- {
- EXPECT_EQ(1, *busId);
- }
- return true;
- };
- typename Bus::BusPtr busPtr;
- Bus::Bind(busPtr, 1);
- EXPECT_NE(nullptr, busPtr);
- Bus::EnumerateHandlersPtr(busPtr, busCallback);
- this->DestroyHandlers();
- }
- class EBus
- : public LeakDetectionFixture
- {};
- TEST_F(EBus, DISABLED_CopyConstructorOfEBusHandlerDoesNotAssertInInternalDestructorOfHandler)
- {
- AZ_TEST_START_TRACE_SUPPRESSION;
- {
- MutexBusHandler sourceHandler;
- // Connect source handler to InterfaceWithMutexBus and then copy it over to a new instance
- // Afterwards disconnect the source handler from the InterfaceWithMutexBus
- sourceHandler.BusConnect(1);
- MutexBusHandler targetHandler(sourceHandler);
- sourceHandler.BusDisconnect();
- }
- AZ_TEST_STOP_TRACE_SUPPRESSION(0);
- }
- TEST_F(EBus, DISABLED_CopyAssignmentOfEBusHandlerDoesNotAssertInInternalDestructorOfHandler)
- {
- AZ_TEST_START_TRACE_SUPPRESSION;
- {
- MutexBusHandler sourceHandler;
- MutexBusHandler targetHandler;
- // Connect source handler to InterfaceWithMutexBus and then copy it over to a new instance
- // Afterwards disconnect the source handler from the InterfaceWithMutexBus
- sourceHandler.BusConnect(1);
- targetHandler = sourceHandler;
- sourceHandler.BusDisconnect();
- }
- AZ_TEST_STOP_TRACE_SUPPRESSION(0);
- }
- TEST_F(EBus, CopyConstructorOfEBusHandler_CopyFromConnected_DoesNotAssert)
- {
- AZ_TEST_START_TRACE_SUPPRESSION;
- {
- MutexBusHandler sourceHandler;
- // Connect source handler to InterfaceWithMutexBus and then copy it over to a new instance
- // Afterwards disconnect the source handler from the InterfaceWithMutexBus
- sourceHandler.BusConnect(1);
- // Copy behavior which connects to source handler's bus if
- // Source handler was connected may be unexpected but it should not assert
- MutexBusHandler targetHandler(sourceHandler);
- sourceHandler.BusDisconnect();
- targetHandler.BusDisconnect();
- }
- AZ_TEST_STOP_TRACE_SUPPRESSION(0);
- }
- TEST_F(EBus, CopyOperatorOfEBusHandler_CopyToConnected_DoesNotAssert)
- {
- AZ_TEST_START_TRACE_SUPPRESSION;
- {
- MutexBusHandler targetHandler;
- // Connect source handler to InterfaceWithMutexBus and then copy it over to a new instance
- // Afterwards disconnect the source handler from the InterfaceWithMutexBus
- targetHandler.BusConnect(1);
- // Copy behavior which connects to source handler's bus if
- // Source handler was connected may be unexpected but it should not assert
- MutexBusHandler sourceHandler;
- targetHandler = sourceHandler;
- sourceHandler.BusDisconnect();
- targetHandler.BusDisconnect();
- }
- AZ_TEST_STOP_TRACE_SUPPRESSION(0);
- }
- /**
- * Tests multi-bus handler (a singe ebus instance that can connect to multiple buses)
- */
- namespace MultBusHandler
- {
- /**
- * Create event that allows MULTI buses. By default we already allow multiple handlers per bus.
- */
- class MyEventGroup
- : public AZ::EBusTraits
- {
- public:
- //////////////////////////////////////////////////////////////////////////
- // EBus interface settings
- static const EBusHandlerPolicy HandlerPolicy = EBusHandlerPolicy::Multiple;
- static const EBusAddressPolicy AddressPolicy = EBusAddressPolicy::ById;
- typedef int BusIdType;
- //////////////////////////////////////////////////////////////////////////
- virtual ~MyEventGroup() {}
- //////////////////////////////////////////////////////////////////////////
- // Define the events in this event group!
- virtual void OnAction(float x, float y) = 0;
- virtual float OnSum(float x, float y) = 0;
- //////////////////////////////////////////////////////////////////////////
- };
- typedef AZ::EBus< MyEventGroup > MyEventGroupBus;
- /**
- * Now implement our event handler.
- */
- class MyEventHandler
- : public MyEventGroupBus::MultiHandler
- {
- public:
- int actionCalls;
- int sumCalls;
- MyEventHandler(MyEventGroupBus::BusIdType busId0, MyEventGroupBus::BusIdType busId1)
- : actionCalls(0)
- , sumCalls(0)
- {
- BusConnect(busId0); // connect to the specific bus
- BusConnect(busId1); // connect to the specific bus
- }
- //////////////////////////////////////////////////////////////////////////
- // Implement some action on the events...
- void OnAction(float x, float y) override
- {
- AZ_Printf("UnitTest", "OnAction1(%.2f,%.2f) called\n", x, y); ++actionCalls;
- }
- float OnSum(float x, float y) override
- {
- float sum = x + y; AZ_Printf("UnitTest", "%.2f OnAction1(%.2f,%.2f) on called\n", sum, x, y); ++sumCalls; return sum;
- }
- //////////////////////////////////////////////////////////////////////////
- };
- }
- TEST_F(EBus, MultBusHandler)
- {
- using namespace MultBusHandler;
- {
- MyEventHandler meh0(0, 1); /// <-- Bind to bus 0 and 1
- // Signal OnAction event on all buses
- MyEventGroupBus::Broadcast(&MyEventGroupBus::Events::OnAction, 1.0f, 2.0f);
- EXPECT_EQ(2, meh0.actionCalls);
- // Signal OnSum event
- MyEventGroupBus::Broadcast(&MyEventGroupBus::Events::OnSum, 2.0f, 5.0f);
- EXPECT_EQ(2, meh0.sumCalls);
- // Signal OnAction event on bus 0
- MyEventGroupBus::Event(0, &MyEventGroupBus::Events::OnAction, 1.0f, 2.0f);
- EXPECT_EQ(3, meh0.actionCalls);
- // Signal OnAction event on bus 1
- MyEventGroupBus::Event(1, &MyEventGroupBus::Events::OnAction, 1.0f, 2.0f);
- EXPECT_EQ(4, meh0.actionCalls);
- meh0.BusDisconnect(1); // we disconnect from receiving events on bus 1
- MyEventGroupBus::Broadcast(&MyEventGroupBus::Events::OnAction, 1.0f, 2.0f); // this signal will NOT trigger only one call
- EXPECT_EQ(5, meh0.actionCalls);
- }
- }
- /**
- *
- */
- namespace QueueMessageTest
- {
- class QueueTestEventsMultiBus
- : public EBusTraits
- {
- public:
- //////////////////////////////////////////////////////////////////////////
- // EBusTraits overrides
- static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
- typedef AZStd::mutex MutexType;
- typedef int BusIdType;
- static const bool EnableEventQueue = true;
- //////////////////////////////////////////////////////////////////////////
- QueueTestEventsMultiBus()
- : m_callCount(0) {}
- virtual ~QueueTestEventsMultiBus() {}
- virtual void OnMessage() { m_callCount++; }
- int m_callCount;
- };
- typedef AZ::EBus<QueueTestEventsMultiBus> QueueTestMultiBus;
- class QueueTestEventsSingleBus
- : public EBusTraits
- {
- public:
- //////////////////////////////////////////////////////////////////////////
- // EBusTraits overrides
- typedef AZStd::mutex MutexType;
- static const bool EnableEventQueue = true;
- //////////////////////////////////////////////////////////////////////////
- QueueTestEventsSingleBus()
- : m_callCount(0) {}
- virtual ~QueueTestEventsSingleBus() {}
- virtual void OnMessage() { m_callCount++; }
- int m_callCount;
- };
- typedef AZ::EBus<QueueTestEventsSingleBus> QueueTestSingleBus;
- JobManager* m_jobManager = nullptr;
- JobContext* m_jobContext = nullptr;
- QueueTestMultiBus::Handler* m_multiHandler = nullptr;
- QueueTestSingleBus::Handler* m_singleHandler = nullptr;
- QueueTestMultiBus::BusPtr m_multiPtr = nullptr;
- void QueueMessage()
- {
- QueueTestMultiBus::QueueEvent(0, &QueueTestMultiBus::Events::OnMessage);
- QueueTestSingleBus::QueueBroadcast(&QueueTestSingleBus::Events::OnMessage);
- }
- void QueueMessagePtr()
- {
- QueueTestMultiBus::QueueEvent(m_multiPtr, &QueueTestMultiBus::Events::OnMessage);
- QueueTestSingleBus::QueueBroadcast(&QueueTestSingleBus::Events::OnMessage);
- }
- }
- TEST_F(EBus, QueueMessage)
- {
- using namespace QueueMessageTest;
- // Setup
- JobManagerDesc jobDesc;
- JobManagerThreadDesc threadDesc;
- jobDesc.m_workerThreads.push_back(threadDesc);
- jobDesc.m_workerThreads.push_back(threadDesc);
- jobDesc.m_workerThreads.push_back(threadDesc);
- m_jobManager = aznew JobManager(jobDesc);
- m_jobContext = aznew JobContext(*m_jobManager);
- JobContext::SetGlobalContext(m_jobContext);
- m_multiHandler = new QueueTestMultiBus::Handler();
- m_singleHandler = new QueueTestSingleBus::Handler();
- m_singleHandler->m_callCount = 0;
- m_multiHandler->m_callCount = 0;
- const int NumCalls = 5000;
- QueueTestMultiBus::Bind(m_multiPtr, 0);
- m_multiHandler->BusConnect(0);
- m_singleHandler->BusConnect();
- for (int i = 0; i < NumCalls; ++i)
- {
- Job* job = CreateJobFunction(&QueueMessageTest::QueueMessage, true);
- job->Start();
- job = CreateJobFunction(&QueueMessageTest::QueueMessagePtr, true);
- job->Start();
- }
- while (m_singleHandler->m_callCount < NumCalls * 2 || m_multiHandler->m_callCount < NumCalls * 2)
- {
- QueueTestMultiBus::ExecuteQueuedEvents();
- QueueTestSingleBus::ExecuteQueuedEvents();
- AZStd::this_thread::yield();
- }
- // use queuing generic functions to disconnect from the bus
- // the same as m_singleHandler.BusDisconnect(); but delayed until QueueTestSingleBus::ExecuteQueuedEvents()
- QueueTestSingleBus::QueueFunction(&QueueTestSingleBus::Handler::BusDisconnect, m_singleHandler);
- // the same as m_multiHandler.BusDisconnect(); but dalayed until QueueTestMultiBus::ExecuteQueuedEvents();
- QueueTestMultiBus::QueueFunction(static_cast<void(QueueTestMultiBus::Handler::*)()>(&QueueTestMultiBus::Handler::BusDisconnect), m_multiHandler);
- EXPECT_EQ(1, QueueTestSingleBus::GetTotalNumOfEventHandlers());
- EXPECT_EQ(1, QueueTestMultiBus::GetTotalNumOfEventHandlers());
- QueueTestSingleBus::ExecuteQueuedEvents();
- QueueTestMultiBus::ExecuteQueuedEvents();
- EXPECT_EQ(0, QueueTestSingleBus::GetTotalNumOfEventHandlers());
- EXPECT_EQ(0, QueueTestMultiBus::GetTotalNumOfEventHandlers());
- // Cleanup
- delete m_singleHandler;
- delete m_multiHandler;
- m_multiPtr = nullptr;
- JobContext::SetGlobalContext(nullptr);
- delete m_jobContext;
- delete m_jobManager;
- }
- class QueueEbusTest
- : public LeakDetectionFixture
- {
- };
- TEST_F(QueueEbusTest, QueueMessageNoQueueing_QueueMessage_Warning)
- {
- using namespace QueueMessageTest;
- {
- AZ::Test::AssertAbsorber assertAbsorber;
- QueueMessage();
- EXPECT_EQ(assertAbsorber.m_warningCount, 0);
- }
- QueueTestSingleBus::ExecuteQueuedEvents();
- QueueTestSingleBus::AllowFunctionQueuing(false);
- {
- AZ::Test::AssertAbsorber assertAbsorber;
- QueueMessage();
- EXPECT_EQ(assertAbsorber.m_warningCount, 1);
- }
- QueueTestMultiBus::ExecuteQueuedEvents();
- QueueTestSingleBus::AllowFunctionQueuing(true);
- }
- class ConnectDisconnectInterface
- : public EBusTraits
- {
- public:
- virtual ~ConnectDisconnectInterface() {}
- virtual void OnConnectChild() = 0;
- virtual void OnDisconnectMe() = 0;
- virtual void OnDisconnectAll() = 0;
- };
- typedef AZ::EBus<ConnectDisconnectInterface> ConnectDisconnectBus;
- class ConnectDisconnectHandler
- : public ConnectDisconnectBus::Handler
- {
- ConnectDisconnectHandler* m_child;
- public:
- ConnectDisconnectHandler(ConnectDisconnectHandler* child)
- : m_child(child)
- {
- s_handlers.push_back(this);
- if (child != nullptr) // if we are the child don't connect yet
- {
- BusConnect();
- }
- }
- ~ConnectDisconnectHandler() override
- {
- s_handlers.erase(AZStd::find(s_handlers.begin(), s_handlers.end(), this));
- }
- void OnConnectChild() override
- {
- if (m_child)
- {
- m_child->BusConnect();
- }
- }
- void OnDisconnectMe() override
- {
- BusDisconnect();
- }
- void OnDisconnectAll() override
- {
- for (size_t i = 0; i < s_handlers.size(); ++i)
- {
- s_handlers[i]->BusDisconnect();
- }
- }
- static AZStd::fixed_vector<ConnectDisconnectHandler*, 5> s_handlers;
- };
- AZStd::fixed_vector<ConnectDisconnectHandler*, 5> ConnectDisconnectHandler::s_handlers;
- class ConnectDisconnectIdOrderedInterface
- : public EBusTraits
- {
- public:
- //////////////////////////////////////////////////////////////////////////
- // EBus interface settings
- static const EBusHandlerPolicy HandlerPolicy = EBusHandlerPolicy::MultipleAndOrdered;
- static const EBusAddressPolicy AddressPolicy = EBusAddressPolicy::ById;
- typedef int BusIdType;
- //////////////////////////////////////////////////////////////////////////
- ConnectDisconnectIdOrderedInterface()
- : m_order(0) {}
- virtual ~ConnectDisconnectIdOrderedInterface() {}
- virtual void OnConnectChild() = 0;
- virtual void OnDisconnectMe() = 0;
- virtual void OnDisconnectAll(int busId) = 0;
- virtual bool Compare(const ConnectDisconnectIdOrderedInterface* rhs) const { return m_order < rhs->m_order; }
- int m_order;
- };
- typedef AZ::EBus<ConnectDisconnectIdOrderedInterface> ConnectDisconnectIdOrderedBus;
- class ConnectDisconnectIdOrderedHandler
- : public ConnectDisconnectIdOrderedBus::Handler
- {
- public:
- ConnectDisconnectIdOrderedHandler(int id, int order, ConnectDisconnectIdOrderedHandler* child)
- : ConnectDisconnectIdOrderedBus::Handler()
- , m_child(child)
- , m_busId(id)
- {
- m_order = order;
- s_handlers.push_back(this);
- if (child != nullptr) // if we are the child don't connect yet
- {
- BusConnect(m_busId);
- }
- }
- ~ConnectDisconnectIdOrderedHandler() override
- {
- s_handlers.erase(AZStd::find(s_handlers.begin(), s_handlers.end(), this));
- }
- void OnConnectChild() override
- {
- if (m_child)
- {
- m_child->BusConnect(m_busId);
- }
- }
- void OnDisconnectMe() override
- {
- BusDisconnect();
- }
- void OnDisconnectAll(int busId) override
- {
- for (size_t i = 0; i < s_handlers.size(); ++i)
- {
- if (busId == -1 || busId == s_handlers[i]->m_busId)
- {
- s_handlers[i]->BusDisconnect();
- }
- }
- }
- static AZStd::fixed_vector<ConnectDisconnectIdOrderedHandler*, 5> s_handlers;
- protected:
- ConnectDisconnectIdOrderedHandler* m_child;
- int m_busId;
- };
- AZStd::fixed_vector<ConnectDisconnectIdOrderedHandler*, 5> ConnectDisconnectIdOrderedHandler::s_handlers;
- /**
- * Tests a bus when we allow to disconnect while executing messages.
- */
- TEST_F(EBus, DisconnectInDispatch)
- {
- ConnectDisconnectHandler child(nullptr);
- EXPECT_EQ(0, ConnectDisconnectBus::GetTotalNumOfEventHandlers());
- ConnectDisconnectHandler l(&child);
- EXPECT_EQ(1, ConnectDisconnectBus::GetTotalNumOfEventHandlers());
- // Test connect in the during the message call
- ConnectDisconnectBus::Broadcast(&ConnectDisconnectBus::Events::OnConnectChild); // connect the child object
- EXPECT_EQ(2, ConnectDisconnectBus::GetTotalNumOfEventHandlers());
- ConnectDisconnectBus::Broadcast(&ConnectDisconnectBus::Events::OnDisconnectAll); // Disconnect all members during a message
- EXPECT_EQ(0, ConnectDisconnectBus::GetTotalNumOfEventHandlers());
- ConnectDisconnectIdOrderedHandler ch10(10, 1, nullptr);
- ConnectDisconnectIdOrderedHandler ch5(5, 20, nullptr);
- EXPECT_EQ(0, ConnectDisconnectIdOrderedBus::GetTotalNumOfEventHandlers());
- ConnectDisconnectIdOrderedHandler pa10(10, 10, &ch10);
- ConnectDisconnectIdOrderedHandler pa20(20, 20, &ch5);
- EXPECT_EQ(2, ConnectDisconnectIdOrderedBus::GetTotalNumOfEventHandlers());
- ConnectDisconnectIdOrderedBus::Broadcast(&ConnectDisconnectIdOrderedBus::Events::OnConnectChild); // connect the child object
- EXPECT_EQ(4, ConnectDisconnectIdOrderedBus::GetTotalNumOfEventHandlers());
- // Disconnect all members from bus 10 (it will be sorted first)
- // This we we can test a bus removal while traversing
- ConnectDisconnectIdOrderedBus::Broadcast(&ConnectDisconnectIdOrderedBus::Events::OnDisconnectAll, 10);
- EXPECT_EQ(2, ConnectDisconnectIdOrderedBus::GetTotalNumOfEventHandlers());
- // Now disconnect all buses
- ConnectDisconnectIdOrderedBus::Broadcast(&ConnectDisconnectIdOrderedBus::Events::OnDisconnectAll, -1);
- EXPECT_EQ(0, ConnectDisconnectIdOrderedBus::GetTotalNumOfEventHandlers());
- }
- class DisconnectNextHandlerInterface
- : public AZ::EBusTraits
- {
- public:
- static constexpr AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
- static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::MultipleAndOrdered;
- using BusIdType = int32_t;
- // Comparison function which always sorts to the end
- struct DisconnectNextHandlerLess
- {
- // Intrusive_multiset requires the first_argument_type parameter for it's comparison function, but it is deprecated in C++17
- // This should be removed when C++17 support is added
- using first_argument_type = DisconnectNextHandlerInterface*;
- constexpr bool operator()(const DisconnectNextHandlerInterface*, const DisconnectNextHandlerInterface*) const
- {
- return false;
- }
- };
- using BusHandlerOrderCompare = DisconnectNextHandlerLess;
- virtual void DisconnectNextHandler() = 0;
- };
- using DisconnectNextHandlerBus = AZ::EBus<DisconnectNextHandlerInterface>;
- class DisconnectNextHandlerByIdImpl
- : public DisconnectNextHandlerBus::MultiHandler
- {
- public:
- void DisconnectNextHandler() override
- {
- if (m_nextHandler)
- {
- m_nextHandler->BusDisconnect(*DisconnectNextHandlerBus::GetCurrentBusId());
- ++m_handlerDisconnectCounter;
- }
- }
- static constexpr int32_t firstBusAddress = 1;
- static constexpr int32_t secondBusAddress = 2;
- DisconnectNextHandlerByIdImpl* m_nextHandler{};
- int32_t m_handlerDisconnectCounter{};
- };
- constexpr int32_t DisconnectNextHandlerByIdImpl::firstBusAddress;
- constexpr int32_t DisconnectNextHandlerByIdImpl::secondBusAddress;
- /**
- * Tests disconnecting the next handler within a bus during a dispatch
- */
- TEST_F(EBus, DisconnectNextHandlerDuringDispatch_DoesNotCrash)
- {
- DisconnectNextHandlerByIdImpl multiHandler1;
- multiHandler1.BusConnect(DisconnectNextHandlerByIdImpl::firstBusAddress);
- multiHandler1.BusConnect(DisconnectNextHandlerByIdImpl::secondBusAddress);
- DisconnectNextHandlerByIdImpl multiHandler2;
- multiHandler2.BusConnect(DisconnectNextHandlerByIdImpl::firstBusAddress);
- multiHandler2.BusConnect(DisconnectNextHandlerByIdImpl::secondBusAddress);
- // Set the first handler m_nextHandler field to point to the second handler
- multiHandler1.m_nextHandler = &multiHandler2;
- // Disconnect the next handlers from the second bus address to catch any issues with the address hash_table iterators becoming invalidated
- DisconnectNextHandlerBus::Event(DisconnectNextHandlerByIdImpl::secondBusAddress, &DisconnectNextHandlerInterface::DisconnectNextHandler);
- EXPECT_EQ(1, multiHandler1.m_handlerDisconnectCounter);
- EXPECT_EQ(0, multiHandler2.m_handlerDisconnectCounter);
- DisconnectNextHandlerBus::Event(DisconnectNextHandlerByIdImpl::firstBusAddress, &DisconnectNextHandlerInterface::DisconnectNextHandler);
- EXPECT_EQ(2, multiHandler1.m_handlerDisconnectCounter);
- EXPECT_EQ(0, multiHandler2.m_handlerDisconnectCounter);
- }
- class DisconnectNextAddressInterface
- : public AZ::EBusTraits
- {
- public:
- static constexpr AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ByIdAndOrdered;
- static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
- using BusIdType = int32_t;
- struct BusIdOrderLess
- {
- constexpr bool operator()(BusIdType lhs, BusIdType rhs) const
- {
- return lhs < rhs;
- }
- };
- using BusIdOrderCompare = BusIdOrderLess;
- virtual void DisconnectNextAddress() = 0;
- };
- using DisconnectNextAddressBus = AZ::EBus<DisconnectNextAddressInterface>;
- class DisconnectNextAddressImpl
- : public DisconnectNextAddressBus::Handler
- {
- public:
- void DisconnectNextAddress() override
- {
- if (m_nextAddressHandler)
- {
- m_nextAddressHandler->BusDisconnect();
- ++m_addressDisconnectCounter;
- }
- }
- static constexpr int32_t firstBusAddress = 1;
- static constexpr int32_t nextBusAddress = 2;
- DisconnectNextAddressImpl* m_nextAddressHandler{};
- int32_t m_addressDisconnectCounter{};
- };
- constexpr int32_t DisconnectNextAddressImpl::firstBusAddress;
- constexpr int32_t DisconnectNextAddressImpl::nextBusAddress;
- /**
- * Tests disconnecting the next address within a bus during a dispatch
- */
- TEST_F(EBus, DisconnectNextAddressDuringDispatch_DoesNotCrash)
- {
- DisconnectNextAddressImpl addressHandler1;
- addressHandler1.BusConnect(DisconnectNextAddressImpl::firstBusAddress);
- DisconnectNextAddressImpl addressHandler2;
- addressHandler2.BusConnect(DisconnectNextAddressImpl::nextBusAddress);
- addressHandler1.m_nextAddressHandler = &addressHandler2;
- // Disconnect the second address handler using the first address handler
- DisconnectNextAddressBus::Event(DisconnectNextAddressImpl::firstBusAddress, &DisconnectNextAddressInterface::DisconnectNextAddress);
- EXPECT_EQ(1, addressHandler1.m_addressDisconnectCounter);
- EXPECT_EQ(0, addressHandler2.m_addressDisconnectCounter);
- }
- /**
- * Test multiple handler.
- */
- namespace MultiHandlerTest
- {
- class MyEventGroup
- : public AZ::EBusTraits
- {
- public:
- //////////////////////////////////////////////////////////////////////////
- // EBus Settings
- static const EBusAddressPolicy AddressPolicy = EBusAddressPolicy::ById;
- typedef unsigned int BusIdType;
- //////////////////////////////////////////////////////////////////////////
- virtual ~MyEventGroup() {}
- //////////////////////////////////////////////////////////////////////////
- // Define the events in this event group!
- virtual void OnAction() = 0;
- //////////////////////////////////////////////////////////////////////////
- };
- typedef AZ::EBus<MyEventGroup> MyEventBus;
- class MultiHandler
- : public MyEventBus::MultiHandler
- {
- public:
- MultiHandler()
- : m_expectedCurrentId(0)
- , m_numCalls(0)
- {}
- void OnAction() override
- {
- const unsigned int* currentIdPtr = MyEventBus::GetCurrentBusId();
- ASSERT_NE(nullptr, currentIdPtr);
- EXPECT_EQ(*currentIdPtr, m_expectedCurrentId);
- ++m_numCalls;
- }
- unsigned int m_expectedCurrentId;
- unsigned int m_numCalls;
- };
- }
- TEST_F(EBus, MultiHandler)
- {
- using namespace MultiHandlerTest;
- MultiHandler ml;
- ml.BusConnect(10);
- ml.BusConnect(12);
- ml.BusConnect(13);
- // test copy handlers and make sure they attached to the same bus
- MultiHandler mlCopy = ml;
- EXPECT_EQ(0, mlCopy.m_numCalls);
- // Called outside of an even it should always return nullptr
- EXPECT_EQ(nullptr, MyEventBus::GetCurrentBusId());
- MyEventBus::Event(1, &MyEventBus::Events::OnAction); // this should not trigger a call
- EXPECT_EQ(0, ml.m_numCalls);
- // Issues calls which we listen for
- ml.m_expectedCurrentId = 10;
- mlCopy.m_expectedCurrentId = 10;
- MyEventBus::Event(10, &MyEventBus::Events::OnAction);
- EXPECT_EQ(1, ml.m_numCalls);
- EXPECT_EQ(1, mlCopy.m_numCalls); // make sure the handler copy is connected
- mlCopy.BusDisconnect();
- ml.m_expectedCurrentId = 12;
- MyEventBus::Event(12, &MyEventBus::Events::OnAction);
- EXPECT_EQ(2, ml.m_numCalls);
- ml.m_expectedCurrentId = 13;
- MyEventBus::Event(13, &MyEventBus::Events::OnAction);
- EXPECT_EQ(3, ml.m_numCalls);
- }
- // Non intrusive EBusTraits
- struct MyCustomTraits
- : public AZ::EBusTraits
- {
- // ... custom traits here
- };
- /**
- * Interface that we don't own and we can't inherit traits
- */
- class My3rdPartyInterface
- {
- public:
- virtual void SomeEvent(int a) = 0;
- };
- // 3rd party interface (which is compliant with EBus requirements)
- typedef AZ::EBus<My3rdPartyInterface, MyCustomTraits> My3rdPartyBus1;
- // 3rd party interface that we want to wrap
- class My3rdPartyInterfaceWrapped
- : public My3rdPartyInterface
- , public AZ::EBusTraits
- {
- };
- typedef AZ::EBus<My3rdPartyInterfaceWrapped> My3rdPartyBus2;
- // regular interface trough traits inheritance, please look at the all the samples above
- // combine an ebus and an interface, so you don't need any typedefs. You will need to specialize a template so the bus can get it's traits
- // Keep in mind that this type will not allow for interfaces to be extended, but it's ok for final interfaces
- class MyEBusInterface
- : public AZ::EBus<MyEBusInterface, MyCustomTraits>
- {
- public:
- virtual void Event(int a) const = 0;
- };
- /**
- * Test and demonstrate different EBus implementations
- */
- namespace ImplementationTest
- {
- class Handler1
- : public My3rdPartyBus1::Handler
- {
- public:
- Handler1()
- : m_calls(0)
- {
- My3rdPartyBus1::Handler::BusConnect();
- }
- int m_calls;
- private:
- void SomeEvent(int a) override
- {
- (void)a;
- ++m_calls;
- }
- };
- class Handler2
- : public My3rdPartyBus2::Handler
- {
- public:
- Handler2()
- : m_calls(0)
- {
- My3rdPartyBus2::Handler::BusConnect();
- }
- int m_calls;
- private:
- void SomeEvent(int a) override
- {
- (void)a;
- ++m_calls;
- }
- };
- class Handler3
- : public MyEBusInterface::Handler
- {
- public:
- Handler3()
- : m_calls(0)
- {
- MyEBusInterface::Handler::BusConnect();
- }
- mutable int m_calls;
- private:
- void Event(int a) const override
- {
- (void)a;
- ++m_calls;
- }
- };
- }
- TEST_F(EBus, ExternalInterface)
- {
- using namespace ImplementationTest;
- Handler1 h1;
- Handler2 h2;
- Handler3 h3;
- // test copy of handler
- Handler1 h1Copy = h1;
- EXPECT_EQ(0, h1Copy.m_calls);
- My3rdPartyBus1::Broadcast(&My3rdPartyBus1::Events::SomeEvent, 1);
- EXPECT_EQ(1, h1.m_calls);
- EXPECT_EQ(1, h1Copy.m_calls); // check that the copy works too
- My3rdPartyBus2::Broadcast(&My3rdPartyBus2::Events::SomeEvent, 2);
- EXPECT_EQ(1, h2.m_calls);
- MyEBusInterface::Broadcast(&MyEBusInterface::Events::Event, 3);
- EXPECT_EQ(1, h3.m_calls);
- }
- /**
- *
- */
- TEST_F(EBus, Results)
- {
- // Test the result logical aggregator for OR
- {
- AZ::EBusLogicalResult<bool, AZStd::logical_or<bool> > or_false_false(false);
- or_false_false = false;
- or_false_false = false;
- EXPECT_FALSE(or_false_false.value);
- }
- {
- AZ::EBusLogicalResult<bool, AZStd::logical_or<bool> > or_true_false(false);
- or_true_false = true;
- or_true_false = false;
- EXPECT_TRUE(or_true_false.value);
- }
- // Test the result logical aggregator for AND
- {
- AZ::EBusLogicalResult<bool, AZStd::logical_and<bool> > and_true_false(true);
- and_true_false = true;
- and_true_false = false;
- EXPECT_FALSE(and_true_false.value);
- }
- {
- AZ::EBusLogicalResult<bool, AZStd::logical_and<bool> > and_true_true(true);
- and_true_true = true;
- and_true_true = true;
- EXPECT_TRUE(and_true_true.value);
- }
- }
- // Routers, Bridging and Versioning
- /**
- * EBusInterfaceV1, since we want to keep binary compatibility (we don't need to recompile)
- * when we are implementing the version messaging we should not change the V1 EBus, all code
- * should be triggered from the new version that is not compiled is customer's code yet.
- */
- class EBusInterfaceV1 : public AZ::EBusTraits
- {
- public:
- virtual void OnEvent(int a)
- {
- (void)a;
- }
- };
- using EBusVersion1 = AZ::EBus<EBusInterfaceV1>;
- /**
- * Version 2 of the interface which communicates with Version 1 of the bus bidirectionally.
- */
- class EBusInterfaceV2 : public AZ::EBusTraits
- {
- public:
- /**
- * Router policy implementation that bridges two EBuses by default.
- * It this case we use it to implement versioning between V1 and V2
- * of a specific EBus version.
- */
- template<typename Bus>
- struct RouterPolicy : public EBusRouterPolicy<Bus>
- {
- struct V2toV1Router : public Bus::NestedVersionRouter
- {
- void OnEvent(int a, int b) override
- {
- if (!m_policy->m_isOnEventRouting)
- {
- m_policy->m_isOnEventRouting = true;
- this->template ForwardEvent<EBusVersion1>(&EBusVersion1::Events::OnEvent, a + b);
- m_policy->m_isOnEventRouting = false;
- }
- }
- typename Bus::RouterPolicy* m_policy = nullptr;
- };
- struct V1toV2Router : public EBusVersion1::Router
- {
- void OnEvent(int a) override
- {
- if(!m_policy->m_isOnEventRouting)
- {
- m_policy->m_isOnEventRouting = true;
- this->template ForwardEvent<Bus>(&Bus::Events::OnEvent, a, 0);
- m_policy->m_isOnEventRouting = false;
- }
- }
- typename Bus::RouterPolicy* m_policy = nullptr;
- };
- RouterPolicy()
- {
- m_v2toV1Router.m_policy = this;
- m_v1toV2Router.m_policy = this;
- m_v2toV1Router.BusRouterConnect(this->m_routers);
- m_v1toV2Router.BusRouterConnect();
- }
- ~RouterPolicy()
- {
- m_v2toV1Router.BusRouterDisconnect(this->m_routers);
- m_v1toV2Router.BusRouterDisconnect();
- }
- // State of current routed events to avoid loopbacks
- // this is NOT needed if we route only one way V2->V1 or V1->V2
- bool m_isOnEventRouting = false;
- // Possible optimization, When we are dealing with version we usually don't expect to have active use of the old version,
- // it's just for compatibility. Having routers trying to route to old version busses that rarely
- // have listeners will have it's overhead. To reduct that we can add m_onDemandRouters list that
- // have a pointer to a router and oder, so we can automatically connect that router only when
- // listeners are attached to the old version of the bus. We are talking only about NewVersion->OldVersion
- // bridge (the opposite can be always connected as the overhead will be on the OldVersion bus which we don't expect to use much anyway).
- V2toV1Router m_v2toV1Router;
- V1toV2Router m_v1toV2Router;
- };
- virtual void OnEvent(int a, int b) { (void)a; (void)b; }
- };
- using EBusVersion2 = AZ::EBus<EBusInterfaceV2>;
- namespace RoutingTest
- {
- class EBusInterceptor : public EBusVersion1::Router
- {
- public:
- void OnEvent(int a) override
- {
- EXPECT_EQ(1020, a);
- m_numOnEvent++;
- }
- int m_numOnEvent = 0;
- };
- class V1EventRouter : public EBusVersion1::Router
- {
- public:
- void OnEvent(int a) override
- {
- (void)a;
- m_numOnEvent++;
- EBusVersion1::SetRouterProcessingState(m_processingState);
- }
- int m_numOnEvent = 0;
- EBusVersion1::RouterProcessingState m_processingState = EBusVersion1::RouterProcessingState::SkipListeners;
- };
- class EBusVersion1Handler : public EBusVersion1::Handler
- {
- public:
- void OnEvent(int a) override
- {
- (void)a;
- m_numOnEvent++;
- }
- int m_numOnEvent = 0;
- };
- class EBusVersion2Handler : public EBusVersion2::Handler
- {
- public:
- void OnEvent(int a, int b) override
- {
- (void)a; (void)b;
- m_numOnEvent++;
- }
- int m_numOnEvent = 0;
- };
- }
- #if !AZ_TRAIT_DISABLE_FAILED_EBUS_ROUTING_TEST
- TEST_F(EBus, Routing)
- {
- using namespace RoutingTest;
- EBusInterceptor interceptor;
- EBusVersion1Handler v1Handler;
- v1Handler.BusConnect();
- interceptor.BusRouterConnect();
- EBusVersion1::Broadcast(&EBusVersion1::Events::OnEvent, 1020);
- EXPECT_EQ(1, interceptor.m_numOnEvent);
- EXPECT_EQ(1, v1Handler.m_numOnEvent);
- interceptor.BusRouterDisconnect();
- EBusVersion1::Broadcast(&EBusVersion1::Events::OnEvent, 1020);
- EXPECT_EQ(1, interceptor.m_numOnEvent);
- EXPECT_EQ(2, v1Handler.m_numOnEvent);
- // routing events
- {
- // reset counter
- v1Handler.m_numOnEvent = 0;
- V1EventRouter v1Router;
- v1Router.BusRouterConnect();
- EBusVersion1::Broadcast(&EBusVersion1::Events::OnEvent, 1020);
- EXPECT_EQ(1, v1Router.m_numOnEvent);
- EXPECT_EQ(0, v1Handler.m_numOnEvent);
- v1Router.BusRouterDisconnect();
- }
- // routing events and skipping further routing
- {
- // reset counter
- v1Handler.m_numOnEvent = 0;
- V1EventRouter v1RouterFirst, v1RouterSecond;
- v1RouterFirst.BusRouterConnect(-1);
- v1RouterSecond.BusRouterConnect();
- EBusVersion1::Broadcast(&EBusVersion1::Events::OnEvent, 1020);
- EXPECT_EQ(1, v1RouterFirst.m_numOnEvent);
- EXPECT_EQ(1, v1RouterSecond.m_numOnEvent);
- EXPECT_EQ(0, v1Handler.m_numOnEvent);
- // now instruct router 1 to block any further event processing
- v1RouterFirst.m_processingState = EBusVersion1::RouterProcessingState::SkipListenersAndRouters;
- EBusVersion1::Broadcast(&EBusVersion1::Events::OnEvent, 1020);
- EXPECT_EQ(2, v1RouterFirst.m_numOnEvent);
- EXPECT_EQ(1, v1RouterSecond.m_numOnEvent);
- EXPECT_EQ(0, v1Handler.m_numOnEvent);
- }
- // test bridging two EBus by using routers. This can be used to handle different bus versions.
- {
- EBusVersion2Handler v2Handler;
- v2Handler.BusConnect();
- // reset counter
- v1Handler.m_numOnEvent = 0;
- EBusVersion2::Broadcast(&EBusVersion2::Events::OnEvent, 10, 20);
- EXPECT_EQ(1, v1Handler.m_numOnEvent);
- EXPECT_EQ(1, v2Handler.m_numOnEvent);
- EBusVersion1::Broadcast(&EBusVersion1::Events::OnEvent, 30);
- EXPECT_EQ(2, v1Handler.m_numOnEvent);
- EXPECT_EQ(2, v2Handler.m_numOnEvent);
- }
- // We can test Queue and Event routing separately,
- // however they do use the same code path (as we don't queue routing and we just use the ID to differentiate between Broadcast and Event)
- }
- #endif // !AZ_TRAIT_DISABLE_FAILED_EBUS_ROUTING_TEST
- struct LocklessEvents
- : public AZ::EBusTraits
- {
- using MutexType = AZStd::mutex;
- static const bool LocklessDispatch = true;
- virtual ~LocklessEvents() = default;
- virtual void RemoveMe() = 0;
- virtual void DeleteMe() = 0;
- virtual void Calculate(int x, int y, int z) = 0;
- };
- using LocklessBus = AZ::EBus<LocklessEvents>;
- struct LocklessImpl
- : public LocklessBus::Handler
- {
- uint32_t m_val;
- uint32_t m_maxSleep;
- LocklessImpl(uint32_t maxSleep = 0)
- : m_val(0)
- , m_maxSleep(maxSleep)
- {
- BusConnect();
- }
- ~LocklessImpl() override
- {
- BusDisconnect();
- }
- void RemoveMe() override
- {
- BusDisconnect();
- }
- void DeleteMe() override
- {
- delete this;
- }
- void Calculate(int x, int y, int z) override
- {
- m_val = x + (y * z);
- if (m_maxSleep)
- {
- AZStd::this_thread::sleep_for(AZStd::chrono::microseconds(m_val % m_maxSleep));
- }
- }
- };
- void ThrashLocklessDispatch(uint32_t maxSleep = 0)
- {
- const size_t threadCount = 8;
- enum : size_t { cycleCount = 1000 };
- AZStd::thread threads[threadCount];
- AZStd::vector<int> results[threadCount];
- LocklessImpl handler(maxSleep);
- auto work = [maxSleep]()
- {
- char sentinel[64] = { 0 };
- char* end = sentinel + AZ_ARRAY_SIZE(sentinel);
- for (int i = 1; i < cycleCount; ++i)
- {
- // Calculate() already includes a modulo-cycled sleep, add more random jitter
- uint32_t extraSleep_us = maxSleep ? rand() % maxSleep : 0;
- if (extraSleep_us % 3)
- {
- AZStd::this_thread::sleep_for(AZStd::chrono::microseconds(extraSleep_us));
- }
- LocklessBus::Broadcast(&LocklessBus::Events::Calculate, i, i * 2, i << 4);
- bool failed = (AZStd::find_if(&sentinel[0], end, [](char s) { return s != 0; }) != end);
- EXPECT_FALSE(failed);
- }
- };
- for (AZStd::thread& thread : threads)
- {
- thread = AZStd::thread(work);
- }
- for (AZStd::thread& thread : threads)
- {
- thread.join();
- }
- }
- TEST_F(EBus, ThrashLocklessDispatchYOLO)
- {
- ThrashLocklessDispatch();
- }
- TEST_F(EBus, ThrashLocklessDispatchSimulateWork)
- {
- ThrashLocklessDispatch(4);
- }
- TEST_F(EBus, DisconnectInLocklessDispatch)
- {
- LocklessImpl handler;
- AZ_TEST_START_TRACE_SUPPRESSION;
- LocklessBus::Broadcast(&LocklessBus::Events::RemoveMe);
- AZ_TEST_STOP_TRACE_SUPPRESSION(1);
- }
- TEST_F(EBus, DeleteInLocklessDispatch)
- {
- LocklessImpl* handler = new LocklessImpl();
- AZ_UNUSED(handler);
- AZ_TEST_START_TRACE_SUPPRESSION;
- LocklessBus::Broadcast(&LocklessBus::Events::DeleteMe);
- AZ_TEST_STOP_TRACE_SUPPRESSION(1);
- }
- namespace LocklessTest
- {
- struct LocklessConnectorEvents
- : public AZ::EBusTraits
- {
- using MutexType = AZStd::recursive_mutex;
- static const bool LocklessDispatch = true;
- static const EBusAddressPolicy AddressPolicy = EBusAddressPolicy::ById;
- typedef uint32_t BusIdType;
- virtual ~LocklessConnectorEvents() = default;
- virtual void DoConnect() = 0;
- virtual void DoDisconnect() = 0;
- };
- using LocklessConnectorBus = AZ::EBus<LocklessConnectorEvents>;
- class MyEventGroup
- : public AZ::EBusTraits
- {
- public:
- using MutexType = AZStd::recursive_mutex;
- static const EBusAddressPolicy AddressPolicy = EBusAddressPolicy::ById;
- typedef uint32_t BusIdType;
- virtual void Calculate(int x, int y, int z) = 0;
- virtual ~MyEventGroup() {}
- };
- using MyEventGroupBus = AZ::EBus< MyEventGroup >;
- struct DoubleEbusImpl
- : public LocklessConnectorBus::Handler,
- MyEventGroupBus::Handler
- {
- uint32_t m_id;
- uint32_t m_val;
- uint32_t m_maxSleep;
- DoubleEbusImpl(uint32_t id, uint32_t maxSleep)
- : m_id(id)
- , m_val(0)
- , m_maxSleep(maxSleep)
- {
- LocklessConnectorBus::Handler::BusConnect(m_id);
- }
- ~DoubleEbusImpl() override
- {
- MyEventGroupBus::Handler::BusDisconnect();
- LocklessConnectorBus::Handler::BusDisconnect();
- }
- void Calculate(int x, int y, int z) override
- {
- m_val = x + (y * z);
- if (m_maxSleep)
- {
- AZStd::this_thread::sleep_for(AZStd::chrono::microseconds(m_val % m_maxSleep));
- }
- }
- void DoConnect() override
- {
- MyEventGroupBus::Handler::BusConnect(m_id);
- }
- void DoDisconnect() override
- {
- MyEventGroupBus::Handler::BusDisconnect();
- }
- };
- }
- TEST_F(EBus, MixedLocklessTest)
- {
- using namespace LocklessTest;
- const int maxSleep = 5;
- const size_t threadCount = 8;
- enum : size_t { cycleCount = 500 };
- AZStd::thread threads[threadCount];
- AZStd::vector<int> results[threadCount];
- AZStd::vector<DoubleEbusImpl> handlerList;
- for (int i = 0; i < threadCount; i++)
- {
- handlerList.emplace_back(i, maxSleep);
- }
- auto work = []()
- {
- char sentinel[64] = { 0 };
- char* end = sentinel + AZ_ARRAY_SIZE(sentinel);
- for (int i = 1; i < cycleCount; ++i)
- {
- uint32_t id = rand() % threadCount;
- LocklessConnectorBus::Event(id, &LocklessConnectorBus::Events::DoConnect);
- // Calculate() already includes a modulo-cycled sleep, add more random jitter
- uint32_t extraSleep_us = maxSleep ? rand() % maxSleep : 0;
- if (extraSleep_us % 3)
- {
- AZStd::this_thread::sleep_for(AZStd::chrono::microseconds(extraSleep_us));
- }
- MyEventGroupBus::Event(id, &MyEventGroupBus::Events::Calculate, i, i * 2, i << 4);
- LocklessConnectorBus::Event(id, &LocklessConnectorBus::Events::DoDisconnect);
- bool failed = (AZStd::find_if(&sentinel[0], end, [](char s) { return s != 0; }) != end);
- EXPECT_FALSE(failed) << "sentinel memory unexpectedly tampered with while handling EBus events";
- }
- };
- for (AZStd::thread& thread : threads)
- {
- thread = AZStd::thread(work);
- }
- for (AZStd::thread& thread : threads)
- {
- thread.join();
- }
- }
- namespace MultithreadConnect
- {
- class MyEventGroup
- : public AZ::EBusTraits
- {
- public:
- using MutexType = AZStd::recursive_mutex;
- virtual ~MyEventGroup() {}
- };
- typedef AZ::EBus< MyEventGroup > MyEventGroupBus;
- struct MyEventGroupImpl :
- MyEventGroupBus::Handler
- {
- MyEventGroupImpl()
- {
- }
- ~MyEventGroupImpl() override
- {
- MyEventGroupBus::Handler::BusDisconnect();
- }
- virtual void DoConnect()
- {
- MyEventGroupBus::Handler::BusConnect();
- }
- virtual void DoDisconnect()
- {
- MyEventGroupBus::Handler::BusDisconnect();
- }
- };
- }
- TEST_F(EBus, MultithreadConnectTest)
- {
- using namespace MultithreadConnect;
- const int maxSleep = 5;
- const size_t threadCount = 8;
- enum : size_t { cycleCount = 1000 };
- AZStd::thread threads[threadCount];
- AZStd::vector<int> results[threadCount];
- MyEventGroupImpl handler;
- auto work = [&handler]()
- {
- for (int i = 1; i < cycleCount; ++i)
- {
- handler.DoConnect();
- // add random jitter
- uint32_t extraSleep_us = maxSleep ? rand() % maxSleep : 0;
- if (extraSleep_us % 3)
- {
- AZStd::this_thread::sleep_for(AZStd::chrono::microseconds(extraSleep_us));
- }
- handler.DoDisconnect();
- }
- };
- for (AZStd::thread& thread : threads)
- {
- thread = AZStd::thread(work);
- }
- for (AZStd::thread& thread : threads)
- {
- thread.join();
- }
- }
- struct LocklessNullMutexEvents
- : public AZ::EBusTraits
- {
- using MutexType = AZ::NullMutex;
- static const bool LocklessDispatch = true;
- virtual ~LocklessNullMutexEvents() = default;
- virtual void AtomicIncrement() = 0;
- };
- using LocklessNullMutexBus = AZ::EBus<LocklessNullMutexEvents>;
- struct LocklessNullMutexImpl
- : public LocklessNullMutexBus::Handler
- {
- AZStd::atomic<uint64_t> m_val{};
- LocklessNullMutexImpl()
- {
- BusConnect();
- }
- ~LocklessNullMutexImpl() override
- {
- BusDisconnect();
- }
- void AtomicIncrement() override
- {
- ++m_val;
- }
- };
- void ThrashLocklessDispatchNullMutex()
- {
- constexpr size_t threadCount = 8;
- enum : size_t { cycleCount = 1000 };
- constexpr uint64_t expectedAtomicCount = threadCount * cycleCount;
- AZStd::thread threads[threadCount];
- LocklessNullMutexImpl handler;
- auto work = []()
- {
- for (int i = 0; i < cycleCount; ++i)
- {
- // add random jitter
- constexpr int maxSleep = 3;
- uint32_t extraSleep_us = rand() % maxSleep;
- if (extraSleep_us != 0)
- {
- AZStd::this_thread::sleep_for(AZStd::chrono::microseconds(extraSleep_us));
- }
- LocklessNullMutexBus::Broadcast(&LocklessNullMutexBus::Events::AtomicIncrement);
- }
- };
- for (AZStd::thread& thread : threads)
- {
- thread = AZStd::thread(work);
- }
- for (AZStd::thread& thread : threads)
- {
- thread.join();
- }
- EXPECT_EQ(expectedAtomicCount, static_cast<uint64_t>(handler.m_val));
- }
- TEST_F(EBus, LocklessDispatchWithNullMutex_Multithread_Thrash)
- {
- ThrashLocklessDispatchNullMutex();
- }
- namespace EBusResultsTest
- {
- class ResultClass
- {
- public:
- int m_value1 = 0;
- int m_value2 = 0;
- bool m_operator_called_const = false;
- bool m_operator_called_rvalue_ref = false;
- ResultClass() = default;
- ResultClass(const ResultClass&) = default;
- bool operator==(const ResultClass& b) const
- {
- return m_value1 == b.m_value1 && m_value2 == b.m_value2;
- }
- ResultClass& operator=(const ResultClass& b)
- {
- m_value1 = b.m_value1 + m_value1;
- m_value2 = b.m_value2 + m_value2;
- m_operator_called_const = true;
- m_operator_called_rvalue_ref = b.m_operator_called_rvalue_ref;
- return *this;
- }
- ResultClass& operator=(ResultClass&& b)
- {
- // combine together to prove its not just an assignment
- m_value1 = b.m_value1 + m_value1;
- m_value2 = b.m_value2 + m_value2;
- // but destroy the original value (emulating move op)
- b.m_value1 = 0;
- b.m_value2 = 0;
- m_operator_called_rvalue_ref = true;
- m_operator_called_const = b.m_operator_called_const;
- return *this;
- }
- };
- class ResultReducerClass
- {
- public:
- bool m_operator_called_const = false;
- bool m_operator_called_rvalue_ref = false;
- ResultClass operator()(const ResultClass& a, const ResultClass& b)
- {
- ResultClass newValue;
- newValue.m_value1 = a.m_value1 + b.m_value1;
- newValue.m_value2 = a.m_value2 + b.m_value2;
- m_operator_called_const = true;
- return newValue;
- }
- ResultClass operator()(const ResultClass& a, ResultClass&& b)
- {
- m_operator_called_rvalue_ref = true;
- ResultClass newValue;
- newValue.m_value1 = a.m_value1 + b.m_value1;
- newValue.m_value2 = a.m_value2 + b.m_value2;
- return newValue;
- }
- };
- class MyInterface
- {
- public:
- virtual ResultClass EventX() = 0;
- virtual const ResultClass& EventY() = 0;
- };
- using MyInterfaceBus = AZ::EBus<MyInterface, AZ::EBusTraits>;
- class MyListener : public MyInterfaceBus::Handler
- {
- public:
- MyListener(int value1, int value2)
- {
- m_result.m_value1 = value1;
- m_result.m_value2 = value2;
- }
- ~MyListener() override
- {
- }
- ResultClass EventX() override
- {
- return m_result;
- }
- const ResultClass& EventY() override
- {
- return m_result;
- }
- ResultClass m_result;
- };
- } // EBusResultsTest
- TEST_F(EBus, ResultsTest)
- {
- using namespace EBusResultsTest;
- MyListener val1(1, 2);
- MyListener val2(3, 4);
- val1.BusConnect();
- val2.BusConnect();
- {
- ResultClass results;
- MyInterfaceBus::BroadcastResult(results, &MyInterfaceBus::Events::EventX);
- // ensure that the RVALUE-REF op was called:
- EXPECT_FALSE(results.m_operator_called_const);
- EXPECT_TRUE(results.m_operator_called_rvalue_ref);
- EXPECT_EQ(results.m_value1, 4); // 1 + 3
- EXPECT_EQ(results.m_value2, 6); // 2 + 4
- // make sure originals are not destroyed
- EXPECT_EQ(val1.m_result.m_value1, 1);
- EXPECT_EQ(val1.m_result.m_value2, 2);
- EXPECT_EQ(val2.m_result.m_value1, 3);
- EXPECT_EQ(val2.m_result.m_value2, 4);
- }
- {
- ResultClass results;
- MyInterfaceBus::BroadcastResult(results, &MyInterfaceBus::Events::EventY);
- // ensure that the const version of operator= was called.
- EXPECT_TRUE(results.m_operator_called_const);
- EXPECT_FALSE(results.m_operator_called_rvalue_ref);
- EXPECT_EQ(results.m_value1, 4); // 1 + 3
- EXPECT_EQ(results.m_value2, 6); // 2 + 4
- // make sure originals are not destroyed
- EXPECT_EQ(val1.m_result.m_value1, 1);
- EXPECT_EQ(val1.m_result.m_value2, 2);
- EXPECT_EQ(val2.m_result.m_value1, 3);
- EXPECT_EQ(val2.m_result.m_value2, 4);
- }
- val1.BusDisconnect();
- val2.BusDisconnect();
- }
- // ensure RVALUE-REF move on RHS does not corrupt existing values.
- TEST_F(EBus, ResultsTest_ReducerCorruption)
- {
- using namespace EBusResultsTest;
- MyListener val1(1, 2);
- MyListener val2(3, 4);
- val1.BusConnect();
- val2.BusConnect();
- {
- EBusReduceResult<ResultClass, ResultReducerClass> resultreducer;
- MyInterfaceBus::BroadcastResult(resultreducer, &MyInterfaceBus::Events::EventX);
- EXPECT_FALSE(resultreducer.unary.m_operator_called_const);
- EXPECT_TRUE(resultreducer.unary.m_operator_called_rvalue_ref);
- // note that operator= is called TWICE here. one on (val1+val2)
- // because the ebus results is defined as "value = unary(a, b)"
- // and in this case both operator = as well as the unary operate here.
- // meaning that the addition is actually run multiple times
- // once for (a+b) and then again, during value = unary(...) for a second time
- EXPECT_EQ(resultreducer.value.m_value1, 7); // (3 + 1) + 3
- EXPECT_EQ(resultreducer.value.m_value2, 10); // (4 + 2) + 4
- // make sure originals are not destroyed in the move
- EXPECT_EQ(val1.m_result.m_value1, 1);
- EXPECT_EQ(val1.m_result.m_value2, 2);
- EXPECT_EQ(val2.m_result.m_value1, 3);
- EXPECT_EQ(val2.m_result.m_value2, 4);
- }
- {
- EBusReduceResult<ResultClass, ResultReducerClass> resultreducer;
- MyInterfaceBus::BroadcastResult(resultreducer, &MyInterfaceBus::Events::EventY);
- EXPECT_TRUE(resultreducer.unary.m_operator_called_const); // we expect the const version to have been called this time
- EXPECT_FALSE(resultreducer.unary.m_operator_called_rvalue_ref);
- EXPECT_EQ(resultreducer.value.m_value1, 7); // (3 + 1) + 3
- EXPECT_EQ(resultreducer.value.m_value2, 10); // (4 + 2) + 4
- // make sure originals are not destroyed in the move
- EXPECT_EQ(val1.m_result.m_value1, 1);
- EXPECT_EQ(val1.m_result.m_value2, 2);
- EXPECT_EQ(val2.m_result.m_value1, 3);
- EXPECT_EQ(val2.m_result.m_value2, 4);
- }
- val1.BusDisconnect();
- val2.BusDisconnect();
- }
- // ensure RVALUE-REF move on RHS does not corrupt existing values and operates correctly
- // even if the other form is used (where T is T&)
- TEST_F(EBus, ResultsTest_ReducerCorruption_Ref)
- {
- using namespace EBusResultsTest;
- MyListener val1(1, 2);
- MyListener val2(3, 4);
- val1.BusConnect();
- val2.BusConnect();
- {
- ResultClass finalResult;
- EBusReduceResult<ResultClass&, ResultReducerClass> resultreducer(finalResult);
- MyInterfaceBus::BroadcastResult(resultreducer, &MyInterfaceBus::Events::EventX);
- EXPECT_FALSE(resultreducer.unary.m_operator_called_const);
- EXPECT_TRUE(resultreducer.unary.m_operator_called_rvalue_ref);
- EXPECT_FALSE(finalResult.m_operator_called_const);
- EXPECT_TRUE(finalResult.m_operator_called_rvalue_ref);
- EXPECT_EQ(resultreducer.value.m_value1, 7); // (3 + 1) + 3
- EXPECT_EQ(resultreducer.value.m_value2, 10); // (4 + 2) + 4
- // make sure originals are not destroyed in the move
- EXPECT_EQ(val1.m_result.m_value1, 1);
- EXPECT_EQ(val1.m_result.m_value2, 2);
- EXPECT_EQ(val2.m_result.m_value1, 3);
- EXPECT_EQ(val2.m_result.m_value2, 4);
- }
- {
- ResultClass finalResult;
- EBusReduceResult<ResultClass&, ResultReducerClass> resultreducer(finalResult);
- MyInterfaceBus::BroadcastResult(resultreducer, &MyInterfaceBus::Events::EventY);
- EXPECT_TRUE(resultreducer.unary.m_operator_called_const); // EventY is const, so we expect this to have happened again
- EXPECT_FALSE(resultreducer.unary.m_operator_called_rvalue_ref);
- // we still expect the actual finalresult to have been populated via RVALUE REF MOVE
- EXPECT_FALSE(finalResult.m_operator_called_const);
- EXPECT_TRUE(finalResult.m_operator_called_rvalue_ref);
- EXPECT_EQ(resultreducer.value.m_value1, 7); // (3 + 1) + 3
- EXPECT_EQ(resultreducer.value.m_value2, 10); // (4 + 2) + 4
- // make sure originals are not destroyed in the move
- EXPECT_EQ(val1.m_result.m_value1, 1);
- EXPECT_EQ(val1.m_result.m_value2, 2);
- EXPECT_EQ(val2.m_result.m_value1, 3);
- EXPECT_EQ(val2.m_result.m_value2, 4);
- }
- val1.BusDisconnect();
- val2.BusDisconnect();
- }
- // ensure RVALUE-REF move on RHS does not corrupt existing values.
- TEST_F(EBus, ResultsTest_AggregatorCorruption)
- {
- using namespace EBusResultsTest;
- MyListener val1(1, 2);
- MyListener val2(3, 4);
- val1.BusConnect();
- val2.BusConnect();
- {
- EBusAggregateResults<ResultClass> resultarray;
- MyInterfaceBus::BroadcastResult(resultarray, &MyInterfaceBus::Events::EventX);
- EXPECT_EQ(resultarray.values.size(), 2);
- // bus connection is unordered, so we just have to find the two values on it, can't assume they're in same order.
- EXPECT_TRUE(resultarray.values[0] == val1.m_result || resultarray.values[1] == val1.m_result);
- EXPECT_TRUE(resultarray.values[0] == val2.m_result || resultarray.values[1] == val2.m_result);
- if (resultarray.values[0] == val1.m_result)
- {
- EXPECT_EQ(resultarray.values[1], val2.m_result);
- }
- if (resultarray.values[0] == val2.m_result)
- {
- EXPECT_EQ(resultarray.values[1], val1.m_result);
- }
- // make sure originals are not destroyed in the move
- EXPECT_EQ(val1.m_result.m_value1, 1);
- EXPECT_EQ(val1.m_result.m_value2, 2);
- EXPECT_EQ(val2.m_result.m_value1, 3);
- EXPECT_EQ(val2.m_result.m_value2, 4);
- }
- {
- EBusAggregateResults<ResultClass> resultarray;
- MyInterfaceBus::BroadcastResult(resultarray, &MyInterfaceBus::Events::EventY);
- // bus connection is unordered, so we just have to find the two values on it, can't assume they're in same order.
- EXPECT_TRUE(resultarray.values[0] == val1.m_result || resultarray.values[1] == val1.m_result);
- EXPECT_TRUE(resultarray.values[0] == val2.m_result || resultarray.values[1] == val2.m_result);
- if (resultarray.values[0] == val1.m_result)
- {
- EXPECT_EQ(resultarray.values[1], val2.m_result);
- }
- if (resultarray.values[0] == val2.m_result)
- {
- EXPECT_EQ(resultarray.values[1], val1.m_result);
- }
- // make sure originals are not destroyed
- EXPECT_EQ(val1.m_result.m_value1, 1);
- EXPECT_EQ(val1.m_result.m_value2, 2);
- EXPECT_EQ(val2.m_result.m_value1, 3);
- EXPECT_EQ(val2.m_result.m_value2, 4);
- }
- val1.BusDisconnect();
- val2.BusDisconnect();
- }
- namespace EBusEnvironmentTest
- {
- class MyInterface
- {
- public:
- virtual void EventX() = 0;
- };
- using MyInterfaceBus = AZ::EBus<MyInterface, AZ::EBusTraits>;
- class MyInterfaceListener : public MyInterfaceBus::Handler
- {
- public:
- MyInterfaceListener(int environmentId = -1)
- : m_environmentId(environmentId)
- , m_numEventsX(0)
- {
- }
- void EventX() override
- {
- ++m_numEventsX;
- }
- int m_environmentId; ///< EBus environment id. -1 is global, otherwise index in the environment array.
- int m_numEventsX;
- };
- class ParallelSeparateEBusEnvironmentProcessor
- {
- public:
- using JobaToProcessArray = AZStd::vector<ParallelSeparateEBusEnvironmentProcessor, AZ::OSStdAllocator>;
- ParallelSeparateEBusEnvironmentProcessor()
- {
- m_busEvironment = AZ::EBusEnvironment::Create();
- }
- ~ParallelSeparateEBusEnvironmentProcessor()
- {
- AZ::EBusEnvironment::Destroy(m_busEvironment);
- }
- void ProcessSomethingInParallel(size_t jobId)
- {
- m_busEvironment->ActivateOnCurrentThread();
- EXPECT_EQ(0, MyInterfaceBus::GetTotalNumOfEventHandlers()); // If environments are properly separated we should have no listeners!"
- MyInterfaceListener uniqueListener((int)jobId);
- uniqueListener.BusConnect();
- const int numEventsToBroadcast = 100;
- for (int i = 0; i < numEventsToBroadcast; ++i)
- {
- // from now on all EBus calls happen in unique environment
- MyInterfaceBus::Broadcast(&MyInterfaceBus::Events::EventX);
- }
- uniqueListener.BusDisconnect();
- // Test that we have only X num events
- EXPECT_EQ(uniqueListener.m_numEventsX, numEventsToBroadcast); // If environments are properly separated we should get only the events from our environment!
- m_busEvironment->DeactivateOnCurrentThread();
- }
- static void ProcessJobsRange(JobaToProcessArray* jobs, size_t startIndex, size_t endIndex)
- {
- for (size_t i = startIndex; i <= endIndex; ++i)
- {
- (*jobs)[i].ProcessSomethingInParallel(i);
- }
- }
- AZ::EBusEnvironment* m_busEvironment;
- };
- } // EBusEnvironmentTest
- TEST_F(EBus, EBusEnvironment)
- {
- using namespace EBusEnvironmentTest;
- ParallelSeparateEBusEnvironmentProcessor::JobaToProcessArray jobsToProcess;
- jobsToProcess.resize(10000);
- MyInterfaceListener globalListener;
- globalListener.BusConnect();
- // broadcast on global bus
- MyInterfaceBus::Broadcast(&MyInterfaceBus::Events::EventX);
- // spawn a few threads to process those jobs
- AZStd::thread thread1(AZStd::bind(&ParallelSeparateEBusEnvironmentProcessor::ProcessJobsRange, &jobsToProcess, 0, 1999));
- AZStd::thread thread2(AZStd::bind(&ParallelSeparateEBusEnvironmentProcessor::ProcessJobsRange, &jobsToProcess, 2000, 3999));
- AZStd::thread thread3(AZStd::bind(&ParallelSeparateEBusEnvironmentProcessor::ProcessJobsRange, &jobsToProcess, 4000, 5999));
- AZStd::thread thread4(AZStd::bind(&ParallelSeparateEBusEnvironmentProcessor::ProcessJobsRange, &jobsToProcess, 6000, 7999));
- AZStd::thread thread5(AZStd::bind(&ParallelSeparateEBusEnvironmentProcessor::ProcessJobsRange, &jobsToProcess, 8000, 9999));
- thread5.join();
- thread4.join();
- thread3.join();
- thread2.join();
- thread1.join();
- globalListener.BusDisconnect();
- EXPECT_EQ(1, globalListener.m_numEventsX); // If environments are properly separated we should get only the events the global/default Environment!
- }
- // Test disconnecting while in ConnectionPolicy
- class BusWithConnectionPolicy
- : public AZ::EBusTraits
- {
- public:
- virtual ~BusWithConnectionPolicy() = default;
- virtual void MessageWhichOccursDuringConnect() = 0;
- template<class Bus>
- struct ConnectionPolicy : public AZ::EBusConnectionPolicy<Bus>
- {
- static void Connect(typename Bus::BusPtr&, typename Bus::Context&, typename Bus::HandlerNode& handler, typename Bus::Context::ConnectLockGuard& , const typename Bus::BusIdType&)
- {
- handler->MessageWhichOccursDuringConnect();
- }
- };
- };
- using BusWithConnectionPolicyBus = AZ::EBus<BusWithConnectionPolicy>;
- class HandlerWhichDisconnectsDuringDelivery
- : public BusWithConnectionPolicyBus::Handler
- {
- void MessageWhichOccursDuringConnect() override
- {
- BusDisconnect();
- }
- };
- TEST_F(EBus, ConnectionPolicy_DisconnectDuringDelivery)
- {
- HandlerWhichDisconnectsDuringDelivery handlerTest;
- handlerTest.BusConnect();
- }
- class BusWithConnectionPolicyUnlocksBeforeHandler
- : public AZ::EBusTraits
- {
- public:
- using MutexType = AZStd::recursive_mutex;
- virtual ~BusWithConnectionPolicyUnlocksBeforeHandler() = default;
- virtual int GetPreUnlockDelay() const { return 0; }
- virtual int GetPostUnlockDelay() const { return 0; }
- virtual bool ShouldUnlock() const { return true; }
- virtual void MessageWhichOccursDuringConnect() = 0;
- template<class Bus>
- struct ConnectionPolicy : public AZ::EBusConnectionPolicy<Bus>
- {
- static void Connect(typename Bus::BusPtr&, typename Bus::Context&, typename Bus::HandlerNode& handler, typename Bus::Context::ConnectLockGuard& connectLock, const typename Bus::BusIdType&)
- {
- AZStd::this_thread::sleep_for(AZStd::chrono::microseconds(handler->GetPreUnlockDelay()));
- if (handler->ShouldUnlock())
- {
- connectLock.unlock();
- }
- AZStd::this_thread::sleep_for(AZStd::chrono::microseconds(handler->GetPostUnlockDelay()));
- handler->MessageWhichOccursDuringConnect();
- }
- };
- };
- using BusWithConnectionPolicyUnlocksBus = AZ::EBus<BusWithConnectionPolicyUnlocksBeforeHandler>;
- class DelayUnlockHandler
- : public BusWithConnectionPolicyUnlocksBus::Handler
- {
- public:
- DelayUnlockHandler() = default;
- DelayUnlockHandler(int preDelay, int postDelay) :
- m_preDelay(preDelay),
- m_postDelay(postDelay)
- {
- }
- void MessageWhichOccursDuringConnect() override
- {
- if (m_connectMethod)
- {
- m_connectMethod();
- }
- m_didConnect = true;
- }
- int GetPreUnlockDelay() const override { return m_preDelay; }
- int GetPostUnlockDelay() const override { return m_postDelay; }
- bool ShouldUnlock() const override { return m_shouldUnlock; }
- bool m_shouldUnlock{ true };
- AZStd::atomic_bool m_didConnect{ false };
- int m_preDelay{ 0 };
- int m_postDelay{ 0 };
- AZStd::function<void()> m_connectMethod;
- };
- TEST_F(EBus, ConnectionPolicy_DisconnectDuringDeliveryUnlocked_Success)
- {
- DelayUnlockHandler handlerTest;
- handlerTest.m_connectMethod = [&handlerTest]() { handlerTest.BusDisconnect(); };
- handlerTest.BusConnect();
- EXPECT_EQ(handlerTest.m_didConnect, true);
- }
- TEST_F(EBus, ConnectionPolicy_DisconnectDuringDeliveryDelayUnlocked_Success)
- {
- constexpr int numTests = 100;
- for (int disconnectTest = 0; disconnectTest < numTests; ++disconnectTest)
- {
- DelayUnlockHandler handlerTest(0, 1);
- handlerTest.BusConnect();
- AZStd::thread disconnectThread([&handlerTest]()
- {
- handlerTest.BusDisconnect();
- }
- );
- disconnectThread.join();
- EXPECT_EQ(handlerTest.m_didConnect, true);
- }
- }
- TEST_F(EBus, ConnectionPolicy_DisconnectDuringDeliveryPreDelayUnlocked_Success)
- {
- constexpr int numTests = 100;
- for (int disconnectTest = 0; disconnectTest < numTests; ++disconnectTest)
- {
- DelayUnlockHandler handlerTest(1, 0);
- handlerTest.BusConnect();
- AZStd::thread disconnectThread([&handlerTest]()
- {
- handlerTest.BusDisconnect();
- }
- );
- disconnectThread.join();
- EXPECT_EQ(handlerTest.m_didConnect, true);
- }
- }
- TEST_F(EBus, ConnectionPolicy_WaitOnSecondHandlerWhileStillLocked_CantComplete)
- {
- DelayUnlockHandler waitHandler;
- // Test without releasing the lock - this is expected to prevent our second handler from connecting
- // so will block this thread
- waitHandler.m_shouldUnlock = false;
- DelayUnlockHandler connectHandler;
- waitHandler.m_connectMethod = [&connectHandler]()
- {
- constexpr int waitMsMax = 100;
- auto startTime = AZStd::chrono::steady_clock::now();
- auto endTime = startTime + AZStd::chrono::milliseconds(waitMsMax);
- // The other bus should not be able to complete because we're still holding the connect lock
- while (AZStd::chrono::steady_clock::now() < endTime && !connectHandler.BusIsConnected())
- {
- AZStd::this_thread::yield();
- }
- EXPECT_GE(AZStd::chrono::steady_clock::now(), endTime);
- };
- AZStd::thread connectThread([&connectHandler, &waitHandler]()
- {
- constexpr int waitMsMax = 100;
- auto startTime = AZStd::chrono::steady_clock::now();
- auto endTime = startTime + AZStd::chrono::milliseconds(waitMsMax);
- while (AZStd::chrono::steady_clock::now() < endTime && !waitHandler.m_didConnect)
- {
- AZStd::this_thread::yield();
- }
- connectHandler.BusConnect();
- }
- );
- waitHandler.BusConnect();
- connectThread.join();
- EXPECT_EQ(connectHandler.m_didConnect, true);
- EXPECT_EQ(waitHandler.m_didConnect, true);
- waitHandler.BusDisconnect();
- connectHandler.BusDisconnect();
- }
- TEST_F(EBus, ConnectionPolicy_WaitOnSecondHandlerWhileUnlocked_CanComplete)
- {
- constexpr int numTests = 20;
- for (int connectTest = 0; connectTest < numTests; ++connectTest)
- {
- DelayUnlockHandler waitHandler;
- DelayUnlockHandler connectHandler;
- waitHandler.m_connectMethod = [&connectHandler]()
- {
- // Check that a connection for the connectHandler has occured
- // within a 1 second, which should be more than enough
- // time for a connection to occur even when the system is under load
- constexpr int waitMsMax = 1000;
- auto startTime = AZStd::chrono::steady_clock::now();
- auto endTime = startTime + AZStd::chrono::milliseconds(waitMsMax);
- // The other bus should be able to connect
- while (AZStd::chrono::steady_clock::now() < endTime && !connectHandler.m_didConnect)
- {
- AZStd::this_thread::yield();
- }
- EXPECT_EQ(connectHandler.BusIsConnected(), true);
- EXPECT_LE(AZStd::chrono::steady_clock::now(), endTime);
- };
- AZStd::thread connectThread([&connectHandler]()
- {
- connectHandler.BusConnect();
- });
- waitHandler.BusConnect();
- connectThread.join();
- EXPECT_EQ(connectHandler.m_didConnect, true);
- EXPECT_EQ(waitHandler.m_didConnect, true);
- waitHandler.BusDisconnect();
- connectHandler.BusDisconnect();
- }
- }
- class IdBusWithConnectionPolicy
- : public AZ::EBusTraits
- {
- public:
- static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
- using BusIdType = int64_t;
- virtual ~IdBusWithConnectionPolicy() = default;
- virtual void MessageWhichOccursDuringConnect() = 0;
- virtual void MessageWhichOccursDuringDisconnect() = 0;
- template<class Bus>
- struct ConnectionPolicy : public AZ::EBusConnectionPolicy<Bus>
- {
- static void Connect(typename Bus::BusPtr&, typename Bus::Context&, typename Bus::HandlerNode& handler, typename Bus::Context::ConnectLockGuard& , const typename Bus::BusIdType&)
- {
- handler->MessageWhichOccursDuringConnect();
- }
- static void Disconnect([[maybe_unused]] typename Bus::Context& context, typename Bus::HandlerNode& handler, [[maybe_unused]] typename Bus::BusPtr& ptr)
- {
- handler->MessageWhichOccursDuringDisconnect();
- }
- };
- };
- using IdBusWithConnectionPolicyBus = AZ::EBus<IdBusWithConnectionPolicy>;
- class MultiHandlerWhichDisconnectsDuringDelivery
- : public IdBusWithConnectionPolicyBus::MultiHandler
- {
- void MessageWhichOccursDuringConnect() override
- {
- auto busIdRef = IdBusWithConnectionPolicyBus::GetCurrentBusId();
- EXPECT_NE(nullptr, busIdRef);
- BusDisconnect(*busIdRef);
- }
- void MessageWhichOccursDuringDisconnect() override
- {
- auto busIdRef = IdBusWithConnectionPolicyBus::GetCurrentBusId();
- EXPECT_NE(nullptr, busIdRef);
- }
- };
- static constexpr int64_t multiHandlerTestBusId = 42;
- TEST_F(EBus, MultiHandlerConnectionPolicy_DisconnectDuringDelivery)
- {
- MultiHandlerWhichDisconnectsDuringDelivery multiHandlerTest;
- multiHandlerTest.BusConnect(multiHandlerTestBusId);
- EXPECT_EQ(0U, IdBusWithConnectionPolicyBus::GetTotalNumOfEventHandlers());
- }
- class BusWithConnectionAndDisconnectPolicy
- : public AZ::EBusTraits
- {
- public:
- static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
- using BusIdType = int32_t;
- virtual ~BusWithConnectionAndDisconnectPolicy() = default;
- virtual void MessageWhichOccursDuringConnect(int32_t id) = 0;
- virtual void MessageWhichOccursDuringDisconnect(int32_t id) = 0;
- template<class Bus>
- struct ConnectionPolicy : public AZ::EBusConnectionPolicy<Bus>
- {
- static void Connect(typename Bus::BusPtr& ptr, [[maybe_unused]] typename Bus::Context& context, typename Bus::HandlerNode& handler, typename Bus::Context::ConnectLockGuard&, const typename Bus::BusIdType& id)
- {
- EXPECT_EQ(handler.GetBusId(), id);
- EXPECT_EQ(handler.m_holder, ptr);
- handler->MessageWhichOccursDuringConnect(handler.GetBusId());
- }
- static void Disconnect([[maybe_unused]] typename Bus::Context& context, typename Bus::HandlerNode& handler, typename Bus::BusPtr& ptr)
- {
- EXPECT_EQ(handler.m_holder, ptr);
- handler->MessageWhichOccursDuringDisconnect(handler.GetBusId());
- }
- };
- };
- using BusWithConnectionAndDisconnectPolicyBus = AZ::EBus<BusWithConnectionAndDisconnectPolicy>;
- struct HandlerTrackingConnectionDisconnectionIds
- : public BusWithConnectionAndDisconnectPolicyBus::Handler
- {
- void MessageWhichOccursDuringConnect(int32_t id) override
- {
- m_lastConnectId = id;
- }
- void MessageWhichOccursDuringDisconnect(int32_t id) override
- {
- m_lastDisconnectId = id;
- }
- int32_t m_lastConnectId = 0;
- int32_t m_lastDisconnectId = 0;
- };
- TEST_F(EBus, ConnectionPolicy_ConnectDisconnect_CorrectIds)
- {
- HandlerTrackingConnectionDisconnectionIds idsHandler;
- EXPECT_EQ(idsHandler.m_lastConnectId, 0);
- EXPECT_EQ(idsHandler.m_lastDisconnectId, 0);
- idsHandler.BusConnect(123);
- EXPECT_TRUE(idsHandler.BusIsConnectedId(123));
- EXPECT_EQ(idsHandler.m_lastConnectId, 123);
- idsHandler.BusDisconnect(123);
- EXPECT_FALSE(idsHandler.BusIsConnectedId(123));
- EXPECT_EQ(idsHandler.m_lastDisconnectId, 123);
- idsHandler.BusConnect(234);
- EXPECT_TRUE(idsHandler.BusIsConnectedId(234));
- EXPECT_EQ(idsHandler.m_lastConnectId, 234);
- idsHandler.BusDisconnect();
- EXPECT_FALSE(idsHandler.BusIsConnectedId(234));
- EXPECT_EQ(idsHandler.m_lastDisconnectId, 234);
- }
- struct LastHandlerDisconnectInterface
- : public AZ::EBusTraits
- {
- static const EBusHandlerPolicy HandlerPolicy = EBusHandlerPolicy::Multiple;
- static const EBusAddressPolicy AddressPolicy = EBusAddressPolicy::ById;
- typedef size_t BusIdType;
- virtual void OnEvent() = 0;
- };
- using LastHandlerDisconnectBus = AZ::EBus<LastHandlerDisconnectInterface>;
- struct LastHandlerDisconnectHandler
- : public LastHandlerDisconnectBus::Handler
- {
- void OnEvent() override
- {
- ++m_numOnEvents;
- BusDisconnect();
- }
- unsigned int m_numOnEvents = 0;
- };
- TEST_F(EBus, LastHandlerDisconnectForward)
- {
- LastHandlerDisconnectHandler lastHandler;
- lastHandler.BusConnect(0);
- EBUS_EVENT_ID(0, LastHandlerDisconnectBus, OnEvent);
- EXPECT_FALSE(lastHandler.BusIsConnected());
- EXPECT_EQ(1, lastHandler.m_numOnEvents);
- }
- TEST_F(EBus, LastHandlerDisconnectReverse)
- {
- LastHandlerDisconnectHandler lastHandler;
- lastHandler.BusConnect(0);
- EBUS_EVENT_ID_REVERSE(0, LastHandlerDisconnectBus, OnEvent);
- EXPECT_FALSE(lastHandler.BusIsConnected());
- EXPECT_EQ(1, lastHandler.m_numOnEvents);
- }
- struct DisconnectAssertInterface
- : public AZ::EBusTraits
- {
- using MutexType = AZStd::recursive_mutex;
- virtual ~DisconnectAssertInterface() = default;
- virtual void OnEvent() {};
- };
- using DisconnectAssertBus = AZ::EBus<DisconnectAssertInterface>;
- struct DisconnectAssertHandler
- : public DisconnectAssertBus::Handler
- {
- };
- TEST_F(EBus, HandlerDestroyedWithoutDisconnect_Asserts)
- {
- // EBus handlers with a non-NullMutex assert on disconnect if they have not been explicitly disconnected before the internal EBus::Handler destructor is invoked.
- // The reason for the assert is because the BusDisconnect call will lock the EBus context mutex to safely disconnect the handler, but if the handler is still
- // connected to the EBus, another thread could access it after the vtable for the derived class has been reset.
- AZ_TEST_START_TRACE_SUPPRESSION;
- {
- DisconnectAssertHandler handler;
- handler.BusConnect();
- }
- AZ_TEST_STOP_TRACE_SUPPRESSION(1);
- }
- TEST_F(EBus, HandlerDestroyedAfterDisconnect_DoesNotAssert)
- {
- {
- DisconnectAssertHandler handler;
- handler.BusConnect();
- handler.BusDisconnect();
- }
- }
- struct SingleHandlerPerIdTestRequests
- : public AZ::EBusTraits
- {
- static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
- static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
- using BusIdType = int32_t;
- };
- using SingleHandlerPerIdTestRequestBus = AZ::EBus<SingleHandlerPerIdTestRequests>;
- struct SingleHandlerPerIdTestImpl : public SingleHandlerPerIdTestRequestBus::Handler
- {
- void Connect(SingleHandlerPerIdTestRequestBus::BusIdType busId)
- {
- SingleHandlerPerIdTestRequestBus::Handler::BusConnect(busId);
- }
- void Disconnect()
- {
- SingleHandlerPerIdTestRequestBus::Handler::BusDisconnect();
- }
- };
- struct MultipleHandlersPerIdTestRequests
- : public AZ::EBusTraits
- {
- static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
- static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
- using BusIdType = int32_t;
- };
- using MultipleHandlersPerIdTestRequestBus = AZ::EBus<MultipleHandlersPerIdTestRequests>;
- struct MultipleHandlersPerIdTestImpl
- : public MultipleHandlersPerIdTestRequestBus::MultiHandler
- {
- void Connect(MultipleHandlersPerIdTestRequestBus::BusIdType busId)
- {
- MultipleHandlersPerIdTestRequestBus::MultiHandler::BusConnect(busId);
- }
- void Disconnect()
- {
- MultipleHandlersPerIdTestRequestBus::MultiHandler::BusDisconnect();
- }
- };
- TEST_F(EBus, HasHandlersAddressId)
- {
- SingleHandlerPerIdTestImpl idTestRequest;
- idTestRequest.Connect(4);
- // note: arbitrary numbers selected
- EXPECT_TRUE(SingleHandlerPerIdTestRequestBus::HasHandlers(4));
- EXPECT_FALSE(SingleHandlerPerIdTestRequestBus::HasHandlers(10));
- idTestRequest.Disconnect();
- }
- TEST_F(EBus, HasHandlersWithSingleHandlerListeningToMultipleIds)
- {
- MultipleHandlersPerIdTestImpl idTestRequest;
- idTestRequest.Connect(4);
- idTestRequest.Connect(7);
- idTestRequest.Connect(10);
- // note: arbitrary numbers selected
- EXPECT_TRUE(MultipleHandlersPerIdTestRequestBus::HasHandlers(4));
- EXPECT_TRUE(MultipleHandlersPerIdTestRequestBus::HasHandlers(7));
- EXPECT_TRUE(MultipleHandlersPerIdTestRequestBus::HasHandlers(10));
- EXPECT_FALSE(MultipleHandlersPerIdTestRequestBus::HasHandlers(15));
- idTestRequest.Disconnect();
- }
- TEST_F(EBus, HasHandlersWithMultipleHandlersOnSameId)
- {
- MultipleHandlersPerIdTestImpl id1, id2;
- id1.Connect(4);
- id2.Connect(4);
- EXPECT_TRUE(MultipleHandlersPerIdTestRequestBus::HasHandlers(4));
- EXPECT_FALSE(MultipleHandlersPerIdTestRequestBus::HasHandlers(7));
- }
- TEST_F(EBus, HasHandlersWithTwoSingleHandlersOnDifferentIds)
- {
- SingleHandlerPerIdTestImpl id1, id2;
- id1.Connect(4);
- id2.Connect(5);
- EXPECT_TRUE(SingleHandlerPerIdTestRequestBus::HasHandlers(4));
- EXPECT_TRUE(SingleHandlerPerIdTestRequestBus::HasHandlers(5));
- EXPECT_FALSE(SingleHandlerPerIdTestRequestBus::HasHandlers(7));
- }
- TEST_F(EBus, HasHandlersAddressPtr)
- {
- // note: arbitrary numbers selected
- constexpr SingleHandlerPerIdTestRequestBus::BusIdType validBusId = 4;
- constexpr SingleHandlerPerIdTestRequestBus::BusIdType invalidBusId = 10;
- SingleHandlerPerIdTestImpl idTestRequest;
- idTestRequest.Connect(validBusId);
- SingleHandlerPerIdTestRequestBus::BusPtr validBusIdPtr;
- SingleHandlerPerIdTestRequestBus::Bind(validBusIdPtr, validBusId);
- SingleHandlerPerIdTestRequestBus::BusPtr invalidBusIdPtr;
- SingleHandlerPerIdTestRequestBus::Bind(invalidBusIdPtr, invalidBusId);
- EXPECT_TRUE(SingleHandlerPerIdTestRequestBus::HasHandlers(validBusIdPtr));
- EXPECT_FALSE(SingleHandlerPerIdTestRequestBus::HasHandlers(invalidBusIdPtr));
- idTestRequest.Disconnect();
- }
- // IsInDispatchThisThread
- struct IsInThreadDispatchRequests
- : AZ::EBusTraits
- {
- using MutexType = AZStd::recursive_mutex;
- };
- using IsInThreadDispatchBus = AZ::EBus<IsInThreadDispatchRequests>;
- class IsInThreadDispatchHandler
- : public IsInThreadDispatchBus::Handler
- {};
- TEST_F(EBus, InvokingIsInThisThread_ReturnsSuccess_OnlyIfThreadIsInDispatch)
- {
- IsInThreadDispatchHandler handler;
- handler.BusConnect();
- auto ThreadDispatcher = [](IsInThreadDispatchRequests*)
- {
- EXPECT_TRUE(IsInThreadDispatchBus::IsInDispatchThisThread());
- auto PerThreadBusDispatch = []()
- {
- EXPECT_FALSE(IsInThreadDispatchBus::IsInDispatchThisThread());
- };
- AZStd::array threads{ AZStd::thread(PerThreadBusDispatch), AZStd::thread(PerThreadBusDispatch) };
- for (AZStd::thread& thread : threads)
- {
- thread.join();
- }
- };
- static constexpr size_t ThreadDispatcherIterations = 4;
- for (size_t iteration = 0; iteration < ThreadDispatcherIterations; ++iteration)
- {
- EXPECT_FALSE(IsInThreadDispatchBus::IsInDispatchThisThread());
- IsInThreadDispatchBus::Broadcast(ThreadDispatcher);
- EXPECT_FALSE(IsInThreadDispatchBus::IsInDispatchThisThread());
- }
- }
- // Thread Dispatch Policy
- struct ThreadDispatchTestBusTraits
- : AZ::EBusTraits
- {
- using MutexType = AZStd::recursive_mutex;
- struct PostThreadDispatchTestInvoker
- {
- ~PostThreadDispatchTestInvoker();
- };
- template <typename DispatchMutex>
- struct ThreadDispatchTestLockGuard
- {
- ThreadDispatchTestLockGuard(DispatchMutex& contextMutex)
- : m_lock{ contextMutex }
- {}
- ThreadDispatchTestLockGuard(DispatchMutex& contextMutex, AZStd::adopt_lock_t adopt_lock)
- : m_lock{ contextMutex, adopt_lock }
- {}
- ThreadDispatchTestLockGuard(const ThreadDispatchTestLockGuard&) = delete;
- ThreadDispatchTestLockGuard& operator=(const ThreadDispatchTestLockGuard&) = delete;
- private:
- PostThreadDispatchTestInvoker m_threadPolicyInvoker;
- using LockType = AZStd::conditional_t<LocklessDispatch, AZ::Internal::NullLockGuard<DispatchMutex>, AZStd::scoped_lock<DispatchMutex>>;
- LockType m_lock;
- };
- template <typename DispatchMutex, bool IsLocklessDispatch>
- using DispatchLockGuard = ThreadDispatchTestLockGuard<DispatchMutex>;
- static inline AZStd::atomic<int32_t> s_threadPostDispatchCalls;
- };
- class ThreadDispatchTestRequests
- {
- public:
- virtual void FirstCall() = 0;
- virtual void SecondCall() = 0;
- virtual void ThirdCall() = 0;
- };
- using ThreadDispatchTestBus = AZ::EBus<ThreadDispatchTestRequests, ThreadDispatchTestBusTraits>;
- ThreadDispatchTestBusTraits::PostThreadDispatchTestInvoker::~PostThreadDispatchTestInvoker()
- {
- if (!ThreadDispatchTestBus::IsInDispatchThisThread())
- {
- ++s_threadPostDispatchCalls;
- }
- }
- class ThreadDispatchTestHandler
- : public ThreadDispatchTestBus::Handler
- {
- public:
- void Connect()
- {
- ThreadDispatchTestBus::Handler::BusConnect();
- }
- void Disconnect()
- {
- ThreadDispatchTestBus::Handler::BusDisconnect();
- }
- void FirstCall() override
- {
- ThreadDispatchTestBus::Broadcast(&ThreadDispatchTestBus::Events::SecondCall);
- }
- void SecondCall() override
- {
- ThreadDispatchTestBus::Broadcast(&ThreadDispatchTestBus::Events::ThirdCall);
- }
- void ThirdCall() override
- {
- }
- };
- template <typename ParamType>
- class EBusParamFixture
- : public LeakDetectionFixture
- , public ::testing::WithParamInterface<ParamType>
- {};
- struct ThreadDispatchParams
- {
- size_t m_threadCount{};
- size_t m_handlerCount{};
- };
- using ThreadDispatchParamFixture = EBusParamFixture<ThreadDispatchParams>;
- INSTANTIATE_TEST_SUITE_P(
- ThreadDispatch,
- ThreadDispatchParamFixture,
- ::testing::Values(
- ThreadDispatchParams{ 1, 1 },
- ThreadDispatchParams{ 2, 1 },
- ThreadDispatchParams{ 1, 2 },
- ThreadDispatchParams{ 2, 2 },
- ThreadDispatchParams{ 16, 8 }
- )
- );
- TEST_P(ThreadDispatchParamFixture, CustomDispatchLockGuard_InvokesPostDispatchFunction_AfterThreadHasFinishedDispatch)
- {
- ThreadDispatchTestBusTraits::s_threadPostDispatchCalls = 0;
- ThreadDispatchParams threadDispatchParams = GetParam();
- AZStd::vector<AZStd::thread> testThreads;
- AZStd::vector<ThreadDispatchTestHandler> testHandlers(threadDispatchParams.m_handlerCount);
- for (ThreadDispatchTestHandler& testHandler : testHandlers)
- {
- testHandler.Connect();
- }
- static constexpr size_t DispatchThreadCalls = 3;
- const size_t totalThreadDispatchCalls = threadDispatchParams.m_threadCount * DispatchThreadCalls;
- auto DispatchThreadWorker = []()
- {
- ThreadDispatchTestBus::Broadcast(&ThreadDispatchTestBus::Events::FirstCall);
- ThreadDispatchTestBus::Broadcast(&ThreadDispatchTestBus::Events::SecondCall);
- ThreadDispatchTestBus::Broadcast(&ThreadDispatchTestBus::Events::ThirdCall);
- };
- for (size_t threadIndex = 0; threadIndex < threadDispatchParams.m_threadCount; ++threadIndex)
- {
- testThreads.emplace_back(DispatchThreadWorker);
- }
- for (AZStd::thread& thread : testThreads)
- {
- thread.join();
- }
- for (ThreadDispatchTestHandler& testHandler : testHandlers)
- {
- testHandler.Disconnect();
- }
- EXPECT_EQ(totalThreadDispatchCalls, ThreadDispatchTestBusTraits::s_threadPostDispatchCalls);
- ThreadDispatchTestBusTraits::s_threadPostDispatchCalls = 0;
- }
- struct ReentrantEBusUseTestRequests : public AZ::EBusTraits
- {
- static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
- static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
- using BusIdType = int32_t;
- // This event will directly call itself recursively via EBus.
- virtual void EventDirectlyCallsItself(bool expectedReentrantResult) = 0;
- // The first event will call the second event via EBus.
- virtual void EventCallsOtherEventOnSameEBus() = 0;
- virtual bool EventCalledByOtherEventOnSameEBus() = 0;
- // The first event will call the second event via EBus, but on different bus IDs.
- virtual void EventCallsOtherEventOnDifferentEBusId(BusIdType busId) = 0;
- virtual bool EventCalledByOtherEventOnDifferentEBusId() = 0;
- };
- using ReentrantEBusUseTestRequestBus = AZ::EBus<ReentrantEBusUseTestRequests>;
- struct ReentrantEBusUseTestImpl : public ReentrantEBusUseTestRequestBus::Handler
- {
- ReentrantEBusUseTestImpl(ReentrantEBusUseTestRequestBus::BusIdType busId)
- {
- m_busId = busId;
- ReentrantEBusUseTestRequestBus::Handler::BusConnect(busId);
- }
- ~ReentrantEBusUseTestImpl()
- {
- ReentrantEBusUseTestRequestBus::Handler::BusDisconnect();
- }
- void EventDirectlyCallsItself(bool expectedReentrantResult) override
- {
- // Verify that we get the expected API result. (We use ASSERT_EQ because a test failure here might cause infinite recursion)
- ASSERT_EQ(ReentrantEBusUseTestRequestBus::HasReentrantEBusUseThisThread(), expectedReentrantResult);
- // Avoid infinite recursion. :)
- if (ReentrantEBusUseTestRequestBus::HasReentrantEBusUseThisThread())
- {
- return;
- }
- // This event calls itself via a nested EBus call. We expect the nested call to detect the reentrancy.
- ReentrantEBusUseTestRequestBus::Event(m_busId, &ReentrantEBusUseTestRequestBus::Events::EventDirectlyCallsItself, true);
- }
- void EventCallsOtherEventOnSameEBus() override
- {
- // Call a second event on the same EBus and verify that it was called.
- bool otherEventCalled = false;
- ReentrantEBusUseTestRequestBus::EventResult(otherEventCalled, m_busId,
- &ReentrantEBusUseTestRequestBus::Events::EventCalledByOtherEventOnSameEBus);
- EXPECT_TRUE(otherEventCalled);
- }
- bool EventCalledByOtherEventOnSameEBus() override
- {
- // Verify that even though two different events have been called on the same EBus,
- // it is still considered reentrant use of the ebus itself.
- EXPECT_TRUE(ReentrantEBusUseTestRequestBus::HasReentrantEBusUseThisThread());
- return true;
- }
- void EventCallsOtherEventOnDifferentEBusId(BusIdType busId) override
- {
- // Call a second event on a different EBus and verify that it was called.
- bool otherEventCalled = false;
- ReentrantEBusUseTestRequestBus::EventResult(otherEventCalled, busId,
- &ReentrantEBusUseTestRequestBus::Events::EventCalledByOtherEventOnDifferentEBusId);
- EXPECT_TRUE(otherEventCalled);
- }
- bool EventCalledByOtherEventOnDifferentEBusId() override
- {
- // Verify that two different nested events on the same EBus but with different EBus IDs will not be detected as reentrant.
- EXPECT_FALSE(ReentrantEBusUseTestRequestBus::HasReentrantEBusUseThisThread());
- return true;
- }
- protected:
- ReentrantEBusUseTestRequestBus::BusIdType m_busId;
- };
- TEST_F(EBus, ReentrantEBusUsageDetectedFromNestedDirectCalls)
- {
- constexpr int32_t busId = 4;
- ReentrantEBusUseTestImpl reentrantEBusUseTestRequest(busId);
- constexpr bool expectedReentrantResult = false;
- ReentrantEBusUseTestRequestBus::Event(
- busId, &ReentrantEBusUseTestRequestBus::Events::EventDirectlyCallsItself, expectedReentrantResult);
- }
- TEST_F(EBus, ReentrantEBusUsageDetectedFromTwoSeparateCallsOnSameBus)
- {
- constexpr int32_t busId = 4;
- ReentrantEBusUseTestImpl reentrantEBusUseTestRequest(busId);
- ReentrantEBusUseTestRequestBus::Event(busId, &ReentrantEBusUseTestRequestBus::Events::EventCallsOtherEventOnSameEBus);
- }
- TEST_F(EBus, ReentrantEBusUsageNotDetectedFromTwoSeparateCallsOnSameBusWithDifferentIds)
- {
- constexpr int32_t busId = 4;
- ReentrantEBusUseTestImpl reentrantEBusUseTestRequest(busId);
- constexpr int32_t secondBusId = 8;
- ReentrantEBusUseTestImpl secondReentrantEBusUseTestRequest(secondBusId);
- ReentrantEBusUseTestRequestBus::Event(busId,
- &ReentrantEBusUseTestRequestBus::Events::EventCallsOtherEventOnDifferentEBusId, secondBusId);
- }
- } // namespace UnitTest
- #if defined(HAVE_BENCHMARK)
- //-------------------------------------------------------------------------
- // PERF TESTS
- //-------------------------------------------------------------------------
- namespace Benchmark
- {
- namespace BenchmarkSettings
- {
- namespace
- {
- // How many addresses/handlers count as "many"
- static const int Many = 1000;
- }
- void Common(::benchmark::internal::Benchmark* benchmark)
- {
- benchmark
- ->Unit(::benchmark::kNanosecond)
- ;
- }
- void OneToOne(::benchmark::internal::Benchmark* benchmark)
- {
- Common(benchmark);
- benchmark
- ->ArgNames({ { "Addresses" },{ "Handlers" } })
- ->Args({ 0, 0 })
- ->Args({ 1, 1 })
- ;
- }
- void OneToMany(::benchmark::internal::Benchmark* benchmark)
- {
- OneToOne(benchmark);
- benchmark
- ->Args({ 1, Many })
- ;
- }
- void ManyToOne(::benchmark::internal::Benchmark* benchmark)
- {
- OneToOne(benchmark);
- benchmark
- ->Args({ Many, 1 })
- ;
- }
- void ManyToMany(::benchmark::internal::Benchmark* benchmark)
- {
- OneToOne(benchmark);
- benchmark
- ->Args({ 1, Many })
- ->Args({ Many, 1 })
- ->Args({ Many, Many })
- ;
- }
- // Expected that this will be called after one of the above, so Common not called
- void Multithreaded(::benchmark::internal::Benchmark* benchmark)
- {
- benchmark
- ->ThreadRange(1, 8)
- ->ThreadPerCpu();
- ;
- }
- }
- // AZ Benchmark environment used to initialize all EBus Handlers and then shared them with each benchmark test
- template<typename Bus>
- class BM_EBusEnvironment
- : public AZ::Test::BenchmarkEnvironmentBase
- {
- public:
- using BusType = Bus;
- using HandlerT = Handler<Bus>;
- BM_EBusEnvironment()
- {
- }
- void SetUpBenchmark() override
- {
- // Created the container for the EBusHandlers
- m_handlers = std::make_unique<std::vector<HandlerT>>();
- // Connect handlers
- constexpr bool multiAddress = Bus::Traits::AddressPolicy != AZ::EBusAddressPolicy::Single;
- constexpr bool multiHandler = Bus::Traits::HandlerPolicy != AZ::EBusHandlerPolicy::Single;
- constexpr int64_t numAddresses{ multiAddress ? BenchmarkSettings::Many : 1 };
- constexpr int64_t numHandlers{ multiHandler ? BenchmarkSettings::Many : 1 };
- constexpr bool connectOnConstruct{ false };
- AZ::BetterPseudoRandom random;
- m_handlers->reserve(numAddresses * numHandlers);
- for (int64_t address = 0; address < numAddresses; ++address)
- {
- for (int64_t handler = 0; handler < numHandlers; ++handler)
- {
- int handlerOrder{};
- random.GetRandom(handlerOrder);
- m_handlers->emplace_back(HandlerT(static_cast<int>(address), handlerOrder, connectOnConstruct));
- }
- }
- }
- void TearDownBenchmark() override
- {
- // Deallocate the memory associated with the EBusHandlers
- m_handlers.reset();
- }
- void Connect(::benchmark::State& state)
- {
- int64_t numAddresses = state.range(0);
- int64_t numHandlers = state.range(1);
- const size_t totalHandlers = static_cast<size_t>(numAddresses * numHandlers);
- // Connect handlers
- for (size_t handlerIndex = 0; handlerIndex < totalHandlers; ++handlerIndex)
- {
- if(handlerIndex < m_handlers->size())
- {
- (*m_handlers)[handlerIndex].Connect();
- }
- }
- }
- void Disconnect(::benchmark::State& state)
- {
- int64_t numAddresses = state.range(0);
- int64_t numHandlers = state.range(1);
- const size_t totalHandlers = static_cast<size_t>(numAddresses * numHandlers);
- // Disconnect handlers
- for (size_t handlerIndex = 0; handlerIndex < totalHandlers; ++handlerIndex)
- {
- if (handlerIndex < m_handlers->size())
- {
- (*m_handlers)[handlerIndex].Disconnect();
- }
- }
- }
- protected:
- std::unique_ptr<std::vector<HandlerT>> m_handlers;
- };
- // Using a variable template to initialize the benchmark EBus_Environment on template instantiation
- template<typename Bus>
- static BM_EBusEnvironment<Bus>& s_benchmarkEBusEnv = AZ::Test::RegisterBenchmarkEnvironment<BM_EBusEnvironment<Bus>>();
- // Internal macro callback for listing all buses requiring ids
- #define BUS_BENCHMARK_PRIVATE_LIST_ID(cb, fn) \
- cb(fn, ManyToOne, ManyToOne) \
- cb(fn, ManyToMany, ManyToMany) \
- cb(fn, ManyToManyOrdered, ManyToMany) \
- cb(fn, ManyOrderedToOne, ManyToOne) \
- cb(fn, ManyOrderedToMany, ManyToMany) \
- cb(fn, ManyOrderedToManyOrdered, ManyToMany)
- // Internal macro callback for listing all buses
- #define BUS_BENCHMARK_PRIVATE_LIST_ALL(cb, fn) \
- cb(fn, OneToOne, OneToOne) \
- cb(fn, OneToMany, OneToMany) \
- cb(fn, OneToManyOrdered, OneToMany) \
- BUS_BENCHMARK_PRIVATE_LIST_ID(cb, fn)
- // Internal macro callback for registering a benchmark
- #define BUS_BENCHMARK_PRIVATE_REGISTER(fn, BusDef, SettingsFn) BENCHMARK_TEMPLATE(fn, BusDef)->Apply(&BenchmarkSettings::SettingsFn);
- // Register a benchmark for all bus permutations requiring ids
- #define BUS_BENCHMARK_REGISTER_ID(fn) BUS_BENCHMARK_PRIVATE_LIST_ID(BUS_BENCHMARK_PRIVATE_REGISTER, fn)
- // Register a benchmark for all bus permutations
- #define BUS_BENCHMARK_REGISTER_ALL(fn) BUS_BENCHMARK_PRIVATE_LIST_ALL(BUS_BENCHMARK_PRIVATE_REGISTER, fn)
- //////////////////////////////////////////////////////////////////////////
- // Single Threaded Events/Broadcasts
- //////////////////////////////////////////////////////////////////////////
- // Baseline benchmark for raw vtable call
- static void BM_EBus_RawCall(::benchmark::State& state)
- {
- constexpr bool connectOnConstruct{ true };
- AZStd::unique_ptr<Handler<OneToOne>> handler = AZStd::make_unique<Handler<OneToOne>>(0, connectOnConstruct);
- while (state.KeepRunning())
- {
- handler->OnEvent();
- }
- }
- BENCHMARK(BM_EBus_RawCall)->Apply(&BenchmarkSettings::Common);
- #define BUS_BENCHMARK_PRIVATE_REGISTER_CONNECTION(fn, BusDef, _) BENCHMARK_TEMPLATE(fn, BusDef)->Apply(&BenchmarkSettings::Common);
- template <typename Bus>
- static void BM_EBus_BusConnect(::benchmark::State& state)
- {
- constexpr bool connectOnConstruct{ false };
- Handler<Bus> handler{ 0, connectOnConstruct };
- while (state.KeepRunning())
- {
- handler.Connect();
- // Pause timing, and disconnect
- state.PauseTiming();
- handler.BusDisconnect();
- state.ResumeTiming();
- }
- }
- BUS_BENCHMARK_PRIVATE_LIST_ALL(BUS_BENCHMARK_PRIVATE_REGISTER_CONNECTION, BM_EBus_BusConnect);
- template <typename Bus>
- static void BM_EBus_BusDisconnect(::benchmark::State& state)
- {
- constexpr bool connectOnConstruct{ true };
- Handler<Bus> handler{ 0, connectOnConstruct };
- while (state.KeepRunning())
- {
- handler.BusDisconnect();
- // Pause timing, and reconnect
- state.PauseTiming();
- handler.Connect();
- state.ResumeTiming();
- }
- }
- BUS_BENCHMARK_PRIVATE_LIST_ALL(BUS_BENCHMARK_PRIVATE_REGISTER_CONNECTION, BM_EBus_BusDisconnect);
- #undef BUS_BENCHMARK_PRIVATE_REGISTER_CONNECTION
- template <typename Bus>
- static void BM_EBus_EnumerateHandlers(::benchmark::State& state)
- {
- auto OnEventVisitor = [](typename Bus::InterfaceType* interfaceInst) -> bool
- {
- interfaceInst->OnEvent();
- return true;
- };
- s_benchmarkEBusEnv<Bus>.Connect(state);
- while (state.KeepRunning())
- {
- Bus::EnumerateHandlers(OnEventVisitor);
- }
- s_benchmarkEBusEnv<Bus>.Disconnect(state);
- }
- BUS_BENCHMARK_REGISTER_ALL(BM_EBus_EnumerateHandlers);
- template <typename Bus>
- static void BM_EBus_Broadcast(::benchmark::State& state)
- {
- s_benchmarkEBusEnv<Bus>.Connect(state);
- while (state.KeepRunning())
- {
- Bus::Broadcast(&Bus::Events::OnEvent);
- }
- s_benchmarkEBusEnv<Bus>.Disconnect(state);
- }
- BUS_BENCHMARK_REGISTER_ALL(BM_EBus_Broadcast);
- template <typename Bus>
- static void BM_EBus_BroadcastResult(::benchmark::State& state)
- {
- s_benchmarkEBusEnv<Bus>.Connect(state);
- while (state.KeepRunning())
- {
- int result = 0;
- Bus::BroadcastResult(result, &Bus::Events::OnEvent);
- ::benchmark::DoNotOptimize(result);
- }
- s_benchmarkEBusEnv<Bus>.Disconnect(state);
- }
- BUS_BENCHMARK_REGISTER_ALL(BM_EBus_BroadcastResult);
- template <typename Bus>
- static void BM_EBus_Event(::benchmark::State& state)
- {
- s_benchmarkEBusEnv<Bus>.Connect(state);
- while (state.KeepRunning())
- {
- Bus::Event(1, &Bus::Events::OnEvent);
- }
- s_benchmarkEBusEnv<Bus>.Disconnect(state);
- }
- BUS_BENCHMARK_REGISTER_ID(BM_EBus_Event);
- template <typename Bus>
- static void BM_EBus_EventResult(::benchmark::State& state)
- {
- s_benchmarkEBusEnv<Bus>.Connect(state);
- while (state.KeepRunning())
- {
- int result = 0;
- Bus::EventResult(result, 1, &Bus::Events::OnEvent);
- ::benchmark::DoNotOptimize(result);
- }
- s_benchmarkEBusEnv<Bus>.Disconnect(state);
- }
- BUS_BENCHMARK_REGISTER_ID(BM_EBus_EventResult);
- template <typename Bus>
- static void BM_EBus_EventCached(::benchmark::State& state)
- {
- s_benchmarkEBusEnv<Bus>.Connect(state);
- typename Bus::BusPtr cachedPtr;
- constexpr typename Bus::BusIdType firstConnectedAddressId{ 0 };
- Bus::Bind(cachedPtr, firstConnectedAddressId);
- while (state.KeepRunning())
- {
- Bus::Event(cachedPtr, &Bus::Events::OnEvent);
- }
- s_benchmarkEBusEnv<Bus>.Disconnect(state);
- }
- BUS_BENCHMARK_REGISTER_ID(BM_EBus_EventCached);
- template <typename Bus>
- static void BM_EBus_EventCachedResult(::benchmark::State& state)
- {
- s_benchmarkEBusEnv<Bus>.Connect(state);
- typename Bus::BusPtr cachedPtr;
- constexpr typename Bus::BusIdType firstConnectedAddressId{ 0 };
- Bus::Bind(cachedPtr, firstConnectedAddressId);
- while (state.KeepRunning())
- {
- int result = 0;
- Bus::EventResult(result, cachedPtr, &Bus::Events::OnEvent);
- ::benchmark::DoNotOptimize(result);
- }
- s_benchmarkEBusEnv<Bus>.Disconnect(state);
- }
- BUS_BENCHMARK_REGISTER_ID(BM_EBus_EventCachedResult);
- //////////////////////////////////////////////////////////////////////////
- // Broadcast/Event Queuing
- //////////////////////////////////////////////////////////////////////////
- // Broadcast
- template <typename Bus>
- static void BM_EBus_QueueBroadcast(::benchmark::State& state)
- {
- while (state.KeepRunning())
- {
- Bus::QueueBroadcast(&Bus::Events::OnEvent);
- // Pause timing, and reset the queue
- state.PauseTiming();
- Bus::ClearQueuedEvents();
- state.ResumeTiming();
- }
- }
- BUS_BENCHMARK_REGISTER_ALL(BM_EBus_QueueBroadcast);
- template <typename Bus>
- static void BM_EBus_ExecuteBroadcast(::benchmark::State& state)
- {
- s_benchmarkEBusEnv<Bus>.Connect(state);
- while (state.KeepRunning())
- {
- // Push an event to the queue to run
- state.PauseTiming();
- Bus::QueueBroadcast(&Bus::Events::OnEvent);
- state.ResumeTiming();
- Bus::ExecuteQueuedEvents();
- }
- s_benchmarkEBusEnv<Bus>.Disconnect(state);
- }
- BUS_BENCHMARK_REGISTER_ALL(BM_EBus_ExecuteBroadcast);
- // Event
- template <typename Bus>
- static void BM_EBus_QueueEvent(::benchmark::State& state)
- {
- while (state.KeepRunning())
- {
- Bus::QueueEvent(1, &Bus::Events::OnEvent);
- // Pause timing, and reset the queue
- state.PauseTiming();
- Bus::ClearQueuedEvents();
- state.ResumeTiming();
- }
- }
- BUS_BENCHMARK_REGISTER_ID(BM_EBus_QueueEvent);
- template <typename Bus>
- static void BM_EBus_ExecuteEvent(::benchmark::State& state)
- {
- s_benchmarkEBusEnv<Bus>.Connect(state);
- while (state.KeepRunning())
- {
- // Push an event to the queue to run
- state.PauseTiming();
- Bus::QueueEvent(1, &Bus::Events::OnEvent);
- state.ResumeTiming();
- Bus::ExecuteQueuedEvents();
- }
- s_benchmarkEBusEnv<Bus>.Disconnect(state);
- }
- BUS_BENCHMARK_REGISTER_ID(BM_EBus_ExecuteEvent);
- // Event Cached
- template <typename Bus>
- static void BM_EBus_QueueEventCached(::benchmark::State& state)
- {
- typename Bus::BusPtr cachedPtr;
- constexpr typename Bus::BusIdType firstConnectedAddressId{ 0 };
- Bus::Bind(cachedPtr, firstConnectedAddressId);
- while (state.KeepRunning())
- {
- Bus::QueueEvent(cachedPtr, &Bus::Events::OnEvent);
- // Pause timing, and reset the queue
- state.PauseTiming();
- Bus::ClearQueuedEvents();
- state.ResumeTiming();
- }
- }
- BUS_BENCHMARK_REGISTER_ID(BM_EBus_QueueEventCached);
- template <typename Bus>
- static void BM_EBus_ExecuteQueueCached(::benchmark::State& state)
- {
- s_benchmarkEBusEnv<Bus>.Connect(state);
- typename Bus::BusPtr cachedPtr;
- constexpr typename Bus::BusIdType firstConnectedAddressId{ 0 };
- Bus::Bind(cachedPtr, firstConnectedAddressId);
- while (state.KeepRunning())
- {
- // Push an event to the queue to run
- state.PauseTiming();
- Bus::QueueEvent(cachedPtr, &Bus::Events::OnEvent);
- state.ResumeTiming();
- Bus::ExecuteQueuedEvents();
- }
- s_benchmarkEBusEnv<Bus>.Disconnect(state);
- }
- BUS_BENCHMARK_REGISTER_ID(BM_EBus_ExecuteQueueCached);
- //////////////////////////////////////////////////////////////////////////
- // Multithreaded Broadcasts
- //////////////////////////////////////////////////////////////////////////
- static void BM_EBus_Multithreaded_Locks(::benchmark::State& state)
- {
- using Bus = TestBus<AZ::EBusAddressPolicy::Single, AZ::EBusHandlerPolicy::Multiple, false>;
- AZStd::unique_ptr<BM_EBusEnvironment<Bus>> ebusBenchmarkEnv;
- if (state.thread_index() == 0)
- {
- ebusBenchmarkEnv = AZStd::make_unique<BM_EBusEnvironment<Bus>>();
- ebusBenchmarkEnv->SetUpBenchmark();
- ebusBenchmarkEnv->Connect(state);
- }
- while (state.KeepRunning())
- {
- Bus::Broadcast(&Bus::Events::OnWait);
- };
- if (state.thread_index() == 0)
- {
- ebusBenchmarkEnv->Disconnect(state);
- ebusBenchmarkEnv->TearDownBenchmark();
- }
- }
- BENCHMARK(BM_EBus_Multithreaded_Locks)->Apply(&BenchmarkSettings::OneToMany)->Apply(&BenchmarkSettings::Multithreaded);
- static void BM_EBus_Multithreaded_Lockless(::benchmark::State& state)
- {
- using Bus = TestBus<AZ::EBusAddressPolicy::Single, AZ::EBusHandlerPolicy::Multiple, true>;
- AZStd::unique_ptr<BM_EBusEnvironment<Bus>> ebusBenchmarkEnv;
- if (state.thread_index() == 0)
- {
- ebusBenchmarkEnv = AZStd::make_unique<BM_EBusEnvironment<Bus>>();
- ebusBenchmarkEnv->SetUpBenchmark();
- ebusBenchmarkEnv->Connect(state);
- }
- while (state.KeepRunning())
- {
- Bus::Broadcast(&Bus::Events::OnWait);
- };
- if (state.thread_index() == 0)
- {
- ebusBenchmarkEnv->Disconnect(state);
- ebusBenchmarkEnv->TearDownBenchmark();
- }
- }
- BENCHMARK(BM_EBus_Multithreaded_Lockless)->Apply(&BenchmarkSettings::OneToMany)->Apply(&BenchmarkSettings::Multithreaded);
- }
- #endif // HAVE_BENCHMARK
|