Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[sre] Ensure that AgentKilled event is fired when the agent is really…
… destroyed.

Previously, the agent may be not totally killed when the AgentKilled is
fired. It means that if this agent is an inner agent, the containing
agent has not an inner event space with a valid state.

close #872

Signed-off-by: Stéphane Galland <galland@arakhne.org>
  • Loading branch information
gallandarakhneorg committed Oct 7, 2018
1 parent 8db0dfd commit a23c90d
Show file tree
Hide file tree
Showing 2 changed files with 152 additions and 16 deletions.
Expand Up @@ -41,6 +41,7 @@
import com.google.inject.Injector;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import org.eclipse.xtext.xbase.lib.Pair;

import io.janusproject.kernel.bic.BuiltinCapacityUtil;
import io.janusproject.services.AbstractDependentService;
Expand Down Expand Up @@ -305,7 +306,11 @@ public boolean killAgent(UUID agentID) {
if (killAgent != null) {
try {
final Logging skill = SREutils.getInternalSkill(killAgent, Logging.class);
skill.warning(warningMessage);
if (skill != null) {
skill.warning(warningMessage);
} else {
this.logger.getKernelLogger().warning(warningMessage);
}
} catch (Exception e) {
throw new Error(Messages.StandardSpawnService_9, e);
}
Expand Down Expand Up @@ -439,6 +444,7 @@ public boolean canKillAgent(Agent agent) {
* @param agent
* - the destroyed agent.
*/
@SuppressWarnings({"checkstyle:npathcomplexity"})
protected void fireAgentDestroyed(Agent agent) {
final ListenerCollection<SpawnServiceListener> list;
synchronized (getAgentLifecycleListenerMutex()) {
Expand All @@ -451,30 +457,23 @@ protected void fireAgentDestroyed(Agent agent) {
ilisteners = null;
}
final SpawnServiceListener[] ilisteners2 = this.globalListeners.getListeners(SpawnServiceListener.class);

// Retrieve the agent's contexts
final List<Pair<AgentContext, Address>> contextRegistrations = new ArrayList<>();
try {
final SynchronizedIterable<AgentContext> sc = BuiltinCapacityUtil.getContextsOf(agent);
synchronized (sc.mutex()) {
final UUID killedAgentId = agent.getID();
final Scope<Address> scope = address -> {
final UUID receiver = address.getUUID();
return !receiver.equals(killedAgentId);
};
for (final AgentContext context : sc) {
final SynchronizedIterable<AgentContext> allContexts = BuiltinCapacityUtil.getContextsOf(agent);
synchronized (allContexts.mutex()) {
for (final AgentContext context : allContexts) {
final EventSpace defSpace = context.getDefaultSpace();
defSpace.emit(
// No need to give an event source because it is explicitly set below.
null,
new AgentKilled(defSpace.getAddress(agent.getID()), agent.getID(), agent.getClass().getName()),
scope);
final Address address = defSpace.getAddress(agent.getID());
contextRegistrations.add(Pair.of(context, address));
}
}
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}

// Local agent and framework destruction
if (ilisteners != null) {
for (final SpawnServiceListener l : ilisteners) {
l.agentDestroy(agent);
Expand All @@ -483,6 +482,28 @@ protected void fireAgentDestroyed(Agent agent) {
for (final SpawnServiceListener l : ilisteners2) {
l.agentDestroy(agent);
}
// Fire AgentKilled into the associated contexts
try {
final UUID killedAgentId = agent.getID();
final Scope<Address> scope = address -> {
final UUID receiver = address.getUUID();
return !receiver.equals(killedAgentId);
};
final String killedAgentType = agent.getClass().getName();
for (final Pair<AgentContext, Address> registration : contextRegistrations) {
final EventSpace defSpace = registration.getKey().getDefaultSpace();
defSpace.emit(
// No need to give an event source because it is explicitly set below.
null,
new AgentKilled(
registration.getValue(), killedAgentId, killedAgentType),
scope);
}
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
}

@Override
Expand Down
@@ -0,0 +1,115 @@
/*
* $Id$
*
* SARL is an general-purpose agent programming language.
* More details on http://www.sarl.io
*
* Copyright (C) 2014-2018 the original authors or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.janusproject.tests.bugs;

import static org.junit.Assert.assertFalse;

import java.util.Collection;
import java.util.UUID;

import org.junit.Rule;
import org.junit.Test;

import io.janusproject.tests.testutils.AbstractJanusRunTest;

import io.sarl.core.AgentKilled;
import io.sarl.core.InnerContextAccess;
import io.sarl.core.Lifecycle;
import io.sarl.core.Schedules;
import io.sarl.lang.SARLVersion;
import io.sarl.lang.annotation.PerceptGuardEvaluator;
import io.sarl.lang.annotation.SarlElementType;
import io.sarl.lang.annotation.SarlSpecification;
import io.sarl.lang.sarl.SarlPackage;
import io.sarl.tests.api.Repeat;
import io.sarl.tests.api.RepeatRule;

/** Tests for issue #872: Invalid synchronization of the member agent collection.
*
* <p>See: https://github.com/sarl/sarl/issues/872
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
* @see "https://github.com/sarl/sarl/issues/872"
*/
@SuppressWarnings("all")
public class Bug872 extends AbstractJanusRunTest {

@Rule
public RepeatRule repeatRule = new RepeatRule();

@Test
@Repeat(5)
public void run_01() throws Exception {
runJanus(TopAgent.class, false, true, NO_TIMEOUT);
assertFalse(getResult(getBootAgent(), Boolean.class, 0));
forgetTheKernel();
}

@SarlSpecification(SARLVersion.SPECIFICATION_RELEASE_VERSION_STRING)
@SarlElementType(SarlPackage.SARL_AGENT)
static class TopAgent extends TestingAgent {

public TopAgent(UUID parentID, UUID agentID) {
super(parentID, agentID);
}

@Override
protected boolean runAgentTest() {
getSkill(Lifecycle.class).spawnInContext(
InnerAgent.class,
getSkill(InnerContextAccess.class).getInnerContext(),
getAgentInitializationParameters());
return false;
}

@PerceptGuardEvaluator
private void guardAgentKilled(AgentKilled occurrence, Collection<Runnable> handlers) {
handlers.add(() -> onAgentKilled(occurrence));
}

private void onAgentKilled(AgentKilled occurrence) {
boolean has = getSkill(InnerContextAccess.class).hasMemberAgent();
addResult(has);
forceKillMe();
}

}

@SarlSpecification(SARLVersion.SPECIFICATION_RELEASE_VERSION_STRING)
@SarlElementType(SarlPackage.SARL_AGENT)
static class InnerAgent extends TestingAgent {

public InnerAgent(UUID parentID, UUID agentID) {
super(parentID, agentID);
}

@Override
protected boolean runAgentTest() {
getSkill(Schedules.class).in(1000, it -> forceKillMe());
return false;
}

}

}

0 comments on commit a23c90d

Please sign in to comment.