dgkf / debugadapter

Debug Adapter Protocol implementation for R
GNU General Public License v3.0
18 stars 2 forks source link

Using `{later}` to sync state #25

Open dgkf opened 1 month ago

dgkf commented 1 month ago

I've been exploring the use of later to repeatedly poll the debug adapter and synchronize state. This would alleviate one of my biggest concerns with this project currently, which is the lag time/expression for synchronization.

I think it's probably the way to go, but I need to really understand how to communicate between processes responsibly from a later callback. In naive tests it's raising an error when trying to reply back to the adapter.

Error in  writeChar(content_str, x)
 ignoring SIGPIPE signal

I think this is because the connection to the adapter is captured in the closure that is passed to later, which as I understand it runs a callback in a separate background process. From what I know of multiprocess R, passing connection objects is always error prone, so I'm guessing it's related to this behavior.

I might need a separate socket connection just for the later callback, but that would probably mean that I need to be more mindful about routing responses.

@lionel- does this sound like a reasonably hypothesis for this error? Any behaviors I should avoid when dealing with multiprocess communication within a later callback?

lionel- commented 1 month ago

hmm AFAIK {later} runs the callback in the same process and thread, when R is idle (determined by checking whether the call stack is empty).

Do you need to keep alive the closure's environment? It may get garbage collected after it has run if you don't register it again? Something along these lines could explain that a connection gets closed, which I'm guessing would explain the SIGPIPE signal?

dgkf commented 1 month ago

Ah, thanks for the tip. That's a good idea for further investigation.

I was trying to glean as much as possible from the background package that the idea spawned from. The code base is a lot smaller, so I was hoping to get a quick idea for the architecture. It seems that part of the implementation didn't make it to later.

I'll see if I can mock up a more minimal example and reproduce it then. I can hook into the reg.finalizer to see if the environment is getting garbage collected.

I had been repeatedly registering a function with later, but is there a better way to constantly poll a function?

f <- function() {
  print("here")
  later::later(f, 1)
}
later::later(f, 1)
lionel- commented 1 month ago

Not that I know of. Well you could use coro and do coro::async(function() { repeat("here"); Sys.sleep(1) }) but that would boil down to the simple timeout registration.