Skip to content

Instantly share code, notes, and snippets.

@noisesmith
Last active December 16, 2016 02:11
Show Gist options
  • Save noisesmith/3d97d7f9f3fddd4daaa5e1e01f795af7 to your computer and use it in GitHub Desktop.
Save noisesmith/3d97d7f9f3fddd4daaa5e1e01f795af7 to your computer and use it in GitHub Desktop.
acts like a future, but has a time-out after which it fails on deref
(ns condor.future-timeout
(:require [clojure.core.async :as >])
(:import (java.util Date)
(java.util.concurrent CancellationException)))
(defn timestamp
[]
(.getTime (Date.)))
(defn timed-future-call
"starts a future immediately, can declare it a failure based on elapsed time
f: called as a function, immediately with no arguments
time-out: milliseconds until failure, defaults to 0
failure: callback which is provided the actual elapsed time in milliseconds
reference for which methods can be cancelled: https://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html#interrupt()"
([f] (timed-future-call f 0 (fn [_])))
([f time-out] (timed-future-call f time-out (fn [_])))
([f time-out failure]
(let [result (promise)
started (timestamp)
future-thread (future-call
(fn cancellable-call []
(try (let [value (f)]
(deliver result value))
(catch CancellationException _)
(finally
(let [value (failure (- (timestamp) started))]
(deliver result value))))))]
(>/go
(>/<! (>/timeout time-out))
(future-cancel future-thread))
result)))
(defmacro timed-future
"
user=> (time (frequencies
(map deref
(doall (repeatedly
10000
#(f/timed-future {:time-out 1000
:failure (constantly :times-up)}
(do (Thread/sleep 1000)
42)))))))
\"Elapsed time: 1902.66766 msecs\"
{42 8894, :times-up 1106}"
([form] `(timed-future {} ~form))
([opts form]
{:pre [(map? opts)]}
(let [failure (or (:failure opts) (fn [_]))
time-out (or (:time-out opts) 0)]
`(timed-future-call (fn [] ~form)
~time-out
~failure))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment