comparison src/channel.c @ 15675:01890a3caefd v8.1.0845

patch 8.1.0845: having job_status() free the job causes problems commit https://github.com/vim/vim/commit/2a4857a1fcf1d188e5b985ac21bcfc532eddde94 Author: Bram Moolenaar <Bram@vim.org> Date: Tue Jan 29 22:29:07 2019 +0100 patch 8.1.0845: having job_status() free the job causes problems Problem: Having job_status() free the job causes problems. Solution: Do not actually free the job or terminal yet, put it in a list and free it a bit later. Do not use a terminal after checking the job status. (closes #3873)
author Bram Moolenaar <Bram@vim.org>
date Tue, 29 Jan 2019 22:30:06 +0100
parents b6f11ff3b6d1
children 287104a1d51e
comparison
equal deleted inserted replaced
15674:9045c248e9eb 15675:01890a3caefd
5159 vim_free(job->jv_argv[i]); 5159 vim_free(job->jv_argv[i]);
5160 vim_free(job->jv_argv); 5160 vim_free(job->jv_argv);
5161 } 5161 }
5162 } 5162 }
5163 5163
5164 /*
5165 * Remove "job" from the list of jobs.
5166 */
5164 static void 5167 static void
5165 job_free_job(job_T *job) 5168 job_unlink(job_T *job)
5166 { 5169 {
5167 if (job->jv_next != NULL) 5170 if (job->jv_next != NULL)
5168 job->jv_next->jv_prev = job->jv_prev; 5171 job->jv_next->jv_prev = job->jv_prev;
5169 if (job->jv_prev == NULL) 5172 if (job->jv_prev == NULL)
5170 first_job = job->jv_next; 5173 first_job = job->jv_next;
5171 else 5174 else
5172 job->jv_prev->jv_next = job->jv_next; 5175 job->jv_prev->jv_next = job->jv_next;
5176 }
5177
5178 static void
5179 job_free_job(job_T *job)
5180 {
5181 job_unlink(job);
5173 vim_free(job); 5182 vim_free(job);
5174 } 5183 }
5175 5184
5176 static void 5185 static void
5177 job_free(job_T *job) 5186 job_free(job_T *job)
5178 { 5187 {
5179 if (!in_free_unref_items) 5188 if (!in_free_unref_items)
5180 { 5189 {
5181 job_free_contents(job); 5190 job_free_contents(job);
5182 job_free_job(job); 5191 job_free_job(job);
5192 }
5193 }
5194
5195 job_T *jobs_to_free = NULL;
5196
5197 /*
5198 * Put "job" in a list to be freed later, when it's no longer referenced.
5199 */
5200 static void
5201 job_free_later(job_T *job)
5202 {
5203 job_unlink(job);
5204 job->jv_next = jobs_to_free;
5205 jobs_to_free = job;
5206 }
5207
5208 static void
5209 free_jobs_to_free_later(void)
5210 {
5211 job_T *job;
5212
5213 while (jobs_to_free != NULL)
5214 {
5215 job = jobs_to_free;
5216 jobs_to_free = job->jv_next;
5217 job_free_contents(job);
5218 vim_free(job);
5183 } 5219 }
5184 } 5220 }
5185 5221
5186 #if defined(EXITFREE) || defined(PROTO) 5222 #if defined(EXITFREE) || defined(PROTO)
5187 void 5223 void
5188 job_free_all(void) 5224 job_free_all(void)
5189 { 5225 {
5190 while (first_job != NULL) 5226 while (first_job != NULL)
5191 job_free(first_job); 5227 job_free(first_job);
5228 free_jobs_to_free_later();
5229
5230 # ifdef FEAT_TERMINAL
5231 free_unused_terminals();
5232 # endif
5192 } 5233 }
5193 #endif 5234 #endif
5194 5235
5195 /* 5236 /*
5196 * Return TRUE if we need to check if the process of "job" has ended. 5237 * Return TRUE if we need to check if the process of "job" has ended.
5357 5398
5358 /* 5399 /*
5359 * NOTE: Must call job_cleanup() only once right after the status of "job" 5400 * NOTE: Must call job_cleanup() only once right after the status of "job"
5360 * changed to JOB_ENDED (i.e. after job_status() returned "dead" first or 5401 * changed to JOB_ENDED (i.e. after job_status() returned "dead" first or
5361 * mch_detect_ended_job() returned non-NULL). 5402 * mch_detect_ended_job() returned non-NULL).
5403 * If the job is no longer used it will be removed from the list of jobs, and
5404 * deleted a bit later.
5362 */ 5405 */
5363 void 5406 void
5364 job_cleanup(job_T *job) 5407 job_cleanup(job_T *job)
5365 { 5408 {
5366 if (job->jv_status != JOB_ENDED) 5409 if (job->jv_status != JOB_ENDED)
5392 clear_tv(&rettv); 5435 clear_tv(&rettv);
5393 --job->jv_refcount; 5436 --job->jv_refcount;
5394 channel_need_redraw = TRUE; 5437 channel_need_redraw = TRUE;
5395 } 5438 }
5396 5439
5397 /* Do not free the job in case the close callback of the associated channel 5440 // Do not free the job in case the close callback of the associated channel
5398 * isn't invoked yet and may get information by job_info(). */ 5441 // isn't invoked yet and may get information by job_info().
5399 if (job->jv_refcount == 0 && !job_channel_still_useful(job)) 5442 if (job->jv_refcount == 0 && !job_channel_still_useful(job))
5400 { 5443 // The job was already unreferenced and the associated channel was
5401 /* The job was already unreferenced and the associated channel was 5444 // detached, now that it ended it can be freed. However, a caller might
5402 * detached, now that it ended it can be freed. Careful: caller must 5445 // still use it, thus free it a bit later.
5403 * not use "job" after this! */ 5446 job_free_later(job);
5404 job_free(job);
5405 }
5406 } 5447 }
5407 5448
5408 /* 5449 /*
5409 * Mark references in jobs that are still useful. 5450 * Mark references in jobs that are still useful.
5410 */ 5451 */
5607 job_T *job = mch_detect_ended_job(first_job); 5648 job_T *job = mch_detect_ended_job(first_job);
5608 5649
5609 if (job == NULL) 5650 if (job == NULL)
5610 break; 5651 break;
5611 did_end = TRUE; 5652 did_end = TRUE;
5612 job_cleanup(job); // may free "job" 5653 job_cleanup(job); // may add "job" to jobs_to_free
5613 } 5654 }
5655
5656 // Actually free jobs that were cleaned up.
5657 free_jobs_to_free_later();
5614 5658
5615 if (channel_need_redraw) 5659 if (channel_need_redraw)
5616 { 5660 {
5617 channel_need_redraw = FALSE; 5661 channel_need_redraw = FALSE;
5618 redraw_after_callback(TRUE); 5662 redraw_after_callback(TRUE);