aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorStefan Monnier2022-11-17 17:00:22 -0500
committerStefan Monnier2022-11-17 17:00:22 -0500
commitfb7f1864da4aa4c09756cfe47db6c56b4e87bd14 (patch)
tree4ccd8ca7e29c5609fa437884c739106a4f1b2cc2 /src
parent13003105a8edf746a8e8819122bd1bcdf7f9ecdd (diff)
downloademacs-fb7f1864da4aa4c09756cfe47db6c56b4e87bd14.tar.gz
emacs-fb7f1864da4aa4c09756cfe47db6c56b4e87bd14.zip
itree.c: Make the iterator reentrant (bug#59183)
Get rid of the global iterator object and instead allocate a separate iterator for every loop. This still uses the "duplicate iterator" code, including the old iterator which needs a stack, make ITREE_FOREACH a bit more expensive than we'd like. * src/itree.h (init_itree, forget_itree, itree_iterator_busy_p): Delete declarations. (itree_iterator_start): Add iterator arg and remove `line` and `file` args. (struct itree_iterator): Move from `itree.c`. Remove `line` and `file` fields. (ITREE_FOREACH): Stack allocate an iterator object and pass it to `itree_iterator_start`. * src/itree.c (struct itree_iterator): Move to itree.h. (iter): Delete global variable. (itree_iterator_create, init_itree, forget_itree, itree_iterator_busy_p): Delete functions. (itree_contains): Adjust assertion. (itree_iterator_finish): Deallocate the iterator's stack. (itree_iterator_start): Take the (uninitialized) iterator as argument. Allocate a fresh new stack. Remove `file` and `line` arguments. Don't check `running` any more since the iterator is not expected to be initialized at all. * src/eval.c (signal_or_quit): * src/alloc.c (garbage_collect): Don't check `itree_iterator_busy_p` any more. * src/emacs.c (main): No need to `init_itree` any more. (Fdump_emacs): No need to `forget_itree` any more.
Diffstat (limited to 'src')
-rw-r--r--src/alloc.c5
-rw-r--r--src/emacs.c3
-rw-r--r--src/eval.c1
-rw-r--r--src/itree.c91
-rw-r--r--src/itree.h46
5 files changed, 37 insertions, 109 deletions
diff --git a/src/alloc.c b/src/alloc.c
index 6862cf916fb..b9d12dff7e0 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -6279,11 +6279,6 @@ garbage_collect (void)
6279 image_prune_animation_caches (false); 6279 image_prune_animation_caches (false);
6280#endif 6280#endif
6281 6281
6282 /* ELisp code run by `gc-post-hook' could result in itree iteration,
6283 which must not happen while the itree is already busy. See
6284 bug#58639. */
6285 eassert (!itree_iterator_busy_p ());
6286
6287 if (!NILP (Vpost_gc_hook)) 6282 if (!NILP (Vpost_gc_hook))
6288 { 6283 {
6289 specpdl_ref gc_count = inhibit_garbage_collection (); 6284 specpdl_ref gc_count = inhibit_garbage_collection ();
diff --git a/src/emacs.c b/src/emacs.c
index c4c8bfc82fb..85102acd28e 100644
--- a/src/emacs.c
+++ b/src/emacs.c
@@ -1932,7 +1932,6 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
1932 running_asynch_code = 0; 1932 running_asynch_code = 0;
1933 init_random (); 1933 init_random ();
1934 init_xfaces (); 1934 init_xfaces ();
1935 init_itree ();
1936 1935
1937#if defined HAVE_JSON && !defined WINDOWSNT 1936#if defined HAVE_JSON && !defined WINDOWSNT
1938 init_json (); 1937 init_json ();
@@ -3105,8 +3104,6 @@ You must run Emacs in batch mode in order to dump it. */)
3105 gflags.will_dump_with_unexec_ = false; 3104 gflags.will_dump_with_unexec_ = false;
3106 gflags.dumped_with_unexec_ = true; 3105 gflags.dumped_with_unexec_ = true;
3107 3106
3108 forget_itree ();
3109
3110 alloc_unexec_pre (); 3107 alloc_unexec_pre ();
3111 3108
3112 unexec (SSDATA (filename), !NILP (symfile) ? SSDATA (symfile) : 0); 3109 unexec (SSDATA (filename), !NILP (symfile) ? SSDATA (symfile) : 0);
diff --git a/src/eval.c b/src/eval.c
index ea238299488..78e606267f6 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -1716,7 +1716,6 @@ signal_or_quit (Lisp_Object error_symbol, Lisp_Object data, bool keyboard_quit)
1716 Lisp_Object clause = Qnil; 1716 Lisp_Object clause = Qnil;
1717 struct handler *h; 1717 struct handler *h;
1718 1718
1719 eassert (!itree_iterator_busy_p ());
1720 if (gc_in_progress || waiting_for_input) 1719 if (gc_in_progress || waiting_for_input)
1721 emacs_abort (); 1720 emacs_abort ();
1722 1721
diff --git a/src/itree.c b/src/itree.c
index 4f25a924b40..9e60071980d 100644
--- a/src/itree.c
+++ b/src/itree.c
@@ -238,72 +238,12 @@ itree_stack_pop (struct itree_stack *stack)
238 238
239/* +-----------------------------------------------------------------------+ */ 239/* +-----------------------------------------------------------------------+ */
240 240
241/* State used when iterating interval. */
242struct itree_iterator
243{
244 struct itree_stack *stack;
245 struct itree_node *node;
246 ptrdiff_t begin;
247 ptrdiff_t end;
248
249 /* A copy of the tree's `otick`. */
250 uintmax_t otick;
251 enum itree_order order;
252 bool running;
253 const char *file;
254 int line;
255};
256
257/* Ideally, every iteration would use its own `iter` object, so we could
258 have several iterations active at the same time. In practice, iterations
259 are limited by the fact we don't allow modifying the tree at the same
260 time, making the use of nested iterations quite rare anyway.
261 So we just use a single global iterator instead for now. */
262static struct itree_iterator *iter = NULL;
263
264static int 241static int
265itree_max_height (const struct itree_tree *tree) 242itree_max_height (const struct itree_tree *tree)
266{ 243{
267 return 2 * log (tree->size + 1) / log (2) + 0.5; 244 return 2 * log (tree->size + 1) / log (2) + 0.5;
268} 245}
269 246
270/* Allocate a new iterator for TREE. */
271
272static struct itree_iterator *
273itree_iterator_create (struct itree_tree *tree)
274{
275 struct itree_iterator *g = xmalloc (sizeof *g);
276 /* 19 here just avoids starting with a silly-small stack.
277 FIXME: Since this stack only needs to be about 2*max_depth
278 in the worst case, we could completely pre-allocate it to something
279 like word-bit-size * 2 and then never worry about growing it. */
280 const int size = (tree ? itree_max_height (tree) : 19) + 1;
281
282 g->stack = itree_stack_create (size);
283 g->node = NULL;
284 g->running = false;
285 g->begin = 0;
286 g->end = 0;
287 g->file = NULL;
288 g->line = 0;
289 return g;
290}
291
292void
293init_itree (void)
294{
295 eassert (!iter);
296 iter = itree_iterator_create (NULL);
297}
298
299#ifdef HAVE_UNEXEC
300void
301forget_itree (void)
302{
303 iter = NULL;
304}
305#endif
306
307struct check_subtree_result 247struct check_subtree_result
308{ 248{
309 /* Node count of the tree. */ 249 /* Node count of the tree. */
@@ -871,7 +811,7 @@ itree_node_set_region (struct itree_tree *tree,
871static bool 811static bool
872itree_contains (struct itree_tree *tree, struct itree_node *node) 812itree_contains (struct itree_tree *tree, struct itree_node *node)
873{ 813{
874 eassert (iter && node); 814 eassert (node);
875 struct itree_node *other; 815 struct itree_node *other;
876 ITREE_FOREACH (other, tree, node->begin, PTRDIFF_MAX, ASCENDING) 816 ITREE_FOREACH (other, tree, node->begin, PTRDIFF_MAX, ASCENDING)
877 if (other == node) 817 if (other == node)
@@ -1304,18 +1244,13 @@ itree_delete_gap (struct itree_tree *tree,
1304 * | Iterator 1244 * | Iterator
1305 * +=======================================================================+ */ 1245 * +=======================================================================+ */
1306 1246
1307bool
1308itree_iterator_busy_p (void)
1309{
1310 return (iter && iter->running);
1311}
1312
1313/* Stop using the iterator. */ 1247/* Stop using the iterator. */
1314 1248
1315void 1249void
1316itree_iterator_finish (struct itree_iterator *iter) 1250itree_iterator_finish (struct itree_iterator *iter)
1317{ 1251{
1318 eassert (iter && iter->running); 1252 eassert (iter && iter->running);
1253 itree_stack_destroy (iter->stack);
1319 iter->running = false; 1254 iter->running = false;
1320} 1255}
1321 1256
@@ -1504,18 +1439,18 @@ itree_iterator_first_node (struct itree_tree *tree,
1504 given ORDER. Only one iterator per tree can be running at any time. */ 1439 given ORDER. Only one iterator per tree can be running at any time. */
1505 1440
1506struct itree_iterator * 1441struct itree_iterator *
1507itree_iterator_start (struct itree_tree *tree, ptrdiff_t begin, 1442itree_iterator_start (struct itree_iterator *iter,
1508 ptrdiff_t end, enum itree_order order, 1443 struct itree_tree *tree,
1509 const char *file, int line) 1444 ptrdiff_t begin, ptrdiff_t end, enum itree_order order)
1510{ 1445{
1511 eassert (iter); 1446 eassert (iter);
1512 if (iter->running) 1447 /* eassert (!iter->running); */
1513 { 1448 /* 19 here just avoids starting with a silly-small stack.
1514 fprintf (stderr, 1449 FIXME: Since this stack only needs to be about 2*max_depth
1515 "Detected nested iteration!\nOuter: %s:%d\nInner: %s:%d\n", 1450 in the worst case, we could completely pre-allocate it to something
1516 iter->file, iter->line, file, line); 1451 like word-bit-size * 2 and then never worry about growing it. */
1517 emacs_abort (); 1452 const int size = (tree ? itree_max_height (tree) : 19) + 1;
1518 } 1453 iter->stack = itree_stack_create (size);
1519 uintmax_t otick= tree->otick; 1454 uintmax_t otick= tree->otick;
1520 iter->begin = begin; 1455 iter->begin = begin;
1521 iter->end = end; 1456 iter->end = end;
@@ -1524,8 +1459,6 @@ itree_iterator_start (struct itree_tree *tree, ptrdiff_t begin,
1524 itree_stack_clear (iter->stack); 1459 itree_stack_clear (iter->stack);
1525 if (begin <= end && tree->root != NULL) 1460 if (begin <= end && tree->root != NULL)
1526 itree_stack_push_flagged (iter->stack, tree->root, false); 1461 itree_stack_push_flagged (iter->stack, tree->root, false);
1527 iter->file = file;
1528 iter->line = line;
1529 iter->running = true; 1462 iter->running = true;
1530 /* itree_stack_ensure_space (iter->stack, 1463 /* itree_stack_ensure_space (iter->stack,
1531 2 * itree_max_height (tree)); */ 1464 2 * itree_max_height (tree)); */
diff --git a/src/itree.h b/src/itree.h
index dc448233224..37cd423d34a 100644
--- a/src/itree.h
+++ b/src/itree.h
@@ -107,10 +107,6 @@ enum itree_order
107 ITREE_POST_ORDER, 107 ITREE_POST_ORDER,
108 }; 108 };
109 109
110extern void init_itree (void);
111#ifdef HAVE_UNEXEC
112extern void forget_itree (void);
113#endif
114extern void itree_node_init (struct itree_node *, bool, bool, Lisp_Object); 110extern void itree_node_init (struct itree_node *, bool, bool, Lisp_Object);
115extern ptrdiff_t itree_node_begin (struct itree_tree *, struct itree_node *); 111extern ptrdiff_t itree_node_begin (struct itree_tree *, struct itree_node *);
116extern ptrdiff_t itree_node_end (struct itree_tree *, struct itree_node *); 112extern ptrdiff_t itree_node_end (struct itree_tree *, struct itree_node *);
@@ -129,17 +125,28 @@ extern void itree_delete_gap (struct itree_tree *, ptrdiff_t, ptrdiff_t);
129 125
130/* Iteration functions. Almost all code should use ITREE_FOREACH 126/* Iteration functions. Almost all code should use ITREE_FOREACH
131 instead. */ 127 instead. */
132extern bool itree_iterator_busy_p (void); 128extern struct itree_iterator *itree_iterator_start (struct itree_iterator *,
133extern struct itree_iterator *itree_iterator_start (struct itree_tree *, 129 struct itree_tree *,
134 ptrdiff_t, 130 ptrdiff_t,
135 ptrdiff_t, 131 ptrdiff_t,
136 enum itree_order, 132 enum itree_order);
137 const char *, int);
138extern void itree_iterator_narrow (struct itree_iterator *, ptrdiff_t, 133extern void itree_iterator_narrow (struct itree_iterator *, ptrdiff_t,
139 ptrdiff_t); 134 ptrdiff_t);
140extern void itree_iterator_finish (struct itree_iterator *); 135extern void itree_iterator_finish (struct itree_iterator *);
141extern struct itree_node *itree_iterator_next (struct itree_iterator *); 136extern struct itree_node *itree_iterator_next (struct itree_iterator *);
142 137
138/* State used when iterating interval. */
139struct itree_iterator
140 {
141 struct itree_node *node; /* FIXME: It should be either `node` or `stack`. */
142 struct itree_stack *stack;
143 ptrdiff_t begin;
144 ptrdiff_t end;
145 uintmax_t otick; /* A copy of the tree's `otick`. */
146 enum itree_order order;
147 bool running;
148 };
149
143/* Iterate over the intervals between BEG and END in the tree T. 150/* Iterate over the intervals between BEG and END in the tree T.
144 N will hold successive nodes. ORDER can be one of : `ASCENDING`, 151 N will hold successive nodes. ORDER can be one of : `ASCENDING`,
145 `DESCENDING`, or `PRE_ORDER`. 152 `DESCENDING`, or `PRE_ORDER`.
@@ -153,29 +160,26 @@ extern struct itree_node *itree_iterator_next (struct itree_iterator *);
153 BEWARE: 160 BEWARE:
154 - The expression T may be evaluated more than once, so make sure 161 - The expression T may be evaluated more than once, so make sure
155 it is cheap and pure. 162 it is cheap and pure.
156 - Only a single iteration can happen at a time, so make sure none of the
157 code within the loop can start another tree iteration, i.e. it shouldn't
158 be able to run ELisp code, nor GC since GC can run ELisp by way
159 of `post-gc-hook`.
160 - If you need to exit the loop early, you *have* to call `ITREE_ABORT` 163 - If you need to exit the loop early, you *have* to call `ITREE_ABORT`
161 just before exiting (e.g. with `break` or `return`). 164 just before exiting (e.g. with `break` or `return`).
162 - Non-local exits are not supported within the body of the loop. 165 - Non-local exits are not supported within the body of the loop.
163 - Don't modify the tree during the iteration. 166 - Don't modify the tree during the iteration.
164 */ 167 */
165#define ITREE_FOREACH(n, t, beg, end, order) \ 168#define ITREE_FOREACH(n, t, beg, end, order) \
166 /* FIXME: We'd want to declare `x` right here, but I can't figure out 169 /* FIXME: We'd want to declare `n` right here, but I can't figure out
167 how to make that work here: the `for` syntax only allows a single 170 how to make that work here: the `for` syntax only allows a single
168 clause for the var declarations where we need 2 different types. 171 clause for the var declarations where we need 2 different types.
169 We could use the `struct {foo x; bar y; } p;` trick to declare two 172 We could use the `struct {foo x; bar y; } p;` trick to declare two
170 vars `p.x` and `p.y` of unrelated types, but then none of the names 173 vars `p.x` and `p.y` of unrelated types, but then none of the names
171 of the vars matches the `n` we receive :-(. */ \ 174 of the vars matches the `n` we receive :-(. */ \
172 if (!t) \ 175 if (!t) \
173 { } \ 176 { } \
174 else \ 177 else \
175 for (struct itree_iterator *itree_iter_ \ 178 for (struct itree_iterator itree_local_iter_, \
176 = itree_iterator_start (t, beg, end, ITREE_##order, \ 179 *itree_iter_ \
177 __FILE__, __LINE__); \ 180 = itree_iterator_start (&itree_local_iter_, \
178 ((n = itree_iterator_next (itree_iter_)) \ 181 t, beg, end, ITREE_##order); \
182 ((n = itree_iterator_next (itree_iter_)) \
179 || (itree_iterator_finish (itree_iter_), false));) 183 || (itree_iterator_finish (itree_iter_), false));)
180 184
181#define ITREE_FOREACH_ABORT() \ 185#define ITREE_FOREACH_ABORT() \