Commit e1962e11 authored by Felix Morgner's avatar Felix Morgner
Browse files

week09: add exercise descriptions

parent 7820d9dd
# Exercises Week 9 - User Defined Literals
# Exercise 9 - Memory Model and Atomics
Goals:
In these exercises you will...
In this exercise...
* ... implement your own user defined literal operators
* ... compare compile-time and run-time execution times
* ... apply tags and traits to implement conversions
* ... apply atomics to the Peterson mutex in C++
* ... analyze code with data races and race conditions
## 1. Compile-Time Fibonacci
1. Implement computing the `n`th fibonacci number in the following means as compile-time computation:
# 0. IMPORTANT NOTE FOR TESTAT 3 `BoundedQueue` with Timed Operations
* a) With a constexpr function `fibo(unsigned)`
* b) With a variable template `fibo_v<unsigned>`. ***Hint:*** If you are using Cevelop, there might be an issue with parsing an expression containing varible templates. Therefore, you might want to put a reference to a varaible template in parentheses, to avoid ambiguities in the parse: `(fibo_v<n-2>)`.
* c) With a user defined literal operator `5_fibo` that is evaluated at compile-time
* d) Measure and compare the compilation and run-time for computing the 46th fibonacci number. Does it make a difference regarding compilation time if the signature is changed to `fibo(unsigned const & n)`?
I (Felix) have forgotten to include the test suite for the third Testat in last weeks exercises.
The test suite is now in published in last weeks `exercise_templates` folder.
Please use the tests from this suites, in conjunction with you own tests, to test your Testat solution.
2. Implement similar constructs to the above that return an `std::array<int, n>` filled with the `n` first fibonacci numbers instead. Unfortunately, even though `std::array` is a literal type in C++17, its comparison operations are not `constexpr`. First implement a function for comparing the content of `std::array` at compile time: `arrayEquals`.
# 1. Peterson Mutex (Not Testat)
From the module Parallel Programming you should be familiar with the Peterson Mutex. If you need to recap it you can find it on the Skripte server [here](https://skripte.hsr.ch/Informatik/Fachbereich/Parallele_Programmierung/ParProg/Uebungen/09_Memory_Models_TESTAT2/).
* a) With a constexpr template function `fiboa` that takes the size of the array as template parameter. E.g. fiboa<5>()
* b) With a variable template `fiboa_v<int>`
* c) ***Optional (for experts):*** A user defined literal operator `10_fiboa` (returns an `array<int, 10>`). You will need to create infrastructure to parse the value of the literal.
Below you have the implementation ported to C++ using volatile. While this works in Java, it is incorrect in C++.
```cpp
struct PetersonMutexVolatile {
// acquire lock by thread 0
void thread0Lock() {
state0 = true;
turn = 1;
while (turn == 1 && state1);
}
## 2. Conversion with Tags and Traits
void thread0Unlock() {
state0 = false;
}
Implement a `temperature` literal type, similar to the `Speed` type you have seen in the lecture. The `Temperature` type shall be taggable with the units `Kelvin`, `Celcius` and `Fahrenheit`.
// acquire lock by thread 1
void thread1Lock() {
state1 = true;
turn = 0;
while (turn == 0 && state0);
}
* Add the required tag types to the namespace `units::tags`.
* Specify the `Temperature` class template that stores the temperature in a `double` value.
* Implement comparison operations for the `Temperature` template for comparison with other objects of the same template instance (`==`, `!=`, `<`, `>`, `<=` and `>=`).
* Implement arithmetic operations for the `Temperature` template (`+`, `-`) - unary and binary.
* Implement the `ConversionTraits` class template with a `convert` member function and add a specialization for every possible conversion. You can lookup the conversion formula on the web.
* Implement a `temperatureCast` function template that delegates conversion to the `ConversionTraits` template.
* Add comparison operation templates to allow comparison of objects of arbitrary `Temperature` instances.
* Add user defined literal operators (`_K`, `_C`, `_F`) to the `unit::literals` namespace.
// release lock by thread 1
void thread1Unlock() {
state1 = false;
}
private:
volatile bool state0 { false };
volatile bool state1 { false };
volatile int turn { 0 };
};
```
***Note:*** The project template for this exercies does not contain test cases for all operations above.
\ No newline at end of file
* It is necessary to use atomics to make the implementation work in C++. Create a type PetersonMutexAtomics that works correctly.
* You might need to set the build option in Cevelop to Release in order to enable optimizations. Otherwise, potentially incorrect implementations might not fail.
# 2. Optional - CVU Student Critique (Not Testat)
In one of the past cvu magazines the following problem was posted:
Can you help the student and analyze her/his code and correct the bugs?
"I wanted to learn a bit about C++ threading to I tried writing a thread pool example. But it sometimes crashes - I've managed to get it down to a small example. Sometimes I get what I expected as output, for example:
```
Worker done
Worker done
Ending thread #2
Ending thread #0
Worker done
Ending thread #1
Worker done
Ending thread #3
Worker done
All done
```
But other times I get a failure, for example:
```
Worker done
Ending thread #0
Worker done
Awaiting thread #1
Worker done
W
<crash>
```
I'm not sure what to do next - can you help? It is sufficient to recognize the issues, you don't need to solve them.
```cpp
#include <algorithm>
using namespace std;
#include <array>
#include <chrono>
using namespace chrono;
#include <cstdlib>
#include <iostream>
#include <thread>
static const int POOL_SIZE = 4;
// Allow up to 4 active threads
array<thread, POOL_SIZE> pool;
// Example 'worker' -- would in practice
// perform some, potentially slow, calculation
void worker()
{
this_thread::sleep_for(
milliseconds(rand() % 1000));
cout << "Worker done\n";
}
// Launch the thread functoid 't' in a new
// thread, if there's room for one
template <typename T>
bool launch(T t)
{
auto it = find_if(pool.begin(), pool.end(),
[](thread const &thr)
{ return thr.get_id() == thread::id(); }
);
if (it == pool.end())
{
// everyone is busy
return false;
}
*it = thread([=]()
{
t();
thread self;
swap(*it, self);
self.detach();
cout << "Ending thread #"
<< (it - pool.begin()) << "\n";
});
return true;
}
int main()
{
while (launch(worker))
{}
// And run one in this thread
// as an example of what we do
// when the pool is full
worker();
for (auto & it : pool)
{
thread thread;
swap(thread, it);
if (thread.joinable())
{
cout << "Awaiting thread #"
<< (&it - &*pool.begin()) << "\n";
thread.join();
}
}
cout << "All done\n";
}
```
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment