Skip to content

Instantly share code, notes, and snippets.

@noisesmith
Created June 3, 2017 22:05
Show Gist options
  • Save noisesmith/02ee2ee5dcb8c0290bd8004c4c4d36aa to your computer and use it in GitHub Desktop.
Save noisesmith/02ee2ee5dcb8c0290bd8004c4c4d36aa to your computer and use it in GitHub Desktop.
core.async/thread but it cancels the future if possible if you close the channel
(ns org.noisesmith.agreeable
(:require [clojure.core.async :as >]
[clojure.core.async.impl.protocols :as >proto]
[clojure.core.async.impl.channels :as >chan])
(:import (clojure.lang Var)))
(deftype CancelableThreadChan [future-task channel]
>proto/ReadPort
(take! [this fn1-handler]
(>proto/take! channel fn1-handler))
>proto/Channel
(close! [this]
(future-cancel future-task)
(>proto/close! channel))
(closed? [this]
(>proto/closed? channel))
>chan/MMC
(cleanup [this]
(future-cancel future-task)
(>chan/cleanup channel))
(abort [this]
(future-cancel future-task)
(>chan/abort channel)))
(defn thread-call
"Copied from clojure.core.async/thread-call, captures a handle to the
thread that we can use for cancellation.
Executes f in another thread, returning immediately to the calling
thread. Returns a channel which will receive the result of calling
f when completed, then close."
[f]
(let [c (>/chan 1)]
(let [binds (Var/getThreadBindingFrame)
future-task (.submit @#'>/thread-macro-executor
(fn []
(Var/resetThreadBindingFrame binds)
(try
(let [ret (f)]
(when-not (nil? ret)
(>/>!! c ret)))
(finally
(>/close! c)))))]
(->CancelableThreadChan future-task c))))
(defmacro thread
"Copied from clojure.core.async/thread-call, the channel it returns will
attempt to cancel the thread, if possible.
Executes the body in another thread, returning immediately to the
calling thread. Returns a channel which will receive the result of
the body when completed, then close."
[& body]
`(thread-call (^:once fn* [] ~@body)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment