Subversion Repositories Kolibri OS

Rev

Rev 9715 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2288 clevermous 1
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2
;;                                                              ;;
10051 ace_dent 3
;; Copyright (C) KolibriOS team 2012-2024. All rights reserved. ;;
2288 clevermous 4
;; Distributed under terms of the GNU General Public License    ;;
5
;;                                                              ;;
6
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
7
 
8
 
9
; Simple implementation of timers. All timers are organized in a double-linked
10
; list, and the OS loop after every timer tick processes the list.
11
 
12
; This structure describes a timer for the kernel.
2381 hidnplayr 13
struct  TIMER
14
        Next            dd      ?
15
        Prev            dd      ?
2288 clevermous 16
; These fields organize a double-linked list of all timers.
2381 hidnplayr 17
        TimerFunc       dd      ?
2288 clevermous 18
; Function to be called when the timer is activated.
2381 hidnplayr 19
        UserData        dd      ?
2288 clevermous 20
; The value that is passed as is to .TimerFunc.
2381 hidnplayr 21
        Time            dd      ?
2288 clevermous 22
; Time at which the timer should be activated.
2381 hidnplayr 23
        Interval        dd      ?
2288 clevermous 24
; Interval between activations of the timer, in 0.01s.
25
ends
26
 
27
iglobal
28
align 4
29
; The head of timer list.
30
timer_list:
31
        dd      timer_list
32
        dd      timer_list
33
endg
34
uglobal
35
; These two variables are used to synchronize access to the global list.
36
; Logically, they form an recursive mutex. Physically, the first variable holds
37
; the slot number of the current owner or 0, the second variable holds the
38
; recursion count.
39
; The mutex should be recursive to allow a timer function to add/delete other
40
; timers or itself.
41
timer_list_owner        dd      0
42
timer_list_numlocks     dd      0
43
; A timer function can delete any timer, including itself and the next timer in
44
; the chain. To handle such situation correctly, we keep the next timer in a
45
; global variable, so the removing operation can update it.
46
timer_next      dd      0
47
endg
48
 
49
; This internal function acquires the lock for the global list.
50
lock_timer_list:
8869 rgimad 51
        mov     edx, [current_slot_idx]
2288 clevermous 52
@@:
53
        xor     eax, eax
54
        lock cmpxchg [timer_list_owner], edx
55
        jz      @f
56
        cmp     eax, edx
57
        jz      @f
58
        call    change_task
59
        jmp     @b
60
@@:
61
        inc     [timer_list_numlocks]
62
        ret
63
 
64
; This internal function releases the lock for the global list.
65
unlock_timer_list:
66
        dec     [timer_list_numlocks]
67
        jnz     .nothing
68
        mov     [timer_list_owner], 0
69
.nothing:
70
        ret
71
 
72
; This function adds a timer.
73
; If deltaStart is nonzero, the timer is activated after deltaStart hundredths
74
; of seconds starting from the current time. If interval is nonzero, the timer
75
; is activated every deltaWork hundredths of seconds starting from the first
76
; activation. The activated timer calls timerFunc as stdcall function with one
77
; argument userData.
78
; Return value is NULL if something has failed or some value which is opaque
79
; for the caller. Later this value can be used for cancel_timer_hs.
80
proc timer_hs stdcall uses ebx, deltaStart:dword, interval:dword, \
81
        timerFunc:dword, userData:dword
82
; 1. Allocate memory for the TIMER structure.
83
; 1a. Call the allocator.
3598 clevermous 84
        movi    eax, sizeof.TIMER
2288 clevermous 85
        call    malloc
86
; 1b. If allocation failed, return (go to 5) with eax = 0.
87
        test    eax, eax
88
        jz      .nothing
89
; 2. Setup the TIMER structure.
90
        xchg    ebx, eax
91
; 2a. Copy values from the arguments.
92
        mov     ecx, [interval]
9715 Doczom 93
        mov     [ebx + TIMER.Interval], ecx
2288 clevermous 94
        mov     ecx, [timerFunc]
9715 Doczom 95
        mov     [ebx + TIMER.TimerFunc], ecx
2288 clevermous 96
        mov     ecx, [userData]
9715 Doczom 97
        mov     [ebx + TIMER.UserData], ecx
2288 clevermous 98
; 2b. Get time of the next activation.
99
        mov     ecx, [deltaStart]
100
        test    ecx, ecx
101
        jnz     @f
102
        mov     ecx, [interval]
103
@@:
104
        add     ecx, [timer_ticks]
9715 Doczom 105
        mov     [ebx + TIMER.Time], ecx
2288 clevermous 106
; 3. Insert the TIMER structure to the global list.
107
; 3a. Acquire the lock.
108
        call    lock_timer_list
109
; 3b. Insert an item at ebx to the tail of the timer_list.
110
        mov     eax, timer_list
9715 Doczom 111
        mov     ecx, [eax + TIMER.Prev]
112
        mov     [ebx + TIMER.Next], eax
113
        mov     [ebx + TIMER.Prev], ecx
114
        mov     [eax + TIMER.Prev], ebx
115
        mov     [ecx + TIMER.Next], ebx
2288 clevermous 116
; 3c. Release the lock.
117
        call    unlock_timer_list
118
; 4. Return with eax = pointer to TIMER structure.
119
        xchg    ebx, eax
120
.nothing:
121
; 5. Returning.
122
        ret
123
endp
124
 
125
; This function removes a timer.
126
; The only argument is [esp+4] = the value which was returned from timer_hs.
127
cancel_timer_hs:
128
        push    ebx     ; save used register to be stdcall
129
; 1. Remove the TIMER structure from the global list.
130
; 1a. Acquire the lock.
131
        call    lock_timer_list
132
        mov     ebx, [esp+4+4]
133
; 1b. Delete an item at ebx from the double-linked list.
9715 Doczom 134
        mov     eax, [ebx + TIMER.Next]
135
        mov     ecx, [ebx + TIMER.Prev]
136
        mov     [eax + TIMER.Prev], ecx
137
        mov     [ecx + TIMER.Next], eax
2288 clevermous 138
; 1c. If we are removing the next timer in currently processing chain,
139
; the next timer for this timer becomes new next timer.
140
        cmp     ebx, [timer_next]
141
        jnz     @f
142
        mov     [timer_next], eax
143
@@:
144
; 1d. Release the lock.
145
        call    unlock_timer_list
146
; 2. Free the TIMER structure.
147
        xchg    eax, ebx
148
        call    free
149
; 3. Return.
150
        pop     ebx     ; restore used register to be stdcall
151
        ret     4       ; purge one dword argument to be stdcall
152
 
153
; This function is regularly called from osloop. It processes the global list
154
; and activates the corresponding timers.
155
check_timers:
156
; 1. Acquire the lock.
157
        call    lock_timer_list
158
; 2. Loop over all registered timers, checking time.
159
; 2a. Get the first item.
9715 Doczom 160
        mov     eax, [timer_list + TIMER.Next]
2288 clevermous 161
        mov     [timer_next], eax
162
.loop:
163
; 2b. Check for end of list.
164
        cmp     eax, timer_list
165
        jz      .done
166
; 2c. Get and store the next timer.
9715 Doczom 167
        mov     edx, [eax + TIMER.Next]
2288 clevermous 168
        mov     [timer_next], edx
169
; 2d. Check time for timer activation.
170
; We can't just compare [timer_ticks] and [TIMER.Time], since overflows are
171
; possible: if the current time is 0FFFFFFFFh ticks and timer should be
172
; activated in 3 ticks, the simple comparison will produce incorrect result.
173
; So we calculate the difference [timer_ticks] - [TIMER.Time]; if it is
174
; non-negative, the time is over; if it is negative, then either the time is
175
; not over or we have not processed this timer for 2^31 ticks, what is very
176
; unlikely.
177
        mov     edx, [timer_ticks]
9715 Doczom 178
        sub     edx, [eax + TIMER.Time]
2288 clevermous 179
        js      .next
180
; The timer should be activated now.
181
; 2e. Store the timer data in the stack. This is required since 2f can delete
182
; the timer, invalidating the content.
9715 Doczom 183
        push    [eax + TIMER.UserData]    ; parameter for TimerFunc
184
        push    [eax + TIMER.TimerFunc]   ; to be restored in 2g
2288 clevermous 185
; 2f. Calculate time of next activation or delete the timer if it is one-shot.
9715 Doczom 186
        mov     ecx, [eax + TIMER.Interval]
187
        add     [eax + TIMER.Time], ecx
2288 clevermous 188
        test    ecx, ecx
189
        jnz     .nodelete
190
        stdcall cancel_timer_hs, eax
191
.nodelete:
192
; 2g. Activate timer, using data from the stack.
193
        pop     eax
194
        call    eax
195
.next:
196
; 2h. Advance to the next timer and continue the loop.
197
        mov     eax, [timer_next]
198
        jmp     .loop
199
.done:
200
; 3. Release the lock.
201
        call    unlock_timer_list
202
; 4. Return.
203
        ret
3534 clevermous 204
 
205
; This is a simplified version of check_timers that does not call anything,
206
; just checks whether check_timers should do something.
207
proc check_timers_has_work?
208
        pushf
209
        cli
9715 Doczom 210
        mov     eax, [timer_list + TIMER.Next]
3534 clevermous 211
.loop:
212
        cmp     eax, timer_list
213
        jz      .done_nowork
214
        mov     edx, [timer_ticks]
9715 Doczom 215
        sub     edx, [eax + TIMER.Time]
3534 clevermous 216
        jns     .done_haswork
9715 Doczom 217
        mov     eax, [eax + TIMER.Next]
3534 clevermous 218
        jmp     .loop
219
.done_nowork:
220
        popf
221
        xor     eax, eax
222
        ret
223
.done_haswork:
224
        popf
225
        xor     eax, eax
226
        inc     eax
227
        ret
228
endp