haskellari / postgresql-libpq

Low-level Haskell bindings for libpq
BSD 3-Clause "New" or "Revised" License
20 stars 25 forks source link

Fix some FFI functions incorrectly being `unsafe`. #19

Closed nh2 closed 1 year ago

nh2 commented 2 years ago

Those functions can be reentrant or do IO.

This fixes a case I encountered on our CI machine where unsafe on PQisBusy() resulted in GHC RTS hangs when a ostgresql notice processor was set to call back into Haskell.

nh2 commented 2 years ago

I also have uneasy feeling, as any of now-still-unsafe functions may start to notify in the future. Whether they call notifier is not in documentation, and would be hard to test.

Yes, implementation change is a risk. Even more likely than notifying that they may notify, they may call libpq_gettext().

I guess that struct accessor functions like PQuser are relatively certain to stay stuct accessor functions, but we don't have guarantees.

Personally I'd really like to have a flag to the GHC RTS that (with some overhead costs) tracks whether any unsafe call calls back into Haskell, and terminates the program. That flag could then be used in test suites.

How bad would be to mark all PGConn/PGResult related functions as safe?

safe calls are made on a separate OS thread so that they cannot block the runtime, and that scheduling is costly (compared to struct access or cached IO, but not to real IO like network or disk access).

On https://gitlab.haskell.org/ghc/ghc/-/issues/13296#note_132110 Simon Marlow estimates the safe overhead to 100 ns.

So the answer would depend on whether there are use cases that call one of these functions very quickly in a loop, e.g. to parse a multi-GB query by parts, or something like that. I have not yet investigated how likely that is to occur.

In any case, I propose that we first merge this fix to fix certain hangs, and then a separate investigation can be launched to see whether a general safer default would be sensible.

nh2 commented 2 years ago

For some googleability, and if others have similar problems, this is how I found the cause of the hang:

phadej commented 2 years ago

i'd prefer to default to all PGConn and PGResult functions to be marked safe. E.g. PQgetlength is called on each table cell, so the price goes up anyway, (in FromRow instances of postgresql-simple e.g.)

So I'd rather error-out to the safe side, and then mark functions unsafe when there is motivation and evidence it's fine to do so.

phadej commented 1 year ago

I made all functions safe in https://github.com/haskellari/postgresql-libpq/pull/48

nh2 commented 1 year ago

Very good!