#204 Design proposal: threadlocal fields

brian Wed 23 Apr 2008

One of the interesting things that has happened with our concurrency model is that we have begun to rely on thread locals quite heavily. A lot of our API design patterns such as locale and web processing are based on thread local variables. Currently thread locals are simply a big Str:Obj map.

Given the role thread locals seem to be playing in our APIs and applications, I propose a new threadlocal keyword which allows us to define strongly typed thread local fields. It will work pretty similar to the ThreadStatic attribute in C#.

// old way
Thread.locals["web.req"] = req
WebReq req := Thread.locals["web.req"]

// new way
threadlocal WebReq req  // declare field
Weblet.req              // access like static field

Design issues:

  • threadlocal is only applicable to fields (not methods, types)
  • can be used in both classes and mixins
  • composable with any protection scope including readonly
  • not composable with abstract, const, final, native, virtual, override, static, once
  • has auto-generated accessor methods
  • can have user-defined accessor methods
  • calling convention is like static field access (must access with type, not instance)
  • storage location will use new threadlocal flag 0x00040000
  • storage get/set will use two new opcodes LoadThreadLocal and StoreThreadLocal
  • JVM emit will use ThreadLocal with appropriate casts
  • IL emit to be determined by Andy

Once this feature is implemented, I suggest we deprecate the Thread.locals method.

Barring any negative feedback, I will probably start this feature tomorrow night.

andy Wed 23 Apr 2008

So how would your proposal look with this code:

class Foo
{
   Void foo()
   {
     Thread.locals["foo"] = 15
   }
}

class Bar
{
  Int foo()
  { 
    return Thread.locals["foo"] as Int
  }
}

brian Wed 23 Apr 2008

Just like a static field - it would be named within a type. So if we put it into Foo:

class Foo
{
  threadlocal Int foo := 15
}

class Bar
{
  Int foo() { return Foo.foo }
}

andy Wed 23 Apr 2008

Yeah, I guess my reservation is you're tying that slot to particular type now. But maybe that actually makes more sense most of the time. Actually now that I think about it, I do like that.

But the case I keep thinking about is Weblet - where the threadlocals are actually defined in the implementation (wisp, tomcat, etc) - and now you've made it more complicated to decouple:

abstract class Weblet
{
  new make()
  {
    req = (WebReq)Thread.locals["web.req"]
    res = (WebRes)Thread.locals["web.res"]
  } 
  ...
}

tactics Wed 23 Apr 2008

Couldn't you bake it into the syntax to allow for something like:

class Foo {

threadlocal foo := 15

}

That way, the type can be inferred as usual from the constructor.

andy Wed 23 Apr 2008

By convention we've said fields should be explicitly typed, which makes the code more readable and consistent. And I think Brian actually enforces that in the compiler.

jodastephen Wed 23 Apr 2008

I wonder if this is still too low level.

Most applications have a notion of scoping for variables - request scope, session scope and application scope being typical examples. Using threadlocal seems to be a way to allow users to create scoped variables, but still require the user to do the work of determining request vs session vs application.

brian Wed 23 Apr 2008

I'm not sure if I entirely follow that.

Many of Fan API's are defined to be thread scoped such as current locale. We also define that a WebReq scope is guaranteed to thread scoped (so effectively they are the same thing). That a key design pattern - APIs are thread scoped, and you map your application scopes to threads (such as a WebReq runs in its own thread). It is kind of something that has grown to be our convention, but it seems to be working really well.

jodastephen Wed 23 Apr 2008

I suspect that we may have a terminology issue. Let me rephrase. Is it possible to get access to an object that represents the session (in a web environment). This object would have the lifecycle of HttpSession for example. Also, there might be a web level session and an application level session, linked but different.

How do you intend to handle this? Does the coder have to go to the WebReq and get a session object?

Another point is that a session object could be accessed by multiple threads at the same time (in a J2EE system). Since this kind of access is part of what fan tries to avoid, how do you solve the use case.

brian Thu 24 Apr 2008

Yes - currently WebSession is accessed via a method on WebReq. How a WebSession is managed is through thread messaging (although it could be thru the namespace). So threads don't take out locks on an object like WebSession - they work with a copy and then when they are done they "check it back in". You can look at the code and see how this works.

The real issue is what happens when multiple threads are working with copies of the objects - they are lots of ways to deal with this from the database world. What Fan does right now for both namespaces and web sessions is the simple way - last guy wins. Obviously that needs to be enhanced to provide more options such as raise exception, merge, transactions, etc. Namespaces still need a lot of work.

I would characterize that problem as concurrency, which to me is a higher level problem and very different issue than threadlocal storage. Threadlocal is really about how we want to design Fan APIs. Do we want pass objects around or just keep thread local "globals".

I was going to plunge into this feature tonight, but I still have an uneasy feeling about adding this complexity to the language. I kind of feel like maybe we ought to wait and get some more experience under our belt, so I'll probably chew off something else next.

AndyDent Thu 25 Nov 2010

What about considering the inverse approach, as taken in the "D" programming language, of making all variables thread-local by default and requiring special attribution to make them shared?

This was described in Concurrency in the D Programming Language

ivan Thu 25 Nov 2010

I've just got actor locals for myself :P

using actorLoc
using magic

class Foo
{
  @Magic { kind = ActorLocal<||> }
  private Int int := 5
}

brian Fri 26 Nov 2010

What about considering the inverse approach, as taken in the "D" programming language, of making all variables thread-local by default and requiring special attribution to make them shared?

Interesting idea, but I think at this point we wouldn't want to introduce a breaking change like that. Plus I think for familiarity to Java programmers having static const fields shared as true const, immutable instances is pretty useful. I use them all over the place myself to share immutable data structures between threads.

go4 Wed 14 Dec 2011

Just remind this topic.

A quick implementation like this:

const class ActorLocal
{
  private const Str key := Uuid().toStr

  Obj? get() { Actor.locals[key] }
  Void set(Obj? obj) { Actor.locals[key] = obj }
}

it's more readable:

Actor.locals["pod.class.bar"] = "bar"
Str s := Actor.locals["pod.class.bar"]
  VS
bar.set("bar")
Str s := bar.get

It also solve the private scope

Login or Signup to reply.