Re: thread resource leaks if thread terminates with uncaught exception
Jens Thiele <[email protected]> Tue, 17 Dec 2024 10:41:32 +0100
| Newsgroups | gmane.lisp.scheme.gauche |
|---|---|
| Message-ID | <[email protected]> |
Jens Thiele <[email protected]> writes: > Jens Thiele <[email protected]> writes: > >> Hi, >> >> I have some resource leaks problems with a long running process. >> >> I still don't have a really simple test but I am getting closer to the >> problem. >> >> Let's say we have some webserver where some requests are really >> slow. For example using makiki: server.scm: >> >> #!/bin/sh >> #| -*- mode: scheme; coding: utf-8; -*- >> exec gosh -I. -- $0 "$@" >> |# >> (use makiki) >> (use text.html-lite) >> >> (define (hello) >> (list (html-doctype) >> (html:html >> (html:head (html:title "hello")) >> (html:body (html:p "hello"))))) >> >> (define-http-handler (GET) "/" (^[req app] (respond/ok req (hello)))) >> (define-http-handler (GET) "/slow" (^[req app] (sys-sleep 10) (respond/ok req (hello)))) >> >> (define (main args) >> (start-http-server :port 8081) >> 0) >> >> Then we have some reverse proxy also using makiki (that will be the >> process with the leaks). It tries to fetch many urls with a timeout in >> parallel and for some reason oaccasionally there might be an error (the >> "(when (= i 100) (error "i=100"))"). >> >> #!/bin/sh >> #| -*- mode: scheme; coding: utf-8; -*- >> #export GC_PRINT_STATS=1 >> exec gosh -I. -- $0 "$@" >> |# >> (use makiki) >> (use text.html-lite) >> (use rfc.http) >> (use gauche.threads) >> (use file.util) >> (use runtime-compile) >> >> (compile-and-load >> `((inline-stub >> (define-cproc get-gc-no () >> (let* ((r::(struct GC_prof_stats_s))) >> (GC_get_prof_stats (& r) (sizeof r)) >> (return (SCM_MAKE_INT (ref r gc_no))))))) >> '(get-gc-no)) >> >> ;; todo: linux specific >> (define (num-open-files) (length (directory-list "/proc/self/fd" :children? #t))) >> >> (define-http-handler (GET) "/" (^[req app] >> (receive (status headers body) >> (http-get "localhost:8081" "/") >> (respond/ok req body)))) >> >> (define-http-handler (GET) "/timeout" (^[req app] >> (let1 threads (map (lambda(i) >> (make-thread >> (lambda() >> ;; if i comment this one it works >> (when (= i 100) (error "i=100")) >> (receive (status headers body) >> (http-get "localhost:8081" "/slow") >> body)))) >> (iota 500)) > > if I use "only" 50 here - it looks like there is no problem in a real-world example the fetched urls might involve slow dns lookups a dns proxy to simulate slow dns lookups: #!/bin/sh #| -*- mode: scheme; coding: utf-8; -*- |# :; exec gosh -I. -- $0 "$@" ;; dns proxy hack to test some issues with slow dns requests ;; notes: ;; - only for testing! (use gauche.net) (use gauche.selector) (use gauche.uvector) (use gauche.sequence) (use gauche.process) (use util.list) (define (sockaddr->string addr) (string-append (inet-address->string (sockaddr-addr addr) (if (equal? (sockaddr-family addr) 'inet6) PF_INET6 PF_INET)) ":" (x->string (sockaddr-port addr)))) (define (udp-server addr port message-handler) (let ((selector (make <selector>)) (saddrs (make-sockaddrs addr port 'udp)) (buf (make-u8vector (ash 1 16)))) (for-each (lambda(saddr) (sockaddr-family saddr) (let1 socket (make-socket (if (equal? (sockaddr-family saddr) 'inet6) PF_INET6 PF_INET) SOCK_DGRAM) (print "udp server started on " (sockaddr->string saddr)) (socket-setsockopt socket SOL_SOCKET SO_REUSEADDR 1) (selector-add! selector (socket-fd (socket-bind socket saddr)) (lambda (input flag) (receive (got sender) (socket-recvfrom! socket buf #t) (message-handler selector socket sender (u8vector-copy buf 0 got)))) '(r)))) saddrs) (do () (#f) (selector-select selector)))) (define (verbose-socket-sendto socket msg to-address) (socket-sendto socket msg to-address) (print "forwarded " (size-of msg) " byte(s) to " (sockaddr->string to-address))) (define (proxy selector socket sender message dest delay) (print) (print "got " (size-of message) " byte(s) from " (sockaddr->string sender)) (let ((tmpsock (make-socket PF_INET SOCK_DGRAM))) (selector-add! selector (socket-fd tmpsock) (lambda(input flag) (let1 buf (make-u8vector (ash 1 16)) (receive (got from) (socket-recvfrom! tmpsock buf (list dest)) (print "received " got " byte(s) from " (sockaddr->string from)) (let1 message (u8vector-copy buf 0 got) ;; todo: use something non-blocking here and schedule packet for ;; delayed delivery (sys-sleep delay) (verbose-socket-sendto socket message sender) (selector-delete! selector (socket-fd tmpsock) #f #f) (socket-close tmpsock))))) '(r)) (verbose-socket-sendto tmpsock message dest))) (define (main args) (let-optionals* args ((prg "server") (addr #f) (port 53) (dest "192.168.178.1") (dest-port "domain") (delay "3")) (udp-server addr (x->number port) (cute proxy <> <> <> <> (ref (car (sys-getaddrinfo dest dest-port (make <sys-addrinfo> :sock-type SOCK_DGRAM))) 'addr) (x->number delay)))) 0) using this proxy in /etc/resolv.conf: $ cat /etc/resolv.conf nameserver 127.0.0.1 then running the reverse proxy test I get: "getaddrinfo failed: System error: Device or resource busy" in the threads quite fast and a little bit later "SYSTEM-ERROR: accept(2) failed: Too many open files" in the main thread but this doesn't really help to simplify the test Best regards Jens