diff options
| author | Stefan Monnier | 2022-02-12 15:25:53 -0500 |
|---|---|---|
| committer | Stefan Monnier | 2022-02-12 15:25:53 -0500 |
| commit | b8460fcb8c320ea6d7449f37f07502d10eb74cd5 (patch) | |
| tree | 49eed9555d20693eb01321881204dd9bc45176d1 /src | |
| parent | 89bb5a5f357e911aeb0b9f14e8b2f7c5a5fbabf7 (diff) | |
| download | emacs-b8460fcb8c320ea6d7449f37f07502d10eb74cd5.tar.gz emacs-b8460fcb8c320ea6d7449f37f07502d10eb74cd5.zip | |
Rewrite thread context switch code (bug#48990)
Make the context switch code handle buffer-local variables more
correctly by reusing the code originally written for `backtrace-eval`.
This has the side benefit of making the `saved_value` field unused.
* src/lisp.h (enum specbind_tag): Remove `saved_value` field.
(rebind_for_thread_switch, unbind_for_thread_switch): Delete decls.
(specpdl_unrewind): Declare function.
* src/eval.c (specpdl_saved_value): Delete function.
(specbind): Delete the code related to `saved_value`, and consolidate
common code between the different branches.
(rebind_for_thread_switch, -unbind_for_thread_switch): Move to `thread.c`.
(specpdl_unrewind): New function, extracted from `backtrace_eval_unrewind`.
Use `SET_INTERNAL_THREAD_SWITCH`. Skip the buffer & excursion unwinds
depending on new arg `vars_only`.
(backtrace_eval_unrewind): Use it.
(mark_specpdl): Don't mark `saved_value`.
* src/thread.c (rebind_for_thread_switch, unbind_for_thread_switch):
Move from `eval.c` and rewrite using `specpdl_unrewind`.
* test/src/thread-tests.el (threads-test-bug48990): New test.
* test/Makefile.in (test_template): Add a + as suggested by make:
"warning: jobserver unavailable: using -j1. Add '+' to parent make rule".
Diffstat (limited to 'src')
| -rw-r--r-- | src/eval.c | 89 | ||||
| -rw-r--r-- | src/lisp.h | 6 | ||||
| -rw-r--r-- | src/thread.c | 16 |
3 files changed, 46 insertions, 65 deletions
diff --git a/src/eval.c b/src/eval.c index d1c45fca56b..6bed7c4a899 100644 --- a/src/eval.c +++ b/src/eval.c | |||
| @@ -104,13 +104,6 @@ specpdl_where (union specbinding *pdl) | |||
| 104 | } | 104 | } |
| 105 | 105 | ||
| 106 | static Lisp_Object | 106 | static Lisp_Object |
| 107 | specpdl_saved_value (union specbinding *pdl) | ||
| 108 | { | ||
| 109 | eassert (pdl->kind >= SPECPDL_LET); | ||
| 110 | return pdl->let.saved_value; | ||
| 111 | } | ||
| 112 | |||
| 113 | static Lisp_Object | ||
| 114 | specpdl_arg (union specbinding *pdl) | 107 | specpdl_arg (union specbinding *pdl) |
| 115 | { | 108 | { |
| 116 | eassert (pdl->kind == SPECPDL_UNWIND); | 109 | eassert (pdl->kind == SPECPDL_UNWIND); |
| @@ -3589,9 +3582,6 @@ specbind (Lisp_Object symbol, Lisp_Object value) | |||
| 3589 | specpdl_ptr->let.kind = SPECPDL_LET; | 3582 | specpdl_ptr->let.kind = SPECPDL_LET; |
| 3590 | specpdl_ptr->let.symbol = symbol; | 3583 | specpdl_ptr->let.symbol = symbol; |
| 3591 | specpdl_ptr->let.old_value = SYMBOL_VAL (sym); | 3584 | specpdl_ptr->let.old_value = SYMBOL_VAL (sym); |
| 3592 | specpdl_ptr->let.saved_value = Qnil; | ||
| 3593 | grow_specpdl (); | ||
| 3594 | do_specbind (sym, specpdl_ptr - 1, value, SET_INTERNAL_BIND); | ||
| 3595 | break; | 3585 | break; |
| 3596 | case SYMBOL_LOCALIZED: | 3586 | case SYMBOL_LOCALIZED: |
| 3597 | case SYMBOL_FORWARDED: | 3587 | case SYMBOL_FORWARDED: |
| @@ -3601,7 +3591,6 @@ specbind (Lisp_Object symbol, Lisp_Object value) | |||
| 3601 | specpdl_ptr->let.symbol = symbol; | 3591 | specpdl_ptr->let.symbol = symbol; |
| 3602 | specpdl_ptr->let.old_value = ovalue; | 3592 | specpdl_ptr->let.old_value = ovalue; |
| 3603 | specpdl_ptr->let.where = Fcurrent_buffer (); | 3593 | specpdl_ptr->let.where = Fcurrent_buffer (); |
| 3604 | specpdl_ptr->let.saved_value = Qnil; | ||
| 3605 | 3594 | ||
| 3606 | eassert (sym->u.s.redirect != SYMBOL_LOCALIZED | 3595 | eassert (sym->u.s.redirect != SYMBOL_LOCALIZED |
| 3607 | || (EQ (SYMBOL_BLV (sym)->where, Fcurrent_buffer ()))); | 3596 | || (EQ (SYMBOL_BLV (sym)->where, Fcurrent_buffer ()))); |
| @@ -3619,22 +3608,17 @@ specbind (Lisp_Object symbol, Lisp_Object value) | |||
| 3619 | having their own value. This is consistent with what | 3608 | having their own value. This is consistent with what |
| 3620 | happens with other buffer-local variables. */ | 3609 | happens with other buffer-local variables. */ |
| 3621 | if (NILP (Flocal_variable_p (symbol, Qnil))) | 3610 | if (NILP (Flocal_variable_p (symbol, Qnil))) |
| 3622 | { | 3611 | specpdl_ptr->let.kind = SPECPDL_LET_DEFAULT; |
| 3623 | specpdl_ptr->let.kind = SPECPDL_LET_DEFAULT; | ||
| 3624 | grow_specpdl (); | ||
| 3625 | do_specbind (sym, specpdl_ptr - 1, value, SET_INTERNAL_BIND); | ||
| 3626 | return; | ||
| 3627 | } | ||
| 3628 | } | 3612 | } |
| 3629 | else | 3613 | else |
| 3630 | specpdl_ptr->let.kind = SPECPDL_LET; | 3614 | specpdl_ptr->let.kind = SPECPDL_LET; |
| 3631 | 3615 | ||
| 3632 | grow_specpdl (); | ||
| 3633 | do_specbind (sym, specpdl_ptr - 1, value, SET_INTERNAL_BIND); | ||
| 3634 | break; | 3616 | break; |
| 3635 | } | 3617 | } |
| 3636 | default: emacs_abort (); | 3618 | default: emacs_abort (); |
| 3637 | } | 3619 | } |
| 3620 | grow_specpdl (); | ||
| 3621 | do_specbind (sym, specpdl_ptr - 1, value, SET_INTERNAL_BIND); | ||
| 3638 | } | 3622 | } |
| 3639 | 3623 | ||
| 3640 | /* Push unwind-protect entries of various types. */ | 3624 | /* Push unwind-protect entries of various types. */ |
| @@ -3710,24 +3694,6 @@ record_unwind_protect_module (enum specbind_tag kind, void *ptr) | |||
| 3710 | grow_specpdl (); | 3694 | grow_specpdl (); |
| 3711 | } | 3695 | } |
| 3712 | 3696 | ||
| 3713 | void | ||
| 3714 | rebind_for_thread_switch (void) | ||
| 3715 | { | ||
| 3716 | union specbinding *bind; | ||
| 3717 | |||
| 3718 | for (bind = specpdl; bind != specpdl_ptr; ++bind) | ||
| 3719 | { | ||
| 3720 | if (bind->kind >= SPECPDL_LET) | ||
| 3721 | { | ||
| 3722 | Lisp_Object value = specpdl_saved_value (bind); | ||
| 3723 | Lisp_Object sym = specpdl_symbol (bind); | ||
| 3724 | bind->let.saved_value = Qnil; | ||
| 3725 | do_specbind (XSYMBOL (sym), bind, value, | ||
| 3726 | SET_INTERNAL_THREAD_SWITCH); | ||
| 3727 | } | ||
| 3728 | } | ||
| 3729 | } | ||
| 3730 | |||
| 3731 | static void | 3697 | static void |
| 3732 | do_one_unbind (union specbinding *this_binding, bool unwinding, | 3698 | do_one_unbind (union specbinding *this_binding, bool unwinding, |
| 3733 | enum Set_Internal_Bind bindflag) | 3699 | enum Set_Internal_Bind bindflag) |
| @@ -3884,22 +3850,6 @@ unbind_to (specpdl_ref count, Lisp_Object value) | |||
| 3884 | return value; | 3850 | return value; |
| 3885 | } | 3851 | } |
| 3886 | 3852 | ||
| 3887 | void | ||
| 3888 | unbind_for_thread_switch (struct thread_state *thr) | ||
| 3889 | { | ||
| 3890 | union specbinding *bind; | ||
| 3891 | |||
| 3892 | for (bind = thr->m_specpdl_ptr; bind > thr->m_specpdl;) | ||
| 3893 | { | ||
| 3894 | if ((--bind)->kind >= SPECPDL_LET) | ||
| 3895 | { | ||
| 3896 | Lisp_Object sym = specpdl_symbol (bind); | ||
| 3897 | bind->let.saved_value = find_symbol_value (sym); | ||
| 3898 | do_one_unbind (bind, false, SET_INTERNAL_THREAD_SWITCH); | ||
| 3899 | } | ||
| 3900 | } | ||
| 3901 | } | ||
| 3902 | |||
| 3903 | DEFUN ("special-variable-p", Fspecial_variable_p, Sspecial_variable_p, 1, 1, 0, | 3853 | DEFUN ("special-variable-p", Fspecial_variable_p, Sspecial_variable_p, 1, 1, 0, |
| 3904 | doc: /* Return non-nil if SYMBOL's global binding has been declared special. | 3854 | doc: /* Return non-nil if SYMBOL's global binding has been declared special. |
| 3905 | A special variable is one that will be bound dynamically, even in a | 3855 | A special variable is one that will be bound dynamically, even in a |
| @@ -4055,11 +4005,13 @@ or a lambda expression for macro calls. */) | |||
| 4055 | value and the old value stored in the specpdl), kind of like the inplace | 4005 | value and the old value stored in the specpdl), kind of like the inplace |
| 4056 | pointer-reversal trick. As it turns out, the rewind does the same as the | 4006 | pointer-reversal trick. As it turns out, the rewind does the same as the |
| 4057 | unwind, except it starts from the other end of the specpdl stack, so we use | 4007 | unwind, except it starts from the other end of the specpdl stack, so we use |
| 4058 | the same function for both unwind and rewind. */ | 4008 | the same function for both unwind and rewind. |
| 4059 | static void | 4009 | This same code is used when switching threads, except in that case |
| 4060 | backtrace_eval_unrewind (int distance) | 4010 | we unwind/rewind the whole specpdl of the threads. */ |
| 4011 | void | ||
| 4012 | specpdl_unrewind (union specbinding *pdl, int distance, bool vars_only) | ||
| 4061 | { | 4013 | { |
| 4062 | union specbinding *tmp = specpdl_ptr; | 4014 | union specbinding *tmp = pdl; |
| 4063 | int step = -1; | 4015 | int step = -1; |
| 4064 | if (distance < 0) | 4016 | if (distance < 0) |
| 4065 | { /* It's a rewind rather than unwind. */ | 4017 | { /* It's a rewind rather than unwind. */ |
| @@ -4077,6 +4029,8 @@ backtrace_eval_unrewind (int distance) | |||
| 4077 | unwind_protect, but the problem is that we don't know how to | 4029 | unwind_protect, but the problem is that we don't know how to |
| 4078 | rewind them afterwards. */ | 4030 | rewind them afterwards. */ |
| 4079 | case SPECPDL_UNWIND: | 4031 | case SPECPDL_UNWIND: |
| 4032 | if (vars_only) | ||
| 4033 | break; | ||
| 4080 | if (tmp->unwind.func == set_buffer_if_live) | 4034 | if (tmp->unwind.func == set_buffer_if_live) |
| 4081 | { | 4035 | { |
| 4082 | Lisp_Object oldarg = tmp->unwind.arg; | 4036 | Lisp_Object oldarg = tmp->unwind.arg; |
| @@ -4085,6 +4039,8 @@ backtrace_eval_unrewind (int distance) | |||
| 4085 | } | 4039 | } |
| 4086 | break; | 4040 | break; |
| 4087 | case SPECPDL_UNWIND_EXCURSION: | 4041 | case SPECPDL_UNWIND_EXCURSION: |
| 4042 | if (vars_only) | ||
| 4043 | break; | ||
| 4088 | { | 4044 | { |
| 4089 | Lisp_Object marker = tmp->unwind_excursion.marker; | 4045 | Lisp_Object marker = tmp->unwind_excursion.marker; |
| 4090 | Lisp_Object window = tmp->unwind_excursion.window; | 4046 | Lisp_Object window = tmp->unwind_excursion.window; |
| @@ -4125,7 +4081,7 @@ backtrace_eval_unrewind (int distance) | |||
| 4125 | Lisp_Object sym = specpdl_symbol (tmp); | 4081 | Lisp_Object sym = specpdl_symbol (tmp); |
| 4126 | Lisp_Object old_value = specpdl_old_value (tmp); | 4082 | Lisp_Object old_value = specpdl_old_value (tmp); |
| 4127 | set_specpdl_old_value (tmp, default_value (sym)); | 4083 | set_specpdl_old_value (tmp, default_value (sym)); |
| 4128 | Fset_default (sym, old_value); | 4084 | set_default_internal (sym, old_value, SET_INTERNAL_THREAD_SWITCH); |
| 4129 | } | 4085 | } |
| 4130 | break; | 4086 | break; |
| 4131 | case SPECPDL_LET_LOCAL: | 4087 | case SPECPDL_LET_LOCAL: |
| @@ -4141,14 +4097,28 @@ backtrace_eval_unrewind (int distance) | |||
| 4141 | { | 4097 | { |
| 4142 | set_specpdl_old_value | 4098 | set_specpdl_old_value |
| 4143 | (tmp, buffer_local_value (symbol, where)); | 4099 | (tmp, buffer_local_value (symbol, where)); |
| 4144 | set_internal (symbol, old_value, where, SET_INTERNAL_UNBIND); | 4100 | set_internal (symbol, old_value, where, |
| 4101 | SET_INTERNAL_THREAD_SWITCH); | ||
| 4145 | } | 4102 | } |
| 4103 | else | ||
| 4104 | /* FIXME: If the var is not local any more, we failed | ||
| 4105 | to swap the old and new values. As long as the var remains | ||
| 4106 | non-local, this is fine, but if it ever reverts to being | ||
| 4107 | local we may end up using this entry "in the wrong | ||
| 4108 | direction". */ | ||
| 4109 | ; | ||
| 4146 | } | 4110 | } |
| 4147 | break; | 4111 | break; |
| 4148 | } | 4112 | } |
| 4149 | } | 4113 | } |
| 4150 | } | 4114 | } |
| 4151 | 4115 | ||
| 4116 | static void | ||
| 4117 | backtrace_eval_unrewind (int distance) | ||
| 4118 | { | ||
| 4119 | specpdl_unrewind (specpdl_ptr, distance, false); | ||
| 4120 | } | ||
| 4121 | |||
| 4152 | DEFUN ("backtrace-eval", Fbacktrace_eval, Sbacktrace_eval, 2, 3, NULL, | 4122 | DEFUN ("backtrace-eval", Fbacktrace_eval, Sbacktrace_eval, 2, 3, NULL, |
| 4153 | doc: /* Evaluate EXP in the context of some activation frame. | 4123 | doc: /* Evaluate EXP in the context of some activation frame. |
| 4154 | NFRAMES and BASE specify the activation frame to use, as in `backtrace-frame'. */) | 4124 | NFRAMES and BASE specify the activation frame to use, as in `backtrace-frame'. */) |
| @@ -4302,7 +4272,6 @@ mark_specpdl (union specbinding *first, union specbinding *ptr) | |||
| 4302 | case SPECPDL_LET: | 4272 | case SPECPDL_LET: |
| 4303 | mark_object (specpdl_symbol (pdl)); | 4273 | mark_object (specpdl_symbol (pdl)); |
| 4304 | mark_object (specpdl_old_value (pdl)); | 4274 | mark_object (specpdl_old_value (pdl)); |
| 4305 | mark_object (specpdl_saved_value (pdl)); | ||
| 4306 | break; | 4275 | break; |
| 4307 | 4276 | ||
| 4308 | case SPECPDL_UNWIND_PTR: | 4277 | case SPECPDL_UNWIND_PTR: |
diff --git a/src/lisp.h b/src/lisp.h index f27c2ad2dd5..19788ef07cc 100644 --- a/src/lisp.h +++ b/src/lisp.h | |||
| @@ -3337,9 +3337,6 @@ union specbinding | |||
| 3337 | ENUM_BF (specbind_tag) kind : CHAR_BIT; | 3337 | ENUM_BF (specbind_tag) kind : CHAR_BIT; |
| 3338 | /* `where' is not used in the case of SPECPDL_LET. */ | 3338 | /* `where' is not used in the case of SPECPDL_LET. */ |
| 3339 | Lisp_Object symbol, old_value, where; | 3339 | Lisp_Object symbol, old_value, where; |
| 3340 | /* Normally this is unused; but it is set to the symbol's | ||
| 3341 | current value when a thread is swapped out. */ | ||
| 3342 | Lisp_Object saved_value; | ||
| 3343 | } let; | 3340 | } let; |
| 3344 | struct { | 3341 | struct { |
| 3345 | ENUM_BF (specbind_tag) kind : CHAR_BIT; | 3342 | ENUM_BF (specbind_tag) kind : CHAR_BIT; |
| @@ -4453,8 +4450,7 @@ extern void set_unwind_protect (specpdl_ref, void (*) (Lisp_Object), | |||
| 4453 | Lisp_Object); | 4450 | Lisp_Object); |
| 4454 | extern void set_unwind_protect_ptr (specpdl_ref, void (*) (void *), void *); | 4451 | extern void set_unwind_protect_ptr (specpdl_ref, void (*) (void *), void *); |
| 4455 | extern Lisp_Object unbind_to (specpdl_ref, Lisp_Object); | 4452 | extern Lisp_Object unbind_to (specpdl_ref, Lisp_Object); |
| 4456 | extern void rebind_for_thread_switch (void); | 4453 | void specpdl_unrewind (union specbinding *pdl, int distance, bool vars_only); |
| 4457 | extern void unbind_for_thread_switch (struct thread_state *); | ||
| 4458 | extern AVOID error (const char *, ...) ATTRIBUTE_FORMAT_PRINTF (1, 2); | 4454 | extern AVOID error (const char *, ...) ATTRIBUTE_FORMAT_PRINTF (1, 2); |
| 4459 | extern AVOID verror (const char *, va_list) | 4455 | extern AVOID verror (const char *, va_list) |
| 4460 | ATTRIBUTE_FORMAT_PRINTF (1, 0); | 4456 | ATTRIBUTE_FORMAT_PRINTF (1, 0); |
diff --git a/src/thread.c b/src/thread.c index 8a6a2de18be..4c98d590b7a 100644 --- a/src/thread.c +++ b/src/thread.c | |||
| @@ -83,6 +83,22 @@ release_global_lock (void) | |||
| 83 | sys_mutex_unlock (&global_lock); | 83 | sys_mutex_unlock (&global_lock); |
| 84 | } | 84 | } |
| 85 | 85 | ||
| 86 | static void | ||
| 87 | rebind_for_thread_switch (void) | ||
| 88 | { | ||
| 89 | ptrdiff_t distance | ||
| 90 | = current_thread->m_specpdl_ptr - current_thread->m_specpdl; | ||
| 91 | specpdl_unrewind (specpdl_ptr, -distance, true); | ||
| 92 | } | ||
| 93 | |||
| 94 | static void | ||
| 95 | unbind_for_thread_switch (struct thread_state *thr) | ||
| 96 | { | ||
| 97 | ptrdiff_t distance = thr->m_specpdl_ptr - thr->m_specpdl; | ||
| 98 | specpdl_unrewind (thr->m_specpdl_ptr, distance, true); | ||
| 99 | } | ||
| 100 | |||
| 101 | |||
| 86 | /* You must call this after acquiring the global lock. | 102 | /* You must call this after acquiring the global lock. |
| 87 | acquire_global_lock does it for you. */ | 103 | acquire_global_lock does it for you. */ |
| 88 | static void | 104 | static void |