Mercurial > vim
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 */ |