문제

For the match on Option[Entity] below, a third case is required to be exhaustive. Why ?

entitiesMap is a var containing an immutable Map[UUID,Entity]. It is accessed and updated within a single Akka Actor to avoid threading issues. Here is an excerpt of the receive function:

class WorldActor extends Actor {

  var world: World = null

  override def receive = {

    case WorldSet(w) =>
      world = w

    // get that state and apply it to its entity
    case s: State if (world != null) =>
      val uuid = new UUID(s.getUuid().getL1(), s.getUuid().getL2())
      world.entitiesMap.get(uuid) match {
        case Some(ent) =>
          // Update entity's state
          ent.setEntityState(s)
        case None =>
          Log.error("World doesn't contain entity uuid: " + uuid)
        case other =>
          Log.error("Received unknown message: " + other)
      }

    case AddEntity(ent) if (world != null && ent != null) =>
      if (!world.entitiesMap.contains(ent.uuid))
        world.entitiesMap += ent.uuid -> ent

    case RemoveEntity(ent) if (world != null && ent != null) =>
      if (world.entitiesMap.contains(ent.uuid))
        world.entitiesMap -= ent.uuid

    case other => // ignore
  }

}

class World {
  // Entity container
  var entitiesMap = Map[UUID,Entity]()
}

From time to time, the above code reports:

Received unknown message: None

Why doesn't the case None pattern just above catch it ?

EDIT

I found the bug occurs when message State comes just before message AddEntity, that is, when entitiesMap doesn't yet contain the Entity refered to by message State.

01:52 ERROR: [State] Received unknown message: None
uuid: 1b234d30-92ae-11e3-aa12-7071bcb09717
Thread: 152

01:52 ERROR: [State] Received unknown message: None
uuid: 1b234d30-92ae-11e3-aa12-7071bcb09717
Thread: 32

01:52  INFO: [AddEntity] 1b234d30-92ae-11e3-aa12-7071bcb09717: Cube@2f9c3beb
Thread: 152

Could this be a threading problem after all ? Within an Actor ?

EDIT 2

Following the suggestion of a poster below, I have logged the class names, class loaders reference and class file paths for both other and None. They're all the same.

EDIT 3

other == None is false

other.eq(None) is false

other.equals(None) is false

other.hashCode == None.hashCode

System.identityHashCode(other) != System.identityHashCode(None)

도움이 되었습니까?

해결책 2

Following advice from everyone telling me this issue was a class loader problem and printing stack trace at None's construction time, I've narrowed it down to my useage of Kryo for serializing messages prior to transmitting them over the network.

It seems the default behaviour of Kryo with singleton objects can lead to my problem exactly. Kryo is reloading None every time it deserializes it, leading to None's System.identityHashCode being different.

The problem, then, was class reloading, and the solution is to use a library which knows how to handle Scala objects properly for serialization such as chill-scala.

package com.twitter.chill

// Singletons are easy, you just return the singleton and don't read:
// It's important you actually do this, or Kryo will generate Nil != Nil, or None != None

class SingletonSerializer[T](obj: T) extends KSerializer[T] {
  def write(kser: Kryo, out: Output, obj: T) {}
  def read(kser: Kryo, in: Input, cls: Class[T]): T = obj
}

다른 팁

This sounds like a class loader/class path problem, where None$ is getting loaded by two class loaders and the two None$.Module$ do not compare equal.

Since the Map is immutable, after you add to it, you've created a new Map. Suppose the class of new Map is loaded by the alien loader; then when that map returns None, it will return the alien None.

Note that when you add to very small Maps, it uses new to create the next bigger Map; but after a few such allocations, it switches to HashMap. So it's possible that competing class loaders could change the apparent behavior of the map you get back.

I would print out not only the class names, but the class loader and where the classes are loaded from.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top