emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Concurrency via isolated process/thread


From: Eli Zaretskii
Subject: Re: Concurrency via isolated process/thread
Date: Sun, 09 Jul 2023 19:35:23 +0300

> From: Ihor Radchenko <yantar92@posteo.net>
> Cc: Stefan Monnier <monnier@iro.umontreal.ca>, luangruo@yahoo.com,
>  emacs-devel@gnu.org
> Date: Sun, 09 Jul 2023 15:49:41 +0000
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> > ... then the buffer-local variables of that buffer present the same
> > problem as global variables.  Take the case-table or display-table,
> > for example: those are buffer-local in many cases, but their changes
> > will affect all the threads that work on the buffer.
> 
> And how frequently are case-table and display-table changed? AFAIK, not
> frequently at all.

Why does it matter?  Would you disallow programs doing that just
because you think it's infrequent?

Processing of various protocols frequently requires to do stuff like

   (with-case-table ascii-case-table
     DO SOMETHING...)

because otherwise some locales, such as the Turkish one, cause trouble
with case conversion of the upper-case 'I'.  So changing the
case-table of a buffer is not such an outlandish operation to do.

> >>    We need to ensure that simultaneous consing will never happen. AFAIU,
> >>    it should be ok if something that does not involve consing is running
> >>    at the same time with cons (correct me if I am wrong here).
> >
> > What do you do if some thread hits the memory-full condition?  The
> > current handling includes GC.
> 
> May you please explain a bit more about the situation you are referring
> to? My above statement was about consing, not GC.

Consing can trigger GC if we detect the memory-full situation, see
alloc.c:memory_full.

> For GC, as I mentioned earlier, we can pause each thread once maybe_gc()
> determines that GC is necessary, until all the threads are paused. Then,
> GC is executed and the threads continue.

If all the threads are paused, which thread will run GC?

> If an async thread want to request redisplay, it should be possible. But
> the redisplay itself must not be done by this same thread. Instead, the
> thread will send a request that Emacs needs redisplay and optionally
> block until that redisplay finishes (optionally, because something like
> displaying notification may not require waiting). The redisplay requests
> will be processed separately.

Ouch! this again kills opportunities for gains from concurrent
processing.  It means, for example, that we will be unable to run in a
thread some processing that affects a window and reflect that
processing on display when it's ready.

> Is Emacs display code even capable of redisplaying two different windows
> at the same time?

Maybe.  At least the GUI redisplay proceeds one window at a time, so
we already deal with each window separately.  There are some caveats,
naturally: redisplay runs hooks, which could access other windows,
redisplaying a window also updates the frame title, etc. -- those will
need to be carefully examined.

> > Also, that issue with prompting the user also needs some solution,
> > otherwise the class of jobs that non-main threads can do will be even
> > smaller.
> 
> We can make reading input using similar idea to the above, but it will
> always block until the response.

This will have to be designed and implemented first, since we
currently have no provision for multiple prompt sources.

> For non-blocking input, you said that it has been discussed.
> I do vaguely recall such discussion in the past and I even recall some
> ideas about it, but it would be better if you can link to that
> discussion, so that the participants of this thread can review the
> previously proposed ideas.

  https://lists.gnu.org/archive/html/emacs-devel/2018-08/msg00456.html

> >>    Only a single `main-thread' should be allowed to modify frames,
> >>    window configurations, and generally trigger redisplay. And thread
> >>    that attempts to do such modifications must wait to become
> >>    `main-thread' first.
> >
> > What about changes to frame-parameters?  Those don't necessarily
> > affect display.
> 
> But doesn't it depend on graphic toolkit?

Not necessarily: frame parameters are also used for "frame-local"
variables, and those have nothing to do with GUI toolkits.

> >>    This means that any code that is using things like
> >>    `save-window-excursion', `display-buffer', and other display-related
> >>    staff cannot run asynchronously.
> >
> > What about with-selected-window? also forbidden?
> 
> Yes.

Too bad.

> In addition, `with-selected-window' triggers redisplay. In particular,
> it triggers redisplaying mode-lines.

No, with-selected-window doesn't do any redisplay, it only marks the
window for redisplay, i.e. it suppresses the redisplay optimization
which could decide that this window doesn't need to be redrawn.
Redisplay itself will happen normally, usually when Emacs is idle,
i.e. when the command which called with-selected-window finishes.

> >>    Async threads will make an assumption that
> >>    (set-buffer "1") (goto-char 100) (set-buffer "2") (set-buffer "1")
> >>    (= (point) 100) invalid.
> >
> > If this is invalid, I don't see how one can write useful Lisp
> > programs, except of we request Lisp to explicitly define critical
> > sections.
> 
> Hmm. I realized that it is already invalid. At least, if `thread-yield'
> is triggered somewhere between `set-buffer' calls and other thread
> happens to move point in buffer "1".

But the programmer is in control!  If no such API is called, point
stays put.  And if such APIs are called, the program can save and
restore point around the calls.  By contrast, you want to be able to
pause and resume threads at will, which means a thread can be
suspended at any time.  So in this case, the programmer will be unable
to do anything against such calamities.

> >> > What if the main thread modifies buffer text, while one of the other
> >> > threads wants to read from it?
> >> 
> >> Reading and writing should be blocked while buffer is being modified.
> >
> > This will basically mean many/most threads will be blocked most of the
> > time.  Lisp programs in Emacs read and write buffers a lot, and the
> > notion of forcing a thread to work only on its own single set of
> > buffers is quite a restriction, IMO.
> 
> But not the same buffers!

I don't see why not.

> To be clear, I do know how this function is designed to work.
> It may not be de-facto pure, but that's just because nobody tried to
> ensure it - the usefulness of pure declarations is questionable in Emacs
> now.

But that's the case with most Emacs Lisp programs: we rarely try too
hard to make functions pure even if they can be pure.  What's more
important, most useful programs cannot be pure at all, because Emacs
is about text processing, which means modifying text, and good Emacs
Lisp programs process text in buffers, instead of returning strings,
which makes them not pure.

> >> There will indeed be a lot of work to make the range of Lisp functions
> >> available for async code large enough. But it does not have to be done
> >> all at once.
> >
> > No, it doesn't.  But until we have enough of those functions
> > available, one will be unable to write applications without
> > implementing and debugging a lot of those new functions as part of the
> > job.  It will make simple programming jobs much larger and more
> > complicated, especially since it will require the programmers to
> > understand very well the limitations and requirements of concurrent
> > code programming, something Lisp programmers don't know very well, and
> > rightfully so.
> 
> I disagree.

So let's agree to disagree.  Because I don't see how it will benefit
anything to continue this dispute.  I've said everything I have to
say, sometimes more than once.  At least Po Lu seems to agree that
concurrent Emacs means we will have to write a lot of new routines,
and that code which is meant to run in non-main threads will have to
be written very specially.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]