Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Properly handle nested eval requests #74

Closed
darwin opened this issue Jan 28, 2018 · 5 comments
Closed

Properly handle nested eval requests #74

darwin opened this issue Jan 28, 2018 · 5 comments

Comments

@darwin
Copy link
Member

darwin commented Jan 28, 2018

Taken from

https://gist.github.com/adamrenklint/497d6db755eba21b965ee56f610314b8

; Consider the following code:

(ns debugger-demo.core)
(defn init []
  (let [a (+ 1 2 3)]
    (js-debugger)))

; Reload the page and let the Dirac REPL connect

; We'll immediately stop at the breakpoint, and by going into the namespace we can inspect the state of the frame at which we have stopped

cljs.user > (require 'debugger-demo.core)
cljs.user > (in-ns 'debugger-demo.core)
debugger-demo.core > a
                   < 6

; But it's easy to break this. First, press play to continue exection.

; And then, let's re-define the init function:

debugger-demo.core > (defn init []
                       (let [a (+ 1 2 5)]
                         (js-debugger)))

; Then call the init function to stop at the breakpoint and try to inspect the state:

debugger-demo.core > (init)
debugger-demo.core > a

; We never get a response in the REPL client, and we see the following error in the REPL server:

SEVERE: Unhandled REPL handler exception processing message {:code a, :dirac short-circuit-presentation, :id 25, :op eval, :scope-info {:frames [{:props [{:name a}], :title Local}]}, :session 48a3efd0-b8f3-4a08-9e64-20d92b89ac27}
java.lang.AssertionError: Assert failed: promise-new-client-response! previous response promise pending
(nil? (clojure.core/deref response-promise-atom)
  at dirac.lib.weasel_server$promise_new_client_response_BANG_.invokeStatic(weasel_server.clj:141)
  at dirac.lib.weasel_server$promise_new_client_response_BANG_.invoke(weasel_server.clj:137)
  at dirac.lib.weasel_server$request_eval.invokeStatic(weasel_server.clj:198)
  at dirac.lib.weasel_server$request_eval.doInvoke(weasel_server.clj:197)
  at clojure.lang.RestFn.invoke(RestFn.java:442)
  at dirac.lib.weasel_server.WeaselREPLEnv._evaluate(weasel_server.clj:50)
  at cljs.repl$evaluate_form.invokeStatic(repl.cljc:518)
  at cljs.repl$evaluate_form.invoke(repl.cljc:452)
  at cljs.repl$repl_STAR_.invokeStatic(repl.cljc:853)
  at cljs.repl$repl_STAR_.invoke(repl.cljc:760)
  at dirac.nrepl.eval$eval_in_cljs_repl_BANG_$start_repl_fn__25076.invoke(eval.clj:245)
  at dirac.nrepl.driver$wrap_with_driver.invokeStatic(driver.clj:142)
  at dirac.nrepl.driver$wrap_with_driver.invoke(driver.clj:126)
  at dirac.nrepl.eval$eval_in_cljs_repl_BANG_.invokeStatic(eval.clj:249)
  at dirac.nrepl.eval$eval_in_cljs_repl_BANG_.doInvoke(eval.clj:212)
  at clojure.lang.RestFn.invoke(RestFn.java:758)
  at dirac.nrepl.utils$evaluate_BANG__STAR_.invokeStatic(utils.clj:134)
  at dirac.nrepl.utils$evaluate_BANG__STAR_.invoke(utils.clj:122)
  at dirac.nrepl.utils$evaluate_BANG_.invokeStatic(utils.clj:148)
  at dirac.nrepl.utils$evaluate_BANG_.invoke(utils.clj:145)
  at dirac.nrepl.piggieback$handle_eval_message_BANG_.invokeStatic(piggieback.clj:63)
  at dirac.nrepl.piggieback$handle_eval_message_BANG_.invoke(piggieback.clj:60)
  at dirac.nrepl.piggieback$handle_nonspecial_nonjoined_message_BANG_.invokeStatic(piggieback.clj:77)
  at dirac.nrepl.piggieback$handle_nonspecial_nonjoined_message_BANG_.invoke(piggieback.clj:70)
  at dirac.nrepl.piggieback$handle_nonspecial_message_BANG_.invokeStatic(piggieback.clj:85)
  at dirac.nrepl.piggieback$handle_nonspecial_message_BANG_.invoke(piggieback.clj:80)
  at dirac.nrepl.piggieback$handle_message_BANG_.invokeStatic(piggieback.clj:92)
  at dirac.nrepl.piggieback$handle_message_BANG_.invoke(piggieback.clj:87)
  at dirac.nrepl.piggieback$handler_job_BANG_.invokeStatic(piggieback.clj:97)
  at dirac.nrepl.piggieback$handler_job_BANG_.invoke(piggieback.clj:94)
  at dirac.nrepl.piggieback$dirac_nrepl_middleware_handler.invokeStatic(piggieback.clj:106)
  at dirac.nrepl.piggieback$dirac_nrepl_middleware_handler.invoke(piggieback.clj:102)
  at clojure.core$partial$fn__5561.invoke(core.clj:2616)
  at clojure.tools.nrepl.middleware$wrap_conj_descriptor$fn__7890.invoke(middleware.clj:22)
  at clojure.tools.nrepl.middleware.pr_values$pr_values$fn__8089.invoke(pr_values.clj:22)
  at clojure.tools.nrepl.middleware$wrap_conj_descriptor$fn__7890.invoke(middleware.clj:22)
  at boot.repl_server$wrap_init_vars$fn__8397$fn__8399.invoke(repl_server.clj:35)
  at clojure.tools.nrepl.middleware.session$session$fn__8241.invoke(session.clj:192)
  at clojure.tools.nrepl.middleware$wrap_conj_descriptor$fn__7890.invoke(middleware.clj:22)
  at clojure.tools.nrepl.server$handle_STAR_.invokeStatic(server.clj:19)
  at clojure.tools.nrepl.server$handle_STAR_.invoke(server.clj:16)
  at clojure.tools.nrepl.server$handle$fn__8314.invoke(server.clj:28)
  at clojure.core$binding_conveyor_fn$fn__5476.invoke(core.clj:2022)
  at clojure.lang.AFn.call(AFn.java:18)
  at java.util.concurrent.FutureTask.run(FutureTask.java:266)
  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
  at java.lang.Thread.run(Thread.java:745))
@darwin
Copy link
Member Author

darwin commented Jan 28, 2018

@adamrenklint aha, thanks for reporting

this probably makes sense, because on server-side, we request-eval from javascript, it stops on a breakpoint and then in dirac prompt we request another code snippet to be compiled and evaluated by javascript, but the server-side component can deal only with one request at a time - it processes them in serial queue and still waiting for result of the first eval

https://github.com/binaryage/dirac/blob/master/src/lib/dirac/lib/weasel_server.clj#L197
https://github.com/binaryage/dirac/blob/master/src/lib/dirac/lib/weasel_server.clj#L141

I will probably have to think about this scenario, maybe allow multiple pending eval requests or somehow treat the eval request paused on a breakpoint as “completed”

@adamrenklint
Copy link

Another piece of information: After getting into this state, and I resume execution after the breakpoint, save the file in my editor with the updated init fn, trigger a code injection via figwheel/boot-reload/boot-figreload, the breakpoint will once again be triggered immediately. Resume execution again.

Then eval (mount-root) from the REPL, and when paused at the breakpoint, eval a. That works, even though I would assume it is also nested requests...

@darwin
Copy link
Member Author

darwin commented Jan 28, 2018

Do you see full call-stack of (mount-root) REPL eval? Wasn't some async call involved there?

Minimal case:

(defn trigger-async-breakpoint [] 
  (js/setTimeout #(js-debugger) 0))

I assume calling (trigger-async-breakpoint) from REPL would not cause nested eval request, because technically the initial eval request finishes and breakpoint is triggered in some async piece of code which wasn't directly called in REPL eval.

@adamrenklint
Copy link

Yes, I can confirm that wrapping js-debugger in a setTimeout solves the nested request issue, but it means that the scope in which the debugger executes is different from what I originally intended, and I am not able to see the value of a.

darwin added a commit that referenced this issue Jan 28, 2018
darwin added a commit that referenced this issue Jan 29, 2018
broken when introducing fix for issue #74
@darwin
Copy link
Member Author

darwin commented Jan 29, 2018

Should be fixed in v1.2.26.

@darwin darwin closed this as completed Jan 29, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants