core.asyncのmultでIllegalArgumentException "No implementation of method:〜"
が発生するケースを調べた際のメモ。
再現手順。
(require '[clojure.core.async :as a])
(a/mult (a/chan)) ;; 一度目は成功
(require 'clojure.core.async :reload-all) ;; 適当な名前空間でreload-all
(a/mult (a/chan)) ;; リロード以降は失敗する
;;=> IllegalArgumentException No implementation of method: :exec of protocol: #'clojure.core.async.impl.protocols/Executor found for class: clojure.core.async.impl.exec.threadpool$thread_pool_executor$reify__489 clojure.core/-cache-protocol-fn (core_deftype.clj:583)
例外のスタックトレース。
#error {
:cause "No implementation of method: :exec of protocol: #'clojure.core.async.impl.protocols/Executor found for class: clojure.core.async.impl.exec.threadpool$thread_pool_executor$reify__23498"
:via
[{:type java.lang.IllegalArgumentException
:message "No implementation of method: :exec of protocol: #'clojure.core.async.impl.protocols/Executor found for class: clojure.core.async.impl.exec.threadpool$thread_pool_executor$reify__23498"
:at [clojure.core$_cache_protocol_fn invokeStatic "core_deftype.clj" 583]}]
:trace
[[clojure.core$_cache_protocol_fn invokeStatic "core_deftype.clj" 583]
[clojure.core$_cache_protocol_fn invoke "core_deftype.clj" 575]
[clojure.core.async.impl.protocols$eval31750$fn__31751$G__31741__31758 invoke "protocols.clj" 43]
[clojure.core.async.impl.dispatch$run invokeStatic "dispatch.clj" 21]
[clojure.core.async.impl.dispatch$run invoke "dispatch.clj" 18]
[clojure.core.async$mult invokeStatic "async.clj" 695]
[clojure.core.async$mult invoke "async.clj" 669]
発生箇所のコード を確認する。
(defn run
"Runs Runnable r in a thread pool thread"
[^Runnable r]
(impl/exec @executor r))
impl/exec
で No implementation of method: :exec of protocol
と怒られているので 額面通りに受け取ると @exector
が指すオブジェクトが clojure.core.async.impl.protocols.Executor
プロトコルを実装していないという事になる。
もし @exector
が nil ならばこの例外発生は至極当然だが、found for class: clojure.core.async.impl.exec.threadpool$thread_pool_executor$reify__23498
とあり nil になってはいない。
ここからは推測になってしまうのだが、原因は
(require 'xxx :reload-all)
で 名前空間clojure.core.async.impl.protocols
がリロードされる- プロトコル
Executor
も再定義。Javaのインタフェースも再作成され同名で別の型が作られる - 再定義されプロトコル
Executor
のexec
関数は新しい型用となる - defonceで束縛されたオブジェクトが実装しているのは同名の古い型
- 新しい
exec
関数から見るとdefonceで束縛されたオブジェクトはExecutor
プロトコルを実装していない
という塩梅で、core.asyncとは直接関係の無い require :reload-allににおけるdefonceとプロトコル固有の問題 と理解した。
Clojure開発で快適なリロード生活を営むため tools.namespaceやns-trackerも含め各種リロード方式の仕組みやその注意点についてちゃんと勉強しておいた方が良さそうだ。