Skip to content
Snippets Groups Projects
m21.md 3.8 KiB
Newer Older
Use the following commands to make a fresh clone of your repository:

```
git clone -b m21 git@gitlab.epfl.ch:lamp/student-repositories-s21/cs206-GASPAR.git m21
```

## Useful links

  * [A guide to the Scala parallel collections](https://docs.scala-lang.org/overviews/parallel-collections/overview.html)
  * [The API documentation of the Scala parallel collections](https://www.javadoc.io/doc/org.scala-lang.modules/scala-parallel-collections_2.13/latest/scala/collection/index.html)
  * [The API documentation of the Scala standard library](https://www.scala-lang.org/files/archive/api/2.13.4)
  * [The API documentation of the Java standard library](https://docs.oracle.com/en/java/javase/15/docs/api/index.html)

**If you have issues with the IDE, try [reimporting the
build](https://gitlab.epfl.ch/lamp/cs206/-/blob/master/labs/example-lab.md#ide-features-like-type-on-hover-or-go-to-definition-do-not-work),
if you still have problems, use `compile` in sbt instead.**

## Exercise

In this question, you will complete the definition of two concurrent data
structures **without using `synchronized`**: `SeqCount` and `MultiWriterSeqCount`.

## Part 1: SeqCount (defined in `SeqCount.scala`)

An instance of this class stores two integers (initially set to 1), the stored
values can be updated using `write` and retrieved using `copy`. Only one thread
at a time is allowed to call `write` but multiple threads can call `copy` at
once.

Your task in this part is to implement `copy` such that this method never
returns _partially updated values_, for example given two threads operating
concurrently on a `SeqCount` `sc`:
```scala
// Thread 1
sc.write(1, 2)
```
```scala
// Thread 2
val result = sc.copy()
```
`result` must either be `(0, 0)` (since the initial values are 0) or `(1, 2)`,
but it must not be `(1, 0)`, `(0, 2)` or any other value.

To successfully implement this method you will need to use `generation`: this
method returns the current value of a volatile variable which is initially set
to 1, gets incremented by one at the beginning of `write`, and incremented by
one again at the end of `write`.

**You are not allowed to use `synchronized` or directly call any of
`myGeneration`, `myX` or `myY` (use the pre-defined getters and setters
instead).**

Hints:
- Remember that a write to a volatile field _happens-before_ every subsequent
  read of that field.
- `generation` will always be odd when a write has completed and always
even when a write is in progress.
- `copy` can be implemented as a tail-recursive method.

## Part 2: MultiWriterSeqCount (defined in `MultiWriterSeqCount.scala`)

Like `SeqCount`, this class stores two integers updated using `write` and
retrieved using `copy`, but unlike `SeqCount` multiple threads are allowed to
call `write` at the same time: these writes will all succeed but they are
allowed to complete in any order, for example given three threads operating
concurrently on a `MultiWriterSeqCount` `msc`:
```scala
// Thread 1
msc.write(1, 2)
```
```scala
// Thread 2
msc.write(10, 20)
```
```scala
// Thread 3
val result = msc.copy()
```
`result` must either be `(0, 0)`, `(1, 2)` or `(10, 20)`.

In this class, the generation is stored in an atomic variable instead of a
volatible field therefore it's important to note that:
- a `set` on an atomic variable _happens-before_ every subsequent `get` of that
  variable.
- A call to `compareAndSet` both gets and set an atomic variable.

Your task in this part is to implement both `copy` and `write`.

**You are not allowed to use `synchronized` or directly call any of
`myGeneration`, `myX` or `myY` (use the pre-defined getters and setters
instead).**

Hints:
- you should be able to just copy-paste the implementation of `copy` you
  implemented in Part 1
- you will need to make use of `compareAndSetGeneration` in `write`
- `write` can be implemented as a tail-recursive method.