unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* OPEN-NETWORK-STREAM connection timeout control?
@ 2024-01-09 16:56 DD
  2024-01-13  4:55 ` DD
  0 siblings, 1 reply; 3+ messages in thread
From: DD @ 2024-01-09 16:56 UTC (permalink / raw)
  To: emacs-devel




Hello,

I'm using OPEN-NETWORK-STREAM and am unsure about how to control how
long to wait for the connection to be established or timed out.

I'm fairly sure I would like the call to be synchronous, at least
until the point of establishing/timing out the connection.  It might
turn out that, for responsiveness perhaps, I would want to include the
:nowait keyword, but hopefully I can ignore that for now, because
after reading the doc and looking at the code I have confusion around
how that works wrt, at least, how the process object is delivered.

I would like a customizable timeout value so that if a service is down
emacs stays responsive and times out quickly, since I'm only
interested in making local area connections in this situation and I
want to move on quickly to try fallback service(s)..

I hoped there was a timeout keyword for O-N-S, but didn't find it.  I
looked at lots of other stuff regarding the process object and got
really side tracked.

I guess I could set an async timer before the O-N-S call and then
explicitly squelch the O-N-S call if the timer expires before I reset
it after O-N-S returns?

I'm looking at the elisp code for an example and coming up empty.
Would be grateful for any help, especially around what is idiomatic in
this situation, as well as alternative approches.
q
Thanks!



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

* Re: OPEN-NETWORK-STREAM connection timeout control?
  2024-01-09 16:56 OPEN-NETWORK-STREAM connection timeout control? DD
@ 2024-01-13  4:55 ` DD
  2024-03-27  4:31   ` Derek Davies
  0 siblings, 1 reply; 3+ messages in thread
From: DD @ 2024-01-13  4:55 UTC (permalink / raw)
  To: DD; +Cc: emacs-devel



I've got the following, but I'm wondering: is the promise package in
wide use these days?  Promises seem to help with a lot of issues around
the command loop when waiting for the results of asynchronous calls.
I'm wondering if promises will be used more in the future or am I wrong
about them helping to clarify this kind of code?

The other thing I worry about is using process-status to poll for the
end of connection setup rather than a process sentinel.  It says in the
doc that the sentinel can skip states, but I don't think it will skip
"open" here since if it does get to open it will stay there, which means
that open will not be skipped since it's not intermittent, but isa
"last" state??  I don't really care either way, what's better?

To implement this without promises requires more state to be tracked for
the timeout, but the promises package hides that nicely.  I would like
to use promises more in the future -- is that I good idea?  Is there a
better way?


Thanks for your time and attention.

Derek

;; p.el -*- lexical-binding:t -*-

(require 'promise)

(defun do-connect (host port)
  (promise-new (lambda (resolve _reject)
                 (let (sobj)
                   (let ((name (concat "netcatbuf-" host ":" (format "%d" port)))
                         (output-buffer nil))
                     (setq sobj (open-network-stream name output-buffer host port :nowait t)))
                   (let ((stat (process-status sobj)))
                     (while (and ;; (not (equal stat 'connect))
                             (not (equal stat 'open))
                             (not (equal stat 'failed))
                             (not (equal stat 'closed))
                             (not (equal stat 'exit)))
                       (sleep-for 0.2)
                       (setq stat (process-status sobj)))
                     (cond
                      ((equal stat 'open)
                       (funcall resolve sobj))))))))

(defun do-netcat (host port text)
  (promise-chain
      (promise-wait 7.0 (do-connect host port))
    (then (lambda (res)
            (let ((pobj (cadr res)))
              (message "DLD| do-connection success")
              (process-send-string pobj text)
              ;; NB: TCP half shutdown is needed to trigger service processing.
              (delete-process pobj))))
    (promise-catch (lambda (reason)
                     (netcatbuf-text "janice" 7201 (message "DLD| do-connect failed due to %S" reason))))))

(do-netcat "sybil" 7201 "HI THERE HOW ARE YOU?")

;; Ends here.


DD <ddavies@ddavies.net> writes:

> Hello,
>
> I'm using OPEN-NETWORK-STREAM and am unsure about how to control how
> long to wait for the connection to be established or timed out.
>
> I'm fairly sure I would like the call to be synchronous, at least
> until the point of establishing/timing out the connection.  It might
> turn out that, for responsiveness perhaps, I would want to include the
> :nowait keyword, but hopefully I can ignore that for now, because
> after reading the doc and looking at the code I have confusion around
> how that works wrt, at least, how the process object is delivered.
>
> I would like a customizable timeout value so that if a service is down
> emacs stays responsive and times out quickly, since I'm only
> interested in making local area connections in this situation and I
> want to move on quickly to try fallback service(s)..
>
> I hoped there was a timeout keyword for O-N-S, but didn't find it.  I
> looked at lots of other stuff regarding the process object and got
> really side tracked.
>
> I guess I could set an async timer before the O-N-S call and then
> explicitly squelch the O-N-S call if the timer expires before I reset
> it after O-N-S returns?
>
> I'm looking at the elisp code for an example and coming up empty.
> Would be grateful for any help, especially around what is idiomatic in
> this situation, as well as alternative approches.
> q
> Thanks!




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

* Re: OPEN-NETWORK-STREAM connection timeout control?
  2024-01-13  4:55 ` DD
@ 2024-03-27  4:31   ` Derek Davies
  0 siblings, 0 replies; 3+ messages in thread
From: Derek Davies @ 2024-03-27  4:31 UTC (permalink / raw)
  To: DD; +Cc: emacs-devel



The following is what I've ended up with so far on this.  Sorry if this
is not appropriate, but I am still unsure how the promises package has
been doing in the field and I have other uses planned in my own
projects.  I haven't had any trouble using this (below) for my TTS
(I am blind) which I've used every day for months now.  Feedback/gotchas
appreciated.  Thanks.  If there's interest I can say more about the use
cases for this.

Derek

==



;; magoo-cat.el -*- lexical-binding:t -*-

(require 'promise)
(require 'cl)

(defcustom magoo-cat-connection-timeout 2.0 "")
(defcustom magoo-cat-response-timeout 2.0 "")

(defun magoo-cat (host port)
  
  (magoo-cat-text host port (magoo-cat-get-text)))

(defun magoo-cat-get-text ()
  (cond
   ((use-region-p)
    
    (buffer-substring
     (region-beginning)
     (region-end)))
   (t
    (thing-at-point 'line t))))

(defun magoo-cat-text (host port text &optional read-response-p)
  (promise-new (lambda (resolve reject)
                 (let ((ostr "")
                       (prom (promise-wait magoo-cat-connection-timeout (magoo-cat-cnx host port))))
                   (setf prom (promise-then prom
                                            (lambda (robj)
                                              (cl-assert (equal (car robj) :fullfilled))
                                              (let ((pobj (cadr robj)))
                                                ;; NB: The P.F. needs to be set before we send input or we can lose output.
                                                (set-process-filter pobj (lambda (p str)
                                                                           (setq ostr (concat ostr str))
                                                                           t))
                                                (process-send-string pobj text)
                                                (cond
                                                 (read-response-p
                                                  (sleep-for 0.02)
                                                  (delete-process pobj)
                                                  (list pobj (prog1 (substring ostr) (setf ostr ""))))
                                                 (t
                                                  (delete-process pobj)
                                                  t))))))
                   (setf prom (promise-then prom
                                            (lambda (robj)
                                              (cond
                                               ((not (eq t robj))
                                                (let ((pobj (car robj))
                                                      (ostr (cadr robj)))
                                                  (promise-chain
                                                      (promise-wait magoo-cat-response-timeout (magoo-cat-read-response pobj ostr))
                                                    (then (lambda (r2)
                                                            (funcall resolve r2)))
                                                    (promise-catch (lambda (read-err)
                                                                     (funcall reject :read-response-timeout))))))
                                               (t
                                                (cl-assert (eq robj t))
                                                (funcall resolve t))))))
                   (setf prom (promise-catch prom
                                             (lambda (robj)
                                               (funcall reject (list :connection-timeout robj)))))))))

(defun magoo-cat-cnx(host port)
  (promise-new (lambda (resolve reject)
                 (let (pobj)
                   (let ((name (concat "magoo-cat-" host ":" (format "%d" port)))
                         (output-buffer nil))
                     (setq pobj (open-network-stream name output-buffer host port :nowait t)))
                   (let ((stat (process-status pobj)))
                     (while (and ;; (not (equal stat 'connect))
                             (not (equal stat 'open))
                             (not (equal stat 'failed))
                             (not (equal stat 'closed))
                             (not (equal stat 'exit)))
                       (sleep-for 0.01)
                       (setq stat (process-status pobj)))
                     (cond
                      ((equal stat 'open)
                       (funcall resolve pobj))))))))

(defun magoo-cat-read-response (pobj ostr)
  (promise-new (lambda (resolve reject)
                 (sleep-for 0.02)
                 (while (accept-process-output pobj 0.2 nil t)
                   (sleep-for 0.02))
                 (cond
                  ((or (not ostr) (< (length ostr) 1))
                   (funcall reject :null-response))
                  (t
                   (funcall resolve ostr))))))


(provide 'magoo-cat)

;; Ends here.



DD <ddavies@ddavies.net> writes:

> I've got the following, but I'm wondering: is the promise package in
> wide use these days?  Promises seem to help with a lot of issues around
> the command loop when waiting for the results of asynchronous calls.
> I'm wondering if promises will be used more in the future or am I wrong
> about them helping to clarify this kind of code?
>
> The other thing I worry about is using process-status to poll for the
> end of connection setup rather than a process sentinel.  It says in the
> doc that the sentinel can skip states, but I don't think it will skip
> "open" here since if it does get to open it will stay there, which means
> that open will not be skipped since it's not intermittent, but isa
> "last" state??  I don't really care either way, what's better?
>
> To implement this without promises requires more state to be tracked for
> the timeout, but the promises package hides that nicely.  I would like
> to use promises more in the future -- is that I good idea?  Is there a
> better way?
>
>
> Thanks for your time and attention.
>
> Derek
>
> ;; p.el -*- lexical-binding:t -*-
>
> (require 'promise)
>
> (defun do-connect (host port)
>   (promise-new (lambda (resolve _reject)
>                  (let (sobj)
>                    (let ((name (concat "netcatbuf-" host ":" (format "%d" port)))
>                          (output-buffer nil))
>                      (setq sobj (open-network-stream name output-buffer host port :nowait t)))
>                    (let ((stat (process-status sobj)))
>                      (while (and ;; (not (equal stat 'connect))
>                              (not (equal stat 'open))
>                              (not (equal stat 'failed))
>                              (not (equal stat 'closed))
>                              (not (equal stat 'exit)))
>                        (sleep-for 0.2)
>                        (setq stat (process-status sobj)))
>                      (cond
>                       ((equal stat 'open)
>                        (funcall resolve sobj))))))))
>
> (defun do-netcat (host port text)
>   (promise-chain
>       (promise-wait 7.0 (do-connect host port))
>     (then (lambda (res)
>             (let ((pobj (cadr res)))
>               (message "DLD| do-connection success")
>               (process-send-string pobj text)
>               ;; NB: TCP half shutdown is needed to trigger service processing.
>               (delete-process pobj))))
>     (promise-catch (lambda (reason)
>                      (netcatbuf-text "janice" 7201 (message "DLD| do-connect failed due to %S" reason))))))
>
> (do-netcat "sybil" 7201 "HI THERE HOW ARE YOU?")
>
> ;; Ends here.
>
>
> DD <ddavies@ddavies.net> writes:
>
>> Hello,
>>
>> I'm using OPEN-NETWORK-STREAM and am unsure about how to control how
>> long to wait for the connection to be established or timed out.
>>
>> I'm fairly sure I would like the call to be synchronous, at least
>> until the point of establishing/timing out the connection.  It might
>> turn out that, for responsiveness perhaps, I would want to include the
>> :nowait keyword, but hopefully I can ignore that for now, because
>> after reading the doc and looking at the code I have confusion around
>> how that works wrt, at least, how the process object is delivered.
>>
>> I would like a customizable timeout value so that if a service is down
>> emacs stays responsive and times out quickly, since I'm only
>> interested in making local area connections in this situation and I
>> want to move on quickly to try fallback service(s)..
>>
>> I hoped there was a timeout keyword for O-N-S, but didn't find it.  I
>> looked at lots of other stuff regarding the process object and got
>> really side tracked.
>>
>> I guess I could set an async timer before the O-N-S call and then
>> explicitly squelch the O-N-S call if the timer expires before I reset
>> it after O-N-S returns?
>>
>> I'm looking at the elisp code for an example and coming up empty.
>> Would be grateful for any help, especially around what is idiomatic in
>> this situation, as well as alternative approches.
>> q
>> Thanks!




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

end of thread, other threads:[~2024-03-27  4:31 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-01-09 16:56 OPEN-NETWORK-STREAM connection timeout control? DD
2024-01-13  4:55 ` DD
2024-03-27  4:31   ` Derek Davies

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