Subject: bug#64423: 29.0.92; save-interprogram-paste-before-kill doesn't prevent streaming large selections

From: [email-masked]
Date: Tue, 04 Jul 2023 01:45:46 +0000 (UTC)

Po Lu <[email-masked]> writes:

> Spencer Baugh <[email-masked]> writes:
>
>> When you do that, you interrupt the operation which is trying to add a
>> new kill.  If you interrupt it and try again, you'll just get the same
>> long delay again.  There's no way to mitigate this from within Emacs,
>> other than by turning off save-interprogram-paste-before-kill.
>
> Then I guess the solution is to temporarily disable
> `save-interprogram-paste-before-kill' if a quit arrives while it is
> reading selection data.

That would be a decent solution.  Although I'm not sure how we'd
implement it.  We want to, somehow, know that after a selection-transfer
has been aborted, we should not try to transfer that selection again.
Is that something we can check?  Whether the selection has changed,
without transferring it?

>> Is there really no way to avoid a large data transfer in ICCCM?
>
> There is none.
>
>> Is there some way to learn the size of the selection in advance 
>
> Also no.  The selection owner may elect to provide a lower bound on the
> size of the selection data when initializing INCR transfer, but the
> requestor must be prepared for the owner to send transfer more data than
> that.
>
>> and then decide not to read it if it's too large?  That would also fit
>> the spec of save-interprogram-paste-before-kill.
>
> Once you start a selection transfer, it must complete 

That is unfortunate.  That seems like a terrible omission...  An
important network protocol principle is "tell the client up front how
much data you are going to send"...

Anyway, there's still a possible solution: we could return control to
the user if the transfer is too large, and continue with the INCR
transfer in the background, just to satisfy this ICCCM requirement,
discarding the data as we receive it.  This would be straightforward in
a program with a normal event loop, but might be difficult in Emacs...

>>> Additionally, the way we deal with potentially long-running data
>>> transfer operations in Emacs is not to apply a limit on the size of
>>> the data being transferred, but to make quitting possible within those
>>> transfers.  What may be slow for you can in fact be perfectly
>>> acceptable for others who are connected to their X servers through
>>> faster connections.
>>
>> Yes, that's why this is a customizable setting rather than hard-coded.
>
> That still contradicts my previous remark: Emacs should not place limits
> on the _amount_ of data transferred in the first place (except perhaps
> to avoid running out of VM) but allow the user to quit from long-running
> operations instead.
>
> Consider the case where selection data is being transferred from an
> owner connected to the X server over a connection with very high
> latency.  Waiting for a single quantum of selection data to become
> available (usually 64k) may still take several seconds, during which no
> data has been read.  Because of that, limiting the amount of selection
> data transferred will have no effect on this delay.

If the round-trip latency is 500ms, then waiting for the first quantum
of selection data will take at least 500ms, yes.  Subsequent quanta will
also take at least 500ms each.  If the selection is large, there may be
many.  If there are 20, then kill-new will take 10 seconds.  But if we
can limit the amount of selection data transferred, kill-new will only
take 500ms.

Wait... am I missing something?  You're saying it's okay for the user to
interactively choose to interrupt an INCR transfer, even though that
will leave things in a bad state?  Couldn't we just do the same thing in
code, then?  Can we wrap a user-customizable with-timeout around
gui-get-selection?

I actually agree now: limiting the amount of data transferred makes no
sense for user experience.  But limiting the *time spent* transferring
data makes total sense!  Users are able to do that today: We should
allow users to automate that!

So I think some new save-interprogram-paste-before-kill-timeout variable
would work perfectly.  All it would do is something users are already
capable of doing, but without aborting the entire kill-new operation.
That seems perfect!