D
Dragan
Hi,
We're working in VS 2005, Team edition, if it makes any difference at
all (should be up-to-date and all that, but could not guarantee it is
100%).
We've implemented a simple generic wrapper parser under C++/CLI. It's
a basic project created under Visual C++/CLR - Class Library - and
it's pretty much what it is, just one /clr compiled class, fully CLR,
no C++ native types or processing, nothing, just managed all the way.
The reason whey we did it in the first place was the better handling
of generics, at least what we thought at the time - e.g. being able to
specify Enum as a constraint, using safe_cast to switch between the
types (as opposed to C# which requires boxing to e.g. cast generic T
to float) and so on. Basically, it's a generic class w/ static methods
which take value types or Enums and parse them (the strings that is -
parsing to specific values for each type). It has some more handling
specific to our case and error handling but it's merely one page and
nothing much in it (attaching an excerpt at the end).
At first it seemed to work just fine. But when we run it through some
heavy load and under multi-threading scenario it started to deadlock
(what seems to be a deadlock but could be nothing else - we're
catching all exceptions and handling any such possible issue which
could cause things to stop) regularly and repeatably.
Our app is feeding off of a stock-market data feed, thus it's quite
heavy and requires multiple threads processing things in the
background - but this part could not be simpler. On the threads side,
we have 2-3 main threads (up to 5 w/ worker threads), one receiving
the data (thru sockets, all is in C#) and others processing things in
the back + the GUI one. What's important is that this processing, done
in C++, is always from one thread, so there is no need to lock
anything there + it's just some 'static' one-off processing so no
'state' or any variables to protect really.
The problems start when we turn on the data-feed and a good deal of
data starts coming in - it works for a minute or so (which is
important to state, so it works and starts working) but then
regularly, like a clockwork, deadlocks somewhere inside C++ library -
and it's always different place, so 'smells' like a typical deadlock
scenario.
We tried tracing it, it's always somewhere inside C++ but nothing
particular to point to. Our best guess it's something to do w/
'global' calls back to CLR core libraries, maybe that String Alloc
which was mentioned or something. At one point it looked like a
safe_cast issue (where we used float% instead of float, we're passing
'by ref' to methods - but even w/ that 'fixed' it still locks
regularly, maybe a tad less often). We also investigated if it could
be the static initializer or something, read thru posts and
everything, but nothing helped.
Then, other thing is, when we copied the code back to C#, all works
fine and under pressure. We ran the thing for hours/days and no
problems if C# to C#. But if we switch over to C++/CLI it deadlocks
pretty soon, rarely ends up running for more then 5 minutes, that's
about max. And we've re-written everything so that it's almost exact
code here and there, no differences whatsoever. Also we removed any
'exotic' stuff, like calling our Logging libraries (C#) from inside C+
+/CLI or anything like it. Basically it's just dealing w/ core
libraries, like strings, Enum parsing, safe_cast here and there and
that's about all there is to it. That's why it is a bit strange.
Finally, we gave up on this approach, as we have little time to deal
w/ such things and w/ real development waiting ahead - so removed the C
++ part entirely and doing everything in C#, and all is ok, so we're
not overly keen on moving it back to C++.
But, being a long-time .NET programmer, and C++ before it for many
years, I'm just a bit curious about what's going on here and what
could be the issue. As, w/o having this resolved, I'd be very
reluctant on doing anything in C++/CLI, while on the other side I've
always been a fan of mixing things, do in C# what it does best, move
to C++ when you have to deal w/ performance intensive stuff and so on.
Also, expected much more from CLI version, after managed C++, thought
it'd be more mature and reliable.
So, in short, any ideas or anybody from Microsoft willing to answer
this and point me to possible/probable places or calls which could
cause this. Not really sure where to look for.
Thanks in advance,
Dragan
part of the code:
public ref class Parser
{
public:
generic <typename TField, typename TValue>
where TField : Enum
where TValue : value class
static FieldSetStatus Set(TValue% value, TField field, String^
strvalue, Context<TField>^ context)
{
try
{
TValue novalue = TValue();
FieldSetStatus status = Parse<TValue>(value, novalue, strvalue);
return status;
//return ProcessStatus<TField>(context, status, field, strvalue,
TValue::typeid->Name);
}
catch( Exception^ e )
{
return FieldSetStatus:arseError;
}
}
private:
generic <typename TValue>
where TValue : value class
static FieldSetStatus Parse(TValue% value, TValue novalue, String^
strvalue, String^ format, IFormatProvider^ culture)
{
value = novalue;
if (strvalue == "Not Found")
return FieldSetStatus::NotFound;
try
{
if (!String::IsNullOrEmpty(strvalue))
{
Type^ type = TValue::typeid;
if( type->Equals(float::typeid) )
return ParseFloat(safe_cast<float>(value), strvalue, format,
culture);
if( type->Equals(int::typeid) )
return ParseInt32( safe_cast<int>(value), strvalue, format,
culture);
if( type->Equals(DateTime::typeid) )
return ParseDate(safe_cast<DateTime>(value), strvalue, format,
culture);
if( type->Equals(TimeSpan::typeid) )
return ParseTime(safe_cast<TimeSpan>(value), strvalue, format,
culture);
return FieldSetStatus:arseError;
}
return FieldSetStatus::Empty;
}
catch( Exception^ e )
{
return FieldSetStatus:arseError;
}
}
static FieldSetStatus ParseFloat(float% value, String^ strvalue,
String^ format, IFormatProvider^ culture)
{
if( Single::TryParse( strvalue, value ) )
return FieldSetStatus::Success;
else
return FieldSetStatus:arseError;
}
static FieldSetStatus ParseInt32(int% value, String^ strvalue,
String^ format, IFormatProvider^ culture)
{
if( Int32::TryParse( strvalue, value ) )
return FieldSetStatus::Success;
else
return FieldSetStatus:arseError;
}
static FieldSetStatus ParseDate(DateTime% value, String^ strvalue,
String^ format, IFormatProvider^ culture)
{
if( format==nullptr )
format = "MM/dd/yyyy";
if (DateTime::TryParseExact(strvalue, format, culture,
System::Globalization:ateTimeStyles::None, value))
return FieldSetStatus::Success;
else
return FieldSetStatus:arseError;
}
static FieldSetStatus ParseTime(TimeSpan% value, String^ strvalue,
String^ format, IFormatProvider^ culture)
{
if( TimeSpan::TryParse( strvalue, value ) )
return FieldSetStatus::Success;
else
return FieldSetStatus:arseError;
}
};
We're working in VS 2005, Team edition, if it makes any difference at
all (should be up-to-date and all that, but could not guarantee it is
100%).
We've implemented a simple generic wrapper parser under C++/CLI. It's
a basic project created under Visual C++/CLR - Class Library - and
it's pretty much what it is, just one /clr compiled class, fully CLR,
no C++ native types or processing, nothing, just managed all the way.
The reason whey we did it in the first place was the better handling
of generics, at least what we thought at the time - e.g. being able to
specify Enum as a constraint, using safe_cast to switch between the
types (as opposed to C# which requires boxing to e.g. cast generic T
to float) and so on. Basically, it's a generic class w/ static methods
which take value types or Enums and parse them (the strings that is -
parsing to specific values for each type). It has some more handling
specific to our case and error handling but it's merely one page and
nothing much in it (attaching an excerpt at the end).
At first it seemed to work just fine. But when we run it through some
heavy load and under multi-threading scenario it started to deadlock
(what seems to be a deadlock but could be nothing else - we're
catching all exceptions and handling any such possible issue which
could cause things to stop) regularly and repeatably.
Our app is feeding off of a stock-market data feed, thus it's quite
heavy and requires multiple threads processing things in the
background - but this part could not be simpler. On the threads side,
we have 2-3 main threads (up to 5 w/ worker threads), one receiving
the data (thru sockets, all is in C#) and others processing things in
the back + the GUI one. What's important is that this processing, done
in C++, is always from one thread, so there is no need to lock
anything there + it's just some 'static' one-off processing so no
'state' or any variables to protect really.
The problems start when we turn on the data-feed and a good deal of
data starts coming in - it works for a minute or so (which is
important to state, so it works and starts working) but then
regularly, like a clockwork, deadlocks somewhere inside C++ library -
and it's always different place, so 'smells' like a typical deadlock
scenario.
We tried tracing it, it's always somewhere inside C++ but nothing
particular to point to. Our best guess it's something to do w/
'global' calls back to CLR core libraries, maybe that String Alloc
which was mentioned or something. At one point it looked like a
safe_cast issue (where we used float% instead of float, we're passing
'by ref' to methods - but even w/ that 'fixed' it still locks
regularly, maybe a tad less often). We also investigated if it could
be the static initializer or something, read thru posts and
everything, but nothing helped.
Then, other thing is, when we copied the code back to C#, all works
fine and under pressure. We ran the thing for hours/days and no
problems if C# to C#. But if we switch over to C++/CLI it deadlocks
pretty soon, rarely ends up running for more then 5 minutes, that's
about max. And we've re-written everything so that it's almost exact
code here and there, no differences whatsoever. Also we removed any
'exotic' stuff, like calling our Logging libraries (C#) from inside C+
+/CLI or anything like it. Basically it's just dealing w/ core
libraries, like strings, Enum parsing, safe_cast here and there and
that's about all there is to it. That's why it is a bit strange.
Finally, we gave up on this approach, as we have little time to deal
w/ such things and w/ real development waiting ahead - so removed the C
++ part entirely and doing everything in C#, and all is ok, so we're
not overly keen on moving it back to C++.
But, being a long-time .NET programmer, and C++ before it for many
years, I'm just a bit curious about what's going on here and what
could be the issue. As, w/o having this resolved, I'd be very
reluctant on doing anything in C++/CLI, while on the other side I've
always been a fan of mixing things, do in C# what it does best, move
to C++ when you have to deal w/ performance intensive stuff and so on.
Also, expected much more from CLI version, after managed C++, thought
it'd be more mature and reliable.
So, in short, any ideas or anybody from Microsoft willing to answer
this and point me to possible/probable places or calls which could
cause this. Not really sure where to look for.
Thanks in advance,
Dragan
part of the code:
public ref class Parser
{
public:
generic <typename TField, typename TValue>
where TField : Enum
where TValue : value class
static FieldSetStatus Set(TValue% value, TField field, String^
strvalue, Context<TField>^ context)
{
try
{
TValue novalue = TValue();
FieldSetStatus status = Parse<TValue>(value, novalue, strvalue);
return status;
//return ProcessStatus<TField>(context, status, field, strvalue,
TValue::typeid->Name);
}
catch( Exception^ e )
{
return FieldSetStatus:arseError;
}
}
private:
generic <typename TValue>
where TValue : value class
static FieldSetStatus Parse(TValue% value, TValue novalue, String^
strvalue, String^ format, IFormatProvider^ culture)
{
value = novalue;
if (strvalue == "Not Found")
return FieldSetStatus::NotFound;
try
{
if (!String::IsNullOrEmpty(strvalue))
{
Type^ type = TValue::typeid;
if( type->Equals(float::typeid) )
return ParseFloat(safe_cast<float>(value), strvalue, format,
culture);
if( type->Equals(int::typeid) )
return ParseInt32( safe_cast<int>(value), strvalue, format,
culture);
if( type->Equals(DateTime::typeid) )
return ParseDate(safe_cast<DateTime>(value), strvalue, format,
culture);
if( type->Equals(TimeSpan::typeid) )
return ParseTime(safe_cast<TimeSpan>(value), strvalue, format,
culture);
return FieldSetStatus:arseError;
}
return FieldSetStatus::Empty;
}
catch( Exception^ e )
{
return FieldSetStatus:arseError;
}
}
static FieldSetStatus ParseFloat(float% value, String^ strvalue,
String^ format, IFormatProvider^ culture)
{
if( Single::TryParse( strvalue, value ) )
return FieldSetStatus::Success;
else
return FieldSetStatus:arseError;
}
static FieldSetStatus ParseInt32(int% value, String^ strvalue,
String^ format, IFormatProvider^ culture)
{
if( Int32::TryParse( strvalue, value ) )
return FieldSetStatus::Success;
else
return FieldSetStatus:arseError;
}
static FieldSetStatus ParseDate(DateTime% value, String^ strvalue,
String^ format, IFormatProvider^ culture)
{
if( format==nullptr )
format = "MM/dd/yyyy";
if (DateTime::TryParseExact(strvalue, format, culture,
System::Globalization:ateTimeStyles::None, value))
return FieldSetStatus::Success;
else
return FieldSetStatus:arseError;
}
static FieldSetStatus ParseTime(TimeSpan% value, String^ strvalue,
String^ format, IFormatProvider^ culture)
{
if( TimeSpan::TryParse( strvalue, value ) )
return FieldSetStatus::Success;
else
return FieldSetStatus:arseError;
}
};