;;; srfi-18.scm --- Multithreading support ;; Copyright (C) 2007 Free Software Foundation, Inc. ;; ;; This library is free software; you can redistribute it and/or ;; modify it under the terms of the GNU Lesser General Public ;; License as published by the Free Software Foundation; either ;; version 2.1 of the License, or (at your option) any later version. ;; ;; This library 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 ;; Lesser General Public License for more details. ;; ;; You should have received a copy of the GNU Lesser General Public ;; License along with this library; if not, write to the Free Software ;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ;;; Author: Julian Graham ;;; Date: 2007-10-27 ;;; Commentary: ;; This is an implementation of SRFI-18 (Multithreading support). ;; ;; All procedures defined in SRFI-18, which are not already defined in ;; the Guile core library, are exported. ;; ;; This module is fully documented in the Guile Reference Manual. ;;; Code: (define-module (srfi srfi-18) :export ( ;;; Threads ;; current-thread <= in the core ;; thread? <= in the core make-thread thread-name thread-specific thread-specific-set! thread-start! thread-yield! thread-sleep! thread-terminate! thread-join! ;;; Mutexes ;; mutex? <= in the core make-mutex mutex-name mutex-specific mutex-specific-set! ;; mutex-state <= in the core mutex-lock! mutex-unlock! ;;; Condition variables ;; condition-variable? <= in the core make-condition-variable condition-variable-name condition-variable-specific condition-variable-specific-set! condition-variable-signal! condition-variable-broadcast! condition-variable-wait! ;;; Time current-time time? time->seconds seconds->time current-exception-handler with-exception-handler raise join-timeout-exception? abandoned-mutex-exception? terminated-thread-exception? uncaught-exception? uncaught-exception-reason ) :re-export (thread? mutex? condition-variable?) :replace (current-time raise make-thread make-mutex make-condition-variable) ) (cond-expand-provide (current-module) '(srfi-18)) (define (unspecified) (display "")) (define (check-arg-type pred arg caller) (if (pred arg) arg (scm-error 'wrong-type-arg caller "Wrong type argument: ~S" (list arg) '()))) (define object-names (make-weak-key-hash-table)) (define object-specifics (make-weak-key-hash-table)) (define thread-start-conds (make-weak-key-hash-table)) (define thread-exception-handlers (make-weak-key-hash-table)) ;; EXCEPTIONS (define (initial-handler key . args) (throw 'uncaught-exception key args)) (define (current-handler-stack) (let ((ct (current-thread))) (or (hashq-ref thread-exception-handlers ct) (hashq-set! thread-exception-handlers ct (list initial-handler))))) (define (current-exception-handler) (car (current-handler-stack))) (define (with-exception-handler handler thunk) (let ((ct (current-thread)) (hl (current-handler-stack))) (check-arg-type procedure? handler "with-exception-handler") (check-arg-type thunk? thunk "with-exception-handler") (hashq-set! thread-exception-handlers ct (cons handler hl)) (catch #t (lambda () (let ((r (thunk))) (hashq-set! thread-exception-handlers ct hl) r)) (lambda (key . args) (hashq-set! thread-exception-handlers ct hl) (apply handler (cons key args)))))) (define raise throw) (define (join-timeout-exception? obj) (eq? obj 'join-timeout-exception)) (define (abandoned-mutex-exception obj) (eq? obj 'abandoned-mutex-exception)) (define (uncaught-exception? obj) (and (pair? obj) (eq? (car obj) 'uncaught-exception))) (define (uncaught-exception-reason exc) (cdr (check-arg-type uncaught-exception? exc "uncaught-exception-reason"))) ;; THREADS (define make-thread (let ((make-cond-wrapper (lambda (thunk lcond lmutex scond smutex) (lambda () (lock-mutex lmutex) (signal-condition-variable lcond) (lock-mutex smutex) (unlock-mutex lmutex) (wait-condition-variable scond smutex) (unlock-mutex smutex) (catch #t thunk initial-handler))))) (lambda (thunk . name) (let ((n (and (pair? name) (car name))) (lm (make-mutex 'launch-mutex)) (lc (make-condition-variable 'launch-condition-variable)) (sm (make-mutex 'start-mutex)) (sc (make-condition-variable 'start-condition-variable))) (lock-mutex lm) (let ((t (call-with-new-thread (make-cond-wrapper thunk lc lm sc sm)))) (hashq-set! thread-start-conds t (cons sm sc)) (and n (hashq-set! object-names t n)) (wait-condition-variable lc lm) (unlock-mutex lm) t))))) (define (thread-name thread) (hashq-ref object-names (check-arg-type thread? thread "thread-name"))) (define (thread-specific thread) (hashq-ref object-specifics (check-arg-type thread? thread "thread-specific"))) (define (thread-specific-set! thread obj) (hashq-set! object-specifics (check-arg-type thread? thread "thread-specific-set!") obj) (unspecified)) (define (thread-start! thread) (let ((x (hashq-ref thread-start-conds (check-arg-type thread? thread "thread-start!")))) (and x (let ((smutex (car x)) (scond (cdr x))) (hashq-remove! thread-start-conds thread) (lock-mutex smutex) (signal-condition-variable scond) (unlock-mutex smutex))) thread)) (define (thread-yield!) (yield) (unspecified)) (define (thread-sleep! timeout) (let* ((ct (time->seconds (current-time))) (t (cond ((time? timeout) (- (time->seconds timeout) ct)) ((number? timeout) (- timeout ct)) (else (scm-error 'wrong-type-arg caller "Wrong type argument: ~S" (list timeout) '())))) (secs (inexact->exact (truncate t))) (usecs (inexact->exact (truncate (* (- t secs) 1000))))) (and (> secs 0) (sleep secs)) (and (> usecs 0) (usleep usecs)) (unspecified))) (define (thread-terminate! thread) (let ((current-handler (thread-cleanup thread))) (if (thunk? current-handler) (set-thread-cleanup! thread (lambda () (current-handler) (throw 'thread-terminated-exception))) (set-thread-cleanup! thread (lambda () (throw 'thread-terminated-exception)))) (cancel-thread thread)) (unspecified)) (define (thread-join! thread . args) (apply join-thread (cons thread args))) ;; MUTEXES (define make-mutex (lambda name (let ((n (and (pair? name) (car name))) (m ((@ (guile) make-mutex)))) (and n (hashq-set! object-names m n)) m))) (define (mutex-name mutex) (hashq-ref object-names (check-arg-type mutex? mutex "mutex-name"))) (define (mutex-specific mutex) (hashq-ref object-specifics (check-arg-type mutex? mutex "mutex-specific"))) (define (mutex-specific-set! mutex obj) (hashq-set! object-specifics (check-arg-type mutex? mutex "mutex-specific-set!") obj) (unspecified)) (define (mutex-lock! mutex . args) (apply lock-mutex (cons mutex args))) (define (mutex-unlock! mutex . args) (apply unlock-mutex (cons mutex args))) ;; CONDITION VARIABLES (define make-condition-variable (lambda name (let ((n (and (pair? name) (car name))) (m ((@ (guile) make-condition-variable)))) (and n (hashq-set! object-names m n)) m))) (define (condition-variable-name condition-variable) (hashq-ref object-names (check-arg-type condition-variable? condition-variable "condition-variable-name"))) (define (condition-variable-specific condition-variable) (hashq-ref object-specifics (check-arg-type condition-variable? condition-variable "condition-variable-specific"))) (define (condition-variable-specific-set! condition-variable obj) (hashq-set! object-specifics (check-arg-type condition-variable? condition-variable "condition-variable-specific-set!") obj) (unspecified)) (define (condition-variable-signal! cond) (signal-condition-variable cond) (unspecified)) (define (condition-variable-broadcast! cond) (broadcast-condition-variable cond) (unspecified)) ;; TIME (define current-time gettimeofday) (define (time? obj) (and (pair? obj) (let ((co (car obj))) (and (integer? co) (>= co 0))) (let ((co (cdr obj))) (and (integer? co) (>= co 0))))) (define (time->seconds time) (and (check-arg-type time? time "time->seconds") (+ (car time) (/ (cdr time) 1000000)))) (define (seconds->time x) (and (check-arg-type number? x "seconds->time") (let ((fx (truncate x))) (cons (inexact->exact fx) (inexact->exact (truncate (* (- x fx) 1000000))))))) ;; srfi-18.scm ends here