Subversion Repositories Kolibri OS

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
6767 clevermous 1
; High-level synchronization primitives.
2
 
3
; Mutex: stands for MUTual EXclusion.
4
; Allows to enforce that only one thread executes some code at a time.
5
; mutex_lock acquires the given mutex, mutex_unlock releases it;
6
; if thread 1 holds the mutex and thread 2 calls mutex_lock,
7
; thread 2 is blocked until thread 1 calls mutex_unlock.
8
; Several threads can wait for the same mutex; when the owner
9
; releases the mutex, one of waiting threads grabs the released mutex,
10
; but it is unspecified which one.
11
 
12
; If there is no contention, i.e. no one calls mutex_lock
13
; while somebody is holding the mutex, then
14
; mutex_lock and mutex_unlock use just a few instructions.
15
; This is the fast path.
16
; Otherwise, mutex_lock and mutex_unlock require a syscall
17
; to enter waiting state and wake someone up correspondingly.
18
 
19
; Implementation. We use one dword for status and
20
; kernel handle for underlying futex to be able to sleep/wake.
21
; Bit 31, the highest bit of status dword,
22
; is set if someone holds the mutex and clear otherwise.
23
; Bits 0-30 form the number of threads waiting in mutex_lock.
24
; All modifications of status dword should be atomic.
25
 
26
struct MUTEX
27
status  dd      ?
28
handle  dd      ?
29
ends
30
 
31
; Initialization. Set status dword to zero and
32
; open the underlying futex.
33
; in: ecx -> MUTEX
34
proc mutex_init
35
        mov     [ecx+MUTEX.status], 0
36
        push    ebx
37
        mov     eax, 77
38
        xor     ebx, ebx
39
        call    FS_SYSCALL_PTR
40
        pop     ebx
41
        mov     [ecx+MUTEX.handle], eax
42
        ret
43
endp
44
 
45
; Finalization. Close the underlying futex.
46
; in: ecx = MUTEX handle
47
proc mutex_destroy
48
        push    ebx
49
        mov     eax, 77
50
        mov     ebx, 1
51
        call    FS_SYSCALL_PTR
52
        pop     ebx
53
        ret
54
endp
55
 
56
; Acquire the mutex.
57
macro mutex_lock mutex
58
{
59
local .done
60
; Atomically set the locked status bit and get the previous value.
61
        lock bts [mutex+MUTEX.status], 31
62
; Fast path: the mutex was not locked. If so, we are done.
63
        jnc     .done
64
if ~(mutex eq ecx)
65
        mov     ecx, mutex
66
end if
67
        call    mutex_lock_slow_path
68
.done:
69
}
70
 
71
; Acquire the mutex, slow path.
72
; Someone holds the mutex... or has held it a moment ago.
73
; in: ecx -> MUTEX
74
proc mutex_lock_slow_path
75
; Atomically increment number of waiters.
76
        lock inc [ecx+MUTEX.status]
77
; When the mutex owner will release the mutex and wake us up,
78
; another thread can sneak in and grab the mutex before us.
79
; So, the following actions are potentially repeated in a loop.
80
.wait_loop:
81
        mov     edx, [ecx+MUTEX.status]
82
; The owner could have unlocked the mutex in parallel with us.
83
; If so, don't sleep: nobody would wake us up.
84
        test    edx, edx
85
        jns     .skip_wait
86
; Pass the fetched value to the kernel along with futex handle.
87
; If the owner unlocks the mutex while we are here,
88
; the kernel will detect mismatch and exit without sleeping.
89
; Otherwise, the owner will wake us up explicitly.
90
        push    ebx ecx esi
91
        mov     eax, 77
92
        mov     ebx, 2
93
        mov     ecx, [ecx+MUTEX.handle]
94
        xor     esi, esi
95
        call    FS_SYSCALL_PTR
96
        pop     esi ecx ebx
97
.skip_wait:
98
; We have woken up.
99
; Or we didn't even sleep because status dword has been changed beneath us.
100
; Anyway, something may have changed, re-evaluate the situation.
101
; Atomically set the locked status bit and get the previous value.
102
        lock bts [ecx+MUTEX.status], 31
103
; If the mutex was locked, someone has grabbed the mutex before us.
104
; Repeat the loop.
105
        jc      .wait_loop
106
; The mutex was unlocked and we have just managed to lock it.
107
; Our status has changed from a waiter to the owner.
108
; Decrease number of waiters and exit.
109
        lock dec [ecx+MUTEX.status]
110
        ret
111
endp
112
 
113
; Release the mutex.
114
macro mutex_unlock mutex
115
{
116
local .done
117
; Atomically clear the locked status bit and check whether someone is waiting.
118
        lock and [mutex+MUTEX.status], 0x7FFFFFFF
119
; Fast path: nobody is waiting.
120
        jz      .done
121
        mov     ecx, [mutex+MUTEX.handle]
122
        call    mutex_unlock_slow_path
123
.done:
124
}
125
 
126
; Release the mutex, slow path.
127
; Someone is sleeping in the kernel, or preparing for the sleep.
128
; Wake one of waiters.
129
; in: ecx = MUTEX handle
130
proc mutex_unlock_slow_path
131
        push    ebx
132
        mov     eax, 77
133
        mov     ebx, 3
134
        mov     edx, 1
135
        call    FS_SYSCALL_PTR
136
        pop     ebx
137
        ret
138
endp