aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDaniel Colascione2015-03-02 02:23:09 -0800
committerDaniel Colascione2015-03-02 15:39:01 -0800
commit9d8d0658147dfe5a90e2fb07ff666f35b1162d6e (patch)
tree6d593ab42386348b1842688c75f892db45c5b59e /src
parentb149ecd8aa3aa9c179dd5496f64e1f50750414fa (diff)
downloademacs-9d8d0658147dfe5a90e2fb07ff666f35b1162d6e.tar.gz
emacs-9d8d0658147dfe5a90e2fb07ff666f35b1162d6e.zip
Add support for finalizers
+2015-03-02 Daniel Colascione <dancol@dancol.org> + + * NEWS: Mention finalizers. + 2015-02-09 Gareth Rees <gdr@garethrees.org> (tiny change) * NEWS.24: Fix typo (bug#19820) diff --git a/src/ChangeLog b/src/ChangeLog index 4aa64c1..2f04d0b 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,21 @@ +2015-03-02 Daniel Colascione <dancol@dancol.org> + + * print.c (print_object): Print finalizers. + + * alloc.c: + (finalizers, doomed_finalizers): New variables. + (init_finalizer_list, finalizer_insert, unchain_finalizer) + (mark_finalizer_list, queue_doomed_finalizers) + (run_finalizer_handler, run_finalizer_function, run_finalizers): + New functions. + (garbage_collect_1, mark_object, sweep_misc) + (init_alloc_once, syms_of_alloc): Support finalizers. + (gc-precise-p): New Lisp variable. + + * lisp.h (Lisp_Misc_Type): New value Lisp_Misc_Finalizer. + (FINALIZERP, XFINALIZER): New functions. + (Lisp_Finalizer): New structure. + 2015-02-28 Paul Eggert <eggert@cs.ucla.edu> * character.c (alphabeticp, decimalnump): Avoid undefined behavior diff --git a/test/ChangeLog b/test/ChangeLog index cf1b2c1..684e98f 100644 --- a/test/ChangeLog +++ b/test/ChangeLog @@ -1,3 +1,9 @@ +2015-03-02 Daniel Colascione <dancol@dancol.org> + + * automated/finalizer-tests.el (finalizer-basic) + (finalizer-circular-reference, finalizer-cross-reference) + (finalizer-error): New tests. + 2015-03-01 Michael Albinus <michael.albinus@gmx.de> * automated/vc-tests.el (vc-test--create-repo): Add check for
Diffstat (limited to 'src')
-rw-r--r--src/ChangeLog18
-rw-r--r--src/alloc.c172
-rw-r--r--src/lisp.h33
-rw-r--r--src/print.c6
4 files changed, 223 insertions, 6 deletions
diff --git a/src/ChangeLog b/src/ChangeLog
index 4aa64c1d6f9..2f04d0b040a 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,21 @@
12015-03-02 Daniel Colascione <dancol@dancol.org>
2
3 * print.c (print_object): Print finalizers.
4
5 * alloc.c:
6 (finalizers, doomed_finalizers): New variables.
7 (init_finalizer_list, finalizer_insert, unchain_finalizer)
8 (mark_finalizer_list, queue_doomed_finalizers)
9 (run_finalizer_handler, run_finalizer_function, run_finalizers):
10 New functions.
11 (garbage_collect_1, mark_object, sweep_misc)
12 (init_alloc_once, syms_of_alloc): Support finalizers.
13 (gc-precise-p): New Lisp variable.
14
15 * lisp.h (Lisp_Misc_Type): New value Lisp_Misc_Finalizer.
16 (FINALIZERP, XFINALIZER): New functions.
17 (Lisp_Finalizer): New structure.
18
12015-02-28 Paul Eggert <eggert@cs.ucla.edu> 192015-02-28 Paul Eggert <eggert@cs.ucla.edu>
2 20
3 * character.c (alphabeticp, decimalnump): Avoid undefined behavior 21 * character.c (alphabeticp, decimalnump): Avoid undefined behavior
diff --git a/src/alloc.c b/src/alloc.c
index 9aa94b8a559..eec53e7d844 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -441,6 +441,15 @@ mmap_lisp_allowed_p (void)
441 return pointers_fit_in_lispobj_p () && !might_dump; 441 return pointers_fit_in_lispobj_p () && !might_dump;
442} 442}
443 443
444/* Head of a circularly-linked list of extant finalizers. */
445static struct Lisp_Finalizer finalizers;
446
447/* Head of a circularly-linked list of finalizers that must be invoked
448 because we deemed them unreachable. This list must be global, and
449 not a local inside garbage_collect_1, in case we GC again while
450 running finalizers. */
451static struct Lisp_Finalizer doomed_finalizers;
452
444 453
445/************************************************************************ 454/************************************************************************
446 Malloc 455 Malloc
@@ -3695,6 +3704,131 @@ make_event_array (ptrdiff_t nargs, Lisp_Object *args)
3695 } 3704 }
3696} 3705}
3697 3706
3707static void
3708init_finalizer_list (struct Lisp_Finalizer *head)
3709{
3710 head->prev = head->next = head;
3711}
3712
3713/* Insert FINALIZER before ELEMENT. */
3714
3715static void
3716finalizer_insert (struct Lisp_Finalizer *element,
3717 struct Lisp_Finalizer* finalizer)
3718{
3719 eassert (finalizer->prev == NULL);
3720 eassert (finalizer->next == NULL);
3721 finalizer->next = element;
3722 finalizer->prev = element->prev;
3723 finalizer->prev->next = finalizer;
3724 element->prev = finalizer;
3725}
3726
3727static void
3728unchain_finalizer (struct Lisp_Finalizer *finalizer)
3729{
3730 if (finalizer->prev != NULL) {
3731 eassert (finalizer->next != NULL);
3732 finalizer->prev->next = finalizer->next;
3733 finalizer->next->prev = finalizer->prev;
3734 finalizer->prev = finalizer->next = NULL;
3735 }
3736}
3737
3738static void
3739mark_finalizer_list (struct Lisp_Finalizer *head)
3740{
3741 for (struct Lisp_Finalizer *finalizer = head->next;
3742 finalizer != head;
3743 finalizer = finalizer->next)
3744 {
3745 finalizer->base.gcmarkbit = 1;
3746 mark_object (finalizer->function);
3747 }
3748}
3749
3750/* Move doomed finalizers in list SRC onto list DEST. A doomed
3751 finalizer is one that is not GC-reachable and whose
3752 finalizer->function is non-nil. (We reset finalizer->function to
3753 before attempting to run it.) */
3754
3755static void
3756queue_doomed_finalizers (struct Lisp_Finalizer *dest,
3757 struct Lisp_Finalizer *src)
3758{
3759 struct Lisp_Finalizer* finalizer = src->next;
3760 while (finalizer != src)
3761 {
3762 struct Lisp_Finalizer *next = finalizer->next;
3763 if (!finalizer->base.gcmarkbit && !NILP (finalizer->function))
3764 {
3765 unchain_finalizer (finalizer);
3766 finalizer_insert (dest, finalizer);
3767 }
3768
3769 finalizer = next;
3770 }
3771}
3772
3773static Lisp_Object
3774run_finalizer_handler (Lisp_Object args)
3775{
3776 add_to_log ("finalizer failed: %S", args, Qnil);
3777 return Qnil;
3778}
3779
3780static void
3781run_finalizer_function (Lisp_Object function)
3782{
3783 struct gcpro gcpro1;
3784 ptrdiff_t count = SPECPDL_INDEX ();
3785
3786 GCPRO1 (function);
3787 specbind (Qinhibit_quit, Qt);
3788 internal_condition_case_1 (call0, function, Qt, run_finalizer_handler);
3789 unbind_to (count, Qnil);
3790 UNGCPRO;
3791}
3792
3793static void
3794run_finalizers (struct Lisp_Finalizer* finalizers)
3795{
3796 struct Lisp_Finalizer* finalizer;
3797 Lisp_Object function;
3798 struct gcpro gcpro1;
3799
3800 while (finalizers->next != finalizers) {
3801 finalizer = finalizers->next;
3802 eassert (finalizer->base.type == Lisp_Misc_Finalizer);
3803 unchain_finalizer (finalizer);
3804 function = finalizer->function;
3805 if (!NILP (function))
3806 {
3807 finalizer->function = Qnil;
3808 run_finalizer_function (function);
3809 }
3810 }
3811}
3812
3813DEFUN ("make-finalizer", Fmake_finalizer, Smake_finalizer, 1, 1, 0,
3814 doc: /* Make a finalizer that will run FUNCTION.
3815FUNCTION will be called after garbage collection when the returned
3816finalizer object becomes unreachable. If the finalizer object is
3817reachable only through references from finalizer objects, it does not
3818count as reachable for the purpose of deciding whether to run
3819FUNCTION. FUNCTION will be run once per finalizer object. */)
3820 (Lisp_Object function)
3821{
3822 Lisp_Object val;
3823 struct Lisp_Finalizer *finalizer;
3824
3825 val = allocate_misc (Lisp_Misc_Finalizer);
3826 finalizer = XFINALIZER (val);
3827 finalizer->function = function;
3828 finalizer->prev = finalizer->next = NULL;
3829 finalizer_insert (&finalizers, finalizer);
3830 return val;
3831}
3698 3832
3699 3833
3700/************************************************************************ 3834/************************************************************************
@@ -5613,9 +5747,9 @@ garbage_collect_1 (void *end)
5613 mark_stack (end); 5747 mark_stack (end);
5614#endif 5748#endif
5615 5749
5616 /* Everything is now marked, except for the data in font caches 5750 /* Everything is now marked, except for the data in font caches,
5617 and undo lists. They're compacted by removing an items which 5751 undo lists, and finalizers. The first two are compacted by
5618 aren't reachable otherwise. */ 5752 removing an items which aren't reachable otherwise. */
5619 5753
5620 compact_font_caches (); 5754 compact_font_caches ();
5621 5755
@@ -5628,6 +5762,16 @@ garbage_collect_1 (void *end)
5628 mark_object (BVAR (nextb, undo_list)); 5762 mark_object (BVAR (nextb, undo_list));
5629 } 5763 }
5630 5764
5765 /* Now pre-sweep finalizers. Here, we add any unmarked finalizers
5766 to doomed_finalizers so we can run their associated functions
5767 after GC. It's important to scan finalizers at this stage so
5768 that we can be sure that unmarked finalizers are really
5769 unreachable except for references from their associated functions
5770 and from other finalizers. */
5771
5772 queue_doomed_finalizers (&doomed_finalizers, &finalizers);
5773 mark_finalizer_list (&doomed_finalizers);
5774
5631 gc_sweep (); 5775 gc_sweep ();
5632 5776
5633 /* Clear the mark bits that we set in certain root slots. */ 5777 /* Clear the mark bits that we set in certain root slots. */
@@ -5728,6 +5872,9 @@ garbage_collect_1 (void *end)
5728 } 5872 }
5729#endif 5873#endif
5730 5874
5875 /* GC is complete: now we can run our finalizer callbacks. */
5876 run_finalizers (&doomed_finalizers);
5877
5731 if (!NILP (Vpost_gc_hook)) 5878 if (!NILP (Vpost_gc_hook))
5732 { 5879 {
5733 ptrdiff_t gc_count = inhibit_garbage_collection (); 5880 ptrdiff_t gc_count = inhibit_garbage_collection ();
@@ -6364,7 +6511,12 @@ mark_object (Lisp_Object arg)
6364 6511
6365 case Lisp_Misc_Overlay: 6512 case Lisp_Misc_Overlay:
6366 mark_overlay (XOVERLAY (obj)); 6513 mark_overlay (XOVERLAY (obj));
6367 break; 6514 break;
6515
6516 case Lisp_Misc_Finalizer:
6517 XMISCANY (obj)->gcmarkbit = 1;
6518 mark_object (XFINALIZER (obj)->function);
6519 break;
6368 6520
6369 default: 6521 default:
6370 emacs_abort (); 6522 emacs_abort ();
@@ -6746,6 +6898,8 @@ sweep_misc (void)
6746 { 6898 {
6747 if (mblk->markers[i].m.u_any.type == Lisp_Misc_Marker) 6899 if (mblk->markers[i].m.u_any.type == Lisp_Misc_Marker)
6748 unchain_marker (&mblk->markers[i].m.u_marker); 6900 unchain_marker (&mblk->markers[i].m.u_marker);
6901 if (mblk->markers[i].m.u_any.type == Lisp_Misc_Finalizer)
6902 unchain_finalizer (&mblk->markers[i].m.u_finalizer);
6749 /* Set the type of the freed object to Lisp_Misc_Free. 6903 /* Set the type of the freed object to Lisp_Misc_Free.
6750 We could leave the type alone, since nobody checks it, 6904 We could leave the type alone, since nobody checks it,
6751 but this might catch bugs faster. */ 6905 but this might catch bugs faster. */
@@ -7115,11 +7269,14 @@ init_alloc_once (void)
7115{ 7269{
7116 /* Even though Qt's contents are not set up, its address is known. */ 7270 /* Even though Qt's contents are not set up, its address is known. */
7117 Vpurify_flag = Qt; 7271 Vpurify_flag = Qt;
7272 gc_precise_p = (GC_MARK_STACK == GC_USE_GCPROS_AS_BEFORE);
7118 7273
7119 purebeg = PUREBEG; 7274 purebeg = PUREBEG;
7120 pure_size = PURESIZE; 7275 pure_size = PURESIZE;
7121 7276
7122 verify_alloca (); 7277 verify_alloca ();
7278 init_finalizer_list (&finalizers);
7279 init_finalizer_list (&doomed_finalizers);
7123 7280
7124#if GC_MARK_STACK || defined GC_MALLOC_CHECK 7281#if GC_MARK_STACK || defined GC_MALLOC_CHECK
7125 mem_init (); 7282 mem_init ();
@@ -7254,7 +7411,11 @@ do hash-consing of the objects allocated to pure space. */);
7254 doc: /* Accumulated time elapsed in garbage collections. 7411 doc: /* Accumulated time elapsed in garbage collections.
7255The time is in seconds as a floating point value. */); 7412The time is in seconds as a floating point value. */);
7256 DEFVAR_INT ("gcs-done", gcs_done, 7413 DEFVAR_INT ("gcs-done", gcs_done,
7257 doc: /* Accumulated number of garbage collections done. */); 7414 doc: /* Accumulated number of garbage collections done. */);
7415
7416 DEFVAR_BOOL ("gc-precise-p", gc_precise_p,
7417 doc: /* Non-nil means GC stack marking is precise.
7418Useful mainly for automated GC tests. Build time constant.*/);
7258 7419
7259 defsubr (&Scons); 7420 defsubr (&Scons);
7260 defsubr (&Slist); 7421 defsubr (&Slist);
@@ -7267,6 +7428,7 @@ The time is in seconds as a floating point value. */);
7267 defsubr (&Smake_bool_vector); 7428 defsubr (&Smake_bool_vector);
7268 defsubr (&Smake_symbol); 7429 defsubr (&Smake_symbol);
7269 defsubr (&Smake_marker); 7430 defsubr (&Smake_marker);
7431 defsubr (&Smake_finalizer);
7270 defsubr (&Spurecopy); 7432 defsubr (&Spurecopy);
7271 defsubr (&Sgarbage_collect); 7433 defsubr (&Sgarbage_collect);
7272 defsubr (&Smemory_limit); 7434 defsubr (&Smemory_limit);
diff --git a/src/lisp.h b/src/lisp.h
index fb436776121..37f3b28242b 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -488,6 +488,7 @@ enum Lisp_Misc_Type
488 Lisp_Misc_Marker, 488 Lisp_Misc_Marker,
489 Lisp_Misc_Overlay, 489 Lisp_Misc_Overlay,
490 Lisp_Misc_Save_Value, 490 Lisp_Misc_Save_Value,
491 Lisp_Misc_Finalizer,
491 /* Currently floats are not a misc type, 492 /* Currently floats are not a misc type,
492 but let's define this in case we want to change that. */ 493 but let's define this in case we want to change that. */
493 Lisp_Misc_Float, 494 Lisp_Misc_Float,
@@ -600,6 +601,7 @@ INLINE bool OVERLAYP (Lisp_Object);
600INLINE bool PROCESSP (Lisp_Object); 601INLINE bool PROCESSP (Lisp_Object);
601INLINE bool PSEUDOVECTORP (Lisp_Object, int); 602INLINE bool PSEUDOVECTORP (Lisp_Object, int);
602INLINE bool SAVE_VALUEP (Lisp_Object); 603INLINE bool SAVE_VALUEP (Lisp_Object);
604INLINE bool FINALIZERP (Lisp_Object);
603INLINE void set_sub_char_table_contents (Lisp_Object, ptrdiff_t, 605INLINE void set_sub_char_table_contents (Lisp_Object, ptrdiff_t,
604 Lisp_Object); 606 Lisp_Object);
605INLINE bool STRINGP (Lisp_Object); 607INLINE bool STRINGP (Lisp_Object);
@@ -610,6 +612,7 @@ INLINE bool (VECTORLIKEP) (Lisp_Object);
610INLINE bool WINDOWP (Lisp_Object); 612INLINE bool WINDOWP (Lisp_Object);
611INLINE bool TERMINALP (Lisp_Object); 613INLINE bool TERMINALP (Lisp_Object);
612INLINE struct Lisp_Save_Value *XSAVE_VALUE (Lisp_Object); 614INLINE struct Lisp_Save_Value *XSAVE_VALUE (Lisp_Object);
615INLINE struct Lisp_Finalizer *XFINALIZER (Lisp_Object);
613INLINE struct Lisp_Symbol *(XSYMBOL) (Lisp_Object); 616INLINE struct Lisp_Symbol *(XSYMBOL) (Lisp_Object);
614INLINE void *(XUNTAG) (Lisp_Object, int); 617INLINE void *(XUNTAG) (Lisp_Object, int);
615 618
@@ -2183,6 +2186,21 @@ XSAVE_OBJECT (Lisp_Object obj, int n)
2183 return XSAVE_VALUE (obj)->data[n].object; 2186 return XSAVE_VALUE (obj)->data[n].object;
2184} 2187}
2185 2188
2189/* A finalizer sentinel. We run FUNCTION when this value becomes
2190 unreachable. We treat these values specially in the GC to ensure
2191 that we still run the finalizer even if FUNCTION contains a
2192 reference to the finalizer; i.e., we run a finalizer's function
2193 when FUNCTION is reachable _only_ through finalizers. */
2194struct Lisp_Finalizer
2195 {
2196 struct Lisp_Misc_Any base;
2197 /* Circular list of all active weak references */
2198 struct Lisp_Finalizer *prev;
2199 struct Lisp_Finalizer *next;
2200 /* Called when this object becomes unreachable */
2201 Lisp_Object function;
2202 };
2203
2186/* A miscellaneous object, when it's on the free list. */ 2204/* A miscellaneous object, when it's on the free list. */
2187struct Lisp_Free 2205struct Lisp_Free
2188 { 2206 {
@@ -2202,6 +2220,7 @@ union Lisp_Misc
2202 struct Lisp_Marker u_marker; 2220 struct Lisp_Marker u_marker;
2203 struct Lisp_Overlay u_overlay; 2221 struct Lisp_Overlay u_overlay;
2204 struct Lisp_Save_Value u_save_value; 2222 struct Lisp_Save_Value u_save_value;
2223 struct Lisp_Finalizer u_finalizer;
2205 }; 2224 };
2206 2225
2207INLINE union Lisp_Misc * 2226INLINE union Lisp_Misc *
@@ -2243,6 +2262,14 @@ XSAVE_VALUE (Lisp_Object a)
2243 eassert (SAVE_VALUEP (a)); 2262 eassert (SAVE_VALUEP (a));
2244 return & XMISC (a)->u_save_value; 2263 return & XMISC (a)->u_save_value;
2245} 2264}
2265
2266INLINE struct Lisp_Finalizer *
2267XFINALIZER (Lisp_Object a)
2268{
2269 eassert (FINALIZERP (a));
2270 return & XMISC (a)->u_finalizer;
2271}
2272
2246 2273
2247/* Forwarding pointer to an int variable. 2274/* Forwarding pointer to an int variable.
2248 This is allowed only in the value cell of a symbol, 2275 This is allowed only in the value cell of a symbol,
@@ -2490,6 +2517,12 @@ SAVE_VALUEP (Lisp_Object x)
2490} 2517}
2491 2518
2492INLINE bool 2519INLINE bool
2520FINALIZERP (Lisp_Object x)
2521{
2522 return MISCP (x) && XMISCTYPE (x) == Lisp_Misc_Finalizer;
2523}
2524
2525INLINE bool
2493AUTOLOADP (Lisp_Object x) 2526AUTOLOADP (Lisp_Object x)
2494{ 2527{
2495 return CONSP (x) && EQ (Qautoload, XCAR (x)); 2528 return CONSP (x) && EQ (Qautoload, XCAR (x));
diff --git a/src/print.c b/src/print.c
index 1a0aebbeba7..d391fd5f7a3 100644
--- a/src/print.c
+++ b/src/print.c
@@ -2043,7 +2043,11 @@ print_object (Lisp_Object obj, Lisp_Object printcharfun, bool escapeflag)
2043 printcharfun); 2043 printcharfun);
2044 } 2044 }
2045 PRINTCHAR ('>'); 2045 PRINTCHAR ('>');
2046 break; 2046 break;
2047
2048 case Lisp_Misc_Finalizer:
2049 strout ("#<finalizer>", -1, -1, printcharfun);
2050 break;
2047 2051
2048 /* Remaining cases shouldn't happen in normal usage, but let's 2052 /* Remaining cases shouldn't happen in normal usage, but let's
2049 print them anyway for the benefit of the debugger. */ 2053 print them anyway for the benefit of the debugger. */