Subversion Repositories Kolibri OS

Rev

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

  1. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  2. ;;                                                              ;;
  3. ;; Copyright (C) KolibriOS team 2012-2024. All rights reserved. ;;
  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.
  13. struct  TIMER
  14.         Next            dd      ?
  15.         Prev            dd      ?
  16. ; These fields organize a double-linked list of all timers.
  17.         TimerFunc       dd      ?
  18. ; Function to be called when the timer is activated.
  19.         UserData        dd      ?
  20. ; The value that is passed as is to .TimerFunc.
  21.         Time            dd      ?
  22. ; Time at which the timer should be activated.
  23.         Interval        dd      ?
  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:
  51.         mov     edx, [current_slot_idx]
  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.
  84.         movi    eax, sizeof.TIMER
  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]
  93.         mov     [ebx + TIMER.Interval], ecx
  94.         mov     ecx, [timerFunc]
  95.         mov     [ebx + TIMER.TimerFunc], ecx
  96.         mov     ecx, [userData]
  97.         mov     [ebx + TIMER.UserData], ecx
  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]
  105.         mov     [ebx + TIMER.Time], ecx
  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
  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
  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.
  134.         mov     eax, [ebx + TIMER.Next]
  135.         mov     ecx, [ebx + TIMER.Prev]
  136.         mov     [eax + TIMER.Prev], ecx
  137.         mov     [ecx + TIMER.Next], eax
  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.
  160.         mov     eax, [timer_list + TIMER.Next]
  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.
  167.         mov     edx, [eax + TIMER.Next]
  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]
  178.         sub     edx, [eax + TIMER.Time]
  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.
  183.         push    [eax + TIMER.UserData]    ; parameter for TimerFunc
  184.         push    [eax + TIMER.TimerFunc]   ; to be restored in 2g
  185. ; 2f. Calculate time of next activation or delete the timer if it is one-shot.
  186.         mov     ecx, [eax + TIMER.Interval]
  187.         add     [eax + TIMER.Time], ecx
  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
  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
  210.         mov     eax, [timer_list + TIMER.Next]
  211. .loop:
  212.         cmp     eax, timer_list
  213.         jz      .done_nowork
  214.         mov     edx, [timer_ticks]
  215.         sub     edx, [eax + TIMER.Time]
  216.         jns     .done_haswork
  217.         mov     eax, [eax + TIMER.Next]
  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
  229.