rsan
rsan

Reputation: 1887

Is it possible to receive or react to a message outside of act function?

I'm using RXTX library to send some data to serial port. After sending data I must wait 1 second for an ACK. I have this functionality implemented using an ArrayBlockingQueue:

Eg.

val queue = ArrayBlockingQueue(1)

def send(data2Send : Array[Byte]) : Array[Byte]{
   out.write(data2Send)
   queue.poll(1000)
}

def receive(receivedData : Array[Byte]){
  queue.add(receivedData)
}

This works perfectly, but since I'm learning Scala I would like to use Actors insted of threads and locking structures.

My first attempt is as follows:

class Serial {

sender = new Sender(...)
new Receiver(...).start

class Sender {
      def send(data2Send : Array[Byte]) : Array[Byte]{
       out.write(data2Send)
         receiveWithin(WAIT_TIMEOUT_MILLIS) {
          case response => response
          case TIMEOUT => null
        }
      }
    }

    class Receiver extends Actor{
      def act{
        loop{
           sender ! read()
        }
      }
    }
}

But this code throws a java.lang.AssertionError: assertion failed: receive from channel belonging to other actor. I think the problem is that I can't use receive or react outside the act definition. Is right the aproach that I'm following?

Second attempt:

class Serial {

    new Sender(...).start
    new Receiver(...).start

    def send() = (sender ?! data2Send).asInstanceOf(Array[Byte])

    class Sender {
          def act() {
          loop{
          receive{
           out.write(data2Send)
             receiveWithin(WAIT_TIMEOUT_MILLIS) {
              case response => response
              case TIMEOUT => null
            }
          }
        }
       }
      }

        class Receiver extends Actor{
          def act{
            loop{
               sender ! read()
            }
          }
        }
    }

In this second attempt I get java.util.NoSuchElementException: head of empty list when sender ! read() line is executed. And it looks a lot more complex

Upvotes: 1

Views: 217

Answers (1)

oxbow_lakes
oxbow_lakes

Reputation: 134330

Unless you are using NIO, you can't avoid blocking in this situation. Ultimately your message is coming from a socket which you need to read() from (i.e. some thread, somewhere, must block).

Looking at your 3 examples (even assuming they all worked), if you found a bug at 2am in six months' time, which code snippet do you think you'd rather be looking at? I know which one I would choose!

Actors are great for sending asynchronous messages around event-driven systems. Where you hit some external API which uses sockets, you can wrap an actor-like facade around them, so that they can interact with other parts of the system (so that the rest of the system is protected from knowing the implementation details). A few pointers:

  • For the actual actor which has to deal with reading/writing to the socket, keep it simple.

  • Try and organize the system such that communication with this actor is asynchronous (i.e. other actors are not blocking and awaiting replies)

  • Given that the scala actor library is imminently being deprecated, I'd start using akka

Upvotes: 1

Related Questions