Dear Marc and list,
A universal HTTP client is taking form, it does connect & logics & IO in a singlethread setting now properly.
Would be happy to be able to cancel blocking read/write-subu8vector or ##device-port-wait-for-input/output! calls gracefully as to be able to abort HTTP connections directly and gracefully like this:
(define r (http-request url))
((r 'abort!)) ; From another thread than the one performing the HTTP request logics & IO. ; (Even if the abort is done at the level of HTTP request, due to the invalid ; protocol state the abort produces the connection needs to be taken down.)
The ways I can see this could be done by would be using
- close-port
- thread-interrupt! or ##thread-int! ??
- A low timeout on any read and write IO primitive invoked, like, 0.05 seconds, and check for the cancel condition on each timeout and if not reached, invoke the IO primitive again.
- thread-terminate! (The HTTP request may be running in the user's thread, so this would require redesign)
It'd be most preferable if this cancel could be done in such a way that
1) The abort action's effects are limited within the scope of the code for the respective HTTP request or connection so user code executed after the HTTP request code is not affected by it. This includes taking care automatically of any "mutex abandoned" exceptions or alike that would otherwise come as surprises later.
2) The thread performing the abort action does not get blocked in turn. (Something like that, the write-subu8vector would block a mutex in the IO system that would make the close-port call used to interrupt it, block until timeout.)
To help 1), I can ensure that a thread-interrupt! or alike call is not done after that the HTTP request code is done and the thread is back in user code, through making a round of locking some mutex at the end of the HTTP request and guaranteeing a thread-interrupt! never is made after that mutex was locked.
It's OK if the interrupt appears to the read/write-subu8vector caller as an IO failure i.e. #f return value.
While I believe I could come up with a solution, I'd be very happy to learn to know your take on it as to do this the best way?
Thanks! Mikael
Afficher les réponses par date
On 2013-03-18, at 9:50 AM, Mikael mikael.rcv@gmail.com wrote:
Dear Marc and list,
A universal HTTP client is taking form, it does connect & logics & IO in a singlethread setting now properly.
Would be happy to be able to cancel blocking read/write-subu8vector or ##device-port-wait-for-input/output! calls gracefully as to be able to abort HTTP connections directly and gracefully like this:
(define r (http-request url))
((r 'abort!)) ; From another thread than the one performing the HTTP request logics & IO. ; (Even if the abort is done at the level of HTTP request, due to the invalid ; protocol state the abort produces the connection needs to be taken down.)
The ways I can see this could be done by would be using
• close-port
• thread-interrupt! or ##thread-int! ??
• A low timeout on any read and write IO primitive invoked, like, 0.05 seconds, and check for the cancel condition on each timeout and if not reached, invoke the IO primitive again.
• thread-terminate! (The HTTP request may be running in the user's thread, so this would require redesign)
It'd be most preferable if this cancel could be done in such a way that
The abort action's effects are limited within the scope of the code for the respective HTTP request or connection so user code executed after the HTTP request code is not affected by it. This includes taking care automatically of any "mutex abandoned" exceptions or alike that would otherwise come as surprises later.
The thread performing the abort action does not get blocked in turn. (Something like that, the write-subu8vector would block a mutex in the IO system that would make the close-port call used to interrupt it, block until timeout.)
To help 1), I can ensure that a thread-interrupt! or alike call is not done after that the HTTP request code is done and the thread is back in user code, through making a round of locking some mutex at the end of the HTTP request and guaranteeing a thread-interrupt! never is made after that mutex was locked.
It's OK if the interrupt appears to the read/write-subu8vector caller as an IO failure i.e. #f return value.
While I believe I could come up with a solution, I'd be very happy to learn to know your take on it as to do this the best way?
Thanks! Mikael
I was going to suggest
(define (abort-io thread port) ;; force the thread to abort IO on the port (input-port-timeout-set! port -inf.0) (thread-interrupt! thread void))
which works by waking up the thread so that it will attempt the IO operation again after setting the timeout of the port so that it will raise a timeout. If the thread was done with the IO when abort-io is called, the thread will not observe a timeout or be terminated forcibly.
Unfortunately, thread-interrupt! has race condition issues. In particular, if the thread has already terminated (normally or not), the thread-interrupt! function will raise an exception. So this is better:
(define (abort-io thread port)
;; force the thread to abort IO on the port
(input-port-timeout-set! port -inf.0)
(with-exception-catcher (lambda (e) (if (inactive-thread-exception? e) (void) (raise e))) (lambda () (thread-interrupt! thread void))))
While it is logically correct, it is not perfect because thread-interrupt! currently has a bug when a started but not-executed-yet thread is interrupted. But this may not affect your code and I plan to fix the bug soon.
Marc
Great!!
Can't wait to try it out!
(lambda (e)
(if (inactive-thread-exception? e) (void) (raise e)))
This condition that can make it raise an exception, could that ever be of any practical value beyond typechecking of the thread?
Would probably be interested in making the exception-catcher return #!void always unless if there could come some valuable debug info out of there.
Thanks, Mikael
2013/3/18 Marc Feeley feeley@iro.umontreal.ca ..
I was going to suggest
(define (abort-io thread port) ;; force the thread to abort IO on the port (input-port-timeout-set! port -inf.0) (thread-interrupt! thread void))
which works by waking up the thread so that it will attempt the IO operation again after setting the timeout of the port so that it will raise a timeout. If the thread was done with the IO when abort-io is called, the thread will not observe a timeout or be terminated forcibly.
Unfortunately, thread-interrupt! has race condition issues. In particular, if the thread has already terminated (normally or not), the thread-interrupt! function will raise an exception. So this is better:
(define (abort-io thread port)
;; force the thread to abort IO on the port
(input-port-timeout-set! port -inf.0)
(with-exception-catcher (lambda (e) (if (inactive-thread-exception? e) (void) (raise e))) (lambda () (thread-interrupt! thread void))))
While it is logically correct, it is not perfect because thread-interrupt! currently has a bug when a started but not-executed-yet thread is interrupted. But this may not affect your code and I plan to fix the bug soon.
Marc
On 2013-03-20, at 1:09 PM, Mikael mikael.rcv@gmail.com wrote:
Great!!
Can't wait to try it out!
(lambda (e) (if (inactive-thread-exception? e) (void) (raise e)))
This condition that can make it raise an exception, could that ever be of any practical value beyond typechecking of the thread?
That exception is not raised due to a type check failure. It is raised when the state of the thread is inactive (i.e. it has stopped running). The intent is to inform the caller that the interrupt will not happen in the target thread because it is terminated. I admit that this exception is of limited use because there is a race condition (the interrupt could be requested just before the thread terminates, but after the last point where interrupts are checked, so the interrupt will not be serviced).
Would probably be interested in making the exception-catcher return #!void always unless if there could come some valuable debug info out of there.
Sure. It is up to you.
Marc
On 2013-03-18, at 11:32 AM, Marc Feeley feeley@iro.umontreal.ca wrote:
On 2013-03-18, at 9:50 AM, Mikael mikael.rcv@gmail.com wrote:
Dear Marc and list,
A universal HTTP client is taking form, it does connect & logics & IO in a singlethread setting now properly.
Would be happy to be able to cancel blocking read/write-subu8vector or ##device-port-wait-for-input/output! calls gracefully as to be able to abort HTTP connections directly and gracefully like this:
(define r (http-request url))
((r 'abort!)) ; From another thread than the one performing the HTTP request logics & IO. ; (Even if the abort is done at the level of HTTP request, due to the invalid ; protocol state the abort produces the connection needs to be taken down.)
The ways I can see this could be done by would be using
• close-port
• thread-interrupt! or ##thread-int! ??
• A low timeout on any read and write IO primitive invoked, like, 0.05 seconds, and check for the cancel condition on each timeout and if not reached, invoke the IO primitive again.
• thread-terminate! (The HTTP request may be running in the user's thread, so this would require redesign)
It'd be most preferable if this cancel could be done in such a way that
The abort action's effects are limited within the scope of the code for the respective HTTP request or connection so user code executed after the HTTP request code is not affected by it. This includes taking care automatically of any "mutex abandoned" exceptions or alike that would otherwise come as surprises later.
The thread performing the abort action does not get blocked in turn. (Something like that, the write-subu8vector would block a mutex in the IO system that would make the close-port call used to interrupt it, block until timeout.)
To help 1), I can ensure that a thread-interrupt! or alike call is not done after that the HTTP request code is done and the thread is back in user code, through making a round of locking some mutex at the end of the HTTP request and guaranteeing a thread-interrupt! never is made after that mutex was locked.
It's OK if the interrupt appears to the read/write-subu8vector caller as an IO failure i.e. #f return value.
While I believe I could come up with a solution, I'd be very happy to learn to know your take on it as to do this the best way?
Thanks! Mikael
I was going to suggest
(define (abort-io thread port) ;; force the thread to abort IO on the port (input-port-timeout-set! port -inf.0) (thread-interrupt! thread void))
which works by waking up the thread so that it will attempt the IO operation again after setting the timeout of the port so that it will raise a timeout. If the thread was done with the IO when abort-io is called, the thread will not observe a timeout or be terminated forcibly.
Unfortunately, thread-interrupt! has race condition issues. In particular, if the thread has already terminated (normally or not), the thread-interrupt! function will raise an exception. So this is better:
(define (abort-io thread port)
;; force the thread to abort IO on the port
(input-port-timeout-set! port -inf.0)
(with-exception-catcher (lambda (e) (if (inactive-thread-exception? e) (void) (raise e))) (lambda () (thread-interrupt! thread void))))
While it is logically correct, it is not perfect because thread-interrupt! currently has a bug when a started but not-executed-yet thread is interrupted. But this may not affect your code and I plan to fix the bug soon.
Marc
After further investigation it appears that there is a simpler approach. Simply defining
(define (abort-io port) (input-port-timeout-set! port -inf.0))
is sufficient. There is no need for the thread-interrupt! call because the functions input/output-port-timeout-set! contain the logic to force the waiting thread to wakeup.
Marc
:D Shining!
2013/4/3 Marc Feeley feeley@iro.umontreal.ca ..
After further investigation it appears that there is a simpler approach. Simply defining
(define (abort-io port) (input-port-timeout-set! port -inf.0))
is sufficient. There is no need for the thread-interrupt! call because the functions input/output-port-timeout-set! contain the logic to force the waiting thread to wakeup.
Marc