Re: schemeunit: How best to discover exceptions in child threads?

Ryan Culpepper <[email protected]> Thu, 16 Aug 2007 17:52:11 -0400
Newsgroups gmane.lisp.scheme.plt.schematics
Message-ID <[email protected]>
On Aug 16, 2007, at 3:48 PM, Eric Hanchrow wrote:

> Here's a problem which I've already solved, but I'm not in love with
> the solution, and am wondering if there's a better way.
>
> I have some functions that create threads, and sometimes those threads
> raise uncaught exceptions.  And sometimes, alas, my tests don't notice
> (because the dead thread didn't directly contribute to the result my
> tests are looking for).  And sometimes I don't notice the exception,
> since its only evidence is a few lines of output, which tend to get
> lost amongst all the other output.
>
> So I'd like a way for the tests to fail when a child thread raises an
> uncaught exception.  Attached is the way I've found to do it, but it
> just seems clunky.  Can someone suggest a better way?
> <thread-trouble.ss>

I would combine the check of the result with the check that no child  
thread threw an exception into one test, because they are part of the  
same overall computation. As a rule, I try to keep my test cases  
totally independent. In your example, the execution of the second test  
depends on the state left by the first test.

Here's a procedure that might help. It's similar to your code, but it's  
designed to be used locally within a test-case. It also checks that any  
auxiliary threads are no longer running when the main computation  
finishes. You may have to customize it to get what you want.

;; check-threads : (-> any) -> any
;; Runs thunk; raises error if any subthreads created by thunk raise
;; errors, or if a subthread outlives thunk's computation.
(define (check-threads thunk)
   (let* ([main-cust (current-custodian)]
          [sub-cust (make-custodian main-cust)]
          [sub-exns null]
          [old-uncaught-exception-handler (uncaught-exception-handler)])
     (parameterize ((current-custodian sub-cust)
                    (uncaught-exception-handler
                     (lambda (exn)
                       (set! sub-exns (cons exn sub-exns))
                       (old-uncaught-exception-handler exn))))
       (let ([result (thunk)])
         ;; Check that sub-custodian has no living threads.
         (let ([threads-still-going?
                (ormap thread-running?
                       (filter thread? (custodian-managed-list sub-cust  
main-cust)))])
           (when (pair? sub-exns)
             (error 'nice-threads "child thread raised exception"))
           (when threads-still-going?
             (error 'nice-threads "child thread left still running"))
           result)))))

;; error: child thread left still running
(check-threads
  (lambda ()
    (thread (lambda () (sleep 1) 17))
    1))

;; error: child thread raised exception
(check-threads
  (lambda ()
    (thread (lambda () (error "whoops")))
    1))

;; okay
(check-threads
  (lambda ()
    (let ([t (thread (lambda () (sleep 1) 17))])
      (thread-wait t)
      18)))

Ryan


> --  
> A bad analogy is like a leaky screwdriver.
>         -- Richard Braakman
> ----------------------------------------------------------------------- 
> --
> This SF.net email is sponsored by: Splunk Inc.
> Still grepping through log files to find problems?  Stop.
> Now Search log events and configuration files using AJAX and a browser.
> Download your FREE copy of Splunk now >>   
> http://get.splunk.com/_______________________________________________
> Schematics-development mailing list
> Schematics-development-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org
> https://lists.sourceforge.net/lists/listinfo/schematics-development


-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >>  http://get.splunk.com/