comparison src/ex_cmds2.c @ 10122:3db463d4df25 v7.4.2332

commit https://github.com/vim/vim/commit/75537a93e985ef32e6c267b06ce93629855dd983 Author: Bram Moolenaar <Bram@vim.org> Date: Mon Sep 5 22:45:28 2016 +0200 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)
author Christian Brabandt <cb@256bit.org>
date Mon, 05 Sep 2016 23:00:07 +0200
parents 32bd4001e398
children 40f6ce4fe30e
comparison
equal deleted inserted replaced
10121:02ba0b8a13fb 10122:3db463d4df25
1086 1086
1087 # endif /* FEAT_PROFILE || FEAT_RELTIME */ 1087 # endif /* FEAT_PROFILE || FEAT_RELTIME */
1088 1088
1089 # if defined(FEAT_TIMERS) || defined(PROTO) 1089 # if defined(FEAT_TIMERS) || defined(PROTO)
1090 static timer_T *first_timer = NULL; 1090 static timer_T *first_timer = NULL;
1091 static int last_timer_id = 0; 1091 static long last_timer_id = 0;
1092 1092
1093 static timer_T *current_timer = NULL; 1093 # ifdef WIN3264
1094 static int free_current_timer = FALSE; 1094 # define GET_TIMEDIFF(timer, now) \
1095 (long)(((double)(timer->tr_due.QuadPart - now.QuadPart) \
1096 / (double)fr.QuadPart) * 1000);
1097 # else
1098 # define GET_TIMEDIFF(timer, now) \
1099 (timer->tr_due.tv_sec - now.tv_sec) * 1000 \
1100 + (timer->tr_due.tv_usec - now.tv_usec) / 1000;
1101 # endif
1095 1102
1096 /* 1103 /*
1097 * Insert a timer in the list of timers. 1104 * Insert a timer in the list of timers.
1098 */ 1105 */
1099 static void 1106 static void
1122 } 1129 }
1123 1130
1124 static void 1131 static void
1125 free_timer(timer_T *timer) 1132 free_timer(timer_T *timer)
1126 { 1133 {
1127 if (timer == current_timer) 1134 free_callback(timer->tr_callback, timer->tr_partial);
1128 free_current_timer = TRUE; 1135 vim_free(timer);
1129 else
1130 {
1131 free_callback(timer->tr_callback, timer->tr_partial);
1132 vim_free(timer);
1133 }
1134 } 1136 }
1135 1137
1136 /* 1138 /*
1137 * Create a timer and return it. NULL if out of memory. 1139 * Create a timer and return it. NULL if out of memory.
1138 * Caller should set the callback. 1140 * Caller should set the callback.
1142 { 1144 {
1143 timer_T *timer = (timer_T *)alloc_clear(sizeof(timer_T)); 1145 timer_T *timer = (timer_T *)alloc_clear(sizeof(timer_T));
1144 1146
1145 if (timer == NULL) 1147 if (timer == NULL)
1146 return NULL; 1148 return NULL;
1147 timer->tr_id = ++last_timer_id; 1149 if (++last_timer_id < 0)
1150 /* Overflow! Might cause duplicates... */
1151 last_timer_id = 0;
1152 timer->tr_id = last_timer_id;
1148 insert_timer(timer); 1153 insert_timer(timer);
1149 if (repeat != 0) 1154 if (repeat != 0)
1150 timer->tr_repeat = repeat - 1; 1155 timer->tr_repeat = repeat - 1;
1151 timer->tr_interval = msec; 1156 timer->tr_interval = msec;
1152 1157
1163 typval_T rettv; 1168 typval_T rettv;
1164 int dummy; 1169 int dummy;
1165 typval_T argv[2]; 1170 typval_T argv[2];
1166 1171
1167 argv[0].v_type = VAR_NUMBER; 1172 argv[0].v_type = VAR_NUMBER;
1168 argv[0].vval.v_number = timer->tr_id; 1173 argv[0].vval.v_number = (varnumber_T)timer->tr_id;
1169 argv[1].v_type = VAR_UNKNOWN; 1174 argv[1].v_type = VAR_UNKNOWN;
1170 1175
1171 call_func(timer->tr_callback, (int)STRLEN(timer->tr_callback), 1176 call_func(timer->tr_callback, (int)STRLEN(timer->tr_callback),
1172 &rettv, 1, argv, NULL, 0L, 0L, &dummy, TRUE, 1177 &rettv, 1, argv, NULL, 0L, 0L, &dummy, TRUE,
1173 timer->tr_partial, NULL); 1178 timer->tr_partial, NULL);
1180 */ 1185 */
1181 long 1186 long
1182 check_due_timer(void) 1187 check_due_timer(void)
1183 { 1188 {
1184 timer_T *timer; 1189 timer_T *timer;
1190 timer_T *timer_next;
1185 long this_due; 1191 long this_due;
1186 long next_due = -1; 1192 long next_due = -1;
1187 proftime_T now; 1193 proftime_T now;
1188 int did_one = FALSE; 1194 int did_one = FALSE;
1195 long current_id = last_timer_id;
1189 # ifdef WIN3264 1196 # ifdef WIN3264
1190 LARGE_INTEGER fr; 1197 LARGE_INTEGER fr;
1191 1198
1192 QueryPerformanceFrequency(&fr); 1199 QueryPerformanceFrequency(&fr);
1193 # endif 1200 # endif
1194 while (!got_int) 1201 profile_start(&now);
1195 { 1202 for (timer = first_timer; timer != NULL && !got_int; timer = timer_next)
1196 profile_start(&now); 1203 {
1197 next_due = -1; 1204 timer_next = timer->tr_next;
1198 for (timer = first_timer; timer != NULL; timer = timer->tr_next) 1205
1199 { 1206 if (timer->tr_id == -1 || timer->tr_firing || timer->tr_paused)
1200 if (timer->tr_paused) 1207 continue;
1201 continue; 1208 this_due = GET_TIMEDIFF(timer, now);
1202 # ifdef WIN3264 1209 if (this_due <= 1)
1203 this_due = (long)(((double)(timer->tr_due.QuadPart - now.QuadPart) 1210 {
1204 / (double)fr.QuadPart) * 1000); 1211 timer->tr_firing = TRUE;
1205 # else 1212 timer_callback(timer);
1206 this_due = (timer->tr_due.tv_sec - now.tv_sec) * 1000 1213 timer->tr_firing = FALSE;
1207 + (timer->tr_due.tv_usec - now.tv_usec) / 1000; 1214 timer_next = timer->tr_next;
1208 # endif 1215 did_one = TRUE;
1209 if (this_due <= 1) 1216
1217 /* Only fire the timer again if it repeats and stop_timer() wasn't
1218 * called while inside the callback (tr_id == -1). */
1219 if (timer->tr_repeat != 0 && timer->tr_id != -1)
1210 { 1220 {
1211 current_timer = timer; 1221 profile_setlimit(timer->tr_interval, &timer->tr_due);
1212 free_current_timer = FALSE; 1222 this_due = GET_TIMEDIFF(timer, now);
1213 timer_callback(timer); 1223 if (this_due < 1)
1214 current_timer = NULL; 1224 this_due = 1;
1215 1225 if (timer->tr_repeat > 0)
1216 did_one = TRUE; 1226 --timer->tr_repeat;
1217 if (timer->tr_repeat != 0 && !free_current_timer)
1218 {
1219 profile_setlimit(timer->tr_interval, &timer->tr_due);
1220 if (timer->tr_repeat > 0)
1221 --timer->tr_repeat;
1222 }
1223 else
1224 {
1225 remove_timer(timer);
1226 free_timer(timer);
1227 }
1228 /* the callback may do anything, start all over */
1229 break;
1230 } 1227 }
1231 if (next_due == -1 || next_due > this_due) 1228 else
1232 next_due = this_due; 1229 {
1233 } 1230 this_due = -1;
1234 if (timer == NULL) 1231 remove_timer(timer);
1235 break; 1232 free_timer(timer);
1233 }
1234 }
1235 if (this_due > 0 && (next_due == -1 || next_due > this_due))
1236 next_due = this_due;
1236 } 1237 }
1237 1238
1238 if (did_one) 1239 if (did_one)
1239 redraw_after_callback(); 1240 redraw_after_callback();
1240 1241
1241 return next_due; 1242 return current_id != last_timer_id ? 1 : next_due;
1242 } 1243 }
1243 1244
1244 /* 1245 /*
1245 * Find a timer by ID. Returns NULL if not found; 1246 * Find a timer by ID. Returns NULL if not found;
1246 */ 1247 */
1247 timer_T * 1248 timer_T *
1248 find_timer(int id) 1249 find_timer(long id)
1249 { 1250 {
1250 timer_T *timer; 1251 timer_T *timer;
1251 1252
1252 for (timer = first_timer; timer != NULL; timer = timer->tr_next) 1253 if (id >= 0)
1253 if (timer->tr_id == id) 1254 {
1254 break; 1255 for (timer = first_timer; timer != NULL; timer = timer->tr_next)
1255 return timer; 1256 if (timer->tr_id == id)
1257 return timer;
1258 }
1259 return NULL;
1256 } 1260 }
1257 1261
1258 1262
1259 /* 1263 /*
1260 * Stop a timer and delete it. 1264 * Stop a timer and delete it.
1261 */ 1265 */
1262 void 1266 void
1263 stop_timer(timer_T *timer) 1267 stop_timer(timer_T *timer)
1264 { 1268 {
1265 remove_timer(timer); 1269 if (timer->tr_firing)
1266 free_timer(timer); 1270 /* Free the timer after the callback returns. */
1271 timer->tr_id = -1;
1272 else
1273 {
1274 remove_timer(timer);
1275 free_timer(timer);
1276 }
1267 } 1277 }
1268 1278
1269 void 1279 void
1270 stop_all_timers(void) 1280 stop_all_timers(void)
1271 { 1281 {
1272 while (first_timer != NULL) 1282 timer_T *timer;
1273 stop_timer(first_timer); 1283 timer_T *timer_next;
1284
1285 for (timer = first_timer; timer != NULL; timer = timer_next)
1286 {
1287 timer_next = timer->tr_next;
1288 stop_timer(timer);
1289 }
1274 } 1290 }
1275 1291
1276 void 1292 void
1277 add_timer_info(typval_T *rettv, timer_T *timer) 1293 add_timer_info(typval_T *rettv, timer_T *timer)
1278 { 1294 {
1287 1303
1288 if (dict == NULL) 1304 if (dict == NULL)
1289 return; 1305 return;
1290 list_append_dict(list, dict); 1306 list_append_dict(list, dict);
1291 1307
1292 dict_add_nr_str(dict, "id", (long)timer->tr_id, NULL); 1308 dict_add_nr_str(dict, "id", timer->tr_id, NULL);
1293 dict_add_nr_str(dict, "time", (long)timer->tr_interval, NULL); 1309 dict_add_nr_str(dict, "time", (long)timer->tr_interval, NULL);
1294 1310
1295 profile_start(&now); 1311 profile_start(&now);
1296 # ifdef WIN3264 1312 # ifdef WIN3264
1297 QueryPerformanceFrequency(&fr); 1313 QueryPerformanceFrequency(&fr);
1298 remaining = (long)(((double)(timer->tr_due.QuadPart - now.QuadPart)
1299 / (double)fr.QuadPart) * 1000);
1300 # else
1301 remaining = (timer->tr_due.tv_sec - now.tv_sec) * 1000
1302 + (timer->tr_due.tv_usec - now.tv_usec) / 1000;
1303 # endif 1314 # endif
1315 remaining = GET_TIMEDIFF(timer, now);
1304 dict_add_nr_str(dict, "remaining", (long)remaining, NULL); 1316 dict_add_nr_str(dict, "remaining", (long)remaining, NULL);
1305 1317
1306 dict_add_nr_str(dict, "repeat", 1318 dict_add_nr_str(dict, "repeat",
1307 (long)(timer->tr_repeat < 0 ? -1 : timer->tr_repeat + 1), NULL); 1319 (long)(timer->tr_repeat < 0 ? -1 : timer->tr_repeat + 1), NULL);
1308 dict_add_nr_str(dict, "paused", (long)(timer->tr_paused), NULL); 1320 dict_add_nr_str(dict, "paused", (long)(timer->tr_paused), NULL);
1331 add_timer_info_all(typval_T *rettv) 1343 add_timer_info_all(typval_T *rettv)
1332 { 1344 {
1333 timer_T *timer; 1345 timer_T *timer;
1334 1346
1335 for (timer = first_timer; timer != NULL; timer = timer->tr_next) 1347 for (timer = first_timer; timer != NULL; timer = timer->tr_next)
1336 add_timer_info(rettv, timer); 1348 if (timer->tr_id != -1)
1349 add_timer_info(rettv, timer);
1337 } 1350 }
1338 1351
1339 /* 1352 /*
1340 * Mark references in partials of timers. 1353 * Mark references in partials of timers.
1341 */ 1354 */