Skip to content

Inconsistent results with deferred/onto #151

Open
@kingpong

Description

@kingpong

I'm trying to ensure that my chained callbacks are always run on a specific thread pool, so I'm using d/onto followed by d/chain to set up the callbacks. What I'm seeing, though, is that sometimes the callbacks will be run directly on the derefing thread. I'm able to reproduce this behavior reliably (clojure 1.8, Java 1.8, manifold 0.1.6).

(require '[manifold.deferred :as d])
(import '(java.util.concurrent ForkJoinPool))

(defn tid []
  (let [t (Thread/currentThread)]
    (format "Thread %d: %s" (.getId t) (.getName t))))

(defn chained-onto-threads
  "Tries n times to run f (which should produce a deferred) then chains the
   result into a callback function that should run on the ForkJoinPool.
   Returns a map containing the frequencies of threads that were actually used."
  [n f]
  (let [forkjoin-pool (ForkJoinPool/commonPool)
        seen-threads  (atom {})]
    (run! deref
          (for [i (range n)]
            (-> (f)
                (d/onto forkjoin-pool)
                (d/chain
                  (fn [_]
                    ;; Expecting this to be ForkJoinPool, but isn't always.
                    (swap! seen-threads update (tid) #(inc (or % 0))))))))
    @seen-threads))

When I run chained-onto-threads with an f that produces a manifold future, sometimes the chained function runs on an nREPL thread, not a ForkJoinPool thread as expected:

user=> (require '[clojure.pprint :as pp])
nil
user=> (pp/pprint (chained-onto-threads 1000 #(d/future "any value")))
{"Thread 33: nREPL-worker-3" 7,
 "Thread 35: ForkJoinPool.commonPool-worker-0" 142,
 "Thread 37: ForkJoinPool.commonPool-worker-1" 123,
 "Thread 38: ForkJoinPool.commonPool-worker-2" 160,
 "Thread 39: ForkJoinPool.commonPool-worker-3" 144,
 "Thread 40: ForkJoinPool.commonPool-worker-4" 143,
 "Thread 43: ForkJoinPool.commonPool-worker-5" 163,
 "Thread 45: ForkJoinPool.commonPool-worker-6" 118}

With an f that produces a success-deferred, the chained function always runs on an nREPL thread, not on the executor that was specified:

user=>   (pp/pprint
  #_=>     (chained-onto-threads 1000 #(d/success-deferred "any value")))
{"Thread 15: nREPL-worker-1" 1000}

Can you shed any light on what's happening? Am I doing something wrong or is this a bug?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions