Subversion Repositories Kolibri OS

Rev

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

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