#1247 Actor locals with private scope

qualidafial Tue 5 Oct 2010

While working on my data binding framework I came across a problem. Here's what I'm trying to achieve:

class Watcher
{
  private static /* mutable */ Watcher[] collected

  static Watcher[] collect(|->| func)
  {
    oldCollected := collected
    try
    {
      collected = Watcher[,]
      func()
      return collected
    }
    finally
    {
      collected = oldCollected
    }
  }

  new make()
  {
    ...

    collected?.add(this)
  }
}

The collect method runs the passed in function and returns an array of all the Watcher instances created during the execution of the function:

watchers := Watcher.collect {
  // Binding expressions creates a watcher for each nested property.
  // The following binding would create a Watcher for Model.customer,
  // Customer.firstName and Text.text properties.
  Bind.twoWay(
      Bind.from(model, 'customer.firstName'),
      Bind.from(firstNameText, 'text'))
}

...

// clean up all listeners e.g. after an editing context has expired
watchers.each { unwatch }

This worked in Flex because the Flash runtime is single-threaded, and static fields can be non-const.

In Java I can use a private static final ThreadLocal to ensure that each thread gets its own watchers accumulator.

In Fantom all statics must be const, so I cannot use this approach. I could use Actor.locals but I don't care for it since locals is visible to any code on the thread/actor. I would like some way to declare an actor-local variable without having to expose my actor's state to the rest of the world.

My current workaround is to use [java]java.lang::ThreadLocal, however I'd like to replace this with pure Fantom code at some point.

brian Tue 5 Oct 2010

This was discussed in #1193, just to recap my thoughts:

Now the second issue (which is what you are probably getting at), is that once the function is called in the actor's context, that function could potentially access Actor.locals which would be considered private state. But this is only a problem assuming:

  1. you know the private key used in the locals map
  2. you know the data structure used in the locals map
  3. the data structure used in the locals map is actually public

So I would consider Actor.locals to be encapsulated from a pragmatic perspective. But Fantom doesn't absolutely enforce encapsulation if you really want to break the documented rules. For example you can use the "->" operator to access internal/private slots.

So in some sense Fantom relies on conventions. You can access Actor.local state or private slots, but you do so knowingly that you are breaking the encapsulation of the public API and there is no guarantee that you are using a published, stable interface.

qualidafial Tue 5 Oct 2010

From a runtime security perspective there are several problems with this:

1. you know the private key used in the locals map

Actor.locals.keys.each { echo(it) } // TA DA!

2. you know the data structure used in the locals map

In many cases this is a simple matter of inspection.

Even if you obfuscate the keys:

class Watcher
{
  private static const Str watchersKey := Uuid().toStr
  private static Watcher[] watchers()
  {
    return Actor.locals.getOrAdd(watchersKey, Watcher[,])
  }
}

The runtime type of the value is Watcher[] which tells you a lot already. There is little you could not learn about the locals map through basic runtime inspection.

3. the data structure used in the locals map is actually public

On the main thread, the locals map is as secure as a public whiteboard. Truthfully this is my main concern. The fact that the locals map is keyed by Str means there is a non-zero chance your locals could be overwritten by some other code that uses the same key for a different purpose. This problem would not exist if we had thread/actor locals.

brian Tue 5 Oct 2010

I guess it depends on what you mean by "security". I don't consider Fantom's encapsulation model to be "security". It is just a basic convention to clearly delineate what is public and what is private implementation details.

In our commercial product we define public APIs that we support. And we have all sorts of non-public APIs which might be private, internal, public with @NoDoc, or use Actor.locals. I don't personally obsess that there isn't some hard security model around the non-public stuff. But when it comes to support and backward compatibility, using that stuff is at your own risk.

brian Wed 6 Oct 2010

Just a couple more thoughts...

I just want to frame the discussion that Fantom encapsulation is not a security model. We should never depend on what Fantom does from a true security point of view like Java might do to isolate applets.

The other thing about Actor.locals is that they are bit hackish to begin with, which is why I don't know if we should consider resurrecting the idea of actorlocal modifier for fields - see #204.

qualidafial Fri 8 Oct 2010

I just want to frame the discussion that Fantom encapsulation is not a security model. We should never depend on what Fantom does from a true security point of view like Java might do to isolate applets.

Sorry, I shouldn't have said security, it's an overloaded term and was not taken the way I meant. I was speaking only about access modifiers e.g. public, private, etc.

I need static actor-local state in a class, and I want it to be private. However because of the rule that static fields must be const, the only (pure Fantom) option left to me is put that state in Actor.locals.

I'm developing a library that's intended to be re-used, so I'm not comfortable that my only option exposes my internal state to the entire thread.

The other thing about Actor.locals is that they are bit hackish to begin with, which is why I don't know if we should consider resurrecting the idea of actorlocal modifier for fields - see #204.

I'm a little confused by your wording, are you suggesting you want to eliminate Actor.locals in favor of the actorlocal keyword proposal?

Personally I would favor this, it seems it would solve my present problem.

brian Fri 8 Oct 2010

Yeah, I am definitely thinking actorlocal might be a nice feature. Using Actor.locals seems to happen enough that a first language feature might warrant it. I got the idea originally from .NET that has a threadlocal attribute you can annotate a field with.

MoOm Sun 10 Oct 2010

I think an actorlocal keyword would be definitely great!

It would also release the rule that says that static fields must be const. We could indeed have a non const static actorlocal field, without breaking the thread-safety, right?

Login or Signup to reply.