#1201 How to cache objects

go4 Thu 9 Sep 2010

My purpose is to put some objects in a pool and share them in appliction. It's like this

using concurrent
class Cache{
  static const Cache instance:=Cache()
  [Str:Obj] map:=[:]

  Obj? get(Str key){
    return map[key]
  }

  Void put(Str key,Obj obj){
    map[key]=obj
  }

  Void main(){
    instance.put("key","Obj")
    o:=instance.get("key")
    echo(o)
  }
}

However it's do not allowed to share mutable in fantom. The Actor.locats can not achieve my purpose. Someone can tell me how to design it?

vkuzkokov Thu 9 Sep 2010

First, the code:

using concurrent

const class Cache
{
  static const Cache instance := Cache()

  private const Actor actor := Actor(ActorPool()) |Obj?[] msg->Obj?| {
    return (msg[0] as Method).func.callList(msg[1])
  }

  private static Obj? doGet(Str key) { Actor.locals[key] }
  private static Void doSet(Str key,Obj val) { Actor.locals[key] = val }

  Obj? get(Str key) { actor.send([#doGet,[key]].toImmutable).get() }
  Void set(Str key,Obj val) { actor.send([#doSet,[key,val]].toImmutable) }

  static Void main() {
    instance.set("key","Obj")
    echo(instance.get("key"))
  }
}

The general idea is that we access Actor.locals from within receive only. As a workaround instead of calling method we send it, so that Actor will call it in appropriate context.

About toImmutable: I think it's a bug that it doesn't work without them.

brian Thu 9 Sep 2010

If you are going to share objects between actors, then your objects must be either immutable or serializable. A good example of caching using actors is wisp::WispSession.

vkuzkokov Thu 9 Sep 2010

The error itself:

sys::IOErr: Not serializable: sys::Method
  fanx.serial.ObjEncoder.writeObj (ObjEncoder.java:84)
  fanx.serial.ObjEncoder.writeList (ObjEncoder.java:219)
  fan.sys.List.encode (List.java:1025)
  fanx.serial.ObjEncoder.writeObj (ObjEncoder.java:66)
  fan.sys.OutStream.writeObj (OutStream.java:269)
  fan.sys.OutStream.writeObj (OutStream.java:266)
  fan.sys.Sys.safe (Sys.java:531)
  concurrent::Actor._send (Actor.java:137)
  concurrent::Actor.send (Actor.java:91)
  test::Cache.set (test.fan:12)
  test::Cache.main (test.fan:15)
  java.lang.reflect.Method.invoke (Method.java:592)
  fan.sys.Method.invoke (Method.java:536)
  fan.sys.Method$MethodFunc.callList (Method.java:182)
  fan.sys.Method.callList (Method.java:147)
  fanx.tools.Fan.callMain (Fan.java:135)
  fanx.tools.Fan.executeType (Fan.java:102)
  fanx.tools.Fan.execute (Fan.java:38)
  fanx.tools.Fan.run (Fan.java:236)
  fanx.tools.Fan.main (Fan.java:274)

sys::Method is immutable (both common sense and docs prove it). We shouldn't even try to serialize it. Just pass reference.

brian Thu 9 Sep 2010

The reason you get that error is that your top-level object is not immutable, so it attempts to serialize the list, which in turn contains a non-serializable method object. The actor framework only considers the top-level message object. If it is a list which is not already immutable, then attempts to serialize.

The code which handles this is in Sys.java:

/**
 * Make a thread-safe copy of the specified object.
 * If it is immutable, then just return it; otherwise
 * we make a serialized copy.
 */
public static Object safe(Object obj)
{
  if (obj == null) return null;
  if (FanObj.isImmutable(obj)) return obj;
  Buf buf = new MemBuf(512);
  buf.out.writeObj(obj);
  buf.flip();
  return buf.in.readObj();
}

go4 Thu 9 Sep 2010

access Actor.locals by an actor, I learned. Thanks everybody

Login or Signup to reply.