ActiveObject and tailoring the internal actor

12 views
Skip to first unread message

Nick

unread,
Jul 6, 2015, 9:02:21 AM7/6/15
to GPars Users
Hi,

When creating ActiveObject classes, I'm just wondering how one should
normally customise the actor implementation - for example, the lifecycle
methods - afterStart(), onInterrupt(), act() etc. The usual means
documented is to extend the base actor class, or define a custom act
function, but this doesn't seem possible here.

I know I could use Groovy's introspection to add methods to the
instance, but there may be a need to do this from Java (to please those
not yet bought into Groovy's advantages). I'd rather avoid that unless I
have to.

In any case, in what context it is safe to do change these things? From
the constructor I would assume, but this may be incorrect.

Thanks,

Nick

Nick

unread,
Jul 7, 2015, 1:23:26 PM7/7/15
to GPars Users
Hello,

Attempting to answer my own question here, having looked at the GPars
source...

Short answer: seems you have to supply and initialise your own
InternalActor field, and then you can control how and when it starts.
In a pinch it seems you can also patch the actor instance's methods,
despite it being a final class, insofar as they are called externally
(i.e. patching doStart() only works when called externally, not
internally, when the original method is still called).

Something like this. Which also shows my use case - I want to wrap an
SWT event loop, and only start actor messaging when it's ready. Note,
this is a condensed illustration, with parts omitted and not fully tested.

@ActiveObject
class GuiActor {
private final InternalActor internalActiveObjectActor; //
Presence prevents create() being called
private Display display; // for SWT
private Shell shell; // for SWT
private final CountDownLatch latch = new CountDownLatch(1);

public GuiActor() {
// We must perform the work that create() does here manually
internalActiveObjectActor = new InternalActor();
internalActiveObjectActor.setParallelGroup(Actors.defaultActorPGroup);

// At this point we can (hopefully safely) perform any
initialisation we need to do prior to messages arriving
// In our case, start a (blocking) SWT event loop in
another thread.
openSwtWindow()

internalActiveObjectActor.silentStart() // start acting

// at this point we can perform any initialisation we need
to do once message can be sent

}

private openSwtWindow() {

Thread t = new Thread({
// Run the gui
display = new Display();
try {
// ... set up display and shell here ...

latch.countDown();

// ... blocking event loop here ...
// Does not return until the user closes the window

}
catch(Exception e) {
// ... log error ...
}
finally{
// ... clean up ...
}

// Stop the relay actor to stop receiving any messages.
internalActiveObjectActor.stop()
});
t.start();

await(); // latch signal that display is ready
}

// waits for gui to start
private boolean await() {
try {
latch.await();
return true;
}
catch (InterruptedException e) {
e.printStackTrace(); // whatever
return false;
}
}

// Toy example of mutating the GUI
@ActiveMethod
public void setTitle(String msg) {
display.asyncExec {
shell.setText(msg);
}
}
}

Feedback as to whether I could be doing these things a better way welcomed.


Cheers!

N



This is what the @ActiveObject annotation appears to do, in more detail
than appears in the docs.

First, as the docs say, by dint of an AST transformation classes
annotated with @ActiveObject need a field called
"internalActiveObjectActor" of type InternalActor.

If it is present and the right class, it will be used as-is (as
exploited above).

If the field is present, but not an InternalActor, this is an error. So
don't do that.

Otherwise, the field gets declared and will be initialised using
InternalActor.create(Object groupId), passing a string naming the
parallel group. (This is the empty string by default implying the
default actor parallel group is used, but can be set to a custom group
name in the ActiveObjectRegistry, via the 'value' field of the
ActiveObject annotation.)

The InternalActor.create() method constructs an InternalActor instance,
sets its parallel group, and invokes its silentStart() method
immediately. This means the actor is started as soon as the object is
constructed.

As an aside, AST transform also clones the original methods of the
active object class but with private + final modifiers, and replaces
them with proxy methods that delegate to the internal actor's submit
method in order to call the originals, something like this:


// non-blocking; returns a promise
final private Object <methodname>(<params>) { return
internalActor.submit(this, <methodname>, <params>); }

// blocking; attempts to unpack the promise, or throw as appropriate
final private Object <methodname>(<params>) {
return internalActor.preProcessReturnValue(
internalActor.submitAndWait(this, <methodname>, <params>),
this,
<methodname>
);
}


To conclude: although my original idea was that the active object
constructor might try to set the internalActiveObjectActor's afterStart
method dynamically, this will never work, because:

a) silentStart() is used in the create() initialiser, which skips
sending the START_MESSAGE, so afterStart won't get invoked, and
b) even if start() was used, the internal actor field is initialised
inline, and so may have been started by the time the user-supplied
constructor runs.

...and the clincher

c) the InternalActor class is declared final, and the AST transform
includes a hardwired check for its fully-qualified class name.

So that's essentially ruled out all the loopholes for modifying the
InternalActor. The only option left is to pre-empt the AST transform
by create the internal actor field manually.


Russel Winder

unread,
Jul 9, 2015, 12:53:31 PM7/9/15
to Nick, GPars Users
Hi,

Just a quick note to say, the email has been spotted. If Václav or
someone doesn't reply by the weekend, I'll take a look.

--
Russel.
=============================================================================
Dr Russel Winder t: +44 20 7585 2200 voip: sip:russel...@ekiga.net
41 Buckmaster Road m: +44 7770 465 077 xmpp: rus...@winder.org.uk
London SW11 1EN, UK w: www.russel.org.uk skype: russel_winder
signature.asc
Reply all
Reply to author
Forward
0 new messages