In src/day1/CoarseGrainedBank.kt
,
make the sequential bank implementation thread-safe.
Please follow the coarse-grained locking scheme to make synchronization efficient.
For that, you need to use a single lock to protect all bank operations.
To test your solution, please run:
./gradlew test --tests CoarseGrainedBankTest
on Linux or MacOSgradlew test --tests CoarseGrainedBankTest
on Windows
In src/day1/FineGrainedBank.kt
,
make the sequential bank implementation thread-safe.
Please follow the fine-grained locking scheme to make synchronization efficient.
For that, you need to use per-account locks, thus, ensuring natural parallelism
when accessing different accounts. The totalAmount()
function should acquire
all the locks to get a consistent snapshot, while transfer(..)
should acquire
the corresponding account locks.
To test your solution, please run:
./gradlew test --tests FineGrainedBankTest
on Linux or MacOSgradlew test --tests FineGrainedBankTest
on Windows
In src/day1/TreiberStack.kt
,
implement the classic Treiber stack algorithm.
To test your solution, please run:
./gradlew test --tests TreiberStackTest
on Linux or MacOSgradlew test --tests TreiberStackTest
on Windows
In src/day1/TreiberStackWithElimination.kt
,
implement the classic Treiber stack algorithm with the elimination technique.
To test your solution, please run:
./gradlew test --tests TreiberStackWithEliminationTest
on Linux or MacOSgradlew test --tests TreiberStackWithEliminationTest
on Windows
In src/day1/MSQueue.kt
,
implement the Michael-Scott queue algorithm.
You might also be interested in the original paper.
To test your solution, please run:
./gradlew test --tests MSQueueTest
on Linux or MacOSgradlew test --tests MSQueueTest
on Windows
In src/day2/FAABasedQueueSimplified.kt
,
implement a concurrent queue that leverages the Fetch-and-Add
synchronization primitive.
The high-level design of this queue bases on a conceptually infinite array for storing elements and manipulates
enqIdx
and deqIdx
counters, which reference the next working cells in the infinite array for enqueue(..)
and dequeue()
operations.
In this task, use a big plain array as the infinite array implementation.
To test your solution, please run:
./gradlew test --tests FAABasedQueueSimplifiedTest
on Linux or MacOSgradlew test --tests FAABasedQueueSimplifiedTest
on Windows
In src/day2/FAABasedQueue.kt
,
implement a concurrent queue that leverages the Fetch-and-Add
synchronization primitive.
The high-level design of this queue bases on a conceptually infinite array for storing elements and manipulates
enqIdx
and deqIdx
counters, which reference the next working cells in the infinite array for enqueue(..)
and dequeue()
operations.
The infinite array implementation should be simulated via a linked list of fixed-size segments. The overall algorithm should be obstruction-free or lock-free.
To test your solution, please run:
./gradlew test --tests FAABasedQueueTest
on Linux or MacOSgradlew test --tests FAABasedQueueTest
on Windows
In src/day3/FlatCombiningQueue.kt
, implement a concurrent queue via the
flat-combining technique,
using a sequential queue under the hood. You might be interested in the corresponding
academic paper
To test your solution, please run:
./gradlew test --tests FlatCombiningQueueTest
on Linux or MacOSgradlew test --tests FlatCombiningQueueTest
on Windows
In src/day2/MSQueueWithLinearTimeRemove.kt
,
implement a Michael-Scott queue with an additional remove(element)
operation.
The implementation should find the first node that contains the specified element
in linear time and then remove this node also in linear time.
To test your solution, please run:
./gradlew test --tests MSQueueWithLinearTimeRemoveTest
on Linux or MacOSgradlew test --tests MSQueueWithLinearTimeRemoveTest
on Windows
In src/day2/MSQueueWithLinearTimeRemove.kt
,
implement a Michael-Scott queue with an additional remove(element)
operation.
The implementation should find the first node that contains the specified element
in linear time, but remove this node in constant time.
./gradlew test --tests MSQueueWithConstantTimeRemoveTest
on Linux or MacOSgradlew test --tests MSQueueWithConstantTimeRemoveTest
on Windows
In src/day3/AtomicCounterArray.kt
,
implement the inc2(..)
function that atomically increments two counters.
using the CAS2 algorithm. In this data structure, all successful updates
install unique values in the array cells.
This property enables simpler CAS2 implementation.
To test your solution, please run:
./gradlew test --tests AtomicCounterArrayTest
on Linux or MacOSgradlew test --tests AtomicCounterArrayTest
on Windows
In src/day3/AtomicArrayWithDCSS.kt
,
implement the dcss(..)
operation. Similarly to CAS2, it requires
allocating a descriptor and installing it in the updating memory location.
We need the dcss(..)
operation for the next task, to resolve the ABA-problem
in the CAS2 algorithm.
To test your solution, please run:
./gradlew test --tests AtomicArrayWithDCSSTest
on Linux or MacOSgradlew test --tests AtomicArrayWithDCSSTest
on Windows
In src/day3/AtomicArrayWithCAS2.kt
,
implement the cas2(..)
operation. Unlike in the array of atomic counters,
which values always increase, now updates are no longer unique.
This can lead to the ABA problem. To solve it, please use
the Double-Compare-Single-Set operation when installing CAS2 descriptors.
To test your solution, please run:
./gradlew test --tests AtomicArrayWithCAS2Test
on Linux or MacOSgradlew test --tests AtomicArrayWithCAS2Test
on Windows
In src/day4/DynamicArraySimplified.kt
,
implement a lock-free dynamic array of limited capacity.
This is reminiscence of vector
in C++ and ArrayList
in Java,
with the only difference that addLast(element)
files and returns false
if it would exceed the specified capacity.
To test your solution, please run:
./gradlew test --tests DynamicArraySimplifiedTest
on Linux or MacOSgradlew test --tests DynamicArraySimplifiedTest
on Windows
In src/day4/DynamicArray.kt
,
implement a lock-free dynamic array of unlimited capacity.
To implement the resizing procedure, use the technique under
the hood of the open addressing hash table.
You do not need to implement an efficient cooperative elements transition; each thread is eligible to go over the whole array to guarantee that all elements are successfully moved to a new version of the array. While this strategy is inefficient in practice, it is good enough to learning new techniques. Implementing efficient cooperative elements transition may take weeks.
To test your solution, please run:
./gradlew test --tests DynamicArrayTest
on Linux or MacOSgradlew test --tests DynamicArrayTest
on Windows
In src/day4/IntIntHashMap.kt
,
make the sequential open-addressing hash table linearizable and lock-free.
- The general code design should stay the same.
- Do not change the initial capacity (the
INITIAL_CAPACITY
field). - The provided
IntIntHashMap
implementation always doubles the table size, even if the table is full of removed elements. You do not need to fix this. - In the class
IntIntHashMap.Core
, add a newnext: AtomicRef<Core>
field, which references the next version of the table. IntIntHashMap
supports only positive keys and values strictly lowerInt.MAX_VALUE
. Use negative numbers andInt.MAX_VALUE
for the algorithm needs.- You do not need to implement cooperative rehashing.
You might also be interested in the corresponding academic paper.
To test your solution, please run:
./gradlew test --tests IntIntHashMapTest
on Linux or MacOSgradlew test --tests IntIntHashMapTest
on Windows