unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* Re: [Emacs-diffs] fix/no-undo-boundary-on-secondary-buffer-change e5ff575 2/2: Add a timer to ensure undo-boundaries in buffers.
       [not found] ` <E1ZSik4-000326-4S@vcs.savannah.gnu.org>
@ 2015-08-30  3:30   ` Stefan Monnier
  2015-09-01 12:24     ` Phillip Lord
  0 siblings, 1 reply; 11+ messages in thread
From: Stefan Monnier @ 2015-08-30  3:30 UTC (permalink / raw)
  To: emacs-devel; +Cc: Phillip Lord

> +(defun undo-ensure-boundary ()
> +  "Add an `undo-boundary' if `buffer-undo-list' is long.
> +
> +Return t if an undo boundary has been added.
> +
> +Normally, Emacs will only garbage collect data from
> +`buffer-undo-list' when this list is longer than `undo-limit'. It
> +then collects data from after the first `undo-boundary' after
> +`undo-limit'. If there are no boundaries in the list,
> +`buffer-undo-list' will grow until it reaches
> +`undo-outer-limit' (by default a much larger value than
> +`undo-limit'), when it will discard the data anyway. In this
> +case, however, it is treated as an exceptional case, and the a
> +warning is signalled.
> +
> +This function add an `undo-boundary' to `buffer-undo-list' if
> +there is no other `undo-boundary', and `buffer-undo-list' is
> +longer than `undo-limit'. It provides a useful default mechanism
> +for adding an `undo-boundary' which retains data where possible,
> +without signalling warnings to the user."

I think this makes sense, but we should do it in the GC code, then.


        Stefan



^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [Emacs-diffs] fix/no-undo-boundary-on-secondary-buffer-change e5ff575 2/2: Add a timer to ensure undo-boundaries in buffers.
  2015-08-30  3:30   ` [Emacs-diffs] fix/no-undo-boundary-on-secondary-buffer-change e5ff575 2/2: Add a timer to ensure undo-boundaries in buffers Stefan Monnier
@ 2015-09-01 12:24     ` Phillip Lord
  2015-09-01 16:16       ` Stefan Monnier
  0 siblings, 1 reply; 11+ messages in thread
From: Phillip Lord @ 2015-09-01 12:24 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

Stefan Monnier <monnier@iro.umontreal.ca> writes:

>> +(defun undo-ensure-boundary ()
>> +  "Add an `undo-boundary' if `buffer-undo-list' is long.
>> +
>> +Return t if an undo boundary has been added.
>> +
>> +Normally, Emacs will only garbage collect data from
>> +`buffer-undo-list' when this list is longer than `undo-limit'. It
>> +then collects data from after the first `undo-boundary' after
>> +`undo-limit'. If there are no boundaries in the list,
>> +`buffer-undo-list' will grow until it reaches
>> +`undo-outer-limit' (by default a much larger value than
>> +`undo-limit'), when it will discard the data anyway. In this
>> +case, however, it is treated as an exceptional case, and the a
>> +warning is signalled.
>> +
>> +This function add an `undo-boundary' to `buffer-undo-list' if
>> +there is no other `undo-boundary', and `buffer-undo-list' is
>> +longer than `undo-limit'. It provides a useful default mechanism
>> +for adding an `undo-boundary' which retains data where possible,
>> +without signalling warnings to the user."
>
> I think this makes sense, but we should do it in the GC code, then.


It's possible, but it then negates the utility of the
`under-outer-limit' functionality. The point is that after
undo-outer-limit GC ditches the undo data anyway in the absence of any
boundaries, but does so noisily. But, if we run undo-ensure-boundary
before compaction, the boundary will mean the undo-list is always
GC'able with noise.

Phil





^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [Emacs-diffs] fix/no-undo-boundary-on-secondary-buffer-change e5ff575 2/2: Add a timer to ensure undo-boundaries in buffers.
  2015-09-01 12:24     ` Phillip Lord
@ 2015-09-01 16:16       ` Stefan Monnier
  2015-09-01 17:19         ` Phillip Lord
  0 siblings, 1 reply; 11+ messages in thread
From: Stefan Monnier @ 2015-09-01 16:16 UTC (permalink / raw)
  To: Phillip Lord; +Cc: emacs-devel

> It's possible, but it then negates the utility of the
> `under-outer-limit' functionality.  The point is that after
> undo-outer-limit GC ditches the undo data anyway in the absence of any
> boundaries, but does so noisily.  But, if we run undo-ensure-boundary
> before compaction, the boundary will mean the undo-list is always
> GC'able with noise.

Hmm... making under-outer-limit (virtually) useless would be good,
actually (and your patch also goes in that direction), but indeed doing
it in the GC doesn't work quite the way I hoped.

Basically, the problem with too-long undo steps is that we can't safely
cut them into smaller pieces once they're created (it's not clear what
is a safe cut-point).
[ I mean "safe" in a soft sense: it's not like we risk a crash.  ]

So pushing a boundary is safer, if we can assume that the current state
of the undo-log corresponds to a safe cut-point.

And for some reason I thought that indeed the current state
of the undo-log during GC would usually correspond to a safe cut-point,
but actually this is not really true.

So, yes, I now think your solution is better.

So let's go with your approach, but we need to fix an efficiency problem
with it: with the code you have now, we always run a timer every 10
seconds which always loops through all buffers.
For power-saving reasons, doing that 24/7 on a mostly-idle machine can
be a bad idea, so we should try and make sure we don't wake up Emacs
when there's nothing to do.

Here's a plan:
- change the C code so that it keeps track of a set of "buffers that
  might need a boundary".
- when we push an undo element, we add the corresponding buffer to that
  set.  If the set was empty, call an Elisp function (that can start
  the timer).
- BTW shouldn't `undo-size' be called `undo-step-size' since it should
  only concern itself with the size of the current step.

The "set" could be represented as a list (and the timer can limit its
scan to the given set of buffers), or as a boolean.

BTW, we could also use the same "set" to change the "post command push
undo boundary" so it pushes boundaries on all modified buffers (so we
can through away the "push boundary upon buffer change" which causes
problems for your code).


        Stefan



^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [Emacs-diffs] fix/no-undo-boundary-on-secondary-buffer-change e5ff575 2/2: Add a timer to ensure undo-boundaries in buffers.
  2015-09-01 16:16       ` Stefan Monnier
@ 2015-09-01 17:19         ` Phillip Lord
  2015-09-06 23:03           ` Stefan Monnier
  0 siblings, 1 reply; 11+ messages in thread
From: Phillip Lord @ 2015-09-01 17:19 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

Stefan Monnier <monnier@iro.umontreal.ca> writes:

>> It's possible, but it then negates the utility of the
>> `under-outer-limit' functionality.  The point is that after
>> undo-outer-limit GC ditches the undo data anyway in the absence of any
>> boundaries, but does so noisily.  But, if we run undo-ensure-boundary
>> before compaction, the boundary will mean the undo-list is always
>> GC'able with noise.
>
> Hmm... making under-outer-limit (virtually) useless would be good,

Yes, I think that having a bit of emacs which is there just to say to
the user "hey, this might be bug I want to tell you about". It might be
a sensible thing to leave in as a developer message (like "Garbage
Collecting....").



> actually (and your patch also goes in that direction), but indeed doing
> it in the GC doesn't work quite the way I hoped.
>
> Basically, the problem with too-long undo steps is that we can't safely
> cut them into smaller pieces once they're created (it's not clear what
> is a safe cut-point).
> [ I mean "safe" in a soft sense: it's not like we risk a crash.  ]
>
> So pushing a boundary is safer, if we can assume that the current state
> of the undo-log corresponds to a safe cut-point.

I don't think we can assume that is "safe" but at least (while emacs is
synchronous) is will be running *between* process interactions, if I
understand correctly. I guess a process interaction happens the C
library Emacs uses for each process decides, which is probably buffered
in many cases. Safe? Don't know. Can't think of anything better though,
abstracted from knowledge of the process.

The disadvantage of this is that when it happens it makes all of the
undo-log open for GC.


> And for some reason I thought that indeed the current state
> of the undo-log during GC would usually correspond to a safe cut-point,
> but actually this is not really true.

I guess that a GC can happen at any point at all, including in the
middle of a process interaction?.

> So, yes, I now think your solution is better.
>
> So let's go with your approach, but we need to fix an efficiency problem
> with it: with the code you have now, we always run a timer every 10
> seconds which always loops through all buffers.
> For power-saving reasons, doing that 24/7 on a mostly-idle machine can
> be a bad idea, so we should try and make sure we don't wake up Emacs
> when there's nothing to do.
>
> Here's a plan:
> - change the C code so that it keeps track of a set of "buffers that
>   might need a boundary".
> - when we push an undo element, we add the corresponding buffer to that
>   set.  If the set was empty, call an Elisp function (that can start
>   the timer).

Iff I do this, I would like to keep into elisp as much as possible.
Partly because the only reason I am doing this is too little of the undo
machinary can only be modified in C. Mostly, though, because I never
actually learned C and am doing it rather blind at the moment. I'm not
adverse to learning, of course, but that Emacs is big system to learn on.

I guess adding undo-pre-extend and undo-post-extend hooks should do the
trick? Run with the relevant buffer current. The post-extend one would
let me know if the last element is NOT a boundary. Then I can add the
buffer to the list and kick off the timer unless it's already running.
Then anyone could switch this behaviour off. Then undo-outer-limit would
become the final backstop.

Two issues. simple.el does not depend on timer, so I am not clear where
to add this. And, I am aware that `buffer-undo-list' is just a list.
Anyone can change it anywhere in lisp, which would make the hooks
difficult to enforce.


> - BTW shouldn't `undo-size' be called `undo-step-size' since it should
>   only concern itself with the size of the current step.

Yes. Probably. Or, better, to have both. I had to implement undo-size
because AFAICT, it's not possible to do from lisp.


>
> The "set" could be represented as a list (and the timer can limit its
> scan to the given set of buffers), or as a boolean.
>
> BTW, we could also use the same "set" to change the "post command push
> undo boundary" so it pushes boundaries on all modified buffers (so we
> can through away the "push boundary upon buffer change" which causes
> problems for your code).

Just to clarify, you are suggesting a post-command add undo-boundary,
rather than post-change (i.e. potentially many times per command)? I
would need to think on this.


Phil



^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [Emacs-diffs] fix/no-undo-boundary-on-secondary-buffer-change e5ff575 2/2: Add a timer to ensure undo-boundaries in buffers.
  2015-09-01 17:19         ` Phillip Lord
@ 2015-09-06 23:03           ` Stefan Monnier
  2015-09-14 14:41             ` RFM: How to make a buffer-local var in C layer Phillip Lord
  0 siblings, 1 reply; 11+ messages in thread
From: Stefan Monnier @ 2015-09-06 23:03 UTC (permalink / raw)
  To: Phillip Lord; +Cc: emacs-devel

> Iff I do this, I would like to keep into elisp as much as possible.

Yes, that's generally what we try to do.

> I guess adding undo-pre-extend and undo-post-extend hooks should do the
> trick?

Not sure.  There's also the desire to keep costs down.  Adding undo
elements can happen *very often* in some cases (for some commands which
perform many changes), so the case of adding an undo record in a buffer
that already has had other undo records added since the last undo
boundary should be fast and I'd rather not run undo-pre-extend and
undo-post-extend hooks in that case.

So I think we should have a hook run whenever we push the first change
after a boundary.  That would let you (from Elisp) add the buffer to
some set of "modified" buffers, start timers, etc...

> Then anyone could switch this behaviour off. Then undo-outer-limit would
> become the final backstop.

Sounds good.

> Two issues. simple.el does not depend on timer, so I am not clear where
> to add this.

I think you can add it to simple.el for a first attempt (timer.el is
preloaded anyway).  Bootstrap is always slightly delicate, and it's
difficult to guess in advance what problems will show up and how to best
fix them.  So just assume the gods will be with you for now, and if they
don't show up, we'll see what needs to be done.

> And, I am aware that `buffer-undo-list' is just a list.  Anyone can
> change it anywhere in lisp, which would make the hooks difficult
> to enforce.

It's a problem in theory, indeed, but in practice it should be mostly
a non-issue.  IOW, I wouldn't worry about it.

> Just to clarify, you are suggesting a post-command add undo-boundary,
> rather than post-change (i.e. potentially many times per command)? I
> would need to think on this.

I'm not suggesting it: that's what the code does currently (look for
Fundo_boundary in src/keyboard.c).


        Stefan



^ permalink raw reply	[flat|nested] 11+ messages in thread

* RFM: How to make a buffer-local var in C layer
  2015-09-06 23:03           ` Stefan Monnier
@ 2015-09-14 14:41             ` Phillip Lord
  2015-09-15  1:48               ` Stefan Monnier
  0 siblings, 1 reply; 11+ messages in thread
From: Phillip Lord @ 2015-09-14 14:41 UTC (permalink / raw)
  To: emacs-devel



Sorry for the total newbie noise, but I am trying to add a buffer-local
variable to the undo machinary. In emacs-lisp I would do...

(defvar-local undo-buffer-undoably-changed nil
  "Non-nil means that that the buffer has had a recent undo-able change.

Recent means since the value of this variable was last set explicitly to nil,
usually as part of the undo machinary.")

My C is very poor (I'm learning it as I go). But I *think* I need to do
this in the C layer, as I am going to use and set this directly from
within undo.c. Or am I wrong about this? Is it easier to just use the
lisp above and access it's value in C (not sure how do do that either).

If I need to do this in C, I have worked out how to add new variables to
Emacs in C, but it's the buffer-local bit that is causing problems. Do,
I need to do follow all the steps in buffer.c, which look something like

- call it "buffer-undoably-changed"
- add to the structure in buffer.h
- add a bset-* method
- do DEFVAR_PER_BUFFER in syms_of_buffer_c

The background to this is in this thread here:

https://lists.gnu.org/archive/html/emacs-devel/2015-09/msg00013.html

Sorry for dumb questions; this bit of Emacs is totally new to me.

Phil



^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: RFM: How to make a buffer-local var in C layer
  2015-09-14 14:41             ` RFM: How to make a buffer-local var in C layer Phillip Lord
@ 2015-09-15  1:48               ` Stefan Monnier
  2015-09-15 12:08                 ` Phillip Lord
  0 siblings, 1 reply; 11+ messages in thread
From: Stefan Monnier @ 2015-09-15  1:48 UTC (permalink / raw)
  To: Phillip Lord; +Cc: emacs-devel

> (defvar-local undo-buffer-undoably-changed nil
>   "Non-nil means that that the buffer has had a recent undo-able change.

This macroexpands to defvar + make-variable-buffer-local.

In C defvar turns into DEFVAR_LISP, and make-variable-buffer-local turns
into Fmake_variable_buffer_local.

fontification-functions is defined in the C code exactly that way.


        Stefan



^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: RFM: How to make a buffer-local var in C layer
  2015-09-15  1:48               ` Stefan Monnier
@ 2015-09-15 12:08                 ` Phillip Lord
  2015-09-15 14:18                   ` Stefan Monnier
  2015-09-15 14:20                   ` Stefan Monnier
  0 siblings, 2 replies; 11+ messages in thread
From: Phillip Lord @ 2015-09-15 12:08 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

Stefan Monnier <monnier@iro.umontreal.ca> writes:

>> (defvar-local undo-buffer-undoably-changed nil
>>   "Non-nil means that that the buffer has had a recent undo-able change.
>
> This macroexpands to defvar + make-variable-buffer-local.
>
> In C defvar turns into DEFVAR_LISP, and make-variable-buffer-local turns
> into Fmake_variable_buffer_local.
>
> fontification-functions is defined in the C code exactly that way.


Ah, perfect! Have that part working now. Next questions!

What is the difference between Vfontification_functions and
Qfontification_functions?

How do I set the buffer local value? Something like:

Fsetq(Qfontification_functions,Qt)

Phil



^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: RFM: How to make a buffer-local var in C layer
  2015-09-15 12:08                 ` Phillip Lord
@ 2015-09-15 14:18                   ` Stefan Monnier
  2015-09-15 14:20                   ` Stefan Monnier
  1 sibling, 0 replies; 11+ messages in thread
From: Stefan Monnier @ 2015-09-15 14:18 UTC (permalink / raw)
  To: Phillip Lord; +Cc: emacs-devel

> What is the difference between Vfontification_functions and
> Qfontification_functions?

Both are C variables of type Lisp_Object.
The "V" one contains the value of the variable
`fontification-functions', while the Q one contains the symbol
`fontification-functions'.

> How do I set the buffer local value? Something like:
> Fsetq(Qfontification_functions,Qt)

You could (tho calling Fset rather than Fsetq), but you can do it much
more simply/cheaply with:

   Vfontification_functions = Qt;


-- Stefan



^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: RFM: How to make a buffer-local var in C layer
  2015-09-15 12:08                 ` Phillip Lord
  2015-09-15 14:18                   ` Stefan Monnier
@ 2015-09-15 14:20                   ` Stefan Monnier
  2015-09-15 15:27                     ` Phillip Lord
  1 sibling, 1 reply; 11+ messages in thread
From: Stefan Monnier @ 2015-09-15 14:20 UTC (permalink / raw)
  To: Phillip Lord; +Cc: emacs-devel

> Fsetq(Qfontification_functions,Qt)

BTW, one notable difference (other than performance) between

    Fset (Qfontification_functions, Qt);
and
    Vfontification_functions = Qt;

is that the former will obey make-variable-buffer-local while the
latter won't.  So if you want the var to be buffer-local, either use the
former, or make sure to call Fmake_local_variable explicitly at
some point.


        Stefan



^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: RFM: How to make a buffer-local var in C layer
  2015-09-15 14:20                   ` Stefan Monnier
@ 2015-09-15 15:27                     ` Phillip Lord
  0 siblings, 0 replies; 11+ messages in thread
From: Phillip Lord @ 2015-09-15 15:27 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

Stefan Monnier <monnier@iro.umontreal.ca> writes:

> Both are C variables of type Lisp_Object.
> The "V" one contains the value of the variable
> `fontification-functions', while the Q one contains the symbol
> `fontification-functions'.

Ah, yes, okay.

>> Fsetq(Qfontification_functions,Qt)
>
> BTW, one notable difference (other than performance) between
>
>     Fset (Qfontification_functions, Qt);
> and
>     Vfontification_functions = Qt;
>
> is that the former will obey make-variable-buffer-local while the
> latter won't.  So if you want the var to be buffer-local, either use the
> former, or make sure to call Fmake_local_variable explicitly at
> some point.


Okay. Performance is irrelevant here, to be honest, so I've gone for
Fset.

I've pushed those changes up to my branch now and they appear to be
working. I think that I have all the C support that I need, and I'd
welcome any comments that you have (don't worry about all the commits
and change messages -- I'm planning on squashing before I'd think about
moving to master).

I'll add the emacs-lisp support next, which should be relatively
simple(.el).

Phil



^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2015-09-15 15:27 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <20150821093606.11577.60349@vcs.savannah.gnu.org>
     [not found] ` <E1ZSik4-000326-4S@vcs.savannah.gnu.org>
2015-08-30  3:30   ` [Emacs-diffs] fix/no-undo-boundary-on-secondary-buffer-change e5ff575 2/2: Add a timer to ensure undo-boundaries in buffers Stefan Monnier
2015-09-01 12:24     ` Phillip Lord
2015-09-01 16:16       ` Stefan Monnier
2015-09-01 17:19         ` Phillip Lord
2015-09-06 23:03           ` Stefan Monnier
2015-09-14 14:41             ` RFM: How to make a buffer-local var in C layer Phillip Lord
2015-09-15  1:48               ` Stefan Monnier
2015-09-15 12:08                 ` Phillip Lord
2015-09-15 14:18                   ` Stefan Monnier
2015-09-15 14:20                   ` Stefan Monnier
2015-09-15 15:27                     ` Phillip Lord

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).