views:

343

answers:

2

In Java, one would synchronize methods or blocks that access a shared resource that is needed in a multi-threaded environment.

I'm wondering how the "Scala Actors" way of doing this would work.

Suppose I have a connection pool of java.sql.Connection objects that I wish to provide thread-safe access to. I implement it as an actor that receives messages and sends the sender a connection back.

It seems there's three ways to do this:

  1. Use a Future
  2. Use !?
  3. Have the class needing the Connection also be an actor

Code:

sealed abstract class ConnectionPoolMessage
case class NewConnection extends ConnectionPoolMessage
case class CloseConnection(c:Connection) extends ConnectionPoolMessage

class ConnectionPool extends Actor {
  def act() {
    while (true) {
      receive() {
        case NewConnection => sender ! getConnectionFromPool
        case CloseConnection(conn) => returnConnection(conn)
      }
    }
  }
}

// Here, my "do stuff" method is all in one place, and I block waiting
// on the Future from the pool; however this could take forever and cause trouble
class UsingFuture {
  val pool = new ConnectionPool
  def doSomething() {
    val connectionFuture = pool !! NewConnection
    val connection = connectionFuture() // concerned that we can't timeout here
    // do stuff with my Connection instance
    pool ! CloseConnection(connection)  
  }
}


// here, I wait and just use a timeout
// Seems clean to me, I guess.
class UsingBangQuestion {
  val pool = new ConnectionPool
  def doSomething() {
    pool !?(TIMEOUT,NewConnection) match {
      case Some(conn) => {
        // do something with connection
        pool ! CloseConnection(conn)
      }
      case None => throw new RuntimeException("timed out")
    }
  }
}

// here, I don't worry about timeouts, cause I only use the
// the connection when I receive a message back with it.  
// The problem is that I now have to split my logic up
// with two methods
class AsAnActor extends Actor {
  val pool = new ConnectionPool
  def startSomething() {
    start
    pool ! NewConnection
  }
  def act() {
    receive() {
      case conn:Connection => finishSomething(conn)
    }
  }
  def finishSomething(conn:Connection) {
    // do stuff with my Connection
    pool ! CloseConnection(conn)
  }
}

The Future version seems cleanest, except for the fact that I could block forever.

Any thoughts, or is my whole conception of this wrong?

+1  A: 

It may be bad style but one way is to mix imperative and functional styles by having your actor (which requires connections) to have the connection pool plugged in directly and use synchronization to get a Connection. To be honest, I don't really see what is wrong with this approach; I much prefer it to the !!or !? one, which just screams deadlock (or even livelock)!

I guess one other way would be to send a message to your pool which expressed the work which needed to be done with the connection and a possible target for the result:

class DbWork(f: Connection => Unit)
class DbWorkWithResult[T](f:Connection => T, target: OutputChannel[Any])

And then you might use it thus:

pool ! new DbWork( { (conn: Connection) => //do something 
                 })

Or:

pool ! new DbWorkWithResult[Int]( (conn: Connection) => //return int
                 }, self)
oxbow_lakes
I'm actually using a connection pool as a means to ask about actors and the means to access a shared resource. Are you saying that actors should not be used to access a shared resource?
davetron5000
No - I'm not saying that at all. However, multiple actors can be injected with the same connection pool and use `synchronization` where appropriate to get a connection. Any of the other methods you describe are also valid, but I would definitely avoid any blocking calls on actors.
oxbow_lakes
I guess my question is from the "Threads and synchronization are hard/difficult/not great and that's why Scala implements Actors" school. i.e., I'm told that Scala solves concurrency with Actors. So, in this very simple case, what is the pattern? The third version is the only one that avoids blocking, but it seems kludgy to me
davetron5000
@davetron5000 - I completely agree. Note that actors *do not get around the need to synchronize* - the synchronization of resources (like the mailbox and the internal state of the actor) is done for you in the `send` and `receive`/`react` methods.
oxbow_lakes
By "get around the need to synchronize" I mean "get around the need for ME to put the synchronize keyword in MY code"
davetron5000
A: 

The Actors way of doing it is not sharing resources. Have all access fed to a single Actor whose job is to deal with access to the shared resource.

This way, the resource itself isn't shared among threads. The Actor is.

Daniel
The trouble with this is that if you have a single `Persistence` actor, you have introduced a bottleneck. But if you want a `ConnectionProvider` actor and wish to avoid blocking calls, there's no *nice* solution
oxbow_lakes
Right, the Connections themselves are not shared resources; the pool of connections is; that is why I am using an Actor to own the pool of resources. I'm wondering how the actor model works WRT to accessing resources from the pool.
davetron5000