Hello, Deadlock!

Программная транзакционная память в Clojure предоставляет программисту простые, мощные и безопасные инструменты для многопоточного программирования. Эти инструменты сконструированы с учетом типичных ошибок и проблем в многопоточных программах. Например, Clojure STM гарантирует что взаимные блокировки никогда не возникнут в процессе её работы.

Но даже не смотря на то, что STM что-то гарантирует, при должном навыке можно умудриться нарваться на добрую парочку старых добрых граблей многопоточного программирования.

Взаимная блокировка, deadlock, возникает в многопоточной среде когда несколько потоков находятся в состоянии бесконечного ожидания ресурсов, захваченных самими этими потоками.

Например, для агента

(def a (agent nil))

попытка организовать взаимную блокировку в лоб обречена на провал — STM нам этого не позволит — агент не может ожидать окончания выполнения действия в самом действии:

(send-off a #(do (await a) %))

> (agent-error a)
#<Exception java.lang.Exception: Can't await in agent action>

future выносит ожидание окончания выполнения дейтсвия агента в отдельный поток, который затем блокируется в потоке действия — происходит взаимоблокировка потоков.

(send-off a #(do (deref (future (await a))) %))

Вуаля — наш агент впал в кому, и больше на сообщения не реагирует:

(send-off a (constantly true))

> a
#<Agent@asdf: nil>