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

Support GHCJS #51

Closed
jkarni opened this issue Apr 20, 2015 · 48 comments
Closed

Support GHCJS #51

jkarni opened this issue Apr 20, 2015 · 48 comments

Comments

@jkarni
Copy link
Member

jkarni commented Apr 20, 2015

this is a good starting point.

@tvh
Copy link
Contributor

tvh commented May 22, 2015

With the change in the error type this got harder in 0.4. The ConnectionError leaks the underlying Implementation. Not sure what the best thing would be here, but this is definitely a step back.

@alpmestan
Copy link
Contributor

Could you expand on why this got harder?

@tvh
Copy link
Contributor

tvh commented May 23, 2015

http-client won't work with GHCJS because of its dependency on network. Previously it was possible to just remove the dependency on http-client, as there was nothing in the interface referencing that package. The new ServantError however catches the HttpException.

@alpmestan
Copy link
Contributor

Do you think we can make that bit be conditionally compiled when the compiler is not ghcjs?

@tvh
Copy link
Contributor

tvh commented May 23, 2015

I would like to avoid that. The issue here is, that any client-side pattern matches would need the same annotations. I think it would be better to replace the exception with a simplified datatype of our own. That could then be populated with appropriate errors from either XMLHttpRequest or http-client.

@alpmestan
Copy link
Contributor

Do you feel like putting a PR together?

@tvh
Copy link
Contributor

tvh commented May 24, 2015

Can do, but I'd like some input on the granularity. HttpException (http://hackage.haskell.org/package/http-client-0.4.11.2/docs/Network-HTTP-Client.html#t:HttpException) exposes a lot of details. I would prefer something simpler.

On actually making GHCJS work: I would probably user ghcjs-dom to abstract the foreign calls away. This is a bit of a moving target though. ghcjs-base is currently being rewritten and ghcjs-dom depends on those changes already.

@jkarni
Copy link
Member Author

jkarni commented Jul 29, 2015

Some development related to this is happening at https://github.com/plow-technologies/ghcjs-servant-client

@soenkehahn
Copy link
Contributor

I just pushed a new branch here: https://github.com/haskell-servant/servant/tree/client-ghcjs

And some explanation about it here: https://github.com/haskell-servant/servant/blob/client-ghcjs/GHCJS.md

I'm not planning to put that much more work into this, but I hope that this branch may serve as a basis for other people to improve the support for ghcjs.

@tdietert
Copy link

Thanks for this. In the next few weeks I may try to dig deeper and fix some of the problems you've outlined in the branch readme.md, but most likely only with respect to the failing test case. I don't feel like playing with javascript enough to tackle the json parsing issue.

There are many branches with 'gchjs-wip" in the name, and I was wondering, for clarity, if you could delete the branches you think are fully unnecessary.

I think as ghcjs and servant become more widespread as haskell development moves to the frontend, this effort will become increasingly more relevant. Thanks for all the effort put in to Servant and trying to get it to work with ghcjs, I'm sure there are many others with as much appreciation as myself. Looking forward to seeing Servant and ghcjs become more compatible.

@soenkehahn soenkehahn removed this from the servant-0.5 milestone May 14, 2016
@alpmestan
Copy link
Contributor

@soenkehahn can surely help in clarifying which branches are of interest.

@soenkehahn
Copy link
Contributor

The branch linked above: https://github.com/haskell-servant/servant/tree/client-ghcjs.

I've also tried to delete all wip branches, but I'm waiting for this before deleting the last one: #425 (comment).

@tdietert
Copy link

@soenkehahn Are the only problems with the ghcjs branch listed in the GHCJS.md, or are they just the issues you are aware of?

I was thinking about the xhr2 request discarding the request bodies, and was wondering if servant-client-ghcjs wants to adhere to that specification. It might be natural to try to adhere to the xhr2 specification and disallow request bodies for GET and HEAD when compiling with GHCJS.

Regarding CI with the ghcjs branch, what in particular is difficult? You said you tried and failed miserably, but if I want to continue where you left off, what should I be looking out for regarding a potential pull request against master?

@jkarni
Copy link
Member Author

jkarni commented May 20, 2016

Regarding CI with the ghcjs branch, what in particular is difficult? You said you tried and failed miserably, but if I want to continue where you left off, what should I be looking out for regarding a potential pull request against master?

@soenkehahn is the person with the more authoritative answer, but from what he said, part of the problem is requiring three (two GHCs, and one GHCJS) compilers, and not being able to cache builds due to the cache blowing up. Note that testing for the GHCJS branch requires building servers with GHC so that the client can be run against some server.

@soenkehahn
Copy link
Contributor

@tdietert: There's also this issue that isn't fixed yet: #425.

Apart from that this hasn't been tested in practice. So I wouldn't be surprised if there were more problems once you start using this branch for real.

I was thinking about the xhr2 request discarding the request bodies, and was wondering if servant-client-ghcjs wants to adhere to that specification. It might be natural to try to adhere to the xhr2 specification and disallow request bodies for GET and HEAD when compiling with GHCJS.

Yes and no. I guess it makes sense to not send request bodies for GET requests. The point is: servant-client compiled with ghc happily works with APIs like that. And it would be nice if servant-client compiled with ghcjs would behave exactly in the same way.

But I'm not strictly against not supporting GET request bodies. How would you go about it, though?
What would you do about the failing test-case that is caused by this? And what would happen if somebody still tried to send a request body with GET?

Regarding CI: I tried different things:

  • Building ghcjs on travis through stack and caching .stack: This takes really a long time when the cache is cold. And the cache became so big that uploading the cache timed out.
  • Using circleCI for CI: IIRC circleCI doesn't build PRs from forks on github. So it's not a viable solution to replace travis. And I didn't want to have 2 CI systems.
  • Using docker to prebuilt ghcjs and other dependencies: The docker image was too huge. docker pull timed out on travis.

I next thing I would try (if I were to work on this) is coming up with a binary distribution for ghcjs and install from that during CI on travis. That seems like a huge undertaking to me, though.

@tdietert
Copy link

tdietert commented May 23, 2016

Looking at the most recent HTTP specification, it says this:

"In particular, the convention has been established that the GET and HEAD methods SHOULD NOT have the significance of taking an action other than retrieval. These methods ought to be considered "safe". This allows user agents to represent other methods, such as POST, PUT and DELETE, in a special way, so that the user is made aware of the fact that a possibly unsafe action is being requested."

With that in the specification, as well as xhr2 not supporting request bodies for GET and HEAD, it seems to me that it is essentially a minor violation of the HTTP protocol, and I'm wondering why Network.HTTP.Client supports this behavior to begin with. Perhaps it was too annoying to write the low-level haskell code to do this, as Network.HTTP.Client.Core seems to ignore request methods throughout its entirety.

Would it be feasible using conditional compilation to disallow APIs that specify Request Bodies in GET and HEAD requests when compiling with GHCJS? I'm not sure what that would entail yet, but if you gave me the goahead I would look into it and try implementing that. We could also have conditional compilation for tests and just ignore the failing test (GET Request with Request body), or rely purely on QueryParams for transmission of data to server during GET and HEAD reqs... but I don't know, and I'm not really sure what kind of challenge this would be.

@soenkehahn @jkarni @arianvp Does anyone have thoughts on this? I don't really see any other way, since xhr2 simply automatically discards the body of GET and HEAD reqs.

@jkarni
Copy link
Member Author

jkarni commented May 23, 2016

I don't care much about whether request bodies in GET are discarded by the client or not. From what I remember from reading the RFCs a while back, it was not a violation of RFCs for a client to send a request body, but it was a violation for a server to do anything with it (e.g., RFC 2616, section 4.3). So for standard-compliant servers, it's not like it matters what the client does.

I'm a little confused by the discussion of side-effects, though. Unless I'm misunderstanding, this seems completely orthogonal to whether request bodies are allowed in GETs.

@tdietert
Copy link

I don't agree that it's completely orthogonal, since if you have a request body in a GET, then naturally the server is able to do something with it or perform a side-effectful action, which is reserved for POST. I have removed that part from the comment above, but I think it's slightly relevant in the sense that, if the server should not do anything with the request body, why have the option for it? As it is, users of servant-client and servant-server can and most likely will use request bodies of GET requests in their server, when it is functionally allowed.

In both ajax and xmlhttprequests, the GET and HEAD req bodies are ignored, and we are using ghcjs to compile haskell code to javascript, so it doesn't really make sense to allow request bodies in those request types, if by specification the server is not supposed to do anything with them. Both the most recent ajax and xmlhttprequests implement this by treating the request body as null before actually performing the request, so why should servant-client-ghcjs work any differently? I ask this genuinely, and am not sure why my initial suggestion was not sensible or why you thought the discussion of side effects based upon request bodies was unrelated, but I am not fluent in frontend web dev so I recognize I may be a bit naive.

@jkarni
Copy link
Member Author

jkarni commented May 23, 2016

To be clear, I'm completely okay with the body being thrown out - we should probably even statically ensure that GET endpoints don't have ReqBody combinators.

But the existence of arguments (specifically in the body) isn't really related to the presence of side effects. DELETE, for example, stands in a similar situation to GET and HEAD w.r.t. bodies, even though it's allowed to have side effects. Conversely, we could imagine an endpoint "succ" that takes a request body that is an integer, and returns its successor, without having side effects. But in any case, I don't think this latter discussion affects my opinion: as I said, I'm happy to drop the body in GHCJS client, in GHC client, and in the other client libraries, if it's at all easier.

@tdietert
Copy link

Ok, I understand. I am not well versed in the semantics of network request types but I'm always learning; I understand the distinction a bit better now.

Would it be worth trying to statically ensure that ReqBody's don't appear in GET api endpoints and for it to be a type error? If so, I think I will try sometime in the next few days. That, along with a small modification to the test that @soenkehahn is concerned about failing so that ReqBodys are actually disallowed.

I'm also going to look into PR #425 and see if the initial fix suggested works well enough.

@jkarni
Copy link
Member Author

jkarni commented May 23, 2016

Would it be worth trying to statically ensure that ReqBody's don't appear in GET api endpoints and for it to be a type error?

I would say so, though I think that should be a separate task than this, since it's both more general than, and not needed for, ghcjs. Ideally, for GHC 8, we would also have a nice type error message describing the reason for the type error.

@tdietert
Copy link

Do you mean different branch or just done in it's own pull request with respect to this branch? I like the idea of throwing out ReqBodys for all GET requests and not just those involved in compilation with GHCJS. However, after that, the test case in the run-tests.sh file for the client-ghcjs branch that fails will have to be altered, perhaps testing for failure when including ReqBody.

Thanks for all your help, I'll try to knock this out sometime in the next week or if I can make enough time.

@soenkehahn
Copy link
Contributor

@tdietert: I'm not really actively working on this branch, so don't expect updates from my side.

If you (or anyone else) wants to work on this, you could:

  • Find out whether GET requests issued from browsers (e.g. chrome or firefox) also ignore request bodies. I'm still not 100% sure whether this might not just be a peculiarity of the xhr2 library. Without this information, I don't think we should make any decision on this issue.
  • Try to get CI for ghcjs working in the client-ghcjs branch.

@arianvp
Copy link
Member

arianvp commented Aug 8, 2016

GET requestbodies MUST be ignored according to the HTTP RFC iirc. So i
think they are discarded.

On Fri, 5 Aug 2016, 14:20 Thomas Dietert, notifications@github.com wrote:

@soenkehahn https://github.com/soenkehahn I was looking into this last
night and had a lot of trouble getting the tests to run stack couldn't find
`hspec-discover'. I have tried installing hspec-discover with stack in the
servant-client directory and installing it with my global stack project.
Have you run into this problem before? I'm running this:

~/Documents/github/servant/servant-client $ stack test
~/Documents/github/servant/servant-client $ ./test/ghcjs/run-tests.sh

And get the error about hspec-discover not being found in my path. All
stack paths and local paths are added to my $PATH env variable. Have you
come across this issue before and do you have any thoughts on how I can
bypass this? I think I had a similar issue months ago on a different
computer, but forgot how I solved it.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#51 (comment),
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAmWo5JDZSXhSNHXlxmA5DTIBkCmorqfks5qc0a9gaJpZM4EEm3z
.

@FPtje
Copy link
Contributor

FPtje commented Sep 30, 2016

I've been playing around with this branch, trying to apply it to a real world application. Since everything I've got to say is about the client-ghcjs branch of this repository, I've decided to post everything in this issue.

Fork

There's a fork of the client-ghcjs branch which I updated to servant-0.9 (the latest master commit, not the tag). It just adds a single merge commit. Should I make a PR for this? I'm not sure whether a rebase is preferred over a merge commit. The merge was non-trivial.

Implementation example

There's a repository containing a simple working example that shows some of the problems I've experienced with servant-client for ghcjs. It contains two test clients: one (called client-0.8.1) depending on the client-ghcjs branch of this repository, and one (called client-0.9) depending on my updated fork. The repository also contains a little server that serves these clients.

I will be referring to this working example in the rest of this post.

Get requests with bodies

GHCJS.md mentions the following:

Sending bodies in requests doesn't work when using methods GET and HEAD. [...] (This might actually work in some browsers, no clue.)

That's a question, and the answer is simply no. It doesn't work in browsers. I've tested it with both Chromium and Firefox. The working example has a get request route with a body. The server simply returns whatever it gets.

In the end the request is performed, but the request body is discarded before the request is actually sent. The server thus both receives and then responds with an empty string.

This behaviour can be reproduced with both client-0.8.1 and client-0.9.

Thread blocked indefinitely

About a second after performing a request, the following exception is thrown in the browser's console:

thread blocked indefinitely in an MVar operation

It does not seem to affect functionality, but I'd recommend addressing it nonetheless. Currently, client-0.8.1 reproduces this exception, throwing the exception twice after performing the requests in the main function. client-0.9 would also reproduce this exception if it didn't throw another exception first (see next section). Commenting this code in client-0.9 causes it to throw the same exception.

FormURLEncoded

One big change in servant-0.9 gives up servant's own FromFormUrlEncoded and ToFormUrlEncoded classes for http-api-data's ToForm and FromForm classes. This is great for servant, but bad news for ghcjs support. The reason is that any requests using ReqBody '[FormUrlEncoded] ... will cause the following runtime exception to be thrown in the browser's console:

uncaught exception in Haskell main thread: unhandled primop: CopyByteArrayToAddrOp (0,5)

This error looks very nasty. I'm not sure what precisely causes it or whether the responsibility of this error lies with http-api-data or ghcjs. What I do know is that it prevents the request from being made. The client-0.9 example reproduces this exception. The client-0.8.1 example does not have this issue, since it uses servant's own FormURLEncoded classes.

Separate package?

This is purely my own opinion, but wouldn't it be better if servant-client-ghcjs was a separate package? There would be quite a few reasons to do this:

  • Requests in servant-client require a Manager. The ghcjs implementation casually discards this manager, but you still have to provide it when calling request functions (or in 0.9, when calling runClientM). The examples fill in undefined for the manager, but that's dirty. The Manager is about requests from http-client, which the ghcjs version has nothing to with.
  • The error in the previous section can be trivially circumvented by using a different way of dealing with FormURLData. More generically, implementation decisions that apply to servant-client do not necessarily apply to ghcjs' implementation. A trivial other example is that servant-client uses http-client whereas the ghcjs version uses Xhr.
  • GHCJS.md mentions that the use of aeson is probably suboptimal. Migrating to a native javascript implementation should be much easier and cleaner when servant-client-ghcjs is a separate package.
  • Requests currently also require a BaseUrl. It would be nice if there was a default BaseUrl that refers to the url of the website on which the ghcjs program is running. Such a default would make no sense for regular servant-client, but it makes perfect sense in servant-client-ghcjs.
  • I'm not sure, but I think some dependencies (http-client, http-client-tls) can be gotten rid of for the ghcjs implementation. It would make the cabal file cleaner (no more if impl(ghcjs)) and it would quicken compile times.

Finally

Despite the things mentioned above, servant-client for ghcjs works and it's awesome. I'd love to see this work become more than just this branch.

FPtje pushed a commit to LumiGuide/servant that referenced this issue Sep 30, 2016
Uses the fix mentioned by @arianvp in
haskell-servant#425.

See also
haskell-servant#51 (comment)

The cause of the issue very is similar to the issue described
in this blog post:
https://www.fpcomplete.com/blog/2016/06/async-exceptions-stm-deadlocks

The main thread creates a waiter, creates an asynchronous callback which is
called when the `readyState` of the request changes. When the readyState
changes to 4, which means 'request finished', the waiter MVar is put.
The main thread takes the MVar and continues to do stuff with the response.

The problem is that the `readyState` can change to 4 more than once,
for some reason. The second time this happens, the waiter MVar can be put
again, since the main thread took it. The main thread, however, won't take
it again. After all, it only needed to take the MVar once to know that the
request was finished. The third time `readyState` is set to 4, the putMVar
would block, causing the following exception to be thrown:

```
thread blocked indefinitely in an MVar operation
```

Since state 4 should really mean that the response is ready, it seems
appropriate to decide that all changes of the state to 4 after the initial one
can be safely ignored.
@FPtje
Copy link
Contributor

FPtje commented Sep 30, 2016

I looked into the "Thread blocked indefinitely" exception and found it to be the same as the issue described by @yogsototh and @nrolland in #425. I ended up implementing @arianvp's suggestion after figuring out what was really going on.

A full description of the issue and its solution can be found in the commit in my fork.

Also, I verified that the callback does indeed get called with state 4 multiple times in a row with debug prints.

@arianvp
Copy link
Member

arianvp commented Sep 30, 2016

Nice to see lumiguide working on this :).

And nice to hear it works so well.

Id prefer a rebased version. If you check out git-rerere it's probably as simple as an explicit merge commit .

Anyhow. The argument to use a separate package is pretty strong due to the fact that we don't need manager. I think I would prefer this.

You have plenty of valid arguments to this being the right choice.

@arianvp
Copy link
Member

arianvp commented Sep 30, 2016

The primop error is a ghcjs problem. Pinging @luite

@arianvp
Copy link
Member

arianvp commented Sep 30, 2016

@FPtje let's create an issue on the ghcjs repo. Also could you check the ghcjs 8.0.1 branch?

@jkarni
Copy link
Member Author

jkarni commented Sep 30, 2016

@FPtje thanks for the work and write-up!

The GET behavior sounds like what we want.

What about CI? This was, so far, the main issue we've had with merging the work onto master.

@FPtje
Copy link
Contributor

FPtje commented Sep 30, 2016

If the issue lies with ghcjs it's probably worth mentioning that I've been using ghcjs-0.2.0 with ghc 7.10.3. Sadly, I haven't had the chance to look at CI or the ghc 8 branch of ghcjs.

@FPtje
Copy link
Contributor

FPtje commented Oct 2, 2016

I've narrowed the primop exception down to a function in the bytestring package. The issue has been reported to ghcjs.

FPtje pushed a commit to LumiGuide/servant that referenced this issue Oct 2, 2016
Uses the fix mentioned by @arianvp in
haskell-servant#425.

See also
haskell-servant#51 (comment)

The cause of the issue very is similar to the issue described
in this blog post:
https://www.fpcomplete.com/blog/2016/06/async-exceptions-stm-deadlocks

The main thread creates a waiter, creates an asynchronous callback which is
called when the `readyState` of the request changes. When the readyState
changes to 4, which means 'request finished', the waiter MVar is put.
The main thread takes the MVar and continues to do stuff with the response.

The problem is that the `readyState` can change to 4 more than once,
for some reason. The second time this happens, the waiter MVar can be put
again, since the main thread took it. The main thread, however, won't take
it again. After all, it only needed to take the MVar once to know that the
request was finished. The third time `readyState` is set to 4, the putMVar
would block, causing the following exception to be thrown:

```
thread blocked indefinitely in an MVar operation
```

Since state 4 should really mean that the response is ready, it seems
appropriate to decide that all changes of the state to 4 after the initial one
can be safely ignored.
@FPtje
Copy link
Contributor

FPtje commented Oct 2, 2016

Alright, the PR has been made.

I took a quick look at the CI problem. The job for the client-ghcjs branch (here) shows the following error.

Building lifted-base-0.2.3.7...
Preprocessing library lifted-base-0.2.3.7...
[1 of 9] Compiling System.Timeout.Lifted ( System/Timeout/Lifted.hs, dist/dist-sandbox-8e67c193/build/System/Timeout/Lifted.o )
[2 of 9] Compiling Foreign.Marshal.Utils.Lifted ( Foreign/Marshal/Utils/Lifted.hs, dist/dist-sandbox-8e67c193/build/Foreign/Marshal/Utils/Lifted.o )
Foreign/Marshal/Utils/Lifted.hs:27:1:
    Foreign: Can't be safely imported! The module itself isn't safe.

This is a documented error. It's described in this issue. The issue is closed as fixed in lifted-base-0.2.3.8. Fixing this particular CI error should be as simple as updating lifted-base.

@luite
Copy link

luite commented Oct 2, 2016

Looks like I missed a primop in the list. It doesn't look fundamentally hard to implement.

Also I think ultimately these safe-haskell inconsistencies shouldn't be there, between GHC and GHCJS. I'm not sure how easy it'd be to fix them though.

@soenkehahn
Copy link
Contributor

@jkarni, @FPtje: If I were working on this I would definitely go for a separate package for ghcjs support for servant. For the reasons @FPtje mentioned here: #51 (comment). Using aeson to decode JSON in browsers may be a viable option temporarily, but I don't see an easy way to fix that in servant-client without complicating the package considerably.

I've also entertained the idea of having a package that gives us pure request stubs for any client side use of servant apis. servant-client-ghcjs could be based on that. (I toyed around with this idea a little here: https://github.com/soenkehahn/servant-request.)

@alpmestan
Copy link
Contributor

+1 for the separate package.

@dmjio
Copy link
Member

dmjio commented Oct 11, 2016

@soenkehahn, @jkarni, @FPtje, @alpmestan

If we did want to go the single package route in servant-client, and provide different decoders (i.e. JSON.parse vs. aeson's decode) based on the compiler version, one clean approach we could take (as opposed to using CPP flags everywhere and #if/#endif) is this:

@luite has mentioned this, and reflex-dom does the same. Provide multiple hs-src-dirs directories based on the compiler, keep the module names the same, but their implementations different.

So the cabal file could look like:

library
  exposed-modules:
    Servant.Client
  ghc-options: -Wall
  hs-source-dirs: src
  build-depends:   
                   base > 4.7 && < 5.0
                 , servant
                 , bytestring
                 , text
  if impl(ghcjs)
    hs-source-dirs: ghcjs-src
    build-depends: ghcjs-base
  else
    hs-source-dirs: ghc-src
    build-depends: aeson, http-client

So it would look like this in the end

.
|-- ghc-src
|   `-- Servant
|       `-- Client.hs
`-- ghcjs-src
    `-- Servant
        `-- Client.hs

Stack and cabal, as long as they're configured correctly, should handle it no problem. For nix expressions there would need to be some kind of conditional to bring the right deps in.

{ mkDerivation, aeson, base, stdenv, http-client, bytestring
, text, compiler, nixpkgs
}:
let
  deps =
    if compiler == "ghcjs"
      then
        let
          ghcjs-base = nixpkgs.haskell.packages.ghcjs.ghcjs-base;
        in
           [ base servant bytestring text ghcjs-base ]
       else
         [ base servant bytestring text aeson http-client ];
in mkDerivation {
  pname = "servant-client";
  version = "0.9.0.1";
  src = ./.;
  libraryHaskellDepends = deps;
  license = stdenv.lib.licenses.bsd3;
}

@alexanderkjeldaas
Copy link
Contributor

What do you think about @dmjio's suggestion? I think it makes sense to share as much as possible in one package.

@FPtje
Copy link
Contributor

FPtje commented Nov 22, 2016

Sounds like a good plan.

@dmjio
Copy link
Member

dmjio commented Nov 22, 2016

+1 for package sharing, but there's still other issues, like supporting more than JSON, needing to make types for Blob / ArrayBuffer storage (and ContentTypes for these things). Since requests are asynchronous, we'll need a way to synchronize the sending thread with the receiving one (Maybe with clever usage of MVar). Lastly, Request bodies aren't supported on GET in the browser (per @soenkehahn's insight). So how can we disallow this invalid state at compile time (when in the browser).

waern pushed a commit to waern/servant that referenced this issue Jan 5, 2017
Uses the fix mentioned by @arianvp in
haskell-servant#425.

See also
haskell-servant#51 (comment)

The cause of the issue very is similar to the issue described
in this blog post:
https://www.fpcomplete.com/blog/2016/06/async-exceptions-stm-deadlocks

The main thread creates a waiter, creates an asynchronous callback which is
called when the `readyState` of the request changes. When the readyState
changes to 4, which means 'request finished', the waiter MVar is put.
The main thread takes the MVar and continues to do stuff with the response.

The problem is that the `readyState` can change to 4 more than once,
for some reason. The second time this happens, the waiter MVar can be put
again, since the main thread took it. The main thread, however, won't take
it again. After all, it only needed to take the MVar once to know that the
request was finished. The third time `readyState` is set to 4, the putMVar
would block, causing the following exception to be thrown:

```
thread blocked indefinitely in an MVar operation
```

Since state 4 should really mean that the response is ready, it seems
appropriate to decide that all changes of the state to 4 after the initial one
can be safely ignored.
@NorfairKing
Copy link
Contributor

I feel a bit silly for asking, but @FPtje said that the 'manager' argument gets ignored. When I try to run servant-client with GHCJS and servant-client-0.10, I get a runtime error when I pass in undefined for the manager.

Am I missing something here?

@FPtje
Copy link
Contributor

FPtje commented Aug 13, 2017

The PR I made never got merged. It was for servant 0.9. How did you get it to work on servant 0.10?

But yeah, in my version the http manager gets ignored, as the browser takes over the task of managing http requests. In the ghcjs implementation the reference to the http manager simply remains unused.

@jkarni
Copy link
Member Author

jkarni commented Mar 6, 2018

I'm going to call this done now that there's servant-client-ghcjs. (Despite it still not being checked by CI...)

@jkarni jkarni closed this as completed Mar 6, 2018
tchoutri pushed a commit that referenced this issue Apr 22, 2024
* Prepare for 0.0.7.2 release
tchoutri pushed a commit that referenced this issue Apr 22, 2024
* Prepare for 0.0.7.2 release
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests