
On Behalf Of scott Subject: RE: [boost] Re: Re: Future of threads (III)
On Behalf Of Hurd, Matthew
AO's should either have a queue for work or hook into a queue. They are a bit like a worker pool with one worker ;-)
yep.
Another idiom is a future value, where you ask for a value and don't block until you "read" the value. ACE also has an implementation of this. You can think of it as a bit like overlapped I/O.
Yes. This was also in the ActiveObject pattern. It was on the "client- side", i.e. available to the thread that submits Method Requests as a means to acquire the results.
While I understand its place in the ActiveObject pattern I would hope that if a boost::active_object came into existence, there would be a single (i.e. not _simple_) mechanism by which data passed from one
thread
to the other, with an associated notification. In a behavioural sense, the future mechanism wouldnt be appropriate for this.
I'd agree here in principle to keep this asynchronous and have the equivalent of message passing with a function call with a void return type. The future_value is an elegant idiom for providing a C++ RPC / LPC equivalent. The whole store and forward expectation issue would suit this style as it just looks like a normal function or method call.
This way of thinking does require that all threads are active_objects which may be too aggressive <shrug>. A small sample of empirical data says its fine.
It works a treat for a lot of things. Especially suited to workflows of things that you want to dynamically configure. Threading is a big overhead though. Consider these numbers: Safe increment (Athlon 2800+) (P4 3.2GHz) Interlocked primitive 11 ns 53 ns Critical section 138 ns 411 ns A bit slower than a single machine instruction. If I make a size_t volatile and time 100 increments in a row I get 3.8ns per increment on the P4 above, more then ten times slower than an interlocked increment. And it doesn't end there, the cache effects also add considerably. So in my mind, the active object pattern is good for configurability and where the work of the object is chunky enough to where the cost of the context switches and cache effects. In terms of work pools, this context overhead also led to the idea of a leader-follower pattern where you try and use the same worker if possible. This is also why we have seen the argument for a shared_ptr with multi-thread safety as a policy. On a Pentium 4 you are typically paying around ten times the cost of a normal increment for the privilege of thread safety.
I also advocate a different architectural neutral style where you can have things at look like function calls that can be: 1. a function call 2. a call to queue work pool or active object 3. an IPC mechanism for same machine transfer, e.g. shared memory 4. a network mechanism, TCP, UDP, TIB or something, for
Understand the reasons you ask for 1...4 but would like to add some other thoughts to see what you think.
An ActiveObject servant contains code. That code is implementation of behaviour and its what (I believe) we tacitly want when we all say that "thread-inherited-classes" or "active objects" are a nice manner in which to deal with threads. (note: more accurately the pattern says that thread==scheduler and a scheduler executes servant code. but i think we can skip this detail)
If the code is in the servant, what do 1 and 2 mean? My best matching of your thinking with the servant-based thinking would be that somehow the "client" can submit something into the "worker pool" that _identifies_ a function that is in the servant. It would be nice if this "work submission" could carry some arguments for presentation to the servant.
I think I understand what you're saying, but I'm not sure. Are you thinking about how the marshalling is done?
A detail that I am labouring is the separation of "calling" (would much rather that this was "sending") and execution. Our active object has no synchronous i/f with methods for us to call; it is simply a "work pool" that we add to. As soon as we make a sync i/f available then we go down that road that leads to servant code in the client.
I suggest that notions of "function call" should be exorcised from discussions that are targeting active objects. That type of thinking is perfectly valid in some other thread (oops) but can scuttle work targeting active objects; the behaviour (i.e. the code) is inherently in the object.
Not sure I agree. Think of it the small talk way. A message is a method call. You could have your active object providing a function call that puts a message on to its internal queue and returns. This way you could perhaps specialise by policy whether or not it had its own thread of control and make your architecture a lot more flexible similar to the relationship of 1. and 2. above.
a client can only submit "worker orders" or "Method Requests" or jobs or messages or signals.
I hope there is enough there to make a case :-)
You certainly have a case. The nirvana I'm looking for is a policy approach to reconfiguring architecture so I can make late decisions about single, multi-thread, multi-process, distributed design. I think your active object can gel with this as I think you can consider parallels to the numbering above: 1. method call 2. method call that generates message to active object thread of control and returns 3. method call -> lightweight message on the same box or LRPC 4. method call -> external system message (tcp, udp, tib, etc) or RPC
If it can be enabled by a policy driven approach then you can change the architecture of your application by simply changing policies.
I think David Abraham's named parameters could help greatly with
approach, along with the serialization lib for marshalling support. This should provide a solid basis for this. I've also thought that perhaps boost::signals might be the correct approach to take for
this this
but I'm unsure.
To support this approach you also need mutex aspects that are also policy driven. I am preparing to submit such locking that builds on the existing mutexes and locks.
<phew> That is quite a scope. But yes, I can see that ultimately it should cover all these dimensions. Maybe a phasing?
Yep, I think the locking and mutex stuff needs to be cleaned up or wrapped to suit a policy approach. Need atomic op support too. Next step is a thread safe queue. Perhaps two, one with priority and a normal one. I've got these I can submit but they're defective in terms of exception safety. Next step is a way having a policy based interface approach to make calling a function result in the calling of a function or marshalling the parameters and calling something else... This is the bit where I'm not sure how best to approach it. Then it is a latter of adding transports to enrich the approach.
Cheers, Scott
Good thinking there Scott. Regards, Matt Hurd _______________________ Susquehanna Pacific P/L hurdm@sig.com +61.2.8226.5029 _______________________ IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.