0
# Concurrency and State Management
1
2
Safe concurrent programming with atoms, refs, agents, and software transactional memory. Clojure provides several reference types for managing state changes in concurrent programs.
3
4
## Capabilities
5
6
### Atoms
7
8
Atoms provide synchronous, uncoordinated access to a single piece of state.
9
10
```clojure { .api }
11
(atom x & options)
12
;; Creates atomic reference to x
13
;; Options: :meta metadata-map, :validator validator-fn
14
;; Returns: clojure.lang.Atom
15
16
(deref atom)
17
;; Returns current value of atom (same as @atom)
18
;; Returns: Object
19
20
(reset! atom newval)
21
;; Sets atom to newval, returns newval
22
;; Returns: Object
23
24
(swap! atom f & args)
25
;; Atomically swaps value using (f current-value args)
26
;; Retries if value changes during computation
27
;; Returns: new value
28
29
(compare-and-set! atom oldval newval)
30
;; Atomically sets to newval if current equals oldval
31
;; Returns: boolean (true if successful)
32
33
(add-watch atom key fn)
34
;; Adds watch function called on state changes
35
;; fn receives: key, atom, old-state, new-state
36
;; Returns: atom
37
38
(remove-watch atom key)
39
;; Removes watch function for key
40
;; Returns: atom
41
42
(set-validator! atom validator-fn)
43
;; Sets validator function (must return truthy for valid states)
44
;; Returns: atom
45
46
(get-validator atom)
47
;; Returns current validator function
48
;; Returns: function or nil
49
```
50
51
### Refs and Software Transactional Memory
52
53
Refs provide coordinated, synchronous access to shared state using transactions.
54
55
```clojure { .api }
56
(ref x & options)
57
;; Creates transactional reference to x
58
;; Options: :meta metadata-map, :validator validator-fn, :min-history int, :max-history int
59
;; Returns: clojure.lang.Ref
60
61
(dosync & exprs)
62
;; Executes expressions in transaction
63
;; All ref operations must occur within dosync
64
;; Automatically retries on conflicts
65
;; Returns: result of last expression
66
67
(alter ref fun & args)
68
;; Sets ref to (fun current-value args) within transaction
69
;; Must be called within dosync
70
;; Returns: new value
71
72
(commute ref fun & args)
73
;; Like alter but allows more concurrency by commuting at commit time
74
;; Use for commutative operations (order doesn't matter)
75
;; Returns: new value
76
77
(ref-set ref val)
78
;; Sets ref to val within transaction
79
;; Must be called within dosync
80
;; Returns: val
81
82
(ensure ref)
83
;; Protects ref from modification by other transactions
84
;; Must be called within dosync
85
;; Returns: current value
86
87
(ref-history-count ref)
88
;; Returns number of historical values retained
89
;; Returns: int
90
91
(ref-min-history ref)
92
;; Returns minimum history count
93
;; Returns: int
94
95
(ref-max-history ref)
96
;; Returns maximum history count
97
;; Returns: int
98
```
99
100
### Agents
101
102
Agents provide asynchronous, independent state management.
103
104
```clojure { .api }
105
(agent state & options)
106
;; Creates agent with initial state
107
;; Options: :meta metadata-map, :validator validator-fn, :error-handler fn, :error-mode keyword
108
;; Returns: clojure.lang.Agent
109
110
(send agent fn & args)
111
;; Queues fn for execution with agent's state
112
;; Uses thread pool for CPU-bound tasks
113
;; Returns: agent
114
115
(send-off agent fn & args)
116
;; Queues fn for execution with agent's state
117
;; Uses expandable thread pool for I/O-bound tasks
118
;; Returns: agent
119
120
(await & agents)
121
;; Blocks until all queued actions for agents complete
122
;; Returns: nil
123
124
(await-for timeout-ms & agents)
125
;; Like await but with timeout
126
;; Returns: logical true if successful, nil if timeout
127
128
(agent-error agent)
129
;; Returns exception that caused agent to fail, nil if no error
130
;; Returns: Exception or nil
131
132
(clear-agent-errors agent)
133
;; Clears error state and restarts agent
134
;; Returns: agent
135
136
(restart-agent agent new-state & options)
137
;; Clears error and sets new state
138
;; Options: :clear-actions boolean
139
;; Returns: agent
140
141
(set-error-handler! agent handler-fn)
142
;; Sets error handler function
143
;; handler-fn receives: agent, exception
144
;; Returns: agent
145
146
(error-handler agent)
147
;; Returns current error handler
148
;; Returns: function or nil
149
150
(set-error-mode! agent mode)
151
;; Sets error mode: :continue or :fail
152
;; Returns: agent
153
154
(error-mode agent)
155
;; Returns current error mode
156
;; Returns: :continue or :fail
157
158
(shutdown-agents)
159
;; Shuts down agent thread pools
160
;; Call at end of program
161
;; Returns: nil
162
```
163
164
### Vars and Dynamic Binding
165
166
Vars provide thread-local bindings and global state management.
167
168
```clojure { .api }
169
(var symbol)
170
;; Returns var object for symbol (same as #'symbol)
171
;; Returns: clojure.lang.Var
172
173
(binding [bindings*] & body)
174
;; Creates thread-local bindings for dynamic vars
175
;; bindings: [var-symbol value var-symbol value ...]
176
;; Returns: result of last body expression
177
178
(with-local-vars [bindings*] & body)
179
;; Creates thread-local vars
180
;; bindings: [symbol init-value symbol init-value ...]
181
;; Returns: result of last body expression
182
183
(var-get var)
184
;; Returns current value of var
185
;; Returns: Object
186
187
(var-set var val)
188
;; Sets thread-local value of var (must be bound)
189
;; Returns: val
190
191
(alter-var-root var f & args)
192
;; Atomically alters root value using (f current-value args)
193
;; Returns: new value
194
195
(with-redefs [bindings*] & body)
196
;; Temporarily redefines vars during body execution
197
;; bindings: [var-symbol temp-value var-symbol temp-value ...]
198
;; Returns: result of last body expression
199
200
(bound? var)
201
;; Returns true if var has thread-local binding
202
;; Returns: boolean
203
204
(thread-bound? var)
205
;; Returns true if var is bound to separate thread-local value
206
;; Returns: boolean
207
```
208
209
### Delays and Promises
210
211
Utilities for delayed computation and coordination between threads.
212
213
```clojure { .api }
214
(delay & body)
215
;; Creates delay object that computes body on first deref
216
;; Computation happens only once and result is cached
217
;; Returns: clojure.lang.Delay
218
219
(force delay)
220
;; Forces computation of delay (same as deref)
221
;; Returns: result of delay body
222
223
(realized? delay)
224
;; Returns true if delay has been computed
225
;; Returns: boolean
226
227
(promise)
228
;; Creates promise object that can be delivered once
229
;; Returns: clojure.lang.Promise
230
231
(deliver promise val)
232
;; Delivers val to promise
233
;; Can only be called once per promise
234
;; Returns: promise
235
236
(future & body)
237
;; Creates future that computes body in another thread
238
;; Returns: java.util.concurrent.Future
239
240
(future-call f)
241
;; Like future but calls function f with no arguments
242
;; Returns: java.util.concurrent.Future
243
244
(future-cancel future)
245
;; Attempts to cancel future
246
;; Returns: boolean
247
248
(future-cancelled? future)
249
;; Returns true if future was cancelled
250
;; Returns: boolean
251
252
(future-done? future)
253
;; Returns true if future completed
254
;; Returns: boolean
255
```
256
257
**Usage Examples:**
258
259
```clojure
260
;; Atoms for simple state
261
(def counter (atom 0))
262
(swap! counter inc) ; => 1
263
@counter ; => 1
264
(reset! counter 10) ; => 10
265
266
;; Atoms with validation
267
(def positive-num (atom 1 :validator pos?))
268
(swap! positive-num inc) ; => 2
269
; (reset! positive-num -1) ; Would throw exception
270
271
;; Refs for coordinated state
272
(def account1 (ref 100))
273
(def account2 (ref 200))
274
275
(defn transfer [from to amount]
276
(dosync
277
(alter from - amount)
278
(alter to + amount)))
279
280
(transfer account1 account2 50)
281
[@account1 @account2] ; => [50 250]
282
283
;; Agents for async state
284
(def log (agent []))
285
(send log conj "Starting application")
286
(send log conj "Loading config")
287
(await log)
288
@log ; => ["Starting application" "Loading config"]
289
290
;; Dynamic binding
291
(def ^:dynamic *debug* false)
292
293
(defn debug-print [msg]
294
(when *debug*
295
(println "DEBUG:" msg)))
296
297
(binding [*debug* true]
298
(debug-print "This will print"))
299
300
;; Delays and promises
301
(def expensive-calc
302
(delay
303
(Thread/sleep 1000)
304
(* 6 7)))
305
306
@expensive-calc ; Takes 1 second, returns 42
307
@expensive-calc ; Returns 42 immediately
308
309
(def p (promise))
310
(future (deliver p "Hello from future"))
311
@p ; => "Hello from future"
312
313
;; Futures
314
(def f (future
315
(Thread/sleep 1000)
316
(+ 1 2 3)))
317
318
@f ; Blocks until complete, returns 6
319
```