Reputation: 34884
I have an Actor and some other object:
object Config {
val readValueFromConfig() = { //....}
}
class MyActor extends Actor {
val confValue = Config.readValueFromConfig()
val initValue = Future {
val a = confValue // sometimes it's null
val a = Config.readValueFromConfig() //always works well
}
//..........
}
The code above is a very simplified version of what I actually have. The odd thing is that sometimes val a = confValue
returns null
, whereas if I replace it with val a = Config.readValueFromConfig()
then it always works well.
I wonder, is this due to the fact that the only way to interact with an actor is sending it a message? Therefore, since val confValue
is not a local variable, I must either use val a = Config.readValueFromConfig()
(a different object, not an actor) or val a = self ! GetConfigValue
and read the result afterwards?
Upvotes: 0
Views: 169
Reputation: 23105
val readValueFromConfig() = { //....}
This gives me a compile error. I assume you mean without parentheses?
val readValueFromConfig = { //....}
Same logic with different timing gives different result = a race condition.
val confValue = Config.readValueFromConfig()
is always executed during construction of MyActor
objects (because it's a field of MyActor). Sometimes this is returning null.val a = Config.readValueFromConfig() //always works well
is always executed later - after MyActor is constructed, when the Future
initValue
is executed by it's Executor
. It seems this never returns null.Possible causes:
readValueFromConfig
was dependent upon another
parallel/async operation having completed. Any chance you're reading the config asynchronously? Given the name of this method, it probably just reads synchronously from a file - meaning this is not the cause.Singleton objects are not threadsafe?? I compiled your code. Here's the decompilation of your singleton object java class:
public final class Config
{
public static String readValueFromConfig()
{
return Config..MODULE$.readValueFromConfig();
}
}
public final class Config$
{
public static final MODULE$;
private final String readValueFromConfig;
static
{
new ();
}
public String readValueFromConfig()
{
return this.readValueFromConfig;
}
private Config$()
{
MODULE$ = this;
this.readValueFromConfig = // ... your logic here;
}
}
Mmmkay... Unless I'm mistaken, that ain't thread-safe.
IF two threads are accessing readValueFromConfig
(say Thread1 accesses it first), then inside method private Config$()
, MODULE$
is unsafely published before this.readValueFromConfig
is set (reference to this
prematurely escapes the constructor). Thread2 which is right behind can read MODULE$.readValueFromConfig
before it is set. Highly likely to be a problem if '... your logic here
' is slow and blocks the thread - which is precisely what synchronous I/O does.
Moral of story: avoid stateful singleton objects from Actors (or any Threads at all, including Executors) OR make them become thread-safe through very careful coding style. Work-Around: change to a def
, which internally caches the value in a private val
.
Upvotes: 2
Reputation: 33033
I wonder, is this due to the fact that the only way to interact with an actor is sending it a message? Therefore, since val confValue is not a local variable, I must either use val a = Config.readValueFromConfig() (a different object, not an actor)
Just because it's not an actor, doesn't mean it's necessarily safe. It probably isn't.
or val a = self ! GetConfigValue and read the result afterwards?
That's almost right. You mean self ? GetConfigValue
, I think - that will return a Future
, which you can then map
over. !
doesn't return anything.
You cannot read from an actor's variables directly inside a Future
because (in general) that Future
could be running on any thread, on any processor core, and you don't have any memory barrier there to force the CPU caches to reload the value from main memory.
Upvotes: 2