/*
* Quasar: lightweight threads and actors for the JVM.
* Copyright (c) 2013-2014, Parallel Universe Software Co. All rights reserved.
*
* This program and the accompanying materials are dual-licensed under
* either the terms of the Eclipse Public License v1.0 as published by
* the Eclipse Foundation
*
* or (per the licensee's choosing)
*
* under the terms of the GNU Lesser General Public License version 3.0
* as published by the Free Software Foundation.
*/
package co.paralleluniverse.actors;
import co.paralleluniverse.concurrent.util.MapUtil;
import co.paralleluniverse.fibers.SuspendExecution;
import co.paralleluniverse.fibers.Suspendable;
import co.paralleluniverse.strands.concurrent.ReentrantLock;
import com.google.common.base.Objects;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A registry used to find registered actors by name. Actors are registered with the {@link Actor#register() } method.
*
* @author pron
*/
class LocalActorRegistry extends co.paralleluniverse.actors.spi.ActorRegistry {
private static final Logger LOG = LoggerFactory.getLogger(LocalActorRegistry.class);
private final ConcurrentMap<String, ActorRef<?>> registeredActors = MapUtil.newConcurrentHashMap();
private final ReentrantLock lock = new ReentrantLock();
private final Condition cond = lock.newCondition();
@Override
@Suspendable
public <Message> void register(Actor<Message, ?> actor, ActorRef<Message> actorRef) {
final String name = actorRef.getName();
if (name == null)
throw new IllegalArgumentException("name is null");
lock.lock();
try {
final ActorRef<?> old = registeredActors.get(name);
if (old != null && Objects.equal(old, actorRef))
return;
if (old != null && LocalActor.isLocal(old) && !LocalActor.isDone(old))
throw new RegistrationException("Actor " + old + " is not dead and is already registered under " + name);
if (old != null)
LOG.info("Re-registering {}: old was {}", name, old);
if (old != null && !registeredActors.remove(name, old))
throw new RegistrationException("Concurrent registration under the name " + name);
if (registeredActors.putIfAbsent(name, actorRef) != null)
throw new RegistrationException("Concurrent registration under the name " + name);
cond.signalAll();
} finally {
lock.unlock();
}
LOG.info("Registering {}: {}", name, actorRef);
}
@Override
public <Message> void unregister(Actor<Message, ?> actor, ActorRef<Message> actorRef) {
registeredActors.remove(actorRef.getName());
}
@Override
public ActorRef<?> tryGetActor(final String name) throws SuspendExecution {
ActorRef<?> actor = registeredActors.get(name);
if (actor == null) {
lock.lock();
try {
actor = registeredActors.get(name);
} finally {
lock.unlock();
}
}
return actor;
}
@Override
public ActorRef<?> getActor(final String name) throws InterruptedException, SuspendExecution {
return getActor(name, 0, null);
}
@Override
public ActorRef<?> getActor(final String name, long timeout, TimeUnit unit) throws InterruptedException, SuspendExecution {
ActorRef<?> actor = registeredActors.get(name);
if (actor == null) {
final long deadline = unit != null ? System.nanoTime() + unit.toNanos(timeout) : 0;
lock.lock();
try {
for (;;) {
actor = registeredActors.get(name);
if (actor != null)
break;
if (deadline > 0) {
final long now = System.nanoTime();
if (now > deadline)
return null;
cond.await(deadline - now, TimeUnit.NANOSECONDS);
} else
cond.await();
}
} finally {
lock.unlock();
}
}
return actor;
}
@Override
public <T extends ActorRef<?>> T getOrRegisterActor(final String name, Callable<T> actorFactory) throws SuspendExecution {
T actor = (T)registeredActors.get(name);
if (actor == null) {
lock.lock();
try {
actor = (T)registeredActors.get(name);
if (actor == null) {
try {
actor = actorFactory.call();
} catch (Exception e) {
throw new RuntimeException(e);
}
LocalActor.register(actor, name);
}
} finally {
lock.unlock();
}
}
return actor;
}
/**
* Use only in tests!
*/
void clear() {
registeredActors.clear();
}
@Override
public void shutdown() {
}
}