Slow Harry Slow Harry - 1 month ago 12
Scala Question

SyncVar in scala implementation

Note: The problem that I solve has only educational purpose, I know that abstraction that I want to create is error prone and so on... I don't need fast solution, I need explanation.

In the book I am reading there is exercise that says that I need to implement SyncVar which has the following interface:

class SyncVar[T] {
def get(): T = ???
def put(x: T): Unit = ???
}


My comment: Alright seems understandable, need some sync variable that I can put or get.

A SyncVar object is used to exchange values between two or more threads.
When created, the SyncVar object is empty:

° Calling get throws an exception

° Calling put adds a value to the SyncVar object
After a value is added to a SyncVar object, we can say that it is non-empty:

° Calling get returns the current value, and changes the state to empty

° Calling put throws an exception

My thoughts: This is variable that throws exception on empty value when calling get, or put when we have a value, when we call get it clears previous value. Seems like I need to use Option.

So I provide the following implementation:

class SyncVar[T] {
var value: Option[T] = None
def get(): T = value match {
case Some(t) => this.synchronized {
value = None
t
}
case None => throw new IllegalArgumentException("error get")
}
def put(x: T): Unit = this.synchronized{
value match {
case Some(t) => throw new IllegalArgumentException("error put")
case None => value = Some(x)
}
}
def isEmpty = value.isEmpty
def nonEmpty = value.nonEmpty
}


My comment:
Synchronously invoking put and get, also have isEmpty and nonEmpty

The next task makes me confused:
The SyncVar object from the previous exercise can be cumbersome to use,
due to exceptions when the SyncVar object is in an invalid state. Implement
a pair of methods isEmpty and nonEmpty on the SyncVar object. Then,
implement a producer thread that transfers a range of numbers 0 until 15
to the consumer thread that prints them.

As I understand I need two threads:

//producer thread that produces numbers from 1 to 15
val producerThread = thread{
for (i <- 0 until 15){
println(s"$i")
if (syncVar.isEmpty) {
println(s"put $i")
syncVar.put(i)
}
}
}

//consumer that prints value from 0 to 15
val consumerThread = thread{
while (true) {
if (syncVar.nonEmpty) println(s"get ${syncVar.get()}")
}
}


Question:
But this code caused by nondeterminism, so it has different result each time, while I need to print numbers from 1 to 15 (in right order). Could you explain me what is wrong with my solution?

Answer

First, your synchronized in get is too narrow. It should surround the entire method, like in put (can you think why?).

After fixing, consider this scenario:

  1. producerThread puts 0 into syncVar.

  2. producerThread continues to run and tries to put 1. syncVar.isEmpty returns false so it doesn't put 1. It continues to loop with next i instead.

  3. consumerThread gets 0.

  4. producerThread puts 2.

Etc. So consumerThread can never get and print 1, because producerThread never puts it there.

Think what producerThread should do if syncVar is not empty and what consumerThread should do if it is.