diff options
Diffstat (limited to 'gnu/gnunet/concurrency/update.scm')
-rw-r--r-- | gnu/gnunet/concurrency/update.scm | 107 |
1 files changed, 107 insertions, 0 deletions
diff --git a/gnu/gnunet/concurrency/update.scm b/gnu/gnunet/concurrency/update.scm new file mode 100644 index 0000000..6a487cf --- /dev/null +++ b/gnu/gnunet/concurrency/update.scm | |||
@@ -0,0 +1,107 @@ | |||
1 | ;; This file is part of scheme-GNUnet. | ||
2 | ;; Copyright (C) 2021 Maxime Devos | ||
3 | ;; | ||
4 | ;; scheme-GNUnet is free software: you can redistribute it and/or modify it | ||
5 | ;; under the terms of the GNU Affero General Public License as published | ||
6 | ;; by the Free Software Foundation, either version 3 of the License, | ||
7 | ;; or (at your option) any later version. | ||
8 | ;; | ||
9 | ;; scheme-GNUnet is distributed in the hope that it will be useful, but | ||
10 | ;; WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | ;; Affero General Public License for more details. | ||
13 | ;; | ||
14 | ;; You should have received a copy of the GNU Affero General Public License | ||
15 | ;; along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | ;; | ||
17 | ;; SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | ;; (Can be relicensed for use in guile-fibers on request) | ||
20 | |||
21 | ;; A stream of values, of which the next value might not | ||
22 | ;; yet be determined, but can be waited upon. All can wait | ||
23 | ;; for an update and read the new value, but only the creator | ||
24 | ;; of the original update object can add new values. | ||
25 | |||
26 | ;; TODO implement a time machine for exploring alternate | ||
27 | ;; time-lines (maybe with a @code{time-machine} parameter, | ||
28 | ;; if time machines can be nested this way). | ||
29 | |||
30 | ;; Old updates are reclaimed by the garbage collector. | ||
31 | ;; TODO an implementation *not* relying on a garbage collector, | ||
32 | ;; at the cost of only allowing access to the latest value, | ||
33 | ;; would be nice to compare with. | ||
34 | |||
35 | ;; Example uses: | ||
36 | ;; - network-size estimation client (TODO) | ||
37 | |||
38 | (define-library (gnu gnunet concurrency update) | ||
39 | (export make-update update? update-value wait-for-update-operation | ||
40 | next-update next-update-peek | ||
41 | |||
42 | &double-update double-update? make-double-update-violation) | ||
43 | (import (rnrs records syntactic) | ||
44 | (rnrs conditions) | ||
45 | (rnrs base) | ||
46 | (rnrs exceptions) | ||
47 | (srfi srfi-8) | ||
48 | (fibers conditions) | ||
49 | (fibers operations) | ||
50 | (ice-9 atomic)) | ||
51 | (begin | ||
52 | (define-condition-type &double-update &violation | ||
53 | make-double-update-violation double-update?) | ||
54 | |||
55 | (define (double-update-violation) | ||
56 | (raise (condition | ||
57 | (make-who-condition 'update!) | ||
58 | (make-double-update-violation) | ||
59 | (make-message-condition "An update already exists!")))) | ||
60 | |||
61 | (define-record-type (<update> make-update update?) | ||
62 | (fields (immutable value update-value) | ||
63 | ;; value in box is #f if not yet updated, | ||
64 | ;; otherwise it is an <update> | ||
65 | (immutable next next-update-box) | ||
66 | (immutable when-next next-update-condition)) | ||
67 | (protocol | ||
68 | (lambda (%make) | ||
69 | (lambda (initial) | ||
70 | "Create an update object, initialised to @var{initial}. | ||
71 | Two values are returned: the update object and the update procedure." | ||
72 | (let ((update (%make initial (make-atomic-box #f) | ||
73 | (make-condition)))) | ||
74 | (define (update! next-value) | ||
75 | "Update the update object to the value @var{next-value}. | ||
76 | |||
77 | If the update object was already updated, raise a @code{&double-update} | ||
78 | instead. If the object was updated successfully, return the next update | ||
79 | object and updater." | ||
80 | (receive (next-update next-update!) (make-update next-value) | ||
81 | (case (atomic-box-compare-and-swap! | ||
82 | (next-update-box update) #f next-update) | ||
83 | ((#f) | ||
84 | (signal-condition! (next-update-condition update)) | ||
85 | (values next-update next-update!)) | ||
86 | (else (double-update-violation))))) | ||
87 | (values update update!))))) | ||
88 | (opaque #t) | ||
89 | (sealed #t)) | ||
90 | |||
91 | (define (wait-for-update-operation update) | ||
92 | "Return an operation for waiting for the next value | ||
93 | of the update @var{update}. The return value of the | ||
94 | operation is the next @code{update?}." | ||
95 | (wrap-operation | ||
96 | (wait-operation (next-update-condition update)) | ||
97 | (lambda () | ||
98 | (atomic-box-ref (next-update-box update))))) | ||
99 | |||
100 | (define (next-update update) | ||
101 | "Return the next update of @var{update}." | ||
102 | (perform-operation (wait-for-update-operation update))) | ||
103 | |||
104 | (define (next-update-peek update) | ||
105 | "If the next update of @var{update} is known, return it, | ||
106 | otherwise return @code{#f}." | ||
107 | (atomic-box-ref (next-update-box update))))) | ||