1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
|
#include <syscall_int.h>
// it is impossible to implement mutex_unlock_vanish without stack in c
// so we implement all lock functions in assembly and mutex_unlock can
// share function body with mutex_unlock_vanish
.mutex_unlock_body:
xor %eax, %eax //
lock xchg %eax, (%ebp) // mp->locked = 0;
.mutex_unlock_try: // while(true) {
xor %eax, %eax //
mov $1, %edi //
lock cmpxchg %edi, 0x4(%ebp) // eax = compare_and_set(&mp->w_lock, 1);
test %eax, %eax // if(eax == 0) {break;}
jnz .mutex_unlock_try // }
mov 0x8(%ebp), %edi // edi = mp->w_list;
test %edi, %edi //
jz .mutex_unlock_w_empty // if(edi != NULL) {
mov $1, %edx //
mov %edx, 0x28(%edi) // edi->mutex_resume = 1;
mov (%edi), %esi //
int $MAKE_RUNNABLE_INT // make_runnable(edi->tid);
.mutex_unlock_w_empty: // }
xor %eax, %eax //
lock xchg %eax, 0x4(%ebp) // mp->w_lock = 0;
jmp *%ebx
.global mutex_unlock
.type mutex_unlock, %function
mutex_unlock:
push %ebx
push %ebp
mov 0xc(%esp), %ebp // ebp = mp
lea .mutex_unlock_done, %ebx // ebx will be used by mutex_unlock_body as return address
jmp .mutex_unlock_body
.mutex_unlock_done:
pop %ebp
pop %ebx
ret
.global mutex_unlock_vanish
.type mutex_unlock_vanish, %function
mutex_unlock_vanish:
mov 0x4(%esp), %ebp
lea .mutex_unlock_do_vanish, %ebx
jmp .mutex_unlock_body
.mutex_unlock_do_vanish:
int $VANISH_INT
.global mutex_lock
.type mutex_lock, %function
mutex_lock:
push %ebx //
push %ebp //
call get_self_tcb //
mov %eax, %ebx // ebx = get_self_tcb();
xor %eax, %eax //
mov %eax, 0x28(%ebx) // ebx->mutex_resume = 0;
mov 0xc(%esp), %ebp // ebp = mp;
.mutex_lock_w_lock: // while(true) {
xor %eax, %eax //
mov $1, %edi //
lock cmpxchg %edi, 0x4(%ebp) // eax = compare_and_set(&mp->w_lock, 1);
test %eax, %eax // if(eax == 0) {break;}
jnz .mutex_lock_w_lock // }
mov 0x8(%ebp), %edi // edi = mp->w_list;
test %edi, %edi //
jz .mutex_lock_w_empty // if(edi != NULL) {
mov %edi, 0x20(%ebx) // ebx->mutex_next = edi;
mov 0x24(%edi), %edx // edx = edi->mutex_prev;
mov %edx, 0x24(%ebx) // ebx->mutex_prev = edx;
mov %ebx, 0x20(%edx) // edx->mutex_next = ebx;
mov %ebx, 0x24(%edi) // edi->mutex_prev = ebx;
jmp .mutex_lock_w_done // }
.mutex_lock_w_empty: // else {
mov %ebx, 0x8(%ebp) // mp->w_list = ebx;
mov %ebx, 0x20(%ebx) // ebx->mutex_next = ebx;
mov %ebx, 0x24(%ebx) // ebx->mutex_prev = ebx;
.mutex_lock_w_done: // }
lock xchg %eax, 0x4(%ebp) // mp->w_lock = 0;
.mutex_lock_try: //
xor %eax, %eax // while(true) {
mov $1, %edi //
lock cmpxchg %edi, (%ebp) // eax = compare_and_set(&mp->locked, 1);
test %eax, %eax //
jz .mutex_lock_success // if(eax == 0) {break;}
lea 0x28(%ebx), %eax //
push %eax //
call deschedule // deschedule(&ebx->mutex_resume);
add $0x4, %esp //
jmp .mutex_lock_try // }
.mutex_lock_success: // while(true) {
xor %eax, %eax //
mov $1, %edi //
lock cmpxchg %edi, 0x4(%ebp) // eax = compare_and_set(&mp->w_lock, 1);
test %eax, %eax // if(eax == 0) {break;}
jnz .mutex_lock_success // }
mov 0x20(%ebx), %edi // edi = ebx->mutex_next;
mov 0x24(%ebx), %edx // edx = ebx->mutex_prev;
cmp %edi, %ebx //
je .mutex_lock_w_last // if(edi != ebx) {
mov %edx, 0x24(%edi) // edi->mutex_prev = edx;
mov %edi, 0x20(%edx) // edx->mutex_next = edi;
cmp %ebx, 0x8(%ebp) //
jne .mutex_lock_w_end // if(ebx == mp->w_list)
mov %edi, 0x8(%ebp) // mp->w_list = edi;
jmp .mutex_lock_w_end // }
.mutex_lock_w_last: // else {
mov %eax, 0x8(%ebp) // mp->w_list = NULL;
.mutex_lock_w_end: // }
lock xchg %eax, 0x4(%ebp) // mp->w_lock = 0;
pop %ebp
pop %ebx
ret
|