Think Stitch
  最近の更新


ミューテックスと条件変数で作るセマフォ 1

ミューテックスと条件変数を使ってセマフォを作ってみます.

POSIX にはプロセス間で使えるセマフォがありますから別に作らなくてもいいんですけど,ちょっとした練習問題にはなります.モデルを作って Pthread Model Checker で検査します.

0  セマフォのモデル

計数セマフォを作ります.セマフォの wait と signal を関数として実装しましょう.カウンタ count を用意します.

xsem_wait ではまず排他のためにロックをかけて,それから count を調べます.もし値が正であればデクリメントしてそのまま通過できます.もし値が0ならば待ちに入ります.

xsem_signal では逆にカウンタ count をインクリメントして,無条件に signal を発行します.待っているスレッドいなければ何も起こりません.待っているスレッドがいた場合は理想的には1個だけ起こされます.そのときはカウンタの値が 1 になっているはずですから起こされたスレッドがそれを再び 0 に戻して xsem_wait から抜けてきます.

もし pthread_cond_signal の呼び出しで2つ以上のスレッドが起きてしまうような実装であっても大丈夫です.カウントの値は 0 -> 1 となっているので通過できるのは1つだけで,残りは while ループを回って再び待ちに入るからです.

そんな実装もあるとかないとか...

pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cv = PTHREAD_COND_INITIALIZER;
int count;

void xsem_wait(void)
{
    pthread_mutex_lock(&m);
    while (count == 0) {
        pthread_cond_wait(&cv, &m);
    }
    count--;
    pthread_mutex_unlock(&m);
}

void xsem_signal(void)
{
    pthread_mutex_lock(&m);
    count++;
    pthread_cond_signal(&cv);
    pthread_mutex_unlock(&m);
}

1  グローバル assert による安全性の検査

このセマフォをどうやって検査するか考えます. xsem_waitxsem_signal を交互に呼び出すスレッドを N 個並行に走らせたとすると,count の初期値を M としたとき,常に M 個以下のスレッドだけが xsem_waitxsem_signal の間に入れるはずです.これを検査しましょう.

観測者の立場で使うカウンタ n と,その排他制御用のミューテックス m2 を用意します.各スレッドは xsem_wait を呼んだら一度 n をインクリメントし,すぐにデクリメントして xsem_signal を呼び出します.すると n の値の範囲は 0 <= n <= M になるはずです.これをグローバル assert で確認します.

モデル全体は次のようにしました.

#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#include <pthread.h>
#include <assert.h>

#define M 2
#define N 4

pthread_t pth[N];
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t m2 = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cv = PTHREAD_COND_INITIALIZER;
int count;
int n;

// @ASSERT(n <= M)

void xsem_wait(void)
{
    pthread_mutex_lock(&m);
    while (count == 0) {
        pthread_cond_wait(&cv, &m);
    }
    count--;
    pthread_mutex_unlock(&m);
}

void xsem_signal(void)
{
    pthread_mutex_lock(&m);
    count++;
    pthread_cond_signal(&cv);
    pthread_mutex_unlock(&m);
}

void *thread(void *arg)
{
    while (true) {
        xsem_wait();

        pthread_mutex_lock(&m2);
        n++;
        pthread_mutex_unlock(&m2);

        pthread_mutex_lock(&m2);
        n--;
        pthread_mutex_unlock(&m2);

        xsem_signal();
    }
    return NULL;
}

int main(void)
{
    count = M;

    for (int i = 0; i < N; ++i) {
        pthread_create(&pth[i], NULL, &thread, NULL);
    }

    for (int i = 0; i < N; ++i) {
        pthread_join(pth[i], NULL);
    }

    return 0;
}

検査を実行してみると,問題ないことがわかります.spurious wakeups オプションをつけても問題ありませんでした.

2  状態の存在確認

変数 n の値がほんとに n == M となることはあるんでしょうか?グローバル assert でその否定を調べてみればわかります.

// @ASSERT(n != M)

検査を実行すると違反が見つかり,確かに n == M となっているケースがあることが確認できます.最短パスを見つけてくるので,残りの2スレッドはまだ起動すらしていませんが...

------------------------
global vars:
    count = 0
    n = 2
mutex:
    m <unlocked>
    m2 pth[1]
thread:
0 main 6
    i = 1
1 pth[0] 24
2 pth[1] 23
------------------------

3  pthread_cond_waitwhile ループ

いつものように pthread_cond_waitwhile ループで囲って条件を再チェックするようになっています.もちろん spurious wakeups 対策として必要ですが,spurious wakeups がない場合はどうでしょうか?

試しに while を if に代えて検査してみると assert 違反が見つかります.このパスを読むと意外なことがわかります.

#12 pth[2] lock m
#15 pth[2] load count
#16 pth[2] store count
    count = 1
#17 pth[2] unlock m

#19 pth[0] lock m
#23 pth[0] load count
#24 pth[2] lock m2
#25 pth[0] store count
    count = 0
#26 pth[2] load n
#27 pth[0] unlock m
#28 pth[2] store n
    n = 1
#29 pth[1] lock m
#30 pth[2] unlock m2
#31 pth[2] lock m2
#32 pth[2] load n
#33 pth[2] store n
    n = 0
#34 pth[1] load count
#35 pth[2] unlock m2
#37 pth[1] wait cv m           # A
#38 pth[2] lock m
#39 pth[2] load count
#40 pth[2] store count
    count = 1
    2 pth[1] 17 wait cv
#41 pth[2] signal cv           # B
    2 pth[1] 17
#42 pth[2] unlock m
#43 pth[2] lock m
#46 pth[2] load count
#47 pth[2] store count         # C
    count = 0
#48 pth[0] lock m2
#49 pth[2] unlock m
#50 pth[0] load n
#51 pth[1] lock m
#52 pth[0] store n
    n = 1
#53 pth[0] unlock m2
#54 pth[1] load count
#55 pth[2] lock m2
#56 pth[1] store count          # D
    count = -1
#57 pth[2] load n
#58 pth[2] store n
    n = 2
#59 pth[1] unlock m
#60 pth[2] unlock m2
#61 pth[1] lock m2
#62 pth[1] load n
#63 pth[1] store n
    n = 3

途中入り混じっていて少し読みにくいのですが,はじめに 2 と 0 が xsem_wait から抜けてきます.そのあとにきた 1 は A で待ちに入ります.通過した 2 が B で signal を発行して 1 を起こします.この時点では確かに count == 1 です.

ところがその 2 はそのまま走り続けて xsem_wait に再度入り,なんと 1 を追い越して先に count をデクリメントしてしまうのです.その結果,出遅れた 1 は count を確認せずにデクリメントしてしまうので -1 にした挙句に通過してしまい,n == M + 1 となってしまっています.

追い越しが問題ならば,各スレッドとも処理を繰り返さず1度だけ xsem_wait, xsem_signal を実行するのであれば問題ないでしょうか?まあ,やってみてください :P

2017/10/10

4  レポート

------------------------
global vars:
    count = 0
    n = 0
mutex:
    m <unlocked>
    m2 <unlocked>
thread:
0 main 1
------------------------
#1 main store count
57: count = M;
------------------------
global vars:
    count = 2
    n = 0
mutex:
    m <unlocked>
    m2 <unlocked>
thread:
0 main 2
------------------------
#2 main tau
59: for (int i = 0; i < N; ++i) {
------------------------
global vars:
    count = 2
    n = 0
mutex:
    m <unlocked>
    m2 <unlocked>
thread:
0 main 3
    i = 0
------------------------
#3 main tau
59: for (int i = 0; i < N; ++i) {
------------------------
global vars:
    count = 2
    n = 0
mutex:
    m <unlocked>
    m2 <unlocked>
thread:
0 main 4
    i = 0
------------------------
#4 main create pth[0]
60: pthread_create(&pth[i], NULL, &thread, NULL);
------------------------
global vars:
    count = 2
    n = 0
mutex:
    m <unlocked>
    m2 <unlocked>
thread:
0 main 6
    i = 0
1 pth[0] 11
    arg = 0
------------------------
#5 main tau
59: for (int i = 0; i < N; ++i) {
------------------------
global vars:
    count = 2
    n = 0
mutex:
    m <unlocked>
    m2 <unlocked>
thread:
0 main 3
    i = 1
1 pth[0] 11
    arg = 0
------------------------
#6 main tau
59: for (int i = 0; i < N; ++i) {
------------------------
global vars:
    count = 2
    n = 0
mutex:
    m <unlocked>
    m2 <unlocked>
thread:
0 main 4
    i = 1
1 pth[0] 11
    arg = 0
------------------------
#7 main create pth[1]
60: pthread_create(&pth[i], NULL, &thread, NULL);
------------------------
global vars:
    count = 2
    n = 0
mutex:
    m <unlocked>
    m2 <unlocked>
thread:
0 main 6
    i = 1
1 pth[0] 11
    arg = 0
2 pth[1] 11
    arg = 0
------------------------
#8 main tau
59: for (int i = 0; i < N; ++i) {
------------------------
global vars:
    count = 2
    n = 0
mutex:
    m <unlocked>
    m2 <unlocked>
thread:
0 main 3
    i = 2
1 pth[0] 11
    arg = 0
2 pth[1] 11
    arg = 0
------------------------
#9 main tau
59: for (int i = 0; i < N; ++i) {
------------------------
global vars:
    count = 2
    n = 0
mutex:
    m <unlocked>
    m2 <unlocked>
thread:
0 main 4
    i = 2
1 pth[0] 11
    arg = 0
2 pth[1] 11
    arg = 0
------------------------
#10 main create pth[2]
60: pthread_create(&pth[i], NULL, &thread, NULL);
------------------------
global vars:
    count = 2
    n = 0
mutex:
    m <unlocked>
    m2 <unlocked>
thread:
0 main 6
    i = 2
1 pth[0] 11
    arg = 0
2 pth[1] 11
    arg = 0
3 pth[2] 11
    arg = 0
------------------------
#11 pth[2] tau
39: while (true) {
------------------------
global vars:
    count = 2
    n = 0
mutex:
    m <unlocked>
    m2 <unlocked>
thread:
0 main 6
    i = 2
1 pth[0] 11
    arg = 0
2 pth[1] 11
    arg = 0
3 pth[2] 12
------------------------
#12 pth[2] lock m
21: pthread_mutex_lock(&m);
------------------------
global vars:
    count = 2
    n = 0
mutex:
    m pth[2]
    m2 <unlocked>
thread:
0 main 6
    i = 2
1 pth[0] 11
    arg = 0
2 pth[1] 11
    arg = 0
3 pth[2] 13
------------------------
#13 pth[2] load count
22: if (count == 0) {
------------------------
global vars:
    count = 2
    n = 0
mutex:
    m pth[2]
    m2 <unlocked>
thread:
0 main 6
    i = 2
1 pth[0] 11
    arg = 0
2 pth[1] 11
    arg = 0
3 pth[2] 14
    t13 = 2
------------------------
#14 pth[2] tau
22: if (count == 0) {
------------------------
global vars:
    count = 2
    n = 0
mutex:
    m pth[2]
    m2 <unlocked>
thread:
0 main 6
    i = 2
1 pth[0] 11
    arg = 0
2 pth[1] 11
    arg = 0
3 pth[2] 16
------------------------
#15 pth[2] load count
25: count--;
------------------------
global vars:
    count = 2
    n = 0
mutex:
    m pth[2]
    m2 <unlocked>
thread:
0 main 6
    i = 2
1 pth[0] 11
    arg = 0
2 pth[1] 11
    arg = 0
3 pth[2] 18
    t14 = 2
------------------------
#16 pth[2] store count
25: count--;
------------------------
global vars:
    count = 1
    n = 0
mutex:
    m pth[2]
    m2 <unlocked>
thread:
0 main 6
    i = 2
1 pth[0] 11
    arg = 0
2 pth[1] 11
    arg = 0
3 pth[2] 19
------------------------
#17 pth[2] unlock m
26: pthread_mutex_unlock(&m);
------------------------
global vars:
    count = 1
    n = 0
mutex:
    m <unlocked>
    m2 <unlocked>
thread:
0 main 6
    i = 2
1 pth[0] 11
    arg = 0
2 pth[1] 11
    arg = 0
3 pth[2] 20
------------------------
#18 pth[0] tau
39: while (true) {
------------------------
global vars:
    count = 1
    n = 0
mutex:
    m <unlocked>
    m2 <unlocked>
thread:
0 main 6
    i = 2
1 pth[0] 12
2 pth[1] 11
    arg = 0
3 pth[2] 20
------------------------
#19 pth[0] lock m
21: pthread_mutex_lock(&m);
------------------------
global vars:
    count = 1
    n = 0
mutex:
    m pth[0]
    m2 <unlocked>
thread:
0 main 6
    i = 2
1 pth[0] 13
2 pth[1] 11
    arg = 0
3 pth[2] 20
------------------------
#20 pth[0] load count
22: if (count == 0) {
------------------------
global vars:
    count = 1
    n = 0
mutex:
    m pth[0]
    m2 <unlocked>
thread:
0 main 6
    i = 2
1 pth[0] 14
    t13 = 1
2 pth[1] 11
    arg = 0
3 pth[2] 20
------------------------
#21 pth[0] tau
22: if (count == 0) {
------------------------
global vars:
    count = 1
    n = 0
mutex:
    m pth[0]
    m2 <unlocked>
thread:
0 main 6
    i = 2
1 pth[0] 16
2 pth[1] 11
    arg = 0
3 pth[2] 20
------------------------
#22 pth[1] tau
39: while (true) {
------------------------
global vars:
    count = 1
    n = 0
mutex:
    m pth[0]
    m2 <unlocked>
thread:
0 main 6
    i = 2
1 pth[0] 16
2 pth[1] 12
3 pth[2] 20
------------------------
#23 pth[0] load count
25: count--;
------------------------
global vars:
    count = 1
    n = 0
mutex:
    m pth[0]
    m2 <unlocked>
thread:
0 main 6
    i = 2
1 pth[0] 18
    t14 = 1
2 pth[1] 12
3 pth[2] 20
------------------------
#24 pth[2] lock m2
42: pthread_mutex_lock(&m2);
------------------------
global vars:
    count = 1
    n = 0
mutex:
    m pth[0]
    m2 pth[2]
thread:
0 main 6
    i = 2
1 pth[0] 18
    t14 = 1
2 pth[1] 12
3 pth[2] 21
------------------------
#25 pth[0] store count
25: count--;
------------------------
global vars:
    count = 0
    n = 0
mutex:
    m pth[0]
    m2 pth[2]
thread:
0 main 6
    i = 2
1 pth[0] 19
2 pth[1] 12
3 pth[2] 21
------------------------
#26 pth[2] load n
43: n++;
------------------------
global vars:
    count = 0
    n = 0
mutex:
    m pth[0]
    m2 pth[2]
thread:
0 main 6
    i = 2
1 pth[0] 19
2 pth[1] 12
3 pth[2] 22
    t9 = 0
------------------------
#27 pth[0] unlock m
26: pthread_mutex_unlock(&m);
------------------------
global vars:
    count = 0
    n = 0
mutex:
    m <unlocked>
    m2 pth[2]
thread:
0 main 6
    i = 2
1 pth[0] 20
2 pth[1] 12
3 pth[2] 22
    t9 = 0
------------------------
#28 pth[2] store n
43: n++;
------------------------
global vars:
    count = 0
    n = 1
mutex:
    m <unlocked>
    m2 pth[2]
thread:
0 main 6
    i = 2
1 pth[0] 20
2 pth[1] 12
3 pth[2] 23
------------------------
#29 pth[1] lock m
21: pthread_mutex_lock(&m);
------------------------
global vars:
    count = 0
    n = 1
mutex:
    m pth[1]
    m2 pth[2]
thread:
0 main 6
    i = 2
1 pth[0] 20
2 pth[1] 13
3 pth[2] 23
------------------------
#30 pth[2] unlock m2
44: pthread_mutex_unlock(&m2);
------------------------
global vars:
    count = 0
    n = 1
mutex:
    m pth[1]
    m2 <unlocked>
thread:
0 main 6
    i = 2
1 pth[0] 20
2 pth[1] 13
3 pth[2] 24
------------------------
#31 pth[2] lock m2
46: pthread_mutex_lock(&m2);
------------------------
global vars:
    count = 0
    n = 1
mutex:
    m pth[1]
    m2 pth[2]
thread:
0 main 6
    i = 2
1 pth[0] 20
2 pth[1] 13
3 pth[2] 25
------------------------
#32 pth[2] load n
47: n--;
------------------------
global vars:
    count = 0
    n = 1
mutex:
    m pth[1]
    m2 pth[2]
thread:
0 main 6
    i = 2
1 pth[0] 20
2 pth[1] 13
3 pth[2] 26
    t10 = 1
------------------------
#33 pth[2] store n
47: n--;
------------------------
global vars:
    count = 0
    n = 0
mutex:
    m pth[1]
    m2 pth[2]
thread:
0 main 6
    i = 2
1 pth[0] 20
2 pth[1] 13
3 pth[2] 27
------------------------
#34 pth[1] load count
22: if (count == 0) {
------------------------
global vars:
    count = 0
    n = 0
mutex:
    m pth[1]
    m2 pth[2]
thread:
0 main 6
    i = 2
1 pth[0] 20
2 pth[1] 14
    t13 = 0
3 pth[2] 27
------------------------
#35 pth[2] unlock m2
48: pthread_mutex_unlock(&m2);
------------------------
global vars:
    count = 0
    n = 0
mutex:
    m pth[1]
    m2 <unlocked>
thread:
0 main 6
    i = 2
1 pth[0] 20
2 pth[1] 14
    t13 = 0
3 pth[2] 28
------------------------
#36 pth[1] tau
22: if (count == 0) {
------------------------
global vars:
    count = 0
    n = 0
mutex:
    m pth[1]
    m2 <unlocked>
thread:
0 main 6
    i = 2
1 pth[0] 20
2 pth[1] 15
3 pth[2] 28
------------------------
#37 pth[1] wait cv m
23: pthread_cond_wait(&cv, &m);
------------------------
global vars:
    count = 0
    n = 0
mutex:
    m <unlocked>
    m2 <unlocked>
thread:
0 main 6
    i = 2
1 pth[0] 20
2 pth[1] 17 wait cv
3 pth[2] 28
------------------------
#38 pth[2] lock m
31: pthread_mutex_lock(&m);
------------------------
global vars:
    count = 0
    n = 0
mutex:
    m pth[2]
    m2 <unlocked>
thread:
0 main 6
    i = 2
1 pth[0] 20
2 pth[1] 17 wait cv
3 pth[2] 29
------------------------
#39 pth[2] load count
32: count++;
------------------------
global vars:
    count = 0
    n = 0
mutex:
    m pth[2]
    m2 <unlocked>
thread:
0 main 6
    i = 2
1 pth[0] 20
2 pth[1] 17 wait cv
3 pth[2] 30
    t15 = 0
------------------------
#40 pth[2] store count
32: count++;
------------------------
global vars:
    count = 1
    n = 0
mutex:
    m pth[2]
    m2 <unlocked>
thread:
0 main 6
    i = 2
1 pth[0] 20
2 pth[1] 17 wait cv
3 pth[2] 31
------------------------
#41 pth[2] signal cv
33: pthread_cond_signal(&cv);
------------------------
global vars:
    count = 1
    n = 0
mutex:
    m pth[2]
    m2 <unlocked>
thread:
0 main 6
    i = 2
1 pth[0] 20
2 pth[1] 17
3 pth[2] 32
------------------------
#42 pth[2] unlock m
34: pthread_mutex_unlock(&m);
------------------------
global vars:
    count = 1
    n = 0
mutex:
    m <unlocked>
    m2 <unlocked>
thread:
0 main 6
    i = 2
1 pth[0] 20
2 pth[1] 17
3 pth[2] 12
------------------------
#43 pth[2] lock m
21: pthread_mutex_lock(&m);
------------------------
global vars:
    count = 1
    n = 0
mutex:
    m pth[2]
    m2 <unlocked>
thread:
0 main 6
    i = 2
1 pth[0] 20
2 pth[1] 17
3 pth[2] 13
------------------------
#44 pth[2] load count
22: if (count == 0) {
------------------------
global vars:
    count = 1
    n = 0
mutex:
    m pth[2]
    m2 <unlocked>
thread:
0 main 6
    i = 2
1 pth[0] 20
2 pth[1] 17
3 pth[2] 14
    t13 = 1
------------------------
#45 pth[2] tau
22: if (count == 0) {
------------------------
global vars:
    count = 1
    n = 0
mutex:
    m pth[2]
    m2 <unlocked>
thread:
0 main 6
    i = 2
1 pth[0] 20
2 pth[1] 17
3 pth[2] 16
------------------------
#46 pth[2] load count
25: count--;
------------------------
global vars:
    count = 1
    n = 0
mutex:
    m pth[2]
    m2 <unlocked>
thread:
0 main 6
    i = 2
1 pth[0] 20
2 pth[1] 17
3 pth[2] 18
    t14 = 1
------------------------
#47 pth[2] store count
25: count--;
------------------------
global vars:
    count = 0
    n = 0
mutex:
    m pth[2]
    m2 <unlocked>
thread:
0 main 6
    i = 2
1 pth[0] 20
2 pth[1] 17
3 pth[2] 19
------------------------
#48 pth[0] lock m2
42: pthread_mutex_lock(&m2);
------------------------
global vars:
    count = 0
    n = 0
mutex:
    m pth[2]
    m2 pth[0]
thread:
0 main 6
    i = 2
1 pth[0] 21
2 pth[1] 17
3 pth[2] 19
------------------------
#49 pth[2] unlock m
26: pthread_mutex_unlock(&m);
------------------------
global vars:
    count = 0
    n = 0
mutex:
    m <unlocked>
    m2 pth[0]
thread:
0 main 6
    i = 2
1 pth[0] 21
2 pth[1] 17
3 pth[2] 20
------------------------
#50 pth[0] load n
43: n++;
------------------------
global vars:
    count = 0
    n = 0
mutex:
    m <unlocked>
    m2 pth[0]
thread:
0 main 6
    i = 2
1 pth[0] 22
    t9 = 0
2 pth[1] 17
3 pth[2] 20
------------------------
#51 pth[1] lock m
23: pthread_cond_wait(&cv, &m);
------------------------
global vars:
    count = 0
    n = 0
mutex:
    m pth[1]
    m2 pth[0]
thread:
0 main 6
    i = 2
1 pth[0] 22
    t9 = 0
2 pth[1] 16
3 pth[2] 20
------------------------
#52 pth[0] store n
43: n++;
------------------------
global vars:
    count = 0
    n = 1
mutex:
    m pth[1]
    m2 pth[0]
thread:
0 main 6
    i = 2
1 pth[0] 23
2 pth[1] 16
3 pth[2] 20
------------------------
#53 pth[0] unlock m2
44: pthread_mutex_unlock(&m2);
------------------------
global vars:
    count = 0
    n = 1
mutex:
    m pth[1]
    m2 <unlocked>
thread:
0 main 6
    i = 2
1 pth[0] 24
2 pth[1] 16
3 pth[2] 20
------------------------
#54 pth[1] load count
25: count--;
------------------------
global vars:
    count = 0
    n = 1
mutex:
    m pth[1]
    m2 <unlocked>
thread:
0 main 6
    i = 2
1 pth[0] 24
2 pth[1] 18
    t14 = 0
3 pth[2] 20
------------------------
#55 pth[2] lock m2
42: pthread_mutex_lock(&m2);
------------------------
global vars:
    count = 0
    n = 1
mutex:
    m pth[1]
    m2 pth[2]
thread:
0 main 6
    i = 2
1 pth[0] 24
2 pth[1] 18
    t14 = 0
3 pth[2] 21
------------------------
#56 pth[1] store count
25: count--;
------------------------
global vars:
    count = -1
    n = 1
mutex:
    m pth[1]
    m2 pth[2]
thread:
0 main 6
    i = 2
1 pth[0] 24
2 pth[1] 19
3 pth[2] 21
------------------------
#57 pth[2] load n
43: n++;
------------------------
global vars:
    count = -1
    n = 1
mutex:
    m pth[1]
    m2 pth[2]
thread:
0 main 6
    i = 2
1 pth[0] 24
2 pth[1] 19
3 pth[2] 22
    t9 = 1
------------------------
#58 pth[2] store n
43: n++;
------------------------
global vars:
    count = -1
    n = 2
mutex:
    m pth[1]
    m2 pth[2]
thread:
0 main 6
    i = 2
1 pth[0] 24
2 pth[1] 19
3 pth[2] 23
------------------------
#59 pth[1] unlock m
26: pthread_mutex_unlock(&m);
------------------------
global vars:
    count = -1
    n = 2
mutex:
    m <unlocked>
    m2 pth[2]
thread:
0 main 6
    i = 2
1 pth[0] 24
2 pth[1] 20
3 pth[2] 23
------------------------
#60 pth[2] unlock m2
44: pthread_mutex_unlock(&m2);
------------------------
global vars:
    count = -1
    n = 2
mutex:
    m <unlocked>
    m2 <unlocked>
thread:
0 main 6
    i = 2
1 pth[0] 24
2 pth[1] 20
3 pth[2] 24
------------------------
#61 pth[1] lock m2
42: pthread_mutex_lock(&m2);
------------------------
global vars:
    count = -1
    n = 2
mutex:
    m <unlocked>
    m2 pth[1]
thread:
0 main 6
    i = 2
1 pth[0] 24
2 pth[1] 21
3 pth[2] 24
------------------------
#62 pth[1] load n
43: n++;
------------------------
global vars:
    count = -1
    n = 2
mutex:
    m <unlocked>
    m2 pth[1]
thread:
0 main 6
    i = 2
1 pth[0] 24
2 pth[1] 22
    t9 = 2
3 pth[2] 24
------------------------
#63 pth[1] store n
43: n++;
------------------------
global vars:
    count = -1
    n = 3
mutex:
    m <unlocked>
    m2 pth[1]
thread:
0 main 6
    i = 2
1 pth[0] 24
2 pth[1] 23
3 pth[2] 24
------------------------
© 2013-2017 PRINCIPIA Limited