|
From: Luigi B. <lui...@gm...> - 2005-09-09 09:49:30
|
On 08/02/2005 06:02:44 AM, Toyin Akin wrote:
> I've been working with QuantLib the last serveral months looking at =20
> the possibility of producing a commercial multithreaded .NET engine.
> The main problem I have found which would prevent this from working =20
> within a multi-threaded environment (Wrapper on top of the C++ =20
> layer), are those of the 3 Singleton classes.
>=20
> ExchangeRateManager, IndexManager and Settings.
Toyin,
apologies for the delay. I still have some catch-up to do after =20
my vacations...
As to the multithreading issue: man, is this a tricky one.
It seems to me that adding an explicit parameter---be it the evaluation =20
date, a thread or session ID, or some other kind of settings =20
object---would percolate through and pollute too many interfaces. It =20
should be added as a parameter to Instrument::NPV(), =20
Instrument::calculate(), YieldTermStructure::whatever(), =20
Xibor::fixing(), CashFlow::amount()... it is painful just to think of =20
it. I would very like prefer that Settings remained globally accessible.
But on the other hand, we should be able to have different threads get =20
hold of different Settings instances, otherwise they will either have =20
to be serialized (defeating the very purpose of threads) or step on =20
each other's toes. A few months ago, Ashish Kulkarni mentioned =20
thread-local storage; as far as I can see, it seems an interesting =20
possibility---although I'm not sure that we can code it in the library =20
directly and in a portable way. Moreover, the library might be called =20
from different languages, and they might provide their own thread =20
implementation.
At this time, I'm inclined to turn the Singleton class into a Multiton =20
and add a hook so that different instances can be requested behind the =20
curtains. I'm thinking of an implementation along the lines of:
#if defined(QL_ALLOW_SESSIONS)
Integer sessionId(); // note: declared, but not defined in
// the library itself
#endif
T& Singleton<T>::instance() const {
static std::map<Integer, boost::shared_ptr<T> > instances_;
#if defined(QL_ALLOW_SESSIONS)
Integer id =3D sessionId();
#else
Integer id =3D 0;
#endif
boost::shared_ptr<T>& instance =3D instances_[id];
if (!instance)
instances_[id] =3D instance =3D boost::shared_ptr<T>(new T);
return *instance;
}
When QL_ALLOW_SESSIONS is undefined (which would be the default) the =20
singleton will work as it does now. However, users wanting to have =20
different instances will have the possibility to #define it. In this =20
case, they'll have to define their own implementation of sessionId(), =20
which will return a different id for different threads, and link it =20
with the library. This will give them the possibility to implement =20
sessionId() in the most convenient way, e.g., using thread-local =20
storage in a C++ application, calling back to Python or Ruby to =20
interrogate their thread manager if the library is exported to such =20
languages, or doing whatever is necessary in .NET to get a thread id.
Thoughts?
Later,
Luigi
----------------------------------------
Quote me as saying I was misquoted.
-- Groucho Marx
|