unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* Threads vs url-http
@ 2024-09-29  0:28 813gan
  2024-09-29  5:26 ` Eli Zaretskii
  0 siblings, 1 reply; 6+ messages in thread
From: 813gan @ 2024-09-29  0:28 UTC (permalink / raw)
  To: emacs-devel@gnu.org

[-- Attachment #1: Type: text/plain, Size: 880 bytes --]

Hi.
I attempted to write thread-friendly wrapper around url-http and it appears to hang Emacs.
Am i doing something wrong or it's bug?

(require 'url-http)
(require 'url-parse)
(defun url-retrieve-thread (url)
"Wrapper around url-http that download `URL' without blocking main thread."
(let* ((parsed-url (if (url-p url) url (url-generic-parse-url url)))
(mut (make-mutex))
(cond-var (make-condition-variable mut))
(buf nil)
(cb (lambda (&rest _) (setq buf (current-buffer)) (with-mutex mut (condition-notify cond-var)) )) )
(url-http parsed-url cb nil)
(with-mutex mut
(condition-wait cond-var) )
buf))

Idea was to call it from thread. In most trivial case:
(make-thread (apply 'url-retrieve-thread "http://example.com" nil))

It makes Emacs unresponsive (even C-g does not work).
I tested it on Emacs 28.2 and once of recent development versions (emacs-31.0.50)

Happy Hacking!

[-- Attachment #2: Type: text/html, Size: 2090 bytes --]

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

* Re: Threads vs url-http
  2024-09-29  0:28 Threads vs url-http 813gan
@ 2024-09-29  5:26 ` Eli Zaretskii
  2024-09-29 23:53   ` 813gan
  0 siblings, 1 reply; 6+ messages in thread
From: Eli Zaretskii @ 2024-09-29  5:26 UTC (permalink / raw)
  To: 813gan; +Cc: emacs-devel

> Date: Sun, 29 Sep 2024 00:28:36 +0000
> From: 813gan <813gan@protonmail.com>
> 
> (require 'url-http)
> (require 'url-parse)
> (defun url-retrieve-thread (url)
>   "Wrapper around url-http that download `URL' without blocking main thread."
>   (let* ((parsed-url (if (url-p url) url (url-generic-parse-url url)))
> (mut (make-mutex))
> (cond-var (make-condition-variable mut))
> (buf nil)
> (cb (lambda (&rest _) (setq buf (current-buffer)) (with-mutex mut (condition-notify cond-var)) )) )
>     (url-http parsed-url cb nil)
>     (with-mutex mut
>       (condition-wait cond-var) )
>     buf))
> 
> Idea was to call it from thread. In most trivial case:
> (make-thread (apply 'url-retrieve-thread "http://example.com" nil))

You are waiting on a condvar in the same thread which should notify
the condvar, don't you?  The callback passed to url-http will be
called in the context of the same thread in which url-http was called,
but that thread is meanwhile blocked in the condition-wait you call
after url-http.

AFAIU your intent, you should wait on the condvar in the main thread,
not in the thread which calls url-http.

> It makes Emacs unresponsive (even C-g does not work).

Yes, because it's deadlock: a thread is waiting in a condvar which no
other thread will call.



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

* Re: Threads vs url-http
  2024-09-29  5:26 ` Eli Zaretskii
@ 2024-09-29 23:53   ` 813gan
  2024-09-30  7:38     ` Michael Albinus
  2024-09-30 12:22     ` Eli Zaretskii
  0 siblings, 2 replies; 6+ messages in thread
From: 813gan @ 2024-09-29 23:53 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

On Sunday, September 29th, 2024 at 07:26, Eli Zaretskii <eliz@gnu.org> wrote:

> 
> 
> > Date: Sun, 29 Sep 2024 00:28:36 +0000
> 
> > From: 813gan 813gan@protonmail.com
> > 
> > (require 'url-http)
> > (require 'url-parse)
> > (defun url-retrieve-thread (url)
> > "Wrapper around url-http that download `URL' without blocking main thread."
> > (let* ((parsed-url (if (url-p url) url (url-generic-parse-url url)))
> > (mut (make-mutex))
> > (cond-var (make-condition-variable mut))
> > (buf nil)
> > (cb (lambda (&rest _) (setq buf (current-buffer)) (with-mutex mut (condition-notify cond-var)) )) )
> > (url-http parsed-url cb nil)
> > (with-mutex mut
> > (condition-wait cond-var) )
> > buf))
> > 
> > Idea was to call it from thread. In most trivial case:
> > (make-thread (apply 'url-retrieve-thread "http://example.com" nil))
> 
> 
> You are waiting on a condvar in the same thread which should notify
> the condvar, don't you? The callback passed to url-http will be
> called in the context of the same thread in which url-http was called,
> but that thread is meanwhile blocked in the condition-wait you call
> after url-http.
> 
> AFAIU your intent, you should wait on the condvar in the main thread,
> not in the thread which calls url-http.
> 
> > It makes Emacs unresponsive (even C-g does not work).
> 
> 
> Yes, because it's deadlock: a thread is waiting in a condvar which no
> other thread will call.

Yeah. my understanding of parallelism seems to be worst than i excepted.

Other implementation (naive?) follows.

(defun wiki-read--url-retrieve (url)
  "Wrapper around url-http that download `URL' without blocking thread."
  (let* ((parsed-url (if (url-p url) url (url-generic-parse-url url)))
	 (buf nil)
	 (cb (lambda (&rest _) (setq buf (current-buffer)) )) )
    (url-https parsed-url cb nil)
    (while (not buf) (thread-yield))
    buf))
(make-thread (apply 'wiki-read--url-retrieve "https://google.com" nil))

Why thread-yield don't unblock this?

Thanks



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

* Re: Threads vs url-http
  2024-09-29 23:53   ` 813gan
@ 2024-09-30  7:38     ` Michael Albinus
  2024-09-30 12:22     ` Eli Zaretskii
  1 sibling, 0 replies; 6+ messages in thread
From: Michael Albinus @ 2024-09-30  7:38 UTC (permalink / raw)
  To: 813gan; +Cc: Eli Zaretskii, emacs-devel

813gan <813gan@protonmail.com> writes:

Hi,

> (defun wiki-read--url-retrieve (url)
>   "Wrapper around url-http that download `URL' without blocking thread."
>   (let* ((parsed-url (if (url-p url) url (url-generic-parse-url url)))
> 	 (buf nil)
> 	 (cb (lambda (&rest _) (setq buf (current-buffer)) )) )
>     (url-https parsed-url cb nil)
>     (while (not buf) (thread-yield))
>     buf))
> (make-thread (apply 'wiki-read--url-retrieve "https://google.com" nil))
>
> Why thread-yield don't unblock this?

Because url-https blocks alrready, I believe.

There's also another trap when using threads with url-https, see
bug#73199. I'm working on a fix.

> Thanks

Best regards, Michael.



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

* Re: Threads vs url-http
  2024-09-29 23:53   ` 813gan
  2024-09-30  7:38     ` Michael Albinus
@ 2024-09-30 12:22     ` Eli Zaretskii
  2024-10-06 15:06       ` 813gan
  1 sibling, 1 reply; 6+ messages in thread
From: Eli Zaretskii @ 2024-09-30 12:22 UTC (permalink / raw)
  To: 813gan; +Cc: emacs-devel

> Date: Sun, 29 Sep 2024 23:53:44 +0000
> From: 813gan <813gan@protonmail.com>
> Cc: emacs-devel@gnu.org
> 
> (defun wiki-read--url-retrieve (url)
>   "Wrapper around url-http that download `URL' without blocking thread."
>   (let* ((parsed-url (if (url-p url) url (url-generic-parse-url url)))
> 	 (buf nil)
> 	 (cb (lambda (&rest _) (setq buf (current-buffer)) )) )
>     (url-https parsed-url cb nil)
>     (while (not buf) (thread-yield))
>     buf))
> (make-thread (apply 'wiki-read--url-retrieve "https://google.com" nil))
> 
> Why thread-yield don't unblock this?

make-thread needs a function as its first argument.  But you pass it
the value returned by 'apply', which is not a function at all.  So
what happens here, I think, is this:

  . Emacs invokes 'apply' to evaluate the argument of make-thread
  . 'apply' invokes your wiki-read--url-retrieve function
  . the function runs in the main thread, not in the thread started by
    make-thread (which is not even called, because the code is stuck
    in 'apply', see below)
  . after url-https returns (which it does immediately), the code
    immediately enters the while-loop, where it calls thread-yield
  . because there's only one running thread, thread-yield grabs the
    global lock immediately after releasing it, and the loop keeps
    looping
  . because the program loops continuously, it doesn't let url-https
    read from the network process (since Emacs only does that when it
    is idle)
  . so the output of the network sub-process is never read, and so the
    sub-process never ends, and so the callback is never called, and
    the loop never ends



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

* Re: Threads vs url-http
  2024-09-30 12:22     ` Eli Zaretskii
@ 2024-10-06 15:06       ` 813gan
  0 siblings, 0 replies; 6+ messages in thread
From: 813gan @ 2024-10-06 15:06 UTC (permalink / raw)
  To: Eli Zaretskii, michael.albinus@gmx.de, emacs-devel@gnu.org


On 9/30/24 14:22, Eli Zaretskii <eliz@gnu.org> wrote:
>  > Why thread-yield don't unblock this?
>  
>  make-thread needs a function as its first argument.  But you pass it
>  the value returned by 'apply', which is not a function at all.  

What a horrible thinko...
Thanks.

On 9/30/24 09:38, Michael Albinus <michael.albinus@gmx.de> wrote:

>  There's also another trap when using threads with url-https, see
>  bug#73199. I'm working on a fix.

I will keep thst in mind. Thanks.




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

end of thread, other threads:[~2024-10-06 15:06 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-09-29  0:28 Threads vs url-http 813gan
2024-09-29  5:26 ` Eli Zaretskii
2024-09-29 23:53   ` 813gan
2024-09-30  7:38     ` Michael Albinus
2024-09-30 12:22     ` Eli Zaretskii
2024-10-06 15:06       ` 813gan

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).