The green-threads Reference Manual

This is the green-threads Reference Manual, version 0.3, generated automatically by Declt version 4.0 beta 2 "William Riker" on Mon Feb 26 16:37:39 2024 GMT+0.

Table of Contents


1 Introduction


2 Systems

The main system appears first, followed by any subsystem dependency.


2.1 green-threads

A lightweight threading / cooperative multitasking library.

Author

Stephen A. Goss

License

Modified BSD

Long Description

# Green-Threads

A lightweight thread / cooperative multitasking library for Common Lisp.

## Usage

Allows for cooperative multitasking with help of CL-CONT for continuations. Tries to mimic BORDEAUX-THREADS API as much as possible.

Let’s show how the threads work and build up to higher level abstractions.

The MAKE-THREAD function takes a closure/function of zero arguments and starts executing it, since we are not using OS threads, it cannot actually preempt the thread, so the thread has to yield by calling QUEUE-NEXT, passing a closure containing the continuation of the thread and return. MAKE-THREAD has an optional keyword parameter :NAME for specifying a name for the thread.

In this example, we create a thread which executes immediately, it creates an additional thread and then continues on, it yields by calling QUEUE-NEXT and passing a continuation. That allows the other thread to execute before continuing the first:

“‘common-lisp
(use-package :green-threads)

(make-thread
(lambda ()
(format t "Do Something~%")
(make-thread
(lambda ()
(format t "Do Something Else.~%")))
(queue-next ;; queue next action and yield
(lambda ()
(format t "Do More.~%")))))

;; output:
;; Do Something
;; Do Something Else.
;; Do More.
“‘

In the above example, the continuation of the primary thread is nested inside a QUEUE-NEXT call. That’s pretty inconvenient. Nobody enjoys programming in continuation-passing-style (CPS), so let’s see how we can avoid that.

In the next example, we use the CL-CONT library directly to transform our code into CPS which allows us to use THREAD-YIELD to yield without having to specify the continuation ourselves. You’ll notice that the primary thread no longer has to nest the continuation in a closure.

“‘common-lisp
(make-thread
(cl-cont:with-call/cc
(lambda ()
(format t "Do Something~%")
(cl-cont:without-call/cc
(make-thread
(cl-cont:with-call/cc
(lambda ()
(format t "Do Something Else~%")))))
(thread-yield) ;; allow other threads to run
(format t "Do More~%"))))
“‘

The last example is a bit noisy with the CL-CONT calls, so we can instead use the WITH-GREEN-THREAD macro which wraps our code in an anonymous closure, and applies CL-CONT macros appropriately.

“‘common-lisp
(with-green-thread
(format t "Do Something~%")
(with-green-thread
(format t "Do Something Else~%"))
(thread-yield) ;; allow other threads to run
(format t "Do More~%"))
“‘

Viola, you can now write cooperatively multitasking code without resorting to writing CPS by hand.

Dynamic bindings don’t over well into green threads, so this library has a similar mechanism as BORDEAUX-THREADS, and that is the \*DEFAULT-SPECIAL-BINDINGS\* special variable which can be set to an alist of symbol/value pairs that will be specially bound in any green threads you create.

“‘common-lisp
(defparameter *foo* "outer")

(with-green-thread
(print *foo*)
(let ((*default-special-bindings* ’((*foo* . "first")))) (with-green-thread
(print *foo*)))
(thread-yield)
(print *foo*))

;; output:
;; "outer"
;; "first"
;; "outer"
“‘

MAKE-THREAD creates a new green thread and runs it if no green threads are currently running. If called from within a running green thread, it will queue the thread to run later.

CURRENT-THREAD returns the currently running thread object or NIL if you are not currently in one.

THREADP returns T if the object passed to it is a thread object.

THREAD-NAME returns the name of the thread passed to it or NIL of none was provided.

ALL-THREADS returns a list of all threads.

DESTROY-THREAD will do just that, cannot be called on currently executing thread.

THREAD-ALIVE-P returns T if thread passed to it is still alive.

JOIN-THREAD (thread) Requires CL-CONT:WITH-CALL/CC environment, causes the current thread to wait for the other thread to complete before continuing.

### Futures

MAKE-FUTURE () creates a future object.

QUEUE-FUTURE (future action &optional thread) queues an action on current (or specified) thread to take place when provided future is completed.

FINISH (future &rest values) signals that a future is complete
and provides return values for the future.

FUTURE-FINISHED-P (future) T if future has already had COMPLETE-FUTURE called on it.

FUTURE-VALUES (future) Returns the values given to the future when it was completed.

WAIT-ON (future) Requires CL-CONT:WITH-CALL/CC environment, causes the current thread to wait for the completion of the specified future and returns the values given to the future when it was completed.

GET-JOIN-FUTURE (thread) Returns a future which will complete when the passed in thread completes. This provides a lower level way to join threads without using JOIN-THREAD which requires CL-CONT:WITH-CALL/CC.

Example use of futures from (taken from tests):

“‘common-lisp
(defparameter *future-one* (make-future))

(defparameter *val1* nil)
(defparameter *val2* nil)

;; WAIT-FOR test
(with-green-thread
(multiple-value-bind (v1 v2) (wait-for *future-one*)
(setf *val1* v1)
(setf *val2* v2)))

(is *val1* nil)
(is *val2* nil)
(is (future-complete-p *future-one*) nil)

(complete-future *future-one* :foo :bar)

(is *val1* :foo)
(is *val2* :bar)
(is (future-complete-p *future-one*) T)
“‘

### Channels

Inspired by sykopomp’s [ChanL](https://github.com/sykopomp/chanl), which relies on BORDEAUX-THREADS, I’ve added basic unbuffered channel support.

(make-instance ’CHANNEL) creates a new channel.

SEND/CC (channel value &key blockp) Requires CL-CONT:WITH-CALL/CC environment. Sends a value to a channel, and blocks, unless blockp is nil. Returns the channel that received the value unless blockp is nil and there is no thread waiting to receive in which case it returns nil.

RECV/CC (channel &key blockp) Requires CL-CONT:WITH-CALL/CC environment. Receives a value from a channel, and blocks, unless blockp is nil. Returns 2 values, the first is the value being received and the second is a generalized boolean that is only nil if blockp is nil and there is no thread waiting to send a value.

SEND (channel value continuation &key blockp) Just like SEND/CC but doesn’t require CL-CONT, you just have to pass in the continuation manually. CPS is fun. Instead of returning the channel that receives the message, it (or nil) is passed to continuation.

RECV (channel continuation &key blockp) Just like RECV/CC but doesn’t require CL-CONT, you just have to pass in the continuation manually. CPS is fun. Instead of returning the 2 values, the continuation is called with them.

Behold, the venerable sieve of Eratosthenes:

“‘common-lisp
(ql:quickload :green-threads)

(cl-cont:defun/cc counter (end chan)
(loop for i from 2 to end
do (gt:send/cc chan i)))

(declaim (ftype function filter)) ;; unnecessary, silence warnings

(cl-cont:defun/cc filter (listen)
(let ((prime (gt:recv/cc listen)))
(format t "~a " prime)
(let ((chan (make-instance ’gt:channel))) (gt:with-green-thread
(filter chan))
(loop
(let ((i (gt:recv/cc listen)))
(if (> (mod i prime) 0)
(gt:send/cc chan i)))))))

(gt:with-green-thread
(let ((count (make-instance ’gt:channel))) (gt:with-green-thread
(counter 100 count))
(gt:with-green-thread
(filter count))))
“‘

## Installation

Clone repo into ~/quicklisp/local-projects. Run the following command:

“‘common-lisp
(ql:quickload :green-threads)
“‘

## TODO

100% test coverage.

## Author

* Stephen A. Goss (steveth45@gmail.com)

## Copyright

Copyright (c) 2012 Stephen A. Goss (steveth45@gmail.com)

# License

Licensed under the Modified BSD License.

Version

0.3

Dependencies
  • cl-cont (system).
  • cl-async-future (system).
Source

green-threads.asd.

Child Component

src (module).


3 Modules

Modules are listed depth-first from the system components tree.


3.1 green-threads/src

Source

green-threads.asd.

Parent Component

green-threads (system).

Child Component

green-threads.lisp (file).


4 Files

Files are sorted by type and then listed depth-first from the systems components trees.


4.1 Lisp


4.1.1 green-threads/green-threads.asd

Source

green-threads.asd.

Parent Component

green-threads (system).

ASDF Systems

green-threads.

Packages

green-threads-asd.


4.1.2 green-threads/src/green-threads.lisp

Source

green-threads.asd.

Parent Component

src (module).

Packages

green-threads.

Public Interface
Internals

5 Packages

Packages are listed by definition order.


5.1 green-threads-asd

Source

green-threads.asd.

Use List
  • asdf/interface.
  • common-lisp.

5.2 green-threads

Source

green-threads.lisp.

Nickname

gt

Use List
  • cl-async-future.
  • cl-cont.
  • common-lisp.
Public Interface
Internals

6 Definitions

Definitions are sorted by export status, category, package, and then by lexicographic order.


6.1 Public Interface


6.1.1 Special variables

Special Variable: *default-special-bindings*
Package

green-threads.

Source

green-threads.lisp.


6.1.2 Macros

Macro: with-green-thread (&body body)

A convenience macro that runs the code in a lambda wrapped in CPS transformin macros and a call to make-thread. Returns thread object.

Package

green-threads.

Source

green-threads.lisp.


6.1.3 Ordinary functions

Function: all-threads ()

Returns a list of all active threads, which are threads that have been created but not destroyed or finished.

Package

green-threads.

Source

green-threads.lisp.

Function: current-thread ()

Returns the currently running greed thread or NIL if not called from within thread.

Package

green-threads.

Source

green-threads.lisp.

Function: destroy-thread (thread)

Destroys the thread passed in, so it will not run any more. It is an error to call this on the current thread.

Package

green-threads.

Source

green-threads.lisp.

Function: future-values (future)

Returns the values given to the future when it was completed.

Package

green-threads.

Source

green-threads.lisp.

Function: get-join-future (thread)

Gets a future object from thread which will be completed when the thread is finished.

Package

green-threads.

Source

green-threads.lisp.

Function: make-thread (function &key name)

Create a new green thread with an optional :name. The first parameter should be a function that takes no arguments. The thread will be queued immediately, so will run immediately if make-thread is called from outside any other green threads. Returns a thread object.

Package

green-threads.

Source

green-threads.lisp.

Function: queue-future (future action &optional thread)

Queues an action on current (or specified) thread to take place when provided future is completed.

Package

green-threads.

Source

green-threads.lisp.

Function: queue-next (action &optional thread)
Package

green-threads.

Source

green-threads.lisp.

Function: thread-alive-p (thread)

Returns T if the passed in thread has not been destroyed.

Package

green-threads.

Source

green-threads.lisp.

Function: thread-name (thread)

Returns the name of the thread given at the time of creation or NIL.

Package

green-threads.

Source

green-threads.lisp.

Function: threadp (obj)

Returns T if the object passed in is a thread.

Package

green-threads.

Source

green-threads.lisp.


6.1.4 Generic functions

Generic Function: recv (channel continuation &key blockp)
Package

green-threads.

Source

green-threads.lisp.

Methods
Method: recv ((channel channel) continuation &key blockp)
Generic Function: send (channel value continuation &key blockp)
Package

green-threads.

Source

green-threads.lisp.

Methods
Method: send ((channel channel) value continuation &key blockp)

6.1.5 Classes

Class: channel
Package

green-threads.

Source

green-threads.lisp.

Direct methods
Direct slots
Slot: waiting-to-send
Initform

(make-instance (quote green-threads::batched-queue))

Slot: waiting-to-recv
Initform

(make-instance (quote green-threads::batched-queue))


6.2 Internals


6.2.1 Special variables

Special Variable: *active-threads*
Package

green-threads.

Source

green-threads.lisp.

Special Variable: *current-thread*
Package

green-threads.

Source

green-threads.lisp.

Special Variable: *thread-queue*
Package

green-threads.

Source

green-threads.lisp.


6.2.2 Ordinary functions

Function: bindings-from-alist (alist)
Package

green-threads.

Source

green-threads.lisp.

Function: construct-batched-queue (front rear)
Package

green-threads.

Source

green-threads.lisp.

Function: queue-next-replace (action &optional thread)
Package

green-threads.

Source

green-threads.lisp.

Function: thread-loop ()
Package

green-threads.

Source

green-threads.lisp.


6.2.3 Generic functions

Generic Reader: alive (object)
Package

green-threads.

Methods
Reader Method: alive ((thread thread))

automatically generated reader method

Source

green-threads.lisp.

Target Slot

alive.

Generic Writer: (setf alive) (object)
Package

green-threads.

Methods
Writer Method: (setf alive) ((thread thread))

automatically generated writer method

Source

green-threads.lisp.

Target Slot

alive.

Generic Reader: binding-symbols (object)
Package

green-threads.

Methods
Reader Method: binding-symbols ((thread thread))

automatically generated reader method

Source

green-threads.lisp.

Target Slot

binding-symbols.

Generic Reader: binding-values (object)
Package

green-threads.

Methods
Reader Method: binding-values ((thread thread))

automatically generated reader method

Source

green-threads.lisp.

Target Slot

binding-values.

Generic Function: empty-p (sequence)
Package

green-threads.

Source

green-threads.lisp.

Methods
Method: empty-p ((queue batched-queue))
Generic Function: head (sequence)
Package

green-threads.

Source

green-threads.lisp.

Methods
Method: head ((queue batched-queue))
Generic Reader: join-future (object)
Package

green-threads.

Methods
Reader Method: join-future ((thread thread))

automatically generated reader method

Source

green-threads.lisp.

Target Slot

join-future.

Generic Reader: name (object)
Package

green-threads.

Methods
Reader Method: name ((thread thread))

automatically generated reader method

Source

green-threads.lisp.

Target Slot

name.

Generic Reader: next-action (object)
Package

green-threads.

Methods
Reader Method: next-action ((thread thread))

automatically generated reader method

Source

green-threads.lisp.

Target Slot

next-action.

Generic Writer: (setf next-action) (object)
Package

green-threads.

Methods
Writer Method: (setf next-action) ((thread thread))

automatically generated writer method

Source

green-threads.lisp.

Target Slot

next-action.

Generic Function: size (sequence)
Package

green-threads.

Source

green-threads.lisp.

Methods
Method: size ((queue batched-queue))
Generic Function: snoc (sequence value)
Package

green-threads.

Source

green-threads.lisp.

Methods
Method: snoc ((queue batched-queue) value)
Generic Function: tail (sequence)
Package

green-threads.

Source

green-threads.lisp.

Methods
Method: tail ((queue batched-queue))

6.2.4 Classes

Class: batched-queue
Package

green-threads.

Source

green-threads.lisp.

Direct methods
Direct slots
Slot: front
Initargs

:front

Slot: rear
Initargs

:rear

Class: thread
Package

green-threads.

Source

green-threads.lisp.

Direct methods
Direct slots
Slot: name
Initargs

:name

Readers

name.

Writers

This slot is read-only.

Slot: binding-symbols
Initargs

:binding-symbols

Readers

binding-symbols.

Writers

This slot is read-only.

Slot: binding-values
Initargs

:binding-values

Readers

binding-values.

Writers

This slot is read-only.

Slot: next-action
Readers

next-action.

Writers

(setf next-action).

Slot: alive
Initform

t

Readers

alive.

Writers

(setf alive).

Slot: join-future
Initform

(cl-async-future:make-future)

Readers

join-future.

Writers

This slot is read-only.


Appendix A Indexes


A.1 Concepts


A.2 Functions

Jump to:   (  
A   B   C   D   E   F   G   H   J   M   N   Q   R   S   T   W  
Index Entry  Section

(
(setf alive): Private generic functions
(setf alive): Private generic functions
(setf next-action): Private generic functions
(setf next-action): Private generic functions

A
alive: Private generic functions
alive: Private generic functions
all-threads: Public ordinary functions

B
binding-symbols: Private generic functions
binding-symbols: Private generic functions
binding-values: Private generic functions
binding-values: Private generic functions
bindings-from-alist: Private ordinary functions

C
construct-batched-queue: Private ordinary functions
current-thread: Public ordinary functions

D
destroy-thread: Public ordinary functions

E
empty-p: Private generic functions
empty-p: Private generic functions

F
Function, all-threads: Public ordinary functions
Function, bindings-from-alist: Private ordinary functions
Function, construct-batched-queue: Private ordinary functions
Function, current-thread: Public ordinary functions
Function, destroy-thread: Public ordinary functions
Function, future-values: Public ordinary functions
Function, get-join-future: Public ordinary functions
Function, make-thread: Public ordinary functions
Function, queue-future: Public ordinary functions
Function, queue-next: Public ordinary functions
Function, queue-next-replace: Private ordinary functions
Function, thread-alive-p: Public ordinary functions
Function, thread-loop: Private ordinary functions
Function, thread-name: Public ordinary functions
Function, threadp: Public ordinary functions
future-values: Public ordinary functions

G
Generic Function, (setf alive): Private generic functions
Generic Function, (setf next-action): Private generic functions
Generic Function, alive: Private generic functions
Generic Function, binding-symbols: Private generic functions
Generic Function, binding-values: Private generic functions
Generic Function, empty-p: Private generic functions
Generic Function, head: Private generic functions
Generic Function, join-future: Private generic functions
Generic Function, name: Private generic functions
Generic Function, next-action: Private generic functions
Generic Function, recv: Public generic functions
Generic Function, send: Public generic functions
Generic Function, size: Private generic functions
Generic Function, snoc: Private generic functions
Generic Function, tail: Private generic functions
get-join-future: Public ordinary functions

H
head: Private generic functions
head: Private generic functions

J
join-future: Private generic functions
join-future: Private generic functions

M
Macro, with-green-thread: Public macros
make-thread: Public ordinary functions
Method, (setf alive): Private generic functions
Method, (setf next-action): Private generic functions
Method, alive: Private generic functions
Method, binding-symbols: Private generic functions
Method, binding-values: Private generic functions
Method, empty-p: Private generic functions
Method, head: Private generic functions
Method, join-future: Private generic functions
Method, name: Private generic functions
Method, next-action: Private generic functions
Method, recv: Public generic functions
Method, send: Public generic functions
Method, size: Private generic functions
Method, snoc: Private generic functions
Method, tail: Private generic functions

N
name: Private generic functions
name: Private generic functions
next-action: Private generic functions
next-action: Private generic functions

Q
queue-future: Public ordinary functions
queue-next: Public ordinary functions
queue-next-replace: Private ordinary functions

R
recv: Public generic functions
recv: Public generic functions

S
send: Public generic functions
send: Public generic functions
size: Private generic functions
size: Private generic functions
snoc: Private generic functions
snoc: Private generic functions

T
tail: Private generic functions
tail: Private generic functions
thread-alive-p: Public ordinary functions
thread-loop: Private ordinary functions
thread-name: Public ordinary functions
threadp: Public ordinary functions

W
with-green-thread: Public macros