diff options
| author | Tom Tromey | 2012-08-15 13:19:24 -0600 |
|---|---|---|
| committer | Tom Tromey | 2012-08-15 13:19:24 -0600 |
| commit | 6c0d5ae50789673f53c834084bbe1f62f5a62731 (patch) | |
| tree | 0f976b74b292b2a4714470eebb2ce39548d47678 /src/process.c | |
| parent | aa14ccd1e2edec2735f9200a4f2e5eee3b0abe09 (diff) | |
| download | emacs-6c0d5ae50789673f53c834084bbe1f62f5a62731.tar.gz emacs-6c0d5ae50789673f53c834084bbe1f62f5a62731.zip | |
process changes
This changes wait_reading_process_output to handle threads better. It
introduces a wrapper for select that releases the global lock, and it
ensures that only a single thread can select a given file descriptor
at a time.
This also adds the thread-locking feature to processes. By default a
process can only have its output accepted by the thread that created
it. This can be changed using set-process-thread. (If the thread
exits, the process is again available for waiting by any thread.)
Note that thread-signal will not currently interrupt a thread blocked
on select. I'll fix this later.
Diffstat (limited to 'src/process.c')
| -rw-r--r-- | src/process.c | 175 |
1 files changed, 147 insertions, 28 deletions
diff --git a/src/process.c b/src/process.c index 0d3355512b8..ada673e3c34 100644 --- a/src/process.c +++ b/src/process.c | |||
| @@ -335,6 +335,13 @@ static struct fd_callback_data | |||
| 335 | void *data; | 335 | void *data; |
| 336 | /* Flags from enum fd_bits. */ | 336 | /* Flags from enum fd_bits. */ |
| 337 | int flags; | 337 | int flags; |
| 338 | /* If this fd is locked to a certain thread, this points to it. | ||
| 339 | Otherwise, this is NULL. If an fd is locked to a thread, then | ||
| 340 | only that thread is permitted to wait on it. */ | ||
| 341 | struct thread_state *thread; | ||
| 342 | /* If this fd is currently being selected on by a thread, this | ||
| 343 | points to the thread. Otherwise it is NULL. */ | ||
| 344 | struct thread_state *waiting_thread; | ||
| 338 | } fd_callback_info[MAXDESC]; | 345 | } fd_callback_info[MAXDESC]; |
| 339 | 346 | ||
| 340 | 347 | ||
| @@ -451,8 +458,17 @@ compute_input_wait_mask (SELECT_TYPE *mask) | |||
| 451 | FD_ZERO (mask); | 458 | FD_ZERO (mask); |
| 452 | for (fd = 0; fd < max (max_process_desc, max_input_desc); ++fd) | 459 | for (fd = 0; fd < max (max_process_desc, max_input_desc); ++fd) |
| 453 | { | 460 | { |
| 461 | if (fd_callback_info[fd].thread != NULL | ||
| 462 | && fd_callback_info[fd].thread != current_thread) | ||
| 463 | continue; | ||
| 464 | if (fd_callback_info[fd].waiting_thread != NULL | ||
| 465 | && fd_callback_info[fd].waiting_thread != current_thread) | ||
| 466 | continue; | ||
| 454 | if ((fd_callback_info[fd].flags & FOR_READ) != 0) | 467 | if ((fd_callback_info[fd].flags & FOR_READ) != 0) |
| 455 | FD_SET (fd, mask); | 468 | { |
| 469 | FD_SET (fd, mask); | ||
| 470 | fd_callback_info[fd].waiting_thread = current_thread; | ||
| 471 | } | ||
| 456 | } | 472 | } |
| 457 | } | 473 | } |
| 458 | 474 | ||
| @@ -464,9 +480,18 @@ compute_non_process_wait_mask (SELECT_TYPE *mask) | |||
| 464 | FD_ZERO (mask); | 480 | FD_ZERO (mask); |
| 465 | for (fd = 0; fd < max (max_process_desc, max_input_desc); ++fd) | 481 | for (fd = 0; fd < max (max_process_desc, max_input_desc); ++fd) |
| 466 | { | 482 | { |
| 483 | if (fd_callback_info[fd].thread != NULL | ||
| 484 | && fd_callback_info[fd].thread != current_thread) | ||
| 485 | continue; | ||
| 486 | if (fd_callback_info[fd].waiting_thread != NULL | ||
| 487 | && fd_callback_info[fd].waiting_thread != current_thread) | ||
| 488 | continue; | ||
| 467 | if ((fd_callback_info[fd].flags & FOR_READ) != 0 | 489 | if ((fd_callback_info[fd].flags & FOR_READ) != 0 |
| 468 | && (fd_callback_info[fd].flags & PROCESS_FD) == 0) | 490 | && (fd_callback_info[fd].flags & PROCESS_FD) == 0) |
| 469 | FD_SET (fd, mask); | 491 | { |
| 492 | FD_SET (fd, mask); | ||
| 493 | fd_callback_info[fd].waiting_thread = current_thread; | ||
| 494 | } | ||
| 470 | } | 495 | } |
| 471 | } | 496 | } |
| 472 | 497 | ||
| @@ -478,9 +503,18 @@ compute_non_keyboard_wait_mask (SELECT_TYPE *mask) | |||
| 478 | FD_ZERO (mask); | 503 | FD_ZERO (mask); |
| 479 | for (fd = 0; fd < max (max_process_desc, max_input_desc); ++fd) | 504 | for (fd = 0; fd < max (max_process_desc, max_input_desc); ++fd) |
| 480 | { | 505 | { |
| 506 | if (fd_callback_info[fd].thread != NULL | ||
| 507 | && fd_callback_info[fd].thread != current_thread) | ||
| 508 | continue; | ||
| 509 | if (fd_callback_info[fd].waiting_thread != NULL | ||
| 510 | && fd_callback_info[fd].waiting_thread != current_thread) | ||
| 511 | continue; | ||
| 481 | if ((fd_callback_info[fd].flags & FOR_READ) != 0 | 512 | if ((fd_callback_info[fd].flags & FOR_READ) != 0 |
| 482 | && (fd_callback_info[fd].flags & KEYBOARD_FD) == 0) | 513 | && (fd_callback_info[fd].flags & KEYBOARD_FD) == 0) |
| 483 | FD_SET (fd, mask); | 514 | { |
| 515 | FD_SET (fd, mask); | ||
| 516 | fd_callback_info[fd].waiting_thread = current_thread; | ||
| 517 | } | ||
| 484 | } | 518 | } |
| 485 | } | 519 | } |
| 486 | 520 | ||
| @@ -492,12 +526,31 @@ compute_write_mask (SELECT_TYPE *mask) | |||
| 492 | FD_ZERO (mask); | 526 | FD_ZERO (mask); |
| 493 | for (fd = 0; fd < max (max_process_desc, max_input_desc); ++fd) | 527 | for (fd = 0; fd < max (max_process_desc, max_input_desc); ++fd) |
| 494 | { | 528 | { |
| 529 | if (fd_callback_info[fd].thread != NULL | ||
| 530 | && fd_callback_info[fd].thread != current_thread) | ||
| 531 | continue; | ||
| 532 | if (fd_callback_info[fd].waiting_thread != NULL | ||
| 533 | && fd_callback_info[fd].waiting_thread != current_thread) | ||
| 534 | continue; | ||
| 495 | if ((fd_callback_info[fd].flags & FOR_WRITE) != 0) | 535 | if ((fd_callback_info[fd].flags & FOR_WRITE) != 0) |
| 496 | FD_SET (fd, mask); | 536 | { |
| 537 | FD_SET (fd, mask); | ||
| 538 | fd_callback_info[fd].waiting_thread = current_thread; | ||
| 539 | } | ||
| 497 | } | 540 | } |
| 498 | } | 541 | } |
| 499 | 542 | ||
| 543 | static void | ||
| 544 | clear_waiting_thread_info (void) | ||
| 545 | { | ||
| 546 | int fd; | ||
| 500 | 547 | ||
| 548 | for (fd = 0; fd < max (max_process_desc, max_input_desc); ++fd) | ||
| 549 | { | ||
| 550 | if (fd_callback_info[fd].waiting_thread == current_thread) | ||
| 551 | fd_callback_info[fd].waiting_thread = NULL; | ||
| 552 | } | ||
| 553 | } | ||
| 501 | 554 | ||
| 502 | 555 | ||
| 503 | /* Compute the Lisp form of the process status, p->status, from | 556 | /* Compute the Lisp form of the process status, p->status, from |
| @@ -709,6 +762,7 @@ make_process (Lisp_Object name) | |||
| 709 | Lisp data to nil, so do it only for slots which should not be nil. */ | 762 | Lisp data to nil, so do it only for slots which should not be nil. */ |
| 710 | PSET (p, status, Qrun); | 763 | PSET (p, status, Qrun); |
| 711 | PSET (p, mark, Fmake_marker ()); | 764 | PSET (p, mark, Fmake_marker ()); |
| 765 | PSET (p, thread, Fcurrent_thread ()); | ||
| 712 | 766 | ||
| 713 | /* Initialize non-Lisp data. Note that allocate_process zeroes out all | 767 | /* Initialize non-Lisp data. Note that allocate_process zeroes out all |
| 714 | non-Lisp data, so do it only for slots which should not be zero. */ | 768 | non-Lisp data, so do it only for slots which should not be zero. */ |
| @@ -746,6 +800,27 @@ remove_process (register Lisp_Object proc) | |||
| 746 | deactivate_process (proc); | 800 | deactivate_process (proc); |
| 747 | } | 801 | } |
| 748 | 802 | ||
| 803 | void | ||
| 804 | update_processes_for_thread_death (Lisp_Object dying_thread) | ||
| 805 | { | ||
| 806 | Lisp_Object pair; | ||
| 807 | |||
| 808 | for (pair = Vprocess_alist; !NILP (pair); pair = XCDR (pair)) | ||
| 809 | { | ||
| 810 | Lisp_Object process = XCDR (XCAR (pair)); | ||
| 811 | if (EQ (XPROCESS (process)->thread, dying_thread)) | ||
| 812 | { | ||
| 813 | struct Lisp_Process *proc = XPROCESS (process); | ||
| 814 | |||
| 815 | proc->thread = Qnil; | ||
| 816 | if (proc->infd >= 0) | ||
| 817 | fd_callback_info[proc->infd].thread = NULL; | ||
| 818 | if (proc->outfd >= 0) | ||
| 819 | fd_callback_info[proc->outfd].thread = NULL; | ||
| 820 | } | ||
| 821 | } | ||
| 822 | } | ||
| 823 | |||
| 749 | 824 | ||
| 750 | DEFUN ("processp", Fprocessp, Sprocessp, 1, 1, 0, | 825 | DEFUN ("processp", Fprocessp, Sprocessp, 1, 1, 0, |
| 751 | doc: /* Return t if OBJECT is a process. */) | 826 | doc: /* Return t if OBJECT is a process. */) |
| @@ -1094,6 +1169,42 @@ See `set-process-sentinel' for more info on sentinels. */) | |||
| 1094 | return XPROCESS (process)->sentinel; | 1169 | return XPROCESS (process)->sentinel; |
| 1095 | } | 1170 | } |
| 1096 | 1171 | ||
| 1172 | DEFUN ("set-process-thread", Fset_process_thread, Sset_process_thread, | ||
| 1173 | 2, 2, 0, | ||
| 1174 | doc: /* FIXME */) | ||
| 1175 | (Lisp_Object process, Lisp_Object thread) | ||
| 1176 | { | ||
| 1177 | struct Lisp_Process *proc; | ||
| 1178 | struct thread_state *tstate; | ||
| 1179 | |||
| 1180 | CHECK_PROCESS (process); | ||
| 1181 | if (NILP (thread)) | ||
| 1182 | tstate = NULL; | ||
| 1183 | else | ||
| 1184 | { | ||
| 1185 | CHECK_THREAD (thread); | ||
| 1186 | tstate = XTHREAD (thread); | ||
| 1187 | } | ||
| 1188 | |||
| 1189 | proc = XPROCESS (process); | ||
| 1190 | proc->thread = thread; | ||
| 1191 | if (proc->infd >= 0) | ||
| 1192 | fd_callback_info[proc->infd].thread = tstate; | ||
| 1193 | if (proc->outfd >= 0) | ||
| 1194 | fd_callback_info[proc->outfd].thread = tstate; | ||
| 1195 | |||
| 1196 | return thread; | ||
| 1197 | } | ||
| 1198 | |||
| 1199 | DEFUN ("process-thread", Fprocess_thread, Sprocess_thread, | ||
| 1200 | 1, 1, 0, | ||
| 1201 | doc: /* FIXME */) | ||
| 1202 | (Lisp_Object process) | ||
| 1203 | { | ||
| 1204 | CHECK_PROCESS (process); | ||
| 1205 | return XPROCESS (process)->thread; | ||
| 1206 | } | ||
| 1207 | |||
| 1097 | DEFUN ("set-process-window-size", Fset_process_window_size, | 1208 | DEFUN ("set-process-window-size", Fset_process_window_size, |
| 1098 | Sset_process_window_size, 3, 3, 0, | 1209 | Sset_process_window_size, 3, 3, 0, |
| 1099 | doc: /* Tell PROCESS that it has logical window size HEIGHT and WIDTH. */) | 1210 | doc: /* Tell PROCESS that it has logical window size HEIGHT and WIDTH. */) |
| @@ -3993,7 +4104,17 @@ Return non-nil if we received any output before the timeout expired. */) | |||
| 3993 | int nsecs; | 4104 | int nsecs; |
| 3994 | 4105 | ||
| 3995 | if (! NILP (process)) | 4106 | if (! NILP (process)) |
| 3996 | CHECK_PROCESS (process); | 4107 | { |
| 4108 | struct Lisp_Process *procp; | ||
| 4109 | |||
| 4110 | CHECK_PROCESS (process); | ||
| 4111 | procp = XPROCESS (process); | ||
| 4112 | |||
| 4113 | /* Can't wait for a process that is dedicated to a different | ||
| 4114 | thread. */ | ||
| 4115 | if (!EQ (procp->thread, Qnil) && !EQ (procp->thread, Fcurrent_thread ())) | ||
| 4116 | error ("FIXME"); | ||
| 4117 | } | ||
| 3997 | else | 4118 | else |
| 3998 | just_this_one = Qnil; | 4119 | just_this_one = Qnil; |
| 3999 | 4120 | ||
| @@ -4249,20 +4370,10 @@ server_accept_connection (Lisp_Object server, int channel) | |||
| 4249 | build_string ("\n"))); | 4370 | build_string ("\n"))); |
| 4250 | } | 4371 | } |
| 4251 | 4372 | ||
| 4252 | /* This variable is different from waiting_for_input in keyboard.c. | ||
| 4253 | It is used to communicate to a lisp process-filter/sentinel (via the | ||
| 4254 | function Fwaiting_for_user_input_p below) whether Emacs was waiting | ||
| 4255 | for user-input when that process-filter was called. | ||
| 4256 | waiting_for_input cannot be used as that is by definition 0 when | ||
| 4257 | lisp code is being evalled. | ||
| 4258 | This is also used in record_asynch_buffer_change. | ||
| 4259 | For that purpose, this must be 0 | ||
| 4260 | when not inside wait_reading_process_output. */ | ||
| 4261 | static int waiting_for_user_input_p; | ||
| 4262 | |||
| 4263 | static Lisp_Object | 4373 | static Lisp_Object |
| 4264 | wait_reading_process_output_unwind (Lisp_Object data) | 4374 | wait_reading_process_output_unwind (Lisp_Object data) |
| 4265 | { | 4375 | { |
| 4376 | clear_waiting_thread_info (); | ||
| 4266 | waiting_for_user_input_p = XINT (data); | 4377 | waiting_for_user_input_p = XINT (data); |
| 4267 | return Qnil; | 4378 | return Qnil; |
| 4268 | } | 4379 | } |
| @@ -4329,6 +4440,10 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd, | |||
| 4329 | int got_some_input = 0; | 4440 | int got_some_input = 0; |
| 4330 | ptrdiff_t count = SPECPDL_INDEX (); | 4441 | ptrdiff_t count = SPECPDL_INDEX (); |
| 4331 | 4442 | ||
| 4443 | eassert (wait_proc == NULL | ||
| 4444 | || EQ (wait_proc->thread, Qnil) | ||
| 4445 | || XTHREAD (wait_proc->thread) == current_thread); | ||
| 4446 | |||
| 4332 | FD_ZERO (&Available); | 4447 | FD_ZERO (&Available); |
| 4333 | FD_ZERO (&Writeok); | 4448 | FD_ZERO (&Writeok); |
| 4334 | 4449 | ||
| @@ -4484,14 +4599,15 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd, | |||
| 4484 | compute_write_mask (&Ctemp); | 4599 | compute_write_mask (&Ctemp); |
| 4485 | 4600 | ||
| 4486 | timeout = make_emacs_time (0, 0); | 4601 | timeout = make_emacs_time (0, 0); |
| 4487 | if ((pselect (max (max_process_desc, max_input_desc) + 1, | 4602 | if ((thread_select (pselect, |
| 4488 | &Atemp, | 4603 | max (max_process_desc, max_input_desc) + 1, |
| 4604 | &Atemp, | ||
| 4489 | #ifdef NON_BLOCKING_CONNECT | 4605 | #ifdef NON_BLOCKING_CONNECT |
| 4490 | (num_pending_connects > 0 ? &Ctemp : NULL), | 4606 | (num_pending_connects > 0 ? &Ctemp : NULL), |
| 4491 | #else | 4607 | #else |
| 4492 | NULL, | 4608 | NULL, |
| 4493 | #endif | 4609 | #endif |
| 4494 | NULL, &timeout, NULL) | 4610 | NULL, &timeout, NULL) |
| 4495 | <= 0)) | 4611 | <= 0)) |
| 4496 | { | 4612 | { |
| 4497 | /* It's okay for us to do this and then continue with | 4613 | /* It's okay for us to do this and then continue with |
| @@ -4639,17 +4755,18 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd, | |||
| 4639 | process_output_skip = 0; | 4755 | process_output_skip = 0; |
| 4640 | } | 4756 | } |
| 4641 | #endif | 4757 | #endif |
| 4758 | nfds = thread_select ( | ||
| 4642 | #if defined (USE_GTK) || defined (HAVE_GCONF) || defined (HAVE_GSETTINGS) | 4759 | #if defined (USE_GTK) || defined (HAVE_GCONF) || defined (HAVE_GSETTINGS) |
| 4643 | nfds = xg_select | 4760 | xg_select |
| 4644 | #elif defined (HAVE_NS) | 4761 | #elif defined (HAVE_NS) |
| 4645 | nfds = ns_select | 4762 | ns_select |
| 4646 | #else | 4763 | #else |
| 4647 | nfds = pselect | 4764 | pselect |
| 4648 | #endif | 4765 | #endif |
| 4649 | (max (max_process_desc, max_input_desc) + 1, | 4766 | , max (max_process_desc, max_input_desc) + 1, |
| 4650 | &Available, | 4767 | &Available, |
| 4651 | (check_write ? &Writeok : (SELECT_TYPE *)0), | 4768 | (check_write ? &Writeok : (SELECT_TYPE *)0), |
| 4652 | NULL, &timeout, NULL); | 4769 | NULL, &timeout, NULL); |
| 4653 | 4770 | ||
| 4654 | #ifdef HAVE_GNUTLS | 4771 | #ifdef HAVE_GNUTLS |
| 4655 | /* GnuTLS buffers data internally. In lowat mode it leaves | 4772 | /* GnuTLS buffers data internally. In lowat mode it leaves |
| @@ -7597,6 +7714,8 @@ The variable takes effect when `start-process' is called. */); | |||
| 7597 | defsubr (&Sprocess_filter); | 7714 | defsubr (&Sprocess_filter); |
| 7598 | defsubr (&Sset_process_sentinel); | 7715 | defsubr (&Sset_process_sentinel); |
| 7599 | defsubr (&Sprocess_sentinel); | 7716 | defsubr (&Sprocess_sentinel); |
| 7717 | defsubr (&Sset_process_thread); | ||
| 7718 | defsubr (&Sprocess_thread); | ||
| 7600 | defsubr (&Sset_process_window_size); | 7719 | defsubr (&Sset_process_window_size); |
| 7601 | defsubr (&Sset_process_inherit_coding_system_flag); | 7720 | defsubr (&Sset_process_inherit_coding_system_flag); |
| 7602 | defsubr (&Sset_process_query_on_exit_flag); | 7721 | defsubr (&Sset_process_query_on_exit_flag); |