Search code examples
clojurelwjgl

How to inject code from one thread to another in Clojure (for live OpenGL editing in the REPL)?


How to inject code from one thread to another in Clojure?

I'm using Clojure with LWJGL and have implemented a game loop. I want to edit it interactively using the REPL. Consequently I have moved the loop and all OpenGL calls to a separate thread (using Java Runnables). I have has some success in changing the loop from the outside by: A. using atoms, accessible to both threads, and changing them from the REPL thread e.g. (:color @globals) and B. storing Clojure code as an atom (:code @globals), changing it from the REPL, and accessing it in the thread loop like so: (eval (:code @globals)).

But Clojure's symbols are all bound to one thread. I can send a message to add 2 and two (+ 2 2), but it doesn't get much further than that.

As a LISP, Clojure has the code as data advantage. I want to leverage this to use my REPL thread to modify my graphics thread, but need to get around the symbol binding thing - how?

My loop:

(while (not (GLFW/glfwWindowShouldClose window))  
    (eval (:code @globals))  
    (GL11/glClearColor (get (:color @globals) 0) (get (:color @globals) 1)  
             (get (:color @globals) 2) (get (:color @globals) 3) )  
     ......  
 )  

Solution

  • You can pass your function as a value to the shared atom. There is no need to pass the raw Clojure code as data and eval it - you can just pass the function value:

    (def my-function (atom (fn [])))
    
    (.start
      (Thread.
        (fn []
          (while true
            (@my-function)
            (Thread/sleep 1000)))))
    
    (reset! my-function
      (fn [] (println "A")))
    
    ;; wait a few seconds    
    ;; prints 'A's with one second interval
    
    
    (reset! my-function
      (fn [] (println "B")))
    
    ;; prints 'B's with one second interval