namgk / ambienttalk

Automatically exported from code.google.com/p/ambienttalk
0 stars 0 forks source link

Concurrency bug when invoking when:disconnected: and other event handlers #35

Closed GoogleCodeExporter closed 8 years ago

GoogleCodeExporter commented 8 years ago
When some event handlers like when:disconnected: are invoked, the scheduling of 
the event 
handlers could happen in the wrong thread (the caller thread instead of the 
thread of the actor 
that owns the event handler).

The following is a manifestation of the bug as submitted by Ben Maene:
Client:

{{{
import /.at.lang.futures;
enableFutures(true);

deftype Server;

def vector := /.at.collections.vector.Vector;

def serverList := vector.new();

whenever: Server discovered: { |server|
    when: server<-getName()@FutureMessage becomes: { |name|
    if: (false == serverList.contains(server))
    then: {
        serverList.add(server);
        system.println("Added server: " + name);

        whenever: server disconnected: {
            system.println("Server offline:" + name);
        };
        whenever: server reconnected: {
            system.println("Server online:" + name);
        };
    };
    };
};

network.online();
}}}

Server:
{{{
deftype Server;

def server := actor: {

    def getName(){
        def username := jlobby.java.lang.System.getProperty("user.name");
        username;
    };

    };

export: server as: Server;
network.online()
}}}

The discovery of the server works, but if the server becomes disconnected and 
the 
when:disconnected: handler is triggered, then the following error occurs:

Thu Oct 22 21:15:20 CEST 2009 INFO at.eventloops.vm - event: 
memberLeft(/192.168.205.1:60723[AmbientTalk]): VM disconnected: 
/192.168.205.1:60723[AmbientTalk]
Thu Oct 22 21:15:20 CEST 2009 DEBUG at.eventloops.remoterefs - 
notifyDisconnected for <far 
ref:behaviour of <actormirror:585824>>
Thu Oct 22 21:15:20 CEST 2009 ERROR at.eventloops.remoterefs - error invoking 
when:disconnected: listener
edu.vub.at.exceptions.XIllegalOperation: Detected illegal invocation of 
base_schedule: sharing via 
Java level of object <obj:12222848{createMessage}@[FuturesModule]>
    at edu.vub.at.objects.coercion.Coercer.invoke(Coercer.java:189)
    at $Proxy1.base_schedule(Unknown Source)
    at edu.vub.at.actors.natives.ELActor.acceptSelfSend(ELActor.java:319)
    at edu.vub.at.eval.Evaluator.trigger(Evaluator.java:494)
    at edu.vub.at.actors.natives.NATFarReference.triggerListener(NATFarReference.java:491)
    at edu.vub.at.actors.natives.NATFarReference.notifyDisconnected(NATFarReference.java:245)
    at edu.vub.at.actors.natives.NATFarReference.disconnected(NATFarReference.java:167)
    at 
edu.vub.at.actors.net.ConnectionListenerManager.notifyDisconnected(ConnectionLis
tenerManage
r.java:138)
    at edu.vub.at.actors.natives.ELVirtualMachine$4.process(ELVirtualMachine.java:237)
    at edu.vub.at.actors.natives.ELVirtualMachine.handle(ELVirtualMachine.java:150)
    at edu.vub.at.actors.eventloops.EventLoop.execute(EventLoop.java:248)
    at edu.vub.at.actors.eventloops.EventLoop$EventProcessor.run(EventLoop.java:269)

Original issue reported on code.google.com by tvcut...@gmail.com on 25 Oct 2009 at 8:34

GoogleCodeExporter commented 8 years ago
The problem above is caused by an illegal invocation of the ELVirtualMachine 
thread (which invokes the 
when:disconnected: event handler) on the actor mirror. Because the actor mirror 
in this case is replaced using 
reflection (in the FuturesModule), the actor mirror is a Coercer and the 
illegal access was trapped.

Cause: the method Evaluator.trigger can be invoked by non-actor threads and may 
directly call methods like 
closure.meta_receive or actor.acceptSelfSend, which then calls 
mirror.base_schedule. Since meta_receive and 
base_schedule may be overridden using reflection, they can execute arbitrary 
AmbientTalk code that should 
be serialized in the owner actor!

The Evaluator.trigger method should instead schedule the call to 
actor.acceptSelfSend to be performed by the 
actor itself.

I still have to figure out what the effect of this change will be on the 
overall interpreter.

Original comment by tvcut...@gmail.com on 25 Oct 2009 at 8:39

GoogleCodeExporter commented 8 years ago
Fixed in interpreter/trunk r1634

The bug was related to the method Evaluator.trigger, which can be invoked by 
any thread (not only actor 
threads) to send an 'apply' message to a closure owned by another actor. 
Unfortunately, Evaluator.trigger 
directly invoked actor.acceptSelfSend(...), which in turn invokes 
mirror.base_schedule(...), meaning that an 
external thread could actually concurrently manipulate another actor's inbox! 
The solution was to add a new 
event, event_trigger, to the public interface of an actor: other threads may 
signal this event if they want the 
actor to perform the asynchronous self-send.

An example where the bug would manifest itself is included in 
at/unit/bugfixes.at , which now, of course, 
passes without problems.

How could this bug manifest in the first place? I think because in the 
implementation, non-actor threads like 
ELFarReference event loops, ELVirtualMachine event loops etc. received direct 
Java references to the 
subscribing closures. This enables them to invoke apply on these closures 
without the interpreter being able 
to catch the bug. Actually, the bug was discovered by Ben because he had set 
enableFutures(true), which 
causes the actor's mirror to be replaced by a custom one. At the 
implementation-level, this means the actor's 
mirror is actually a coercer (of type ATActorMirror), and coercers *do* trap 
illegal method invocations. It was 
thanks to this trap that I could discover the bug.

Original comment by tvcut...@gmail.com on 10 Nov 2009 at 3:58