Re: thread resource leaks if thread terminates with uncaught exception

Jens Thiele <[email protected]> Thu, 09 Jan 2025 09:25:23 +0100
Newsgroups gmane.lisp.scheme.gauche
Message-ID <[email protected]>
Jens Thiele <[email protected]> writes:

> Slightly off-topic/different topic (but this is how I ended up here):
> it looks like pmap with the fully-concurrent-mapper doesn't join all
> threads in run-map in the case where proc raised an error.

the reverse proxy example using pmap:

#!/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 gauche.sequence)
(use control.pmap)

;; todo: linux specific
(define (num-open-files)
  (guard (e
	  [else
	   +nan.0])
	 (length (directory-list "/proc/self/fd" :children? #t))))

(define (num-threads)
  (guard (e
	  [else
	   +nan.0])
	 (length (directory-list "/proc/self/task" :children? #t))))

(define-http-handler (GET) "/" (^[req app]
				 (receive (status headers body)
				     (http-get "localhost:8081" "/")
				   (respond/ok req body))))

(define (http-get-body)
  (receive (status headers body)
      ;; todo: detect host name and use it to force a real
      ;; dns lookup to occur?
      (http-get "localhost:8081" "/slow")
    body))

(define-http-handler (GET)
  "/timeout"
  (lambda(req app)
    (let1 r (guard (e
		    [(uncaught-exception-condition? e)
		     #?=(uncaught-exception-condition-reason e)]
		    [else #?=e])
		   (pmap (lambda(i) (http-get-body))
			 (iota 400)
			 :mapper (make-fully-concurrent-mapper
				  0.1 'timeout)))
      (cond [(and (list? r) (eq? (car r) 'timeout))
	     (respond/ok req
			 (list
			  (html-doctype)
			  (html:html
			   (html:head (html:title "timeout"))
			   (html:body
			    (html:p "timeout")
			    (html:p
			     (string-append
			      (x->string (num-open-files))
			      " open files"))))))]
	    [(list? r)
	     (car body)]
	    [else
	     (request-error :body r)]))))

(define (main args)
  #?=(sys-getpid)
  (debug-print-width 4000)
  (start-http-server :port 8080 :error-log #t)
  0)

I run it with setarch -R to "reduce the randomness" (the pointer
addresses still vary between runs) like this:

$ time setarch -R ./reverse-proxy-pmap.scm 2>&1|tee /tmp/log

and get relatively early:
#?="././reverse-proxy-pmap.scm":68:(sys-getpid)
#?-    1648580
#?=[5]"././reverse-proxy-pmap.scm":44:(uncaught-exception-condition-reason e)
#?-[5]    #<system-error "getaddrinfo failed: System error: Device or resource busy">
WARNING: A thread #f (11848) died a lonely death with an uncaught exception #<system-error "getaddrinfo failed: System error: Device or resource busy">.
WARNING: A thread #f (11830) died a lonely death with an uncaught exception #<system-error "getaddrinfo failed: System error: Device or resource busy">.
WARNING: A thread #f (11849) died a lonely death with an uncaught exception #<system-error "getaddrinfo failed: System error: Device or resource busy">.
WARNING: A thread #f (11839) died a lonely death with an uncaught exception #<system-error "getaddrinfo failed: System error: Device or resource busy">.
WARNING: A thread #f (11850) died a lonely death with an uncaught exception #<system-error "getaddrinfo failed: System error: Device or resource busy">.
...
WARNING: A thread #f (16812) died a lonely death with an uncaught exception #<system-error "getaddrinfo failed: System error: Device or resource busy">.
*** SYSTEM-ERROR: accept(2) failed: Too many open files
Stack Trace:
_______________________________________
  0  (report-error e)
  1  (socket-accept s)
        at "/usr/share/gauche-0.98/site/lib/makiki.scm":800
  2  (proc (car xs))
  3  (for-each (^h (apply (car h) (cdr h))) (append (pick-handlers ...
        at "/usr/share/gauche-0.98/0.9.15/lib/gauche/selector.scm":121
  4  (selector-select sel)
        at "/usr/share/gauche-0.98/site/lib/makiki.scm":819
  5  (do () ((not (unbox looping))) (selector-select sel))
        at "/usr/share/gauche-0.98/0.9.15/lib/gauche/common-macros.scm":104
        expanded from (while (unbox looping) (selector-select sel))
        at "/usr/share/gauche-0.98/site/lib/makiki.scm":819
  6  thunk
  7  (start-http-server :port 8080 :error-log #t)
        at "././reverse-proxy-pmap.scm":70
  8  (main args)

Best regards
Jens