#1265 once method

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.

// 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.

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

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.

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 a static once, and if the actor called a static 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

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.

qualidafial Mon 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. :)

Edit: use := for field assignment

brian Mon 25 Oct 2010

Actually one technique I do use is AtomicRef:

static LazyVal lazy()
{
  if (lazyRef.val == null) lazyRef.val = LazyVal()
  return lazyRef.val
}

private static AtomicRef lazyRef := 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?

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()

brian Mon 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.

Login or Signup to reply.