Why once methods can not be used in static and const class?
vkuzkokovSun 24 Oct 2010
once is basically a fistful of sugar.
// hard way
Str fullName
{
get
{
if (&fullName == null) &fullName = "$firstName $lastName"
return &fullName
}
}
// easy way
once Str fullName() { return "$firstName $lastName" }
This is copied from docLang.
As you see fullName is mutable here, so it can't be const or static const.
go4Mon 25 Oct 2010
Thanks.perhaps there is a hidden field.
However,I think the once method is immutable.No way to change once initialized.
brianMon 25 Oct 2010
vkuzkokov details the problem correctly - a once field essentially has a mutating implementation that could potential cause race conditions during its calculation.
We could potentially store that field as volatile. But then there would be no guarantee that the same instance was seen by all threads which could potentially cause all sorts of subtle confusion. That might actually be an okay trade-off, but for now I do the safest, most conservative thing which is just disallow it. We can always relax the restrictions in the future.
qualidafialMon 25 Oct 2010
What about just synchronizing the static method?
brianMon 25 Oct 2010
What about just synchronizing the static method?
I considered that too, but I feel that might be the worse situation because then multiple chaining static/const once methods can cause deadlocks. Although I suppose we could use one single global lock for every single static/const once method.
qualidafialMon 25 Oct 2010
That's actually a good point. Now that I think about it I think even a global lock wouldn't work--you could potentially call actor.send(msg).get from within a static once, and if the actor called a static once from its method then you would get a deadlock.
qualidafialMon 25 Oct 2010
A thread-local static once would work though.
qualidafialMon 25 Oct 2010
Brian or Andy, is there a pattern you guys use already that accomplishes the same thing as a static once?
brianMon 25 Oct 2010
Brian or Andy, is there a pattern you guys use already that accomplishes the same thing as a static once?
In virtually all of my code, I just initialize stuff in my static initializer and don't worry about making it lazy. Same thing for instance methods on const classes.
qualidafialMon 25 Oct 2010
I guess if you really want lazy init you can do something like:
class Foo
{
static /* once */ Bar bar() { return BarSingleton.instance }
}
const class Bar { ... }
internal class BarSingleton
{
static const Bar instance := Bar()
}
You're leaning on the classloader to get lazy init (since BarSingleton class initializer won't be run until the class is needed in Foo.bar). Kind of a hack but hey it works. :)
That essentially gives you the semantics of using a volatile field, but has the same problem I described earlier, that there is no guarantee that everyone sees the same instance of LazyVal (but in my cases that is ok).
qualidafialMon 25 Oct 2010
Couldn't you use compareAndSet to ensure a single instance is used?
static LazyVal lazy()
{
if (lazyRef.val == null)
{
lazyVal := LazyVal()
if (!lazyRef.compareAndSet(null, lazyVal))
{
// cleanup val if holding any resources
}
}
return lazyRef.val
}
private static AtomicRef lazyRef := AtomicRef()
brianMon 25 Oct 2010
Couldn't you use compareAndSet to ensure a single instance is used?
Actually I think that would probably work. You might actually run thru the LazyVal constructor multiple times, but in the end only the first compareAndSet would be publicly visible.
So that might be a good pattern to use as a work around for once static methods.
go4 Sun 24 Oct 2010
Why once methods can not be used in static and const class?
vkuzkokov Sun 24 Oct 2010
once
is basically a fistful of sugar.This is copied from
docLang
.As you see
fullName
is mutable here, so it can't beconst
orstatic const
.go4 Mon 25 Oct 2010
Thanks.perhaps there is a hidden field.
However,I think the once method is immutable.No way to change once initialized.
brian Mon 25 Oct 2010
vkuzkokov details the problem correctly - a once field essentially has a mutating implementation that could potential cause race conditions during its calculation.
We could potentially store that field as volatile. But then there would be no guarantee that the same instance was seen by all threads which could potentially cause all sorts of subtle confusion. That might actually be an okay trade-off, but for now I do the safest, most conservative thing which is just disallow it. We can always relax the restrictions in the future.
qualidafial Mon 25 Oct 2010
What about just synchronizing the static method?
brian Mon 25 Oct 2010
I considered that too, but I feel that might be the worse situation because then multiple chaining static/const once methods can cause deadlocks. Although I suppose we could use one single global lock for every single static/const once method.
qualidafial Mon 25 Oct 2010
That's actually a good point. Now that I think about it I think even a global lock wouldn't work--you could potentially call
actor.send(msg).get
from within astatic once
, and if the actor called astatic once
from its method then you would get a deadlock.qualidafial Mon 25 Oct 2010
A thread-local static once would work though.
qualidafial Mon 25 Oct 2010
Brian or Andy, is there a pattern you guys use already that accomplishes the same thing as a
static once
?brian Mon 25 Oct 2010
In virtually all of my code, I just initialize stuff in my static initializer and don't worry about making it lazy. Same thing for instance methods on const classes.
qualidafial Mon 25 Oct 2010
I guess if you really want lazy init you can do something like:
You're leaning on the classloader to get lazy init (since BarSingleton class initializer won't be run until the class is needed in Foo.bar). Kind of a hack but hey it works. :)
Edit: use := for field assignment
brian Mon 25 Oct 2010
Actually one technique I do use is AtomicRef:
That essentially gives you the semantics of using a volatile field, but has the same problem I described earlier, that there is no guarantee that everyone sees the same instance of LazyVal (but in my cases that is ok).
qualidafial Mon 25 Oct 2010
Couldn't you use compareAndSet to ensure a single instance is used?
brian Mon 25 Oct 2010
Actually I think that would probably work. You might actually run thru the LazyVal constructor multiple times, but in the end only the first compareAndSet would be publicly visible.
So that might be a good pattern to use as a work around for once static methods.