| 1 | ;;+----------------------------------------------------------------------------- |
|---|
| 2 | ;;+ Isidorus |
|---|
| 3 | ;;+ (c) 2008-2010 Marc Kuester, Christoph Ludwig, Lukas Georgieff |
|---|
| 4 | ;;+ |
|---|
| 5 | ;;+ Isidorus is freely distributable under the LLGPL license. |
|---|
| 6 | ;;+ You can find a detailed description in trunk/docs/LLGPL-LICENSE.txt and |
|---|
| 7 | ;;+ trunk/docs/LGPL-LICENSE.txt. |
|---|
| 8 | ;;+----------------------------------------------------------------------------- |
|---|
| 9 | |
|---|
| 10 | (defpackage :isidorus-threading |
|---|
| 11 | (:use :cl :sb-thread) |
|---|
| 12 | (:export :current-readers |
|---|
| 13 | :with-reader-lock |
|---|
| 14 | :with-writer-lock)) |
|---|
| 15 | |
|---|
| 16 | (in-package :isidorus-threading) |
|---|
| 17 | |
|---|
| 18 | (defvar *readerlist-lock* (make-mutex :name "isidorus-threading: current readers lock")) |
|---|
| 19 | (defvar *writer-lock* (make-mutex :name "isidorus-threading: writer lock")) |
|---|
| 20 | (defvar *current-readers* nil) |
|---|
| 21 | |
|---|
| 22 | |
|---|
| 23 | (defun current-readers () |
|---|
| 24 | "Returns a copy of the list which contains all current reader |
|---|
| 25 | threads, *current-readers*" |
|---|
| 26 | (with-mutex (*readerlist-lock*) |
|---|
| 27 | (copy-list *current-readers*))) |
|---|
| 28 | |
|---|
| 29 | |
|---|
| 30 | |
|---|
| 31 | (defun add-thread-to-reader-list () |
|---|
| 32 | "Adds the current thread to the reader list" |
|---|
| 33 | (with-mutex (*writer-lock*) |
|---|
| 34 | (with-mutex (*readerlist-lock*) |
|---|
| 35 | (push *current-thread* *current-readers*)))) |
|---|
| 36 | |
|---|
| 37 | |
|---|
| 38 | (defun remove-thread-from-reader-list () |
|---|
| 39 | "Removes the current threads from the reader list" |
|---|
| 40 | (with-mutex (*readerlist-lock*) |
|---|
| 41 | (setf *current-readers* |
|---|
| 42 | (delete *current-thread* *current-readers*)))) |
|---|
| 43 | |
|---|
| 44 | |
|---|
| 45 | (defmacro with-reader-lock (&body body) |
|---|
| 46 | "Executes the passed 'body' with the reader lock" |
|---|
| 47 | `(progn |
|---|
| 48 | (add-thread-to-reader-list) |
|---|
| 49 | (let ((result nil)) |
|---|
| 50 | (handler-case |
|---|
| 51 | (setf result ,@body) |
|---|
| 52 | (condition (c) |
|---|
| 53 | (progn |
|---|
| 54 | (remove-thread-from-reader-list) |
|---|
| 55 | (error c)))) |
|---|
| 56 | (remove-thread-from-reader-list) |
|---|
| 57 | result))) |
|---|
| 58 | |
|---|
| 59 | |
|---|
| 60 | (defmacro with-writer-lock (&body body) |
|---|
| 61 | "Executes the passed body when the reader list is empty otherwise |
|---|
| 62 | the do macor loops in 500 ms time interval for a next chance." |
|---|
| 63 | `(with-mutex (*writer-lock*) |
|---|
| 64 | (do |
|---|
| 65 | ((remaining-readers (current-readers) (current-readers))) |
|---|
| 66 | ((null remaining-readers)) |
|---|
| 67 | (sleep 0.05)) |
|---|
| 68 | ,@body)) |
|---|