Patch 7.4.2332
Problem:    Crash when stop_timer() is called in a callback of a callback.
            Vim hangs when the timer callback uses too much time.
Solution:   Set tr_id to -1 when a timer is to be deleted. Don't keep calling
            callbacks forever. (Ozaki Kiichi)
Files:      src/evalfunc.c, src/ex_cmds2.c, src/structs.h,
            src/proto/ex_cmds2.pro, src/testdir/test_timers.vim


*** ../vim-7.4.2331/src/evalfunc.c      2016-09-04 21:42:32.410056052 +0200
--- src/evalfunc.c      2016-09-05 22:32:11.684576179 +0200
***************
*** 12398,12409 ****
      static void
  f_timer_start(typval_T *argvars, typval_T *rettv)
  {
!     long    msec = (long)get_tv_number(&argvars[0]);
!     timer_T *timer;
!     int           repeat = 0;
!     char_u  *callback;
!     dict_T  *dict;
  
      if (check_secure())
        return;
      if (argvars[2].v_type != VAR_UNKNOWN)
--- 12398,12411 ----
      static void
  f_timer_start(typval_T *argvars, typval_T *rettv)
  {
!     long      msec = (long)get_tv_number(&argvars[0]);
!     timer_T   *timer;
!     int               repeat = 0;
!     char_u    *callback;
!     dict_T    *dict;
!     partial_T *partial;
  
+     rettv->vval.v_number = -1;
      if (check_secure())
        return;
      if (argvars[2].v_type != VAR_UNKNOWN)
***************
*** 12418,12430 ****
            repeat = get_dict_number(dict, (char_u *)"repeat");
      }
  
!     timer = create_timer(msec, repeat);
!     callback = get_callback(&argvars[1], &timer->tr_partial);
      if (callback == NULL)
!     {
!       stop_timer(timer);
!       rettv->vval.v_number = -1;
!     }
      else
      {
        if (timer->tr_partial == NULL)
--- 12420,12432 ----
            repeat = get_dict_number(dict, (char_u *)"repeat");
      }
  
!     callback = get_callback(&argvars[1], &partial);
      if (callback == NULL)
!       return;
! 
!     timer = create_timer(msec, repeat);
!     if (timer == NULL)
!       free_callback(callback, partial);
      else
      {
        if (timer->tr_partial == NULL)
***************
*** 12432,12438 ****
        else
            /* pointer into the partial */
            timer->tr_callback = callback;
!       rettv->vval.v_number = timer->tr_id;
      }
  }
  
--- 12434,12441 ----
        else
            /* pointer into the partial */
            timer->tr_callback = callback;
!       timer->tr_partial = partial;
!       rettv->vval.v_number = (varnumber_T)timer->tr_id;
      }
  }
  
*** ../vim-7.4.2331/src/ex_cmds2.c      2016-09-02 22:18:45.458101130 +0200
--- src/ex_cmds2.c      2016-09-05 22:33:24.691896430 +0200
***************
*** 1088,1097 ****
  
  # if defined(FEAT_TIMERS) || defined(PROTO)
  static timer_T        *first_timer = NULL;
! static int    last_timer_id = 0;
  
! static timer_T        *current_timer = NULL;
! static int    free_current_timer = FALSE;
  
  /*
   * Insert a timer in the list of timers.
--- 1088,1104 ----
  
  # if defined(FEAT_TIMERS) || defined(PROTO)
  static timer_T        *first_timer = NULL;
! static long   last_timer_id = 0;
  
! # ifdef WIN3264
! #  define GET_TIMEDIFF(timer, now) \
!       (long)(((double)(timer->tr_due.QuadPart - now.QuadPart) \
!                                          / (double)fr.QuadPart) * 1000);
! # else
! #  define GET_TIMEDIFF(timer, now) \
!       (timer->tr_due.tv_sec - now.tv_sec) * 1000 \
!                          + (timer->tr_due.tv_usec - now.tv_usec) / 1000;
! # endif
  
  /*
   * Insert a timer in the list of timers.
***************
*** 1124,1136 ****
      static void
  free_timer(timer_T *timer)
  {
!     if (timer == current_timer)
!       free_current_timer = TRUE;
!     else
!     {
!       free_callback(timer->tr_callback, timer->tr_partial);
!       vim_free(timer);
!     }
  }
  
  /*
--- 1131,1138 ----
      static void
  free_timer(timer_T *timer)
  {
!     free_callback(timer->tr_callback, timer->tr_partial);
!     vim_free(timer);
  }
  
  /*
***************
*** 1144,1150 ****
  
      if (timer == NULL)
        return NULL;
!     timer->tr_id = ++last_timer_id;
      insert_timer(timer);
      if (repeat != 0)
        timer->tr_repeat = repeat - 1;
--- 1146,1155 ----
  
      if (timer == NULL)
        return NULL;
!     if (++last_timer_id < 0)
!       /* Overflow!  Might cause duplicates... */
!       last_timer_id = 0;
!     timer->tr_id = last_timer_id;
      insert_timer(timer);
      if (repeat != 0)
        timer->tr_repeat = repeat - 1;
***************
*** 1165,1171 ****
      typval_T  argv[2];
  
      argv[0].v_type = VAR_NUMBER;
!     argv[0].vval.v_number = timer->tr_id;
      argv[1].v_type = VAR_UNKNOWN;
  
      call_func(timer->tr_callback, (int)STRLEN(timer->tr_callback),
--- 1170,1176 ----
      typval_T  argv[2];
  
      argv[0].v_type = VAR_NUMBER;
!     argv[0].vval.v_number = (varnumber_T)timer->tr_id;
      argv[1].v_type = VAR_UNKNOWN;
  
      call_func(timer->tr_callback, (int)STRLEN(timer->tr_callback),
***************
*** 1182,1258 ****
  check_due_timer(void)
  {
      timer_T   *timer;
      long      this_due;
      long      next_due = -1;
      proftime_T        now;
      int               did_one = FALSE;
  # ifdef WIN3264
      LARGE_INTEGER   fr;
  
      QueryPerformanceFrequency(&fr);
  # endif
!     while (!got_int)
      {
!       profile_start(&now);
!       next_due = -1;
!       for (timer = first_timer; timer != NULL; timer = timer->tr_next)
        {
!           if (timer->tr_paused)
!               continue;
! # ifdef WIN3264
!           this_due = (long)(((double)(timer->tr_due.QuadPart - now.QuadPart)
!                                              / (double)fr.QuadPart) * 1000);
! # else
!           this_due = (timer->tr_due.tv_sec - now.tv_sec) * 1000
!                              + (timer->tr_due.tv_usec - now.tv_usec) / 1000;
! # endif
!           if (this_due <= 1)
            {
!               current_timer = timer;
!               free_current_timer = FALSE;
!               timer_callback(timer);
!               current_timer = NULL;
! 
!               did_one = TRUE;
!               if (timer->tr_repeat != 0 && !free_current_timer)
!               {
!                   profile_setlimit(timer->tr_interval, &timer->tr_due);
!                   if (timer->tr_repeat > 0)
!                       --timer->tr_repeat;
!               }
!               else
!               {
!                   remove_timer(timer);
!                   free_timer(timer);
!               }
!               /* the callback may do anything, start all over */
!               break;
            }
-           if (next_due == -1 || next_due > this_due)
-               next_due = this_due;
        }
!       if (timer == NULL)
!           break;
      }
  
      if (did_one)
        redraw_after_callback();
  
!     return next_due;
  }
  
  /*
   * Find a timer by ID.  Returns NULL if not found;
   */
      timer_T *
! find_timer(int id)
  {
      timer_T *timer;
  
!     for (timer = first_timer; timer != NULL; timer = timer->tr_next)
!       if (timer->tr_id == id)
!           break;
!     return timer;
  }
  
  
--- 1187,1262 ----
  check_due_timer(void)
  {
      timer_T   *timer;
+     timer_T   *timer_next;
      long      this_due;
      long      next_due = -1;
      proftime_T        now;
      int               did_one = FALSE;
+     long      current_id = last_timer_id;
  # ifdef WIN3264
      LARGE_INTEGER   fr;
  
      QueryPerformanceFrequency(&fr);
  # endif
!     profile_start(&now);
!     for (timer = first_timer; timer != NULL && !got_int; timer = timer_next)
      {
!       timer_next = timer->tr_next;
! 
!       if (timer->tr_id == -1 || timer->tr_firing || timer->tr_paused)
!           continue;
!       this_due = GET_TIMEDIFF(timer, now);
!       if (this_due <= 1)
        {
!           timer->tr_firing = TRUE;
!           timer_callback(timer);
!           timer->tr_firing = FALSE;
!           timer_next = timer->tr_next;
!           did_one = TRUE;
! 
!           /* Only fire the timer again if it repeats and stop_timer() wasn't
!            * called while inside the callback (tr_id == -1). */
!           if (timer->tr_repeat != 0 && timer->tr_id != -1)
!           {
!               profile_setlimit(timer->tr_interval, &timer->tr_due);
!               this_due = GET_TIMEDIFF(timer, now);
!               if (this_due < 1)
!                   this_due = 1;
!               if (timer->tr_repeat > 0)
!                   --timer->tr_repeat;
!           }
!           else
            {
!               this_due = -1;
!               remove_timer(timer);
!               free_timer(timer);
            }
        }
!       if (this_due > 0 && (next_due == -1 || next_due > this_due))
!           next_due = this_due;
      }
  
      if (did_one)
        redraw_after_callback();
  
!     return current_id != last_timer_id ? 1 : next_due;
  }
  
  /*
   * Find a timer by ID.  Returns NULL if not found;
   */
      timer_T *
! find_timer(long id)
  {
      timer_T *timer;
  
!     if (id >= 0)
!     {
!       for (timer = first_timer; timer != NULL; timer = timer->tr_next)
!           if (timer->tr_id == id)
!               return timer;
!     }
!     return NULL;
  }
  
  
***************
*** 1262,1276 ****
      void
  stop_timer(timer_T *timer)
  {
!     remove_timer(timer);
!     free_timer(timer);
  }
  
      void
  stop_all_timers(void)
  {
!     while (first_timer != NULL)
!       stop_timer(first_timer);
  }
  
      void
--- 1266,1292 ----
      void
  stop_timer(timer_T *timer)
  {
!     if (timer->tr_firing)
!       /* Free the timer after the callback returns. */
!       timer->tr_id = -1;
!     else
!     {
!       remove_timer(timer);
!       free_timer(timer);
!     }
  }
  
      void
  stop_all_timers(void)
  {
!     timer_T *timer;
!     timer_T *timer_next;
! 
!     for (timer = first_timer; timer != NULL; timer = timer_next)
!     {
!       timer_next = timer->tr_next;
!       stop_timer(timer);
!     }
  }
  
      void
***************
*** 1289,1306 ****
        return;
      list_append_dict(list, dict);
  
!     dict_add_nr_str(dict, "id", (long)timer->tr_id, NULL);
      dict_add_nr_str(dict, "time", (long)timer->tr_interval, NULL);
  
      profile_start(&now);
  # ifdef WIN3264
      QueryPerformanceFrequency(&fr);
-     remaining = (long)(((double)(timer->tr_due.QuadPart - now.QuadPart)
-                                              / (double)fr.QuadPart) * 1000);
- # else
-     remaining = (timer->tr_due.tv_sec - now.tv_sec) * 1000
-                              + (timer->tr_due.tv_usec - now.tv_usec) / 1000;
  # endif
      dict_add_nr_str(dict, "remaining", (long)remaining, NULL);
  
      dict_add_nr_str(dict, "repeat",
--- 1305,1318 ----
        return;
      list_append_dict(list, dict);
  
!     dict_add_nr_str(dict, "id", timer->tr_id, NULL);
      dict_add_nr_str(dict, "time", (long)timer->tr_interval, NULL);
  
      profile_start(&now);
  # ifdef WIN3264
      QueryPerformanceFrequency(&fr);
  # endif
+     remaining = GET_TIMEDIFF(timer, now);
      dict_add_nr_str(dict, "remaining", (long)remaining, NULL);
  
      dict_add_nr_str(dict, "repeat",
***************
*** 1333,1339 ****
      timer_T *timer;
  
      for (timer = first_timer; timer != NULL; timer = timer->tr_next)
!       add_timer_info(rettv, timer);
  }
  
  /*
--- 1345,1352 ----
      timer_T *timer;
  
      for (timer = first_timer; timer != NULL; timer = timer->tr_next)
!       if (timer->tr_id != -1)
!           add_timer_info(rettv, timer);
  }
  
  /*
*** ../vim-7.4.2331/src/structs.h       2016-09-04 19:50:50.512985798 +0200
--- src/structs.h       2016-09-05 22:31:12.977124504 +0200
***************
*** 3166,3177 ****
  typedef struct timer_S timer_T;
  struct timer_S
  {
!     int               tr_id;
  #ifdef FEAT_TIMERS
      timer_T   *tr_next;
      timer_T   *tr_prev;
      proftime_T        tr_due;             /* when the callback is to be 
invoked */
!     int               tr_paused;          /* when TRUE callback is not 
invoked */
      int               tr_repeat;          /* number of times to repeat, -1 
forever */
      long      tr_interval;        /* msec */
      char_u    *tr_callback;       /* allocated */
--- 3166,3178 ----
  typedef struct timer_S timer_T;
  struct timer_S
  {
!     long      tr_id;
  #ifdef FEAT_TIMERS
      timer_T   *tr_next;
      timer_T   *tr_prev;
      proftime_T        tr_due;             /* when the callback is to be 
invoked */
!     char      tr_firing;          /* when TRUE callback is being called */
!     char      tr_paused;          /* when TRUE callback is not invoked */
      int               tr_repeat;          /* number of times to repeat, -1 
forever */
      long      tr_interval;        /* msec */
      char_u    *tr_callback;       /* allocated */
*** ../vim-7.4.2331/src/proto/ex_cmds2.pro      2016-08-07 18:22:30.426091047 
+0200
--- src/proto/ex_cmds2.pro      2016-09-05 22:33:47.167687629 +0200
***************
*** 20,26 ****
  void profile_zero(proftime_T *tm);
  timer_T *create_timer(long msec, int repeat);
  long check_due_timer(void);
! timer_T *find_timer(int id);
  void stop_timer(timer_T *timer);
  void stop_all_timers(void);
  void add_timer_info(typval_T *rettv, timer_T *timer);
--- 20,26 ----
  void profile_zero(proftime_T *tm);
  timer_T *create_timer(long msec, int repeat);
  long check_due_timer(void);
! timer_T *find_timer(long id);
  void stop_timer(timer_T *timer);
  void stop_all_timers(void);
  void add_timer_info(typval_T *rettv, timer_T *timer);
*** ../vim-7.4.2331/src/testdir/test_timers.vim 2016-09-01 21:26:16.166308476 
+0200
--- src/testdir/test_timers.vim 2016-09-05 22:42:17.966533913 +0200
***************
*** 143,146 ****
--- 143,176 ----
    call assert_equal([], timer_info(t))
  endfunc
  
+ func StopTimer1(timer)
+   let g:timer2 = timer_start(10, 'StopTimer2')
+   " avoid maxfuncdepth error
+   call timer_pause(g:timer1, 1)
+   sleep 40m
+ endfunc
+ 
+ func StopTimer2(timer)
+   call timer_stop(g:timer1)
+ endfunc
+ 
+ func Test_stop_in_callback()
+   let g:timer1 = timer_start(10, 'StopTimer1')
+   sleep 40m
+ endfunc
+ 
+ func StopTimerAll(timer)
+   call timer_stopall()
+ endfunc
+ 
+ func Test_stop_all_in_callback()
+   let g:timer1 = timer_start(10, 'StopTimerAll')
+   let info = timer_info()
+   call assert_equal(1, len(info))
+   sleep 40m
+   let info = timer_info()
+   call assert_equal(0, len(info))
+ endfunc
+ 
+ 
  " vim: shiftwidth=2 sts=2 expandtab
*** ../vim-7.4.2331/src/version.c       2016-09-05 21:51:10.271270426 +0200
--- src/version.c       2016-09-05 22:07:31.470505700 +0200
***************
*** 765,766 ****
--- 765,768 ----
  {   /* Add new patch number below this line */
+ /**/
+     2332,
  /**/

-- 
hundred-and-one symptoms of being an internet addict:
175. You send yourself e-mail before you go to bed to remind you
     what to do when you wake up.

 /// Bram Moolenaar -- b...@moolenaar.net -- http://www.Moolenaar.net   \\\
///        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\  an exciting new programming language -- http://www.Zimbu.org        ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to vim_dev+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Raspunde prin e-mail lui