diff --git a/sre/io.janusproject/io.janusproject.plugin/src/main/sarl/io/sarl/sre/services/lifecycle/AgentLife.sarl b/sre/io.janusproject/io.janusproject.plugin/src/main/sarl/io/sarl/sre/services/lifecycle/AgentLife.sarl index 695e4328ad..e359042a13 100644 --- a/sre/io.janusproject/io.janusproject.plugin/src/main/sarl/io/sarl/sre/services/lifecycle/AgentLife.sarl +++ b/sre/io.janusproject/io.janusproject.plugin/src/main/sarl/io/sarl/sre/services/lifecycle/AgentLife.sarl @@ -63,21 +63,19 @@ import static extension io.sarl.lang.core.SREutils.* @PrivateAPI(isCallerOnly = true) final class AgentLife { - volatile var state = AgentState::UNSTARTED + val lock : ReadWriteLock + + var stateObject = AgentState::UNSTARTED var agentInstance : Agent var innerContextInstance : Context - val innerContextLock : ReadWriteLock - - volatile var innerSpaceAddress : Address - - val innerSpaceAddressLock : ReadWriteLock + var innerSpaceAddress : Address var externalContextInstances : ConcurrentHashMap - volatile var defaultContextInstance : ContextReference + var defaultContextInstance : ContextReference var loggingCapacity : AtomicSkillReference @@ -121,12 +119,11 @@ final class AgentLife { * @param lockProvider the provider of locks */ new (lockProvider : Provider) { - this.innerContextLock = lockProvider.get - this.innerSpaceAddressLock = lockProvider.get + this.lock = lockProvider.get } - def toString : String { - this.^agent?.ID + "[" + this.state.name + "]" + override toString : String { + this.^agent?.ID + "[" + getState.name + "]" } /** Replies the agent's logger. @@ -176,11 +173,27 @@ final class AgentLife { */ def start(skillUninstaller : SkillUninstaller, logger : LoggingService, spawningAgent : UUID, spawningContext : Context, initializationParameters : Object*) : boolean { - var st : AgentState = this.state + val rlock0 = this.lock.readLock + var st : AgentState + rlock0.lock + try { + st = this.stateObject + } finally { + rlock0.unlock + } if (st === AgentState::UNSTARTED) { try { - this.state = AgentState::INITIALIZING - st = AgentState::INITIALIZING + val wlock0 = this.lock.writeLock + wlock0.lock + try { + st = this.stateObject + if (st !== AgentState::UNSTARTED) { + return false + } + this.stateObject = AgentState::INITIALIZING + } finally { + wlock0.unlock + } var eb = spawningContext.attachAgentToPlatform @@ -192,8 +205,21 @@ final class AgentLife { var delayedEvents = eb.fireEventAndWait(initEvent, true, true) // State may have changed - if (this.state === AgentState::INITIALIZING) { - this.state = AgentState::ALIVE + val rlock1 = this.lock.readLock + rlock1.lock + try { + st = this.stateObject + } finally { + rlock1.unlock + } + if (st === AgentState::INITIALIZING) { + val wlock1 = this.lock.writeLock + wlock1.lock + try { + this.stateObject = AgentState::ALIVE + } finally { + wlock1.unlock + } if (delayedEvents !== null) { for (delayedEvent : delayedEvents) { eb.fireEvent(delayedEvent) @@ -213,9 +239,8 @@ final class AgentLife { stop(skillUninstaller, logger, true) throw e } - } else { - return false } + return false } /** Set up the agent in order to be attached properly to the plaform. @@ -246,7 +271,14 @@ final class AgentLife { * @param enclosingContexts is the list of context for which we should be sure that the agent is out. */ protected def detachAgentFromPlatform(listener : EventListener, enclosingContexts : ConcurrentLinkedDeque) : void { - var contextReference : ContextReference = this.defaultContextInstance + val rlock = this.lock.readLock + var contextReference : ContextReference + rlock.lock + try { + contextReference = this.defaultContextInstance + } finally { + rlock.unlock + } // Unregister the agent on its parent default space. var theDefaultSpace : OpenEventSpace = null @@ -341,7 +373,13 @@ final class AgentLife { } finally { // Clear the references dyingAgent.setSreSpecificData(null) - this.defaultContextInstance = null + val wlock = this.lock.writeLock + wlock.lock + try { + this.defaultContextInstance = null + } finally { + wlock.unlock + } this.externalContextInstances = null this.agentInstance = null this.state = AgentState::DEAD @@ -364,14 +402,26 @@ final class AgentLife { /** Replies the agent state. */ def getState : AgentState { - return this.state + val lock = this.lock.readLock + lock.lock + try { + return this.stateObject + } finally { + lock.unlock + } } /** Change the agent state. */ def setState(state : AgentState) { assert state !== null - this.state = state + val lock = this.lock.writeLock + lock.lock + try { + this.stateObject = state + } finally { + lock.unlock + } } /** Set the inner context reference. @@ -380,7 +430,7 @@ final class AgentLife { * @return the previous context, or {@code null}. */ def setInnerContext(innerContext : Context) : Context { - val lock = this.innerContextLock.writeLock + val lock = this.lock.writeLock lock.lock try { var old = this.innerContextInstance @@ -398,7 +448,7 @@ final class AgentLife { * @return the instance of inner context, or {@code null} */ def getInnerContext(creator : (AgentLife)=>Context) : Context { - val rlock = this.innerContextLock.readLock + val rlock = this.lock.readLock var ctx : Context rlock.lock try { @@ -407,12 +457,13 @@ final class AgentLife { rlock.unlock } if (ctx === null && creator !== null) { - val wlock = this.innerContextLock.writeLock + val newInstance = creator.apply(this) + val wlock = this.lock.writeLock wlock.lock try { ctx = this.innerContextInstance if (ctx === null) { - this.innerContextInstance = creator.apply(this) + this.innerContextInstance = newInstance ctx = this.innerContextInstance } } finally { @@ -427,7 +478,7 @@ final class AgentLife { * @return the address, never {@code null}. */ def getAddressInInnerDefaultSpace : Address { - val rlock = this.innerSpaceAddressLock.readLock + val rlock = this.lock.readLock var adr : Address rlock.lock try { @@ -436,14 +487,13 @@ final class AgentLife { rlock.unlock } if (adr === null) { - val wlock = this.innerSpaceAddressLock.writeLock + var aid = this.^agent.ID + var spaceid = new SpaceID(aid, UUID::randomUUID, typeof(OpenEventSpaceSpecification)) + adr = new Address(spaceid, aid); + val wlock = this.lock.writeLock wlock.lock try { - adr = this.innerSpaceAddress - if (adr === null) { - var aid = this.^agent.ID - var spaceid = new SpaceID(aid, UUID::randomUUID, typeof(OpenEventSpaceSpecification)) - adr = new Address(spaceid, aid); + if (this.innerSpaceAddress === null) { this.innerSpaceAddress = adr } } finally { @@ -462,11 +512,17 @@ final class AgentLife { def addExternalContext(context : Context, address : Address) : ContextReference { assert context !== null assert address !== null - var continueAddition = false - continueAddition = this.defaultContextInstance === null || context.ID != this.defaultContextInstance.context.ID + val rlock = this.lock.readLock + var cr : ContextReference + rlock.lock + try { + cr = this.defaultContextInstance + } finally { + rlock.unlock + } - if (continueAddition) { + if (cr === null || context.ID != cr.context.ID) { var ref = new ContextReference(this, context, address) ensureExternalContextInstances.put(context.ID, ref) @@ -514,7 +570,14 @@ final class AgentLife { def getExternalContexts : ConcurrentLinkedDeque { var result = new ConcurrentLinkedDeque val instances = this.externalContextInstances - val dc = this.defaultContextInstance + val rlock = this.lock.readLock + var dc : ContextReference + rlock.lock + try { + dc = this.defaultContextInstance + } finally { + rlock.unlock + } if (instances !== null) { val dcId = dc?.context?.ID var foundDefaultContext = false @@ -548,7 +611,14 @@ final class AgentLife { * @see {@link #getExternalContexts()} */ def getEnclosingContexts : ConcurrentLinkedDeque { - val dc = this.defaultContextInstance + val rlock = this.lock.readLock + var dc : ContextReference + rlock.lock + try { + dc = this.defaultContextInstance + } finally { + rlock.unlock + } val ec = this.externalContextInstances if (dc !== null) { var result = new ConcurrentLinkedDeque @@ -581,11 +651,10 @@ final class AgentLife { * @return the context, or {@code null} */ def getExternalContext(identifier : UUID) : ContextReference { - - if (this.externalContextInstances !== null) { - return this.externalContextInstances.get(identifier) + val col = this.externalContextInstances + if (col !== null) { + return col.get(identifier) } - return null } @@ -599,8 +668,15 @@ final class AgentLife { assert context !== null assert address !== null val nc = new ContextReference(this, context, address) - var oldDefaultSpace : ContextReference = this.defaultContextInstance - this.defaultContextInstance = nc + var oldDefaultSpace : ContextReference + val wlock = this.lock.writeLock + wlock.lock + try { + oldDefaultSpace = this.defaultContextInstance + this.defaultContextInstance = nc + } finally { + wlock.unlock + } removeExternalContext(context) return oldDefaultSpace } @@ -619,7 +695,13 @@ final class AgentLife { * @return the default context, never {@code null}. */ def getDefaultContext : ContextReference { - return this.defaultContextInstance + val lock = this.lock.readLock + lock.lock + try { + return this.defaultContextInstance + } finally { + lock.unlock + } } } diff --git a/sre/io.janusproject/io.janusproject.tests/src/test/sarl/io/sarl/sre/tests/runtime/bugs/bug861/Bug861Test.sarl b/sre/io.janusproject/io.janusproject.tests/src/test/sarl/io/sarl/sre/tests/runtime/bugs/bug861/Bug861Test.sarl index 7890218dc3..77814b065f 100644 --- a/sre/io.janusproject/io.janusproject.tests/src/test/sarl/io/sarl/sre/tests/runtime/bugs/bug861/Bug861Test.sarl +++ b/sre/io.janusproject/io.janusproject.tests/src/test/sarl/io/sarl/sre/tests/runtime/bugs/bug861/Bug861Test.sarl @@ -40,6 +40,7 @@ import static io.sarl.sre.test.framework.Constants.* import static extension io.sarl.tests.api.tools.TestAssertions.* import static extension org.junit.jupiter.api.Assertions.* +import io.sarl.sre.tests.runtime.internal.mocks.Bye /** Tests for issue #861: Cannot use emit function in the Initialize handler. * @@ -67,28 +68,30 @@ class Bug861Test { var kern = setupTheSreKernel(null, null) val id0 = UUID::randomUUID - var w0 = waitForAgentSpawned(id0) - - kern.startAgentWithID(typeof(BootAgent0), id0, agentInitializationParameters) - - w0.doWait + waitForAgentSpawned(id0) [ + kern.startAgentWithID(typeof(BootAgent0), id0, agentInitializationParameters) + ] - kern.startAgent(typeof(EmptyAgent0), agentInitializationParameters) + val id1 = UUID::randomUUID + waitForAgentSpawned(id1) [ + kern.startAgentWithID(typeof(EmptyAgent0), id1, agentInitializationParameters) + ] + + rootContext.defaultSpace.emit(UUID::randomUUID, new Bye) waitForTheKernel(STANDARD_TIMEOUT) - assertEquals(2, getNumberOfResults(bootAgentId)) + assertEquals(2, getNumberOfResults(id0)) - val childAgent = getFirstResultOfType(bootAgentId, typeof(UUID)) + val childAgent = getFirstResultOfType(id0, typeof(UUID)) childAgent.assertNotNull + id1.assertEquals(childAgent) - val initEvent0 = getFirstResultOfType(bootAgentId, typeof(Initialized)) + val initEvent0 = getFirstResultOfType(id0, typeof(Initialized)) initEvent0.assertNotNull childAgent.assertEquals(initEvent0.source.UUID) - assertEquals(1, getNumberOfResults(childAgent)) - - val initEvent1 = getResult(childAgent, typeof(Initialized), 0) + val initEvent1 = getResult(id0, typeof(Initialized), 0) initEvent1.assertNotNull childAgent.assertEquals(initEvent1.source.UUID) } diff --git a/sre/io.janusproject/io.janusproject.tests/src/test/sarl/io/sarl/sre/tests/runtime/bugs/bug861/mocks/BootAgent0.sarl b/sre/io.janusproject/io.janusproject.tests/src/test/sarl/io/sarl/sre/tests/runtime/bugs/bug861/mocks/BootAgent0.sarl index 23f14a744d..602658c54d 100644 --- a/sre/io.janusproject/io.janusproject.tests/src/test/sarl/io/sarl/sre/tests/runtime/bugs/bug861/mocks/BootAgent0.sarl +++ b/sre/io.janusproject/io.janusproject.tests/src/test/sarl/io/sarl/sre/tests/runtime/bugs/bug861/mocks/BootAgent0.sarl @@ -25,14 +25,12 @@ import io.sarl.core.Initialize import io.sarl.core.Lifecycle import io.sarl.sre.test.framework.skills.TestingCapacity import io.sarl.sre.test.framework.skills.TestingSkill -import java.util.concurrent.atomic.AtomicBoolean +import io.sarl.sre.tests.runtime.internal.mocks.Bye agent BootAgent0 { uses Lifecycle - val done = new AtomicBoolean - uses TestingCapacity on Initialize { @@ -41,16 +39,14 @@ agent BootAgent0 { on AgentSpawned { addResult(occurrence.agentID) - if (this.done.getAndSet(true)) { - killMe - } } on Initialized { addResult(occurrence) - if (this.done.getAndSet(true)) { - killMe - } + } + + on Bye { + killMe } } diff --git a/sre/io.janusproject/io.janusproject.tests/src/test/sarl/io/sarl/sre/tests/runtime/bugs/bug861/mocks/EmptyAgent0.sarl b/sre/io.janusproject/io.janusproject.tests/src/test/sarl/io/sarl/sre/tests/runtime/bugs/bug861/mocks/EmptyAgent0.sarl index bca654d89d..f080ae8bcd 100644 --- a/sre/io.janusproject/io.janusproject.tests/src/test/sarl/io/sarl/sre/tests/runtime/bugs/bug861/mocks/EmptyAgent0.sarl +++ b/sre/io.janusproject/io.janusproject.tests/src/test/sarl/io/sarl/sre/tests/runtime/bugs/bug861/mocks/EmptyAgent0.sarl @@ -20,25 +20,20 @@ */ package io.sarl.sre.tests.runtime.bugs.bug861.mocks -import io.sarl.core.DefaultContextInteractions import io.sarl.core.Initialize import io.sarl.core.Lifecycle -import io.sarl.sre.test.framework.skills.TestingCapacity -import io.sarl.sre.test.framework.skills.TestingSkill +import io.sarl.sre.tests.runtime.internal.mocks.Bye +import io.sarl.core.DefaultContextInteractions agent EmptyAgent0 { - uses DefaultContextInteractions, Lifecycle - - uses TestingCapacity + uses Lifecycle, DefaultContextInteractions on Initialize { - setSkill(new TestingSkill(occurrence)) - new Initialized().emit + emit(new Initialized) } - on Initialized { - addResult(occurrence) + on Bye { killMe }