[Quickfix-developers] Fixed spurious System.NullReferenceException in .NET Application.toApp()
Brought to you by:
orenmnero
From: Vladimir A. <Vla...@FF...> - 2004-10-19 17:03:13
|
Oren, I=20have=20fixed=20a=20nasty=20bug=20in=20.NET=20Application=20class=20in=20= QuickFIX=201.9.2. From=20time=20to=20time,=20especially=20under=20high=20system=20load,=20se= nding=20a=20FIX message=20throws=20an=20exception=20like=20this=20one: System.NullReferenceException:=20Object=20reference=20not=20set=20to=20an=20= instance of=20an=20object. =20=20=20at=20FIX.message_order.=3D(message_order*=20,=20message_order*=20= ) =20=20=20at=20FIX.message_order.__ctor(message_order*=20,=20message_order*= =20copy) =20=20=20at std._Tree<std::_Tmap_traits<int,FIX::FieldBase,FIX::message_order,...> >.key_comp(_Tree<...>*=20,=20message_order*=20) =20=20=20at=20FIX.FieldMap.=3D(FieldMap*=20,=20FieldMap*=20) =20=20=20at=20FIX.Message.=3D(Message*=20,=20Message*=20) =20=20=20at=20Application.toApp(Application*=20,=20Message*=20message,=20S= essionID* sessionID) =20=20=20at=20FIX.Session.sendToTarget(Message*=20,=20SessionID*=20) =20=20=20at=20QuickFix.Session.sendToTarget(Message=20message,=20SessionID= sessionID) (The=20exception=20was=20thrown=20at=20random=20code=20locations,=20but=20= always=20"below" FIX.FieldMap.operator=3D) As=20I=20have=20found,=20the=20problem=20lies=20in=20mixing=20unmanaged=20= code=20and garbage-collected=20data=20structures.=20Method=20toApp()=20creates=20mana= ged wrapper=20class=20QuickFix::Message=20for=20unmanaged=20class=20FIX::Messa= ge,=20calls managed=20method=20toApp()=20and=20copies=20the=20(modified?)=20message=20= back=20to FIX::Message.=20The=20problem=20is=20in=20the=20last=20operation,=20which=20= will=20fail=20if the=20.NET=20garbage=20collector=20decides=20to=20move=20QuickFix::Message= =20in=20memory while=20the=20unmanaged=20assignment=20operator=20is=20obliviously=20copyi= ng toMessage->unmanaged()=20back=20to=20message.=20This=20happens=20quite=20r= arely=20(once in=2010000=20messages=20or=20so),=20but=20the=20message=20is=20lost=20and=20= never=20sent=20to=20the client=20(sequence=20gap=20does=20not=20occur),=20which=20makes=20the=20pr= oblem=20very severe. =20=20void=20toApp(=20FIX::Message&=20message,=20const=20FIX::SessionID&=20= sessionID=20) [simplified] =20=20{ =20=20=20=20QuickFix::Message=20*=20toMessage=20=3D=20create(=20message=20= ); =20=20=20=20m_application->toApp(=20toMessage,=20new=20QuickFix::SessionID= (=20sessionID )=20); =20=20=20=20message=20=3D=20toMessage->unmanaged(); =20=20=20=20^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ =20=20} The=20solution=20is=20quite=20easy:=20(well,=20once=20you=20identify=20the= =20problem...=20;) Simply=20'tag'=20all=20pointers=20to=20GC'ed=20classes=20with=20"__pin"=20= keyword: =20=20=20=20QuickFix::Message=20__pin=20*=20toMessage=20=3D=20create(=20me= ssage=20); The=20toMessage=20object=20stays=20locked=20in=20memory=20in=20the=20metho= d=20body=20even=20if GC=20is=20activated. I=20have=20seen=20the=20problem=20only=20in=20toApp(),=20but=20I=20believe= =20that=20toAdmin()=20is affected=20in=20the=20same=20way.=20Since=20'pinning'=20a=20pointer=20for=20= a=20short=20time does=20not=20hurt=20performance,=20I=20decided=20to=20'pin'=20all=20QuickF= ix::Message pointers=20in=20the=20Application=20class. It=20is=20quite=20possible=20that=20other=20C#-to-C++=20wrappers=20suffer=20= from=20the=20same problem,=20but=20I=20have=20not=20analyzed=20them. Important=20note:=20I=20was=20unable=20to=20reproduce=20the=20problem=20wh= ile=20executing=20a QuickFIX=20application=20in=20Visual=20Studio.NET's=20debugger.=20The=20ap= plication must=20be=20executed=20stand-alone=20from=20the=20command=20line,=20becaus= e=20VS.NET changes=20the=20way=20the=20garbage=20collector=20works.=20Both=20Debug=20= and=20Release builds=20produce=20the=20same=20erroneous=20behaviour. Here=20is=20the=20diff: ---=20Application.h=092004-05-31=2015:27:42.000000000=20+0200 +++=20Application.h.new=092004-10-19=2017:22:15.011923000=20+0200 @@=20-71,7=20+71,7=20@@ =20 =20=20=20void=20toAdmin(=20FIX::Message&=20message,=20const=20FIX::Session= ID&=20sessionID ) =20=20=20{ -=20=20=20=20QuickFix::Message=20*=20toMessage=20=3D=20create(=20message=20= ); +=20=20=20=20QuickFix::Message=20__pin=20*=20toMessage=20=3D=20create(=20m= essage=20); =20=20=20=20=20m_application->toAdmin(=20toMessage,=20new=20QuickFix::Sess= ionID( sessionID=20)=20); =20=20=20=20=20message=20=3D=20toMessage->unmanaged(); =20=20=20} @@=20-79,7=20+79,7=20@@ =20=20=20void=20toApp(=20FIX::Message&=20message,=20const=20FIX::SessionID= &=20sessionID=20) =20=20=20throw(=20FIX::DoNotSend&=20) =20=20=20{ -=20=20=20=20QuickFix::Message=20*=20toMessage=20=3D=20create(=20message=20= ); +=20=20=20=20QuickFix::Message=20__pin=20*=20toMessage=20=3D=20create(=20m= essage=20); =20=20=20=20=20try =20=20=20=20=20{ =20=20=20=20=20=20=20m_application->toApp(=20toMessage,=20new=20QuickFix::= SessionID( sessionID=20)=20); @@=20-95,7=20+95,7=20@@ =20=09=20FIX::IncorrectTagValue&,=20 =20=09=20FIX::RejectLogon&=20) =20=20=20{ -=20=20=20=20QuickFix::Message=20*=20toMessage=20=3D=20create(=20message=20= ); +=20=20=20=20QuickFix::Message=20__pin=20*=20toMessage=20=3D=20create(=20m= essage=20); =20=20=20=20=20try =20=20=20=20=20{ =20=20=20=20=20=20=20m_application->fromAdmin @@=20-122,7=20+122,7=20@@ =20=09=20FIX::IncorrectTagValue&,=20 =20=09=20FIX::UnsupportedMessageType&=20) =20=20=20{ -=20=20=20=20QuickFix::Message=20*=20toMessage=20=3D=20create(=20message=20= ); +=20=20=20=20QuickFix::Message=20__pin=20*=20toMessage=20=3D=20create(=20m= essage=20); =20=20=20=20=20try =20=20=20=20=20{ =20=20=20=20=20=20=20m_application->fromApp @@=20-153,7=20+153,7=20@@ =20=20=20=20=20FIX::MsgType=20msgType; =20=20=20=20=20unmanaged.getHeader().getField(=20beginString=20); =20=20=20=20=20unmanaged.getHeader().getField(=20msgType=20); -=20=20=20=20QuickFix::Message*=20message=20 +=20=20=20=20QuickFix::Message=20__pin=20*=20message=20 =20=20=20=20=20=20=20=3D=20m_factory->create =20=20=20=20=20=20=20(=20beginString.getValue().c_str(),=20msgType.getValu= e().c_str()=20); =20=20=20=20=20message->setUnmanaged(=20unmanaged=20); Please=20put=20this=20patch=20to=20the=20official=20source=20tree=20ASAP. Cheers, =20=20Vlad ------------------------------------------------------------- Ing.=20Vladimir=20Arnost,=20Developer,=20FFastFill=20Europe=20Ltd. Vaclavske=20namesti=2055,=20Prague,=20Czech=20Republic ________________________________________________________________________ This=20email=20and=20any=20files=20transmitted=20with=20it=20are=20confide= ntial=20and intended=20solely=20for=20the=20use=20of=20the=20individual=20or=20entity=20= to=20whom=20they are=20addressed.=20If=20you=20have=20received=20this=20email=20in=20error=20= please=20notify sec...@ff... This=20email=20has=20been=20scanned=20for=20all=20viruses=20by=20the=20FFa= stFill=20Email Security=20System. ________________________________________________________________________ |