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
Use stream json rpc for transport #10
Conversation
@baronfel this is a continuation (alternative implementation) of #6 where I converted this is somewhat functional (apart from reporting errors via does this look ok to you? (please check only the second commit, 0f968fe) |
src/LanguageServerProtocol.fs
Outdated
| :? JsonException as ex -> | ||
async.Return (Result.Error (Error.Create(ErrorCodes.parseError, ex.ToString()))) | ||
let runAsTask param ct = | ||
Async.StartAsTask(runAndUnwrap param, cancellationToken=ct) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@baronfel I am in a bit of a pickle here with request ordering
Basically, StreamJsonRpc provides good request ordering, but it does that in a TPL/C# Task-specific way.. it ensures another task delegate is not invoked until the current one returns a Task so the application can have a change to enqueue current request to internal queue/some kind of lock object before allowing the next one to come in. See first paragraph in https://github.com/microsoft/vs-streamjsonrpc/blob/main/doc/resiliency.md#nonconcurrentsynchronizationcontext
If I were to implement it in C# then it would be relatively ok, as I could just do all the queuing of the work in non-async code before returning a Task from the delegate method. But in F# I am forced to launch Async.StartAsTask, if I want to attach a cancellation token to async workflow... And F# async are "cold" tasks.. So I was mucking with this code a bit and I probably don't know how to accomplish that thing with current prototype for (run: 'server -> 'param -> AsyncLspResult<'result>)
.
Maybe you have an idea. Otherwise I will have to change the prototype of run
to return a Task<LspResult>
instead to account for StreamJsonRpc sequencing signaling in my handler code that does enqueue text changes ("patches") from the client to roslyn model in good sequence before returning a Task
with remainder of the handler code that can then be run on task pool as it is ok at that point for me.
Is there some way to set Async.CancellationToken w/o invoking Async.StartAsTask..? I am not sure myself what would be the correct approach before converting the thing to use task
s..
This is mostly evident/uncovered in csharp-ls when there are multiple textDocument/didChange
requests from the client and those are dumped to TPL task pool as they come in by Async.StartAsTask and are executed randomly(?) by the thread pool code. The result is that csharp-ls
processes document deltas in wrong order and the document model gets corrupted. I was hoping I could get by with Async.StartAsTask hack but alas..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's probably ok to just accept needing to use tasks. That's what I did with my prior attempt, and we have good interop with that now in f# 6/with ply et al.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, then I will have to pass in ct to every request handler, that patch will get ugly :/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I know. It was a lot of churn in FSAC when I attempted it in the past, but I think it's worth it to take the pain. We get a well supported and fast json rpc base, and in exchange we have to be a bit more explicit about things.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm, I actually managed to avoid the need to convert everything to task-based flow. I simply do it like for tasks -- return async computation/object/block (not sure what is the right name for that ?) at the last moment and thus I have a chance to obtain state locks and to not drop to TPL thread pool until I have things in place when handling a request.
I will need to update this PR later.
Also, to make it easier for me to compose handler behaviour I will introduce another version of start
function that does not require a server "class" -- just a map of handlers. I loose some of type checking (I guess..) but I can compose the behaviour more easily with functional techniques. I will leave the original version in place too, so we should be good.
0c07bb9
to
7efcfa4
Compare
* Rename requestHandling -> serverRequestHandling; * Add requestHandling that does not depend on LspServer instance (for more functional implementations); * Add startWithSetup so we can code a server w/o LspServer instance.
7efcfa4
to
c4924f4
Compare
@baronfel this is up for review.. some very questionable :) changes were made, it is very up for discussion:
as i said in this comment, there was actually no problem serializing Task vs Async processing as in both cases I can just run sync code with locks until the very point I actually launch the handler which is then run asynchronously no problem |
sorry it took so long, got my new pc delivered, then it broke for weeks, then I was tracking emacs bug related to dotnet (https://lists.gnu.org/archive/html/emacs-devel/2022-02/msg00009.html) -- then life happened :) |
also it is somewhat a shame paket does not suport paket.local for .net core projects :( -- really hard to test things when you have to start your own nuget server to test your project integration before publishing complaining because csproj/sln does not this problem (even if it has other issues). this is why i was pushing #8 |
sorry for bugging @baronfel but does this need more work from me before getting it merged? |
ping @baronfel |
Hey! Sorry this took so long for me to get back to - it's great! Really excited to have this in :) |
$/cancelRequest
automaticallytype Client
is not reimplementedremove paket
commit, that is an optional part