Boost logo

Boost :

From: scott (scottw_at_[hidden])
Date: 2004-02-26 23:19:11


> -----Original Message-----
> From: boost-bounces_at_[hidden]
> [mailto:boost-bounces_at_[hidden]]On Behalf Of Matthew Vogt

> Yep, there's gotta be mutexes somewhere.
>
> The mechanism you're referring to is the task_queue of
> fully-parameterised
> method invocations? So, how precisely does this work? Say I
> have a scheduler
> that has performed some work via a method in a servant S, and
> that method
> produced a result of type int. I want to return that result
> to the caller C,
> but the caller may not be threaded (may not be associated
> with any scheduler).
> Does that mean that instead of queueing the response to the
> object, I will
> perform some type of registered action in C, in the same
> thread context as the
> method invocation of S?

Damn. I can see why you ask the questions that you are asking
but the answers are very messy. I suspect it may be best to
half-answer within the scope of your active<object> and then
try to explain the "signal processing" equivalent.

At the point where the scheduler is holding the result of type
int, it needs to be able to "deliver" it to the caller. The only
way it can do this is call a method on the appropriate object
(instance) passing the results. Which method and which object?
The scheduler can only get these from the queued task, i.e.
the task must hold the method-to-run and pointers necessary for
the return delivery.

This method will be another proxy (e.g. non_void_with_param_returned)
that in turn, queues the result. This decouples the scheduler from the
recipient of the results; eventually the thread working on that
task list will reach the "results task" and will run that.

How does the object and method address get in the task object?
You may well ask and this was (some of) the reason for my
initial response.

A by-product of working on "active objects" is a set of
classes/templates that constitute a little thread-toolkit. This
toolkit should facillitate the design and implementation of
pretty much any snarling, thread-beast; if that is what you need.

The "damn" part of my response stems from the fact that I
believe the "Method Request" model is not viable as a model
of execution for the thread toolkit. It is simply wrong and
bending it to this task is a deadend.

In signal processing the equivalent to "Method Request" is
"send". This primitive takes an object to send and the
address of the destination (e.g. an active<object>);

        send( int_results, calling_object )

These calls are made from within the methods that you
already have working, i.e. the non-proxy _real_ methods.

The trick with "send" is that it can be coded to
populate the "task" with all the appropriate object
and member pointers so that the receiving thread
knows where it came from. Pretty typical SDL
classes will look like;

struct servant :
{
        void send( signal_base &, servant_address );
};

struct db_slave : public servant
{
        // Example only!!!

        void operator()( signal_base &s )
        {
                switch( s.code() )
                {
                case int_results:
                        send( other_thing, third_party );
                        break;
                ..
                }
}

servant::send( signal_base &b, servant_address a )
{
        task t; // Pseudo only matt!

        t.message = b; // Payload
        t.source = this; // Return address
        ..

        a.queue( t ); // Again - just pseudo
}

I really hope this helps because I can see the inductive
leap required to get over the chasm of despair.

I only have one more esoteric observation to make. The "Method
Request" model of execution is nice and familiar to us. It works
brilliantly - mostly. Return addresses are managed automagically
for us. But return addresses on a CPU stack are pretty meaningless
when it comes to inter-thread communication. With the "Method
Request" model we are trying to "call" across thread boundaries -
IMHO thats just wrong.

The signal processing model (once implemented) automanages a
diferent type of address. A type of address that can be meaningful
with reference to multi-threading.

With a bunch more scaffolding this is what my reactive_object
code looks like;

        int
        db_interface::transition( machine_state<READY>, typed_bind<interface_open>
& )
        {
                client.insert( sender() );

                send( status, sender() );

                return READY;
        }

There is a whole lot of background missing here but hopefully there
are some key elements that reinforce this alternate "model of execution".
Some notes;

        db_interface: the class derived from scheduler (i.e. its a thread) and
servant
        transition: overloaded name that handles different state+message combos
        interface_open: the closest thing to a "method"
        client: a set<> of servant_addresses
        send: the signal processing primitive
        status: an object suitable for sending, like "interface_open"
        sender(): the address of the object that sent the "interface_open" and
                        caused this transition to run.
        READY: one of several defined machine states (enum)

<sigh>

Apologies if this is all badly presented. I have a huge body of code
from which the ActiveObject portion would be impossible to remove. A major
reason for starting or contributing to this thread is that I think my
existing code desparately needs boostifying and the best way (or even
a good way) is beyond me. Hence our messages.

Hope this was useful. I suspect your right and I should start with
another subject. After thinking long and hard about the most constructive
approach. Hmmmmmmmm.

Cheers,
Scott

ps:
the sample code from mark blewett looks very promising?


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