Subversion Repositories Kolibri OS

Rev

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

  1. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  2. ;;                                                              ;;
  3. ;; Copyright (C) KolibriOS team 2012-2015. All rights reserved. ;;
  4. ;; Distributed under terms of the GNU General Public License    ;;
  5. ;;                                                              ;;
  6. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  7.  
  8. $Revision: 5363 $
  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.         movi    eax, sizeof.TIMER
  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
  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
  230.