;;;; ;;;; thread.stk -- Threads support ;;;; ;;;; Copyright © 2006-2011 Erick Gallesio - I3S-CNRS/ESSI ;;;; ;;;; ;;;; This program is free software; you can redistribute it and/or modify ;;;; it under the terms of the GNU General Public License as published by ;;;; the Free Software Foundation; either version 2 of the License, or ;;;; (at your option) any later version. ;;;; ;;;; This program is distributed in the hope that it will be useful, ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;;;; GNU General Public License for more details. ;;;; ;;;; You should have received a copy of the GNU General Public License ;;;; along with this program; if not, write to the Free Software ;;;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, ;;;; USA. ;;;; ;;;; Author: Erick Gallesio [eg@essi.fr] ;;;; Creation date: 26-Jan-2006 22:56 (eg) ;;;; Last file update: 27-May-2011 23:10 (eg) ;;;; (define (%thread-timeout->seconds timeout) (cond ((time? timeout) (time->seconds timeout)) ((not timeout) timeout) ((real? timeout) (+ (time->seconds (current-time)) timeout)) (else (error "bad timeout ~S" timeout)))) #| |# (define (make-thread thunk :optional (name (symbol->string (gensym "thread"))) stack-size) (define (show-thread-error c) (when (and (condition? c) (condition-has-type? c &error-message)) (let* ((name (thread-name (current-thread))) (who (condition-ref c 'location)) (msg (condition-ref c 'message)) (bt (condition-ref c 'backtrace)) (loc (%build-error-location who bt))) (display (do-color 'bold 'red "**** Error " 'blue (format "(in thread ~S):\n" name) 'bold 'red (format "~A: ~A\n" (car loc) msg) 'clear " (this error may be signaled again later)\n") (current-error-port))))) (define (thread-handler c) (%thread-end-exception-set! (current-thread) c) (when (thread-handler-error-show) ;; show a message as soon as the error occurs instead of postponing ;; it until the thread is joined (show-thread-error c)) c) (%make-thread (lambda () (with-handler thread-handler (thunk))) name stack-size)) #| |# (define thread-handler-error-show (make-parameter #t)) (define (thread-sleep! timeout) (let ((n (%thread-timeout->seconds timeout))) (unless n (error 'thread-sleep! "cannot used #f as timeout")) (%thread-sleep! n))) (define (thread-join! thread :optional timeout (timeout-val #f timeout-val?)) (if (and (eq? thread (current-thread)) (not timeout)) (error 'thread-join! "cannot join on myself (deadlock will occur)") (let ((join (%thread-join! thread (%thread-timeout->seconds timeout)))) (cond (join ;; We had a timeout (if timeout-val? timeout-val (raise (make-condition &thread-join-timeout)))) ((%thread-end-exception thread) ;; We had an exception in thread. Raise an uncaught-exception (let ((old-exception (%thread-end-exception thread))) (raise (make-condition &uncaught-exception 'reason old-exception)))) (else ;; No exception. Return the thread-result (%thread-end-result thread)))))) ;; ********************************************************************** ;; ;; Mutexes ;; ;; ********************************************************************** (define (mutex-lock! mtx :optional timeout (thread (current-thread))) (let ((res (%mutex-lock! mtx (%thread-timeout->seconds timeout) thread))) ;; Different cases for res: ;; - The owning thread which is now terminated (a condition must be raised) ;; - #f: we had a timeout ;; - #t: otherwise (if (thread? res) (make-condition &thread-abandonned-mutex) res))) (define (mutex-unlock! mtx :optional condv timeout) (%mutex-unlock! mtx condv timeout)) ;; ********************************************************************** ;; ;; Conditions ;; ;; ********************************************************************** (%define-condition-type-accessors &thread-join-timeout &condition join-timeout-exception?) (%define-condition-type-accessors &thread-abandonned-mutex &condition abandoned-mutex-exception?) (%define-condition-type-accessors &thread-terminated &condition terminated-thread-exception?) ;; The following definitions correspond to the macro-expansion of the form ;; ;; (define-condition-type &uncaught-exception &condition ;; &uncaught-exception? ;; (reason uncaught-exception-reason)) :: ;; Using the macro-expansion avoid the auto-loading of SRFI-35 (define &uncaught-exception (make-condition-type '&uncaught-exception &condition '(reason))) (%define-condition-type-accessors &uncaught-exception &condition uncaught-exception? (reason uncaught-exception-reason)) #| |#