commit
55bef09244
@ -0,0 +1,15 @@
|
|||||||
|
Go-like channels for atomically passing information between threads.
|
||||||
|
|
||||||
|
(make-channel) ; make new channel
|
||||||
|
(make-channel 2) ; make new channel with max size of 2 (untested)
|
||||||
|
|
||||||
|
(-> chan item) ; send item to channel
|
||||||
|
(<- chan) ; receive from channel (values item is-not-closed)
|
||||||
|
(release chan) ; close channel
|
||||||
|
(closed chan) ; is channel closed
|
||||||
|
|
||||||
|
|
||||||
|
(make-dispatcher) ; make dispatcher
|
||||||
|
(hook disp name lambda) ; add hook
|
||||||
|
(sig name (optional value)) ; signal name in parallel
|
||||||
|
(sig-serial name (optional value)) ; signal name in serial
|
@ -0,0 +1,13 @@
|
|||||||
|
;;;; cl-channel.asd
|
||||||
|
|
||||||
|
(asdf:defsystem #:cl-channel
|
||||||
|
:description "golang-like channels for CL"
|
||||||
|
:author "Avril <flanchan@cumallover.me>"
|
||||||
|
:license "None"
|
||||||
|
:version "0.0.1"
|
||||||
|
:serial t
|
||||||
|
:depends-on ( :flan-utils
|
||||||
|
:bt-semaphore )
|
||||||
|
:components ((:file "package")
|
||||||
|
(:file "cl-channel")
|
||||||
|
(:file "cl-dispatcher")))
|
@ -0,0 +1,126 @@
|
|||||||
|
|
||||||
|
|
||||||
|
(in-package :cl-channel)
|
||||||
|
|
||||||
|
(flan-utils:enable-all-readers)
|
||||||
|
|
||||||
|
(defstruct %queue
|
||||||
|
internal
|
||||||
|
mutex)
|
||||||
|
|
||||||
|
[
|
||||||
|
(defun make-queue (&optional (from nil))
|
||||||
|
(let ((q (make-%queue)))
|
||||||
|
(setf (%queue-internal q) from)
|
||||||
|
(setf (%queue-mutex q) (bt:make-lock))
|
||||||
|
q))
|
||||||
|
|
||||||
|
(defun queue-> (q i)
|
||||||
|
(bt:with-lock-held ((%queue-mutex q))
|
||||||
|
(setf (%queue-internal q) (reverse (cons i (%queue-internal q))))))
|
||||||
|
|
||||||
|
(defun queue-poll (q)
|
||||||
|
(bt:with-lock-held ((%queue-mutex q))
|
||||||
|
(list-length (%queue-internal q))))
|
||||||
|
|
||||||
|
(defun queue<- (q)
|
||||||
|
(bt:with-lock-held ((%queue-mutex q))
|
||||||
|
(if (< (list-length (%queue-internal q)) 1) (values nil nil)
|
||||||
|
(values (pop (%queue-internal q)) t))))
|
||||||
|
|
||||||
|
(defun queue-clear (q)
|
||||||
|
(bt:with-lock-held ((%queue-mutex q))
|
||||||
|
(setf (%queue-internal q) nil)))
|
||||||
|
]
|
||||||
|
|
||||||
|
(defstruct %channel
|
||||||
|
internal
|
||||||
|
mutex
|
||||||
|
rel-send
|
||||||
|
rel-recv
|
||||||
|
rel-close
|
||||||
|
max
|
||||||
|
closed)
|
||||||
|
|
||||||
|
(defmacro %atomic (chan &body body)
|
||||||
|
`(bt:with-lock-held ((%channel-mutex ,chan))
|
||||||
|
,@body))
|
||||||
|
|
||||||
|
(defun sigall (sem)
|
||||||
|
(bt-sem:signal-semaphore sem (bt-sem:semaphore-waiters sem)))
|
||||||
|
|
||||||
|
[
|
||||||
|
(defun make-channel (&optional (max 0))
|
||||||
|
(let ((c (make-%channel)))
|
||||||
|
(setf (%channel-internal c) (make-queue))
|
||||||
|
(setf (%channel-mutex c) (bt:make-lock))
|
||||||
|
(setf (%channel-rel-send c) (bt-sem:make-semaphore))
|
||||||
|
(setf (%channel-rel-recv c) (bt-sem:make-semaphore))
|
||||||
|
(setf (%channel-rel-close c) (bt-sem:make-semaphore))
|
||||||
|
(setf (%channel-max c) max)
|
||||||
|
(setf (%channel-closed c) nil)
|
||||||
|
c))
|
||||||
|
|
||||||
|
(defun closed (chan) (%atomic chan
|
||||||
|
(%channel-closed chan)))
|
||||||
|
|
||||||
|
(defun poll (chan)
|
||||||
|
(%atomic chan
|
||||||
|
(queue-poll (%channel-internal chan))))
|
||||||
|
|
||||||
|
(defun <- (chan)
|
||||||
|
(let ((out nil)
|
||||||
|
(rout nil))
|
||||||
|
(loop while (and (null out) ¬(closed chan)) do
|
||||||
|
(progn
|
||||||
|
(if (> (poll chan) 0)
|
||||||
|
(%atomic chan
|
||||||
|
(when (> (queue-poll (%channel-internal chan)) 0)
|
||||||
|
(setf out t)
|
||||||
|
(setf rout (queue<- (%channel-internal chan)))
|
||||||
|
(bt-sem:signal-semaphore (%channel-rel-send chan) 1)))
|
||||||
|
(bt-sem:wait-on-semaphore (%channel-rel-recv chan)))))
|
||||||
|
(if (closed chan) (values nil nil) (values rout t))))
|
||||||
|
|
||||||
|
(defun -> (chan item)
|
||||||
|
(loop while (and ¬(closed chan) (> (%channel-max chan) 0) (%atomic chan (>= (queue-poll (%channel-internal chan)) (%channel-max chan))))
|
||||||
|
do (bt-sem:wait-on-semaphore (%channel-rel-send)))
|
||||||
|
(let ((lv (%atomic chan
|
||||||
|
(if ¬(if (or (%channel-closed chan) (and (> (%channel-max chan) 0) (>= (queue-poll (%channel-internal chan)) (%channel-max chan))))
|
||||||
|
nil
|
||||||
|
(progn
|
||||||
|
(queue-> (%channel-internal chan) item)
|
||||||
|
(bt-sem:signal-semaphore (%channel-rel-recv chan) 1)
|
||||||
|
t))
|
||||||
|
(if (%channel-closed chan) nil 'reset)
|
||||||
|
t))))
|
||||||
|
(if (eq lv 'reset)
|
||||||
|
(-> chan item)
|
||||||
|
lv)))
|
||||||
|
|
||||||
|
(defun release (chan)
|
||||||
|
(%atomic chan
|
||||||
|
(setf (%channel-closed chan) t)
|
||||||
|
(sigall (%channel-rel-recv chan))
|
||||||
|
(sigall (%channel-rel-send chan))))
|
||||||
|
|
||||||
|
(defun poll (chan)
|
||||||
|
(%atomic chan
|
||||||
|
(queue-poll (%channel-internal chan))))
|
||||||
|
|
||||||
|
(defmacro make ()
|
||||||
|
`(make-channel))
|
||||||
|
]
|
||||||
|
|
||||||
|
(defun test ()
|
||||||
|
(let ((chan (make-channel)))
|
||||||
|
$(progn
|
||||||
|
(loop while ¬(closed chan) do (let ((val (<- chan)))
|
||||||
|
(pprint val)
|
||||||
|
(pprint ".")
|
||||||
|
(when (string-equal val "CLOSE")
|
||||||
|
(release chan))))
|
||||||
|
(print "Thread end")
|
||||||
|
(print "."))
|
||||||
|
(loop while ¬(closed chan) do (-> chan (write-to-string (read)))))
|
||||||
|
(print "End"))
|
@ -0,0 +1,54 @@
|
|||||||
|
|
||||||
|
|
||||||
|
(in-package :cl-dispatcher)
|
||||||
|
|
||||||
|
(flan-utils:enable-all-readers)
|
||||||
|
|
||||||
|
(defstruct %dispatcher
|
||||||
|
hooks
|
||||||
|
lock)
|
||||||
|
|
||||||
|
(defmacro %atomic (disp &body thing)
|
||||||
|
`(bt:with-lock-held ((%dispatcher-lock disp))
|
||||||
|
,@thing))
|
||||||
|
|
||||||
|
[
|
||||||
|
(defun make-dispatcher ()
|
||||||
|
(let ((d (make-%dispatcher)))
|
||||||
|
(setf (%dispatcher-hooks d ) nil)
|
||||||
|
(setf (%dispatcher-lock d) (bt:make-lock))
|
||||||
|
d))
|
||||||
|
|
||||||
|
(defmacro make ()
|
||||||
|
`(make-dispatcher))
|
||||||
|
|
||||||
|
(defun hook (disp name lam)
|
||||||
|
(%atomic disp
|
||||||
|
(if (assoc name (%dispatcher-hooks disp))
|
||||||
|
(push lam (cdr (assoc name (%dispatcher-hooks disp))))
|
||||||
|
(push (cons name (list lam)) (%dispatcher-hooks disp)))))
|
||||||
|
|
||||||
|
(defun sig (disp name &optional (x nil))
|
||||||
|
(%atomic disp
|
||||||
|
(let ((hooks (assoc name (%dispatcher-hooks disp))))
|
||||||
|
(if (null hooks)
|
||||||
|
nil
|
||||||
|
(mapc #'(lambda (y) $(funcall y x)) (cdr hooks))))))
|
||||||
|
|
||||||
|
(defun sig-serial (disp name &optional (x nil))
|
||||||
|
(%atomic disp
|
||||||
|
(let ((hooks (assoc name (%dispatcher-hooks disp))))
|
||||||
|
(if (null hooks)
|
||||||
|
nil
|
||||||
|
(mapc #'(lambda (y) (funcall y x)) (cdr hooks))))))
|
||||||
|
]
|
||||||
|
|
||||||
|
(defun test ()
|
||||||
|
(let ((d (make)))
|
||||||
|
(hook d "test" (lambda (x) (print x)))
|
||||||
|
(hook d "test" (lambda (x) (print (cons "!!" x))))
|
||||||
|
(hook d "test" (lambda (x) (print "HELLO")))
|
||||||
|
|
||||||
|
(sig-serial d "test" 'uwu)
|
||||||
|
(print 'signalled)))
|
||||||
|
|
@ -0,0 +1,9 @@
|
|||||||
|
;;;; package.lisp
|
||||||
|
|
||||||
|
(defpackage #:cl-channel
|
||||||
|
(:use #:cl)
|
||||||
|
(:nicknames :channel))
|
||||||
|
|
||||||
|
(defpackage #:cl-dispatcher
|
||||||
|
(:use #:cl)
|
||||||
|
(:nicknames :dispatcher))
|
Loading…
Reference in new issue