Subversion Repositories Kolibri OS

Rev

Rev 2434 | Rev 3555 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  2. ;;                                                              ;;
  3. ;; Copyright (C) KolibriOS team 2012. All rights reserved.      ;;
  4. ;; Distributed under terms of the GNU General Public License    ;;
  5. ;;                                                              ;;
  6. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  7.  
  8. $Revision: 2381 $
  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.
  14. struct  TIMER
  15.         Next            dd      ?
  16.         Prev            dd      ?
  17. ; These fields organize a double-linked list of all timers.
  18.         TimerFunc       dd      ?
  19. ; Function to be called when the timer is activated.
  20.         UserData        dd      ?
  21. ; The value that is passed as is to .TimerFunc.
  22.         Time            dd      ?
  23. ; Time at which the timer should be activated.
  24.         Interval        dd      ?
  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
  55.         lock cmpxchg [timer_list_owner], edx
  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.
  85.         push    sizeof.TIMER
  86.         pop     eax
  87.         call    malloc
  88. ; 1b. If allocation failed, return (go to 5) with eax = 0.
  89.         test    eax, eax
  90.         jz      .nothing
  91. ; 2. Setup the TIMER structure.
  92.         xchg    ebx, eax
  93. ; 2a. Copy values from the arguments.
  94.         mov     ecx, [interval]
  95.         mov     [ebx+TIMER.Interval], ecx
  96.         mov     ecx, [timerFunc]
  97.         mov     [ebx+TIMER.TimerFunc], ecx
  98.         mov     ecx, [userData]
  99.         mov     [ebx+TIMER.UserData], ecx
  100. ; 2b. Get time of the next activation.
  101.         mov     ecx, [deltaStart]
  102.         test    ecx, ecx
  103.         jnz     @f
  104.         mov     ecx, [interval]
  105. @@:
  106.         add     ecx, [timer_ticks]
  107.         mov     [ebx+TIMER.Time], ecx
  108. ; 3. Insert the TIMER structure to the global list.
  109. ; 3a. Acquire the lock.
  110.         call    lock_timer_list
  111. ; 3b. Insert an item at ebx to the tail of the timer_list.
  112.         mov     eax, timer_list
  113.         mov     ecx, [eax+TIMER.Prev]
  114.         mov     [ebx+TIMER.Next], eax
  115.         mov     [ebx+TIMER.Prev], ecx
  116.         mov     [eax+TIMER.Prev], ebx
  117.         mov     [ecx+TIMER.Next], ebx
  118. ; 3c. Release the lock.
  119.         call    unlock_timer_list
  120. ; 4. Return with eax = pointer to TIMER structure.
  121.         xchg    ebx, eax
  122. .nothing:
  123. ; 5. Returning.
  124.         ret
  125. endp
  126.  
  127. ; This function removes a timer.
  128. ; The only argument is [esp+4] = the value which was returned from timer_hs.
  129. cancel_timer_hs:
  130.         push    ebx     ; save used register to be stdcall
  131. ; 1. Remove the TIMER structure from the global list.
  132. ; 1a. Acquire the lock.
  133.         call    lock_timer_list
  134.         mov     ebx, [esp+4+4]
  135. ; 1b. Delete an item at ebx from the double-linked list.
  136.         mov     eax, [ebx+TIMER.Next]
  137.         mov     ecx, [ebx+TIMER.Prev]
  138.         mov     [eax+TIMER.Prev], ecx
  139.         mov     [ecx+TIMER.Next], eax
  140. ; 1c. If we are removing the next timer in currently processing chain,
  141. ; the next timer for this timer becomes new next timer.
  142.         cmp     ebx, [timer_next]
  143.         jnz     @f
  144.         mov     [timer_next], eax
  145. @@:
  146. ; 1d. Release the lock.
  147.         call    unlock_timer_list
  148. ; 2. Free the TIMER structure.
  149.         xchg    eax, ebx
  150.         call    free
  151. ; 3. Return.
  152.         pop     ebx     ; restore used register to be stdcall
  153.         ret     4       ; purge one dword argument to be stdcall
  154.  
  155. ; This function is regularly called from osloop. It processes the global list
  156. ; and activates the corresponding timers.
  157. check_timers:
  158. ; 1. Acquire the lock.
  159.         call    lock_timer_list
  160. ; 2. Loop over all registered timers, checking time.
  161. ; 2a. Get the first item.
  162.         mov     eax, [timer_list+TIMER.Next]
  163.         mov     [timer_next], eax
  164. .loop:
  165. ; 2b. Check for end of list.
  166.         cmp     eax, timer_list
  167.         jz      .done
  168. ; 2c. Get and store the next timer.
  169.         mov     edx, [eax+TIMER.Next]
  170.         mov     [timer_next], edx
  171. ; 2d. Check time for timer activation.
  172. ; We can't just compare [timer_ticks] and [TIMER.Time], since overflows are
  173. ; possible: if the current time is 0FFFFFFFFh ticks and timer should be
  174. ; activated in 3 ticks, the simple comparison will produce incorrect result.
  175. ; So we calculate the difference [timer_ticks] - [TIMER.Time]; if it is
  176. ; non-negative, the time is over; if it is negative, then either the time is
  177. ; not over or we have not processed this timer for 2^31 ticks, what is very
  178. ; unlikely.
  179.         mov     edx, [timer_ticks]
  180.         sub     edx, [eax+TIMER.Time]
  181.         js      .next
  182. ; The timer should be activated now.
  183. ; 2e. Store the timer data in the stack. This is required since 2f can delete
  184. ; the timer, invalidating the content.
  185.         push    [eax+TIMER.UserData]    ; parameter for TimerFunc
  186.         push    [eax+TIMER.TimerFunc]   ; to be restored in 2g
  187. ; 2f. Calculate time of next activation or delete the timer if it is one-shot.
  188.         mov     ecx, [eax+TIMER.Interval]
  189.         add     [eax+TIMER.Time], ecx
  190.         test    ecx, ecx
  191.         jnz     .nodelete
  192.         stdcall cancel_timer_hs, eax
  193. .nodelete:
  194. ; 2g. Activate timer, using data from the stack.
  195.         pop     eax
  196.         call    eax
  197. .next:
  198. ; 2h. Advance to the next timer and continue the loop.
  199.         mov     eax, [timer_next]
  200.         jmp     .loop
  201. .done:
  202. ; 3. Release the lock.
  203.         call    unlock_timer_list
  204. ; 4. Return.
  205.         ret
  206.