summaryrefslogtreecommitdiff
path: root/user/libthread/mutex_lock.S
blob: 731ab59f2ae38a47c1cd50a46cfb88bbe0e7893a (plain)
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