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

Release lock during io #29

Closed

Conversation

brendanlong
Copy link
Contributor

No description provided.

@brendanlong
Copy link
Contributor Author

brendanlong commented Sep 26, 2018

I'm not sure why it wouldn't let me re-open #27 but this is the same thing but with code to track whether or not we have the runtime lock, which makes it much easier to verify that we're doing things right in the callbacks.

This passes the tests in this repo and also my company's tests (which are unfortunately based on a higher-level library so I can't easily merge them into this repo), so I think it's ready to merge if you're happy with it. Let me know if you want any changes.

@brendanlong
Copy link
Contributor Author

Actually one more issue.. the error and message handlers can be called by dbopen, which is before we have our lock tracker setup.

@brendanlong brendanlong force-pushed the release-lock-during-io branch 2 times, most recently from d071311 to ea0878a Compare September 26, 2018 01:32
@brendanlong
Copy link
Contributor Author

Ok it should be good now. I added special handling for the dbopen case and a new test_bad_login test.

if (had_lock) {
release_lock(dbproc);
}
CAMLreturn(0);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: This used to have a comment /* should not return */, but this function will return as long as the callback doesn't throw an exception. Also the Sybase docs for dbmsghandle say:

The message handler must return a value of 0 to DB-Library.

http://infocenter.sybase.com/help/index.jsp?topic=/com.sybase.help.ocs_12.5.1.dblib/html/dblib/X12458.htm

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, and it's not entirely clear to me if it's safe to call CAMLreturn without the lock, but i don't know how to trigger CAMLreturn's cleanups otherwise?

if (had_lock) {
release_lock(dbproc);
}
CAMLreturn(INT_CANCEL);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another case where this can return if the error handle doesn't throw an exception.

Makefile Outdated
@@ -1,7 +1,7 @@
all: build

build:
@jbuilder build @install @test --dev
@jbuilder build @install @test
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--dev is now implicit in dune. Will make the explicit switch to dune so you can rebase on it.

Copy link
Contributor Author

@brendanlong brendanlong Sep 26, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I was getting this error with the --dev:

dune: --dev is no longer accepted as it is now the default.
Usage: dune build [OPTION]... [TARGET]...
Try `dune build --help' or `dune --help' for more information.
make: *** [Makefile:4: build] Error 1

I added this as pull request #28 if you want to review that on its own.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just pushed a full port to Dune (and the included configurator). Could you rebase on top if it?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Remove your changes to the Makefile and use dune instead of jbuild.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just rebased it

@Chris00
Copy link
Collaborator

Chris00 commented Sep 26, 2018

May you remind me: the library does not accept several concurrent calls in the same program?

@brendanlong
Copy link
Contributor Author

May you remind me: the library does not accept several concurrent calls in the same program?

http://www.freetds.org/faq.html#thread.safe

Different threads may all use separate connections without interfering with each other. Threads may not share a DBPROCESS or CS_CONNECTION without controlling access via a mutex.

So we can use multiple FreeTDS connections in different threads, we just can't use the same connection in multiple threads.

@brendanlong
Copy link
Contributor Author

brendanlong commented Sep 26, 2018

Hm I don't understand the new build failures.. Maybe it's related to the Dune change?

@brendanlong
Copy link
Contributor Author

Ah yeah, the CI build failures seem to be from the previous commit, not this one.

It does build and pass tests locally so I think there's just something wrong with the CI builds.

@Chris00 Chris00 force-pushed the master branch 2 times, most recently from 0135dd7 to aa28e2b Compare September 27, 2018 11:02
@Chris00
Copy link
Collaborator

Chris00 commented Sep 27, 2018

Travis is fixed. For AppVeyor, there remains a bug in opam.

@Chris00
Copy link
Collaborator

Chris00 commented Sep 27, 2018

May you please rebase?

@brendanlong
Copy link
Contributor Author

Rebased

@brendanlong
Copy link
Contributor Author

Hm just ran into a segfault in code that seems possibly related to this.. I'm going to check into it more. This is really complex to get right :(


/* OCaml severity. Keep in sync with OCaml [Dblib]. */
#define SEVERITY_PROGRAM Val_int(6)
#define SEVERITY_RESOURCE Val_int(7)
#define SEVERITY_FATAL Val_int(9)
#define SEVERITY_CONSISTENCY Val_int(10)

static int release_lock(DBPROCESS *dbproc)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't freetds_release_runtime_system be clearer?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense

int *have_lock = (int*)dbgetuserdata(dbproc);

// If have_lock wasn't setup, then we're in the msg/error callback for dbopen
if (have_lock == NULL) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the lock be allocated & initialized when the dbproc value is created (and released by its finalizer)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's what this pull request does. The problem is that dbopen both allocates a dbproc and attempts to connect to the remote server, so if the connection fails, we'll have the exception handler called before we have a chance to call dbsetuserdata.

server = String_val(vserver);
caml_release_runtime_system();
dbproc = dbopen(login, server);
caml_acquire_runtime_system();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Chris00 This is the case where we need to handle have_runtime_lock being null, since we don't want to hold the lock while we're making the initial connection to the server, but we don't have a (non-null) dbproc until line 302.

This releases OCaml's runtime lock whenever we call C functions that do
IO. It should allow users of this to run ocaml-freetds in parallel, or
when using Async/Lwt.

I had to re-arrange some code to avoid calling any OCaml functions
while the lock was released.

We use wrappers around the release/acquire functions to ensure that we
don't try to acquire or release the lock twice, which could potentially
happen in the msg/error callbacks.
@brendanlong
Copy link
Contributor Author

brendanlong commented Sep 28, 2018

Ok I figured out what I was causing problems in our system. I had the check at the end of the message handler backwards:

  if (had_lock) {
    freetds_release_runtime_system(dbproc);
  }

Should have been:

  if (!had_lock) {
    freetds_release_runtime_system(dbproc);
  }

That's fixed and now it's working well in our systems, so this can be merged if you're happy with it.

I also renamed the functions as requested.

Note that I added another commit to add (name) to the dune-project file, which was necessary to make opam pin work.

@Chris00
Copy link
Collaborator

Chris00 commented Sep 28, 2018

Thanks fort working on this. Unfortunately, I won't have time to do a thorough review until next Tuesday. I'll merge or make additional comments then.

@brendanlong
Copy link
Contributor Author

We're seeing some random inexplicable syntax errors that may be related to this change (syntax error on character not in the query), although it's hard to tell since it occurs at random. Our guess right now is that either FreeTDS is not as thread-safe as it claims to be (supposedly it should be safe to use different connections concurrently, it's just that individual connection handles aren't thread safe), or the way we throw exceptions out of C code in the callbacks is doing bad things to FreeTDS's internals.

I'd like to fix this library to never throw exeptions in C callbacks but it's kind of hard.

I'm also looking into re-implementing TDS from scratch in https://github.com/brendanlong/ocaml-tds

@Chris00
Copy link
Collaborator

Chris00 commented Oct 22, 2018

Did you ask on the freeTDS mailing list?

@brendanlong
Copy link
Contributor Author

We're investigating a different approach, trying to use the Ctlib backend instead since it doesn't have callbacks.

@brendanlong brendanlong closed this Nov 8, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants