# linux2023: Eddie-064 - HW1
> [quiz0](https://hackmd.io/@sysprog/linux2023-summer-quiz0)
> [linux2023-summer](https://github.com/Eddie-064/linux2023-summer)
## 測驗 α - 1 : 解釋程式碼運作原理
### S-tree 相關函式
- `st_first` : 以遞迴的方式找到 S-tree 最左邊的節點,而因 S-tree 為一種 BST,因此最左邊的節點也是整個 S-tree 的最小值。
```cpp=
struct st_node *st_first(struct st_node *n)
{
if (!st_left(n))
return n;
return st_first(st_left(n));
}
```
- `st_last` : 以遞迴的方式找到 S-tree 最右邊的節點,也是整個 S-tree 的最大值。
```cpp=
struct st_node *st_last(struct st_node *n)
{
if (!st_right(n))
return n;
return st_last(st_right(n));
}
```
- `st_rotate_left` : 當 AVL tree 左半邊不平衡時,需要做的 rotate 操作。
```cpp=
static inline void st_rotate_left(struct st_node *n)
{
struct st_node *l = st_left(n), *p = st_parent(n);
st_parent(l) = st_parent(n);
st_left(n) = st_right(l);
st_parent(n) = l;
st_right(l) = n;
if (p && st_left(p) == n)
st_left(p) = l;
else if (p)
st_right(p) = l;
if (st_left(n))
st_lparent(n) = n;
}
```
- `st_balance` : 計算 AVL tree 是否平衡,正常值為 -1, 0, 1。
```cpp=
static inline int st_balance(struct st_node *n)
{
int l = 0, r = 0;
if (st_left(n))
l = st_left(n)->hint + 1;
if (st_right(n))
r = st_right(n)->hint + 1;
return l - r;
}
```
- `st_max_hint` : 計算 n 節點的最大高度。
```cpp=
static inline int st_max_hint(struct st_node *n)
{
int l = 0, r = 0;
if (st_left(n))
l = st_left(n)->hint + 1;
if (st_right(n))
r = st_right(n)->hint + 1;
return l > r ? l : r;
}
```
- `st_update` : 判斷以 n 節點為首的子樹是否平衡,如不平衡則做 rotate。最後判斷子樹高度是否有改變,如果改變則執行 `st_update(root, p)` 判斷與 Sibling 是否平衡。
```cpp=
static inline void st_update(struct st_node **root, struct st_node *n)
{
if (!n)
return;
int b = st_balance(n);
int prev_hint = n->hint;
struct st_node *p = st_parent(n);
if (b < -1) {
/* leaning to the right */
if (n == *root)
*root = st_right(n);
st_rotate_right(n);
}
else if (b > 1) {
/* leaning to the left */
if (n == *root)
*root = st_left(n);
st_rotate_left(n);
}
n->hint = st_max_hint(n);
if (n->hint == 0 || n->hint != prev_hint)
st_update(root, p);
}
```
- `st_insert` : 插入 n 節點至指定的 d 位置,確認 s-tree 是否平衡。
```cpp=
void st_insert(struct st_node **root,
struct st_node *p,
struct st_node *n,
enum st_dir d)
{
if (d == LEFT)
st_left(p) = n;
else
st_right(p) = n;
st_parent(n) = p;
st_update(root, n);
}
```
- `st_replace_right` : 在二元搜尋樹中刪除節點時需要選取子樹的一個節點做替換,這個函式是在替換節點為右子樹的情境下,實作以 `r` 節點替換 `n` 節點。
```cpp=
static inline void st_replace_right(struct st_node *n, struct st_node *r)
{
struct st_node *p = st_parent(n), *rp = st_parent(r);
if (st_left(rp) == r) {
st_left(rp) = st_right(r);
if (st_right(r))
st_rparent(r) = rp;
}
if (st_parent(rp) == n)
st_parent(rp) = r;
st_parent(r) = p;
st_left(r) = st_left(n);
if (st_right(n) != r) {
st_right(r) = st_right(n);
st_rparent(n) = r;
}
if (p && st_left(p) == n)
st_left(p) = r;
else if (p)
st_right(p) = r;
if (st_left(n))
st_lparent(n) = r;
}
```
- `st_remove` : 實作 s-tree 移除的函式。
- `AAAA` : 當被移除節點 `del` 存在右節點,尋找右子樹最小值替換以符合 BST 特性,而找到替換節點後還需要處理 node link 細節,因此填空答案應為 **st_replace_right(del, least)**。
- `BBBB` : 因為移除的節點是 `least`,所以應該更新原先 `least` 的 parent 才是最小的 cost,但因為 link 已經被刪去,所以次處應填 **st_update(root, st_right(least))** 或 **st_update(root, st_first(least))**。
- `CCCC` : 找到替換節點後還需要處理 node link 細節,因此填空答案應為 **st_replace_left(del, most)**。
- `DDDD` : 因為移除的節點是 `most`,所以應該更新原先 `most` 的 parent 才是最小的 cost,但因為 link 已經被刪去,所以次處應填 **st_update(root, st_left(most))** 或 **st_update(root, st_last(most))**。
- `EEEE` : n 節點為 leaf node 移除自己後,需要確保 s-tree 為平衡,因此要執行 **st_update(root, parent)**。
```cpp=
void st_remove(struct st_node **root, struct st_node *del)
{
if (st_right(del)) {
struct st_node *least = st_first(st_right(del));
if (del == *root)
*root = least;
AAAA;
BBBB;
return;
}
if (st_left(del)) {
struct st_node *most = st_last(st_left(del));
if (del == *root)
*root = most;
CCCC;
DDDD;
return;
}
if (del == *root) {
*root = 0;
return;
}
/* empty node */
struct st_node *parent = st_parent(del);
if (st_left(parent) == del)
st_left(parent) = 0;
else
st_right(parent) = 0;
EEEE;
}
```
### Test program
- `__treeint_dump` : 這裡要將 s-tree 中所有節點的大小依升序排列輸出至終端機,而二元樹的 in-order Traversal 正好能夠實現這個功能。因此 `FFFF` 為應先尋訪的 **st_left(n)**,而 `GGGG` 為最後尋訪的 **st_right(n)**。
```cpp=
/* ascending order */
static void __treeint_dump(struct st_node *n, int depth)
{
if (!n)
return;
__treeint_dump(st_left(n), depth + 1); // FFFF
struct treeint *v = treeint_entry(n);
printf("%d\n", v->value);
__treeint_dump(st_right(n), depth + 1); // GGGG
}
```
## 測驗 α - 2
## 測驗 β - 1 : 說明程式碼原理
- 這個函式計算記憶體對齊後應該分配的空間,將對齊大小分為 2 的冪和非 2 的冪,2 的冪可以透過位元運算降低計算的 cost。
- `(alignment & mask) == 0` 判斷當對齊大小為 2 的冪時計算,可以透過 `&` 將餘數清空,但因為計算後不能小於原本使用的 `sz` 大小,所以要先加上 `alignment - 1`,因此 `MMMM` 應該填 **(sz + mask)&(~mask)**。
```cpp=
#include <stdint.h>
static inline uintptr_t align_up(uintptr_t sz, size_t alignment)
{
uintptr_t mask = alignment - 1;
if ((alignment & mask) == 0) { /* power of two? */
return MMMM;
}
return (((sz + mask) / alignment) * alignment);
}
```
## 測驗 γ - 1 : 說明程式碼原理
下方包含 `qsort_mt` 和 `qsort_thread` 函式。
- 下方程式碼的 18 至 32 行為初始化 mutex 及創建 threads,並設置 `qs` 的 status 為 idle。
- `HHHH` : 建立多個 `qsort_thread` 的執行緒後,一開始會在狀態 idle 等待被觸發,因為 `qs->stat` 為多個執行緒共用的資料,因此更動時需要 mutex 機制保護。而如果要控制執行順序則需要透過 condvar 的機制,所以 `HHHH` 應為 **pthread_cond_wait(&qs->cond_st, &qs->mtx_st)** 等待 idle 狀態被修改後觸發 condvar。
- `JJJJ` : 下方程式碼的 116 至 119 行,因為修改了 `qs2` 的狀態因此需要透過 **pthread_cond_signal(&qs2->cond_st)** 通知。
```cpp=
/* The multithreaded qsort public interface */
void qsort_mt(void *a,
size_t n,
size_t es,
cmp_t *cmp,
int maxthreads,
int forkelem)
{
struct qsort *qs;
struct common c;
int i, islot;
bool bailout = true;
if (n < forkelem)
goto f1;
errno = 0;
/* Try to initialize the resources we need. */
if (pthread_mutex_init(&c.mtx_al, NULL) != 0)
goto f1;
if ((c.pool = calloc(maxthreads, sizeof(struct qsort))) == NULL)
goto f2;
for (islot = 0; islot < maxthreads; islot++) {
qs = &c.pool[islot];
if (pthread_mutex_init(&qs->mtx_st, NULL) != 0)
goto f3;
if (pthread_cond_init(&qs->cond_st, NULL) != 0) {
verify(pthread_mutex_destroy(&qs->mtx_st));
goto f3;
}
qs->st = ts_idle;
qs->common = &c;
if (pthread_create(&qs->id, NULL, qsort_thread, qs) != 0) {
verify(pthread_mutex_destroy(&qs->mtx_st));
verify(pthread_cond_destroy(&qs->cond_st));
goto f3;
}
}
/* All systems go. */
bailout = false;
/* Initialize common elements. */
c.swaptype = ((char *) a - (char *) 0) % sizeof(long) || es % sizeof(long)
? 2
: es == sizeof(long) ? 0
: 1;
c.es = es;
c.cmp = cmp;
c.forkelem = forkelem;
c.idlethreads = c.nthreads = maxthreads;
/* Hand out the first work batch. */
qs = &c.pool[0];
verify(pthread_mutex_lock(&qs->mtx_st));
qs->a = a;
qs->n = n;
qs->st = ts_work;
c.idlethreads--;
verify(pthread_cond_signal(&qs->cond_st));
verify(pthread_mutex_unlock(&qs->mtx_st));
/* Wait for all threads to finish, and free acquired resources. */
f3:
for (i = 0; i < islot; i++) {
qs = &c.pool[i];
if (bailout) {
verify(pthread_mutex_lock(&qs->mtx_st));
qs->st = ts_term;
verify(pthread_cond_signal(&qs->cond_st));
verify(pthread_mutex_unlock(&qs->mtx_st));
}
verify(pthread_join(qs->id, NULL));
verify(pthread_mutex_destroy(&qs->mtx_st));
verify(pthread_cond_destroy(&qs->cond_st));
}
free(c.pool);
f2:
verify(pthread_mutex_destroy(&c.mtx_al));
if (bailout) {
fprintf(stderr, "Resource initialization failed; bailing out.\n");
f1:
qsort(a, n, es, cmp);
}
}
/* Thread-callable quicksort. */
static void *qsort_thread(void *p)
{
struct qsort *qs, *qs2;
int i;
struct common *c;
qs = p;
c = qs->common;
again:
/* Wait for work to be allocated. */
verify(pthread_mutex_lock(&qs->mtx_st));
while (qs->st == ts_idle)
verify(HHHH);
verify(pthread_mutex_unlock(&qs->mtx_st));
if (qs->st == ts_term) {
return NULL;
}
assert(qs->st == ts_work);
qsort_algo(qs);
verify(pthread_mutex_lock(&c->mtx_al));
qs->st = ts_idle;
c->idlethreads++;
if (c->idlethreads == c->nthreads) {
for (i = 0; i < c->nthreads; i++) {
qs2 = &c->pool[i];
if (qs2 == qs)
continue;
verify(pthread_mutex_lock(&qs2->mtx_st));
qs2->st = ts_term;
verify(JJJJ);
verify(pthread_mutex_unlock(&qs2->mtx_st));
}
verify(pthread_mutex_unlock(&c->mtx_al));
return NULL;
}
verify(pthread_mutex_unlock(&c->mtx_al));
goto again;
}
```