Boost logo

Boost :

From: Matthew Vogt (mvogt_at_[hidden])
Date: 2004-02-23 18:05:32


scott <scottw <at> qbik.com> writes:

> In my opinion, the ActiveObject pattern can be divided into two operational
> aspects. One is where a "main thread" has one or more ActiveObjects
> available
> to it and the other is where ActiveObjects interact with each other.
>
> If these operational architectures were referred to as asymmetric and
> symmetric
> activation, respectively, then my goals with respect to implementation of
> the pattern are focused on "symmetric activation".
>
> Distinguishing these two architectures I feel, is crucial. Your latest
> implementation code includes "futures" _and_ "method requests". I
> acknowledge
> this (again . But there is no mechansim for the results of a "method
> request" to be returned _asynchronously_. For this reason I (and I am open
> to explanations of why I am wrong) view your implementation to be in the
> direction of asymmetric activation (AA).

Well, ok. Futures (in my code, and in the pattern) do allow a caller to
access a result asynchronously, if you define asynchronously to mean that
the caller is free to pursue other interests before acquiring the result.

What is lacking is the ability to multiplex over events relating to the
delivery of future values. You can't use select, poll or WFMO to determine
whether any of an outstanding set of futures have been delivered. I think
that this probably makes active objects an inappropriate choice for designs
that are inherently reactive.

> To highlight the significance of this target (i.e. AA) I will return to the
> database server example that several contributors have made reference to.
>
> Lets say that the database server is most effective as a threaded,
> ActiveObject. Lets also say that due to the architecture of the underlying
> interface to an RDBMS, there are significant throughput gains to be
> made with 4 slave threads. If a GUI app is the "main thread", the database
> server is a threaded, ActiveObject and the slaves are threaded,
> ActiveObjects
> (all examples of AA) then the result is non-optimal. It would not realise
> the advertised benefits of 4 slave threads as the database server must block
> waiting for completion of a slave method (e.g. evaluation of a future).
>

You could apply the active object pattern to this design, but the simplest
way to do so would be to constrain the slave threads to have void-returning
methods, and to deliver the results of methods asynchronously, like this:

// Psuedocode

struct slave {
  void queueRequest(RequestData data, future<something>& futureResult) {
    // perform request
    server::deliverResult(data, futureResult);
};

struct server {
  future<something> doSomething(void) {
    future<something> futureResult;
    slave::queueRequest(someRequest, resultResult);
    retuyrn futureResult;
  }

  void deliverResult(RequestData data, future<something>& futureResult) {
    futureResult.finalise(data);
  }
};

struct client {
  something someMethod(void) {
    return server::doSomething();
  };
};

(I think this is a variant on the 'half-sync, half-asynch' pattern?)

Ideally, though, the server is a reactive design and not terribly suited to
the active object pattern.

> A cruder example of what I am trying to highlight is where a group of
> interacting ActiveObjects manage to create a "circular activation", i.e.
> A calls B, B calls C and C calls A. This is a fatal symptom of the
> underlying
> problem associated with AA.
>
> I immediately concede that you may implement some sophisticated "event
> notification" between the database server and its slaves to solve this.
> You may even choose to not implement the slaves as ActiveObjects to give
> yourself the necessary "freedom".
>

Well, yes, you could (and it could certainly be generic, I'm sure). But it
doesn't seem to be in the spirit of the design. Active object is a trade-off
between simplicity at the client, and suboptimal performance at the server.

> My response to this would be that the custom event notification is
> completely
> unnecessary. Application of an SA-focused ActiveObject removes the need for
> any such one-off mechanisms (and who wants to write those again, and
> again...).
> Finally, having deployed (SA) ActiveObjects I would be disappointed to see
> any new mechanism for thread communication. Proof of a successful
> implementation
> of SA (IMHO) would be that it became the _only_ mechansim for inter-thread
> communication. Of course, in the real world, this is not going to happen but
> I would offer it as a noble intent
>

This is certainly achievable, but is it using the active object design? You
can implement an exactly equivalent design using the platform AIO primitives,
but this would yield code which was much harder to follow and implement
than active objects communicating through function calls. If you want
optimum throughput throughout the interracting components, I don't think
active object is the pattern you're looking for.
 
> I tentatively suggest that the ActiveObject that most of us want is the AA
............................<Mentally changed to 'SA', as per your followup>
> variety.
> We can see the objects exchanging the Method Requests in a symphony of
> optimal
> operation - in our heads. But between our heads and the "tools at hand" I
> believe
> the symphony becomes something else, primarily due to AA-based environments
> and
> associated culture.
>
> Having made my case (as best I can) things now get muddy. Firstly I dont
> see any mechansim for the delivery of asynchronous results in the pattern!
> Please
> note that I wanted to verify this claim but have failed to connect to
> siteseer
> for the last two hours. In my ActiveWorld the same mechansim that is used to
> queue Method Requests (in your implementation - "tasks") is also used to
> queue
> results.
>

The asynchronous delivery mechanism is the conversion of the 'future' template
object to it's parameterised type. For example, the following blocks:

future<int> f = someActiveObject.someMethod(); // Non-blocking
int i = f; // Blocks until result delivered

Of course, the caller may not need the result yet, and can postpone the wait
indefinitely:

std::vector<future<int> > results;
results.push_back(someActiveObject.someMethod()); // Non-blocking
... // Whatever
 
> While I can see the pragmatic value in including both AA and SA in all
> ActiveObject initiatives, I also wonder about the psychology of developers.
> While implementation of SA ActiveObjects is a little bit more difficult I
> suspect there are other reasons that it doesnt "catch on". Firstly,
> we tend to shy away from new, foreign models of execution and secondly AA is
> always there to "fall back" on.
>
> It might be interesting to note that my ActiveWorld implementation is
> _completely_
> SA. The very pleasant surprise for me has been the successful manner in
> which
> it has been deployed in existing codebases. It may appear foreign but our
> best
> coding is naturally trying to emulate the SA ActiveObject.
>

What interface are you using to allow active objects to access results
generated by other active objects?

> Cheers,
> Scott
>
> ps:
> I mentioned the "anything declared in private scope is thread-safe" to see
> if we were "on the same wavelength". After reading your response(s) I think
> the answer to that is "yes". Or am I deluding myself Any _data_ declared
> with private scope in the "struct object" would only be accessible to the
> methods
> of that same struct and those methods are only every called by
> "boost::thread thread"
> in "class active". Voila! We dont need any mutexes around that data!
>

Yes, but that only applies to a single-threaded active object.

Matt


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk