Mercurial > vim
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); |