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

Correct reference counting and handling of NULL pointers in new OCaml bindings. #2

Merged

Conversation

martin-neuhaeusser
Copy link

This PR contains two commits.

The first corrects a bug in the reference counting mechanism used in the C layer of the new OCaml bindings. Instead of storing the reference counters directly in a custom block that holds the context (and that might become copied), the patch allocates memory to store the reference counters and only stores a pointer to this memory block in the context's custom blocks.

The second commit refactors parts of the update_api script (only the OCaml parts are affected) to translate properly between OCaml options and NULL pointers. Moreover, it avoids some duplicate allocations for OCaml values and adds some missing allocations.

The Z3 context and its reference counters are stored in a structure which is allocated
by the C layer outside the OCaml heap, whenever a Z3 context is created. The structure
and its Z3 context are disposed, once the last reference counter reaches zero. Reference
counters are decremented by C-level finalizers.

The OCaml representations for a Z3 context wrap only a pointer to the corresponding structure.
This patch refactors the update_api script and the z3.ml implementation
to properly translate between OCaml options and NULL pointers. Some
unifications and simplifications (avoidance of unnecessary value allocation)
in the script that creates the native bindings.
@wintersteiger
Copy link
Owner

Looks good to me, thanks for the fixes!

Was it really necessary to introduce another pointer indirection for the refcount custom blocks? I remember putting extra effort into custom blocks that get copied, so from my point of view this was (or should have been) working.

@wintersteiger wintersteiger self-assigned this Apr 5, 2016
@wintersteiger wintersteiger merged commit 991eae8 into wintersteiger:new-ml-api Apr 5, 2016
@martin-neuhaeusser
Copy link
Author

Hi Christoph,

I suspect that the critical part was the context_of part of the C-layer (the macro n_context_of_ ## X):

Here, the custom block that holds the Z3_context_plus with the Z3_context and its corresponding reference counters gets copied into a new custom block. Firstly, the reference counters may diverge from that point onwards, depending on how the new copy is used. Secondly, the new custom block gets the Z3_context_plus_finalize finalizer attached (see the line above).

Hence, the finalizer Z3_context_plus_finalize will be called twice (once for the original Z3_context_plus data, and once for its copy) for a single Z3_context with non-consistent ref counters.

The patch allocates exactly one Z3_context_plus_data structure with a reference counter for each context that is created. The custom blocks that represent the OCaml values than merely store a pointer to that structure. Therefore, the reference counters remain in sync even if multiple custom blocks are around holding a pointer to that unique-per-context structure.
Each such block increments the ref counter by one. Upon finalization, the ref counter is decremented; once it is 0, the Z3_context is disposed and the memory holding the Z3_context_plus_data structure is freed.

@martin-neuhaeusser martin-neuhaeusser deleted the ml_api_patch2 branch April 6, 2016 14:44
wintersteiger pushed a commit that referenced this pull request May 10, 2017
(this leads to a not-implemented-yet in final_check_eh()
due to missing code surrounding free variable production)
wintersteiger pushed a commit that referenced this pull request Jul 6, 2018
fix build of obj_ref_hashtable
wintersteiger pushed a commit that referenced this pull request Oct 4, 2023
…junctions (Z3Prover#6779)

After introducing the rewriter.sort_disjunctions option (Z3Prover#6774), I
noticed a segfault in a Z3 run that was working fine for me before the
PR.

I traced the difference to a slight discrepancy between the first patch
I submitted and the one we ended up merging: my first version would skip
sorting the disjuncts in mk_nflat_core, but still return BR_DONE, while
the patch in master returns BR_FAILED instead.

This patch fixes that problem, and it makes slightly more sense to me to
return a BR_DONE since, if `s` is true, some disjunct (e.g. a `false`
or a repeat) might have been simplified away. However I don't fully
understand this code.

... and I can't say I understand why the segfault happens. Perhaps that
is a separate issue?

This is the file to reproduce:
 https://gist.github.com/mtzguido/b7360c74d3d2e42d89f1bd9149ad26f6

Here's a stack trace of the failure, mk_nflat_or_core is not involved.
```
 (gdb) where
 #0  0x0000555555b98497 in smt::context::get_lit_assignment(unsigned int) const ()
 #1  0x0000555555b984cb in smt::context::get_assignment(sat::literal) const ()
 #2  0x0000555555b98504 in smt::context::get_assignment(unsigned int) const ()
 #3  0x0000555555ca83b8 in smt::context::get_assignment_core(expr*) const ()
 #4  0x0000555555c9af5a in smt::context::get_assignment(expr*) const ()
 #5  0x0000555555d7bd1d in (anonymous namespace)::has_child_assigned_to(smt::context&, app*, lbool, expr*&, unsigned int) ()
 #6  0x0000555555d7c413 in (anonymous namespace)::rel_case_split_queue::next_case_split_core(ptr_vector<expr>&, unsigned int&, unsigned int&, lbool&) ()
 #7  0x0000555555d7c589 in (anonymous namespace)::rel_case_split_queue::next_case_split(unsigned int&, lbool&) ()
 #8  0x0000555555c9c1b7 in smt::context::decide() ()
 #9  0x0000555555ca39fd in smt::context::bounded_search() ()
 #10 0x0000555555ca30c2 in smt::context::search() ()
 #11 0x0000555555ca273d in smt::context::check(unsigned int, expr* const*, bool) ()
 #12 0x0000555555cb166a in smt::kernel::check(unsigned int, expr* const*) ()
 #13 0x0000555555cb9695 in (anonymous namespace)::smt_solver::check_sat_core2(unsigned int, expr* const*) ()
 #14 0x00005555560dc0c6 in solver_na2as::check_sat_core(unsigned int, expr* const*) ()
 #15 0x00005555560d73f3 in combined_solver::check_sat_core(unsigned int, expr* const*) ()
 #16 0x00005555560d34e3 in solver::check_sat(unsigned int, expr* const*) ()
 #17 0x0000555556097b26 in cmd_context::check_sat(unsigned int, expr* const*) ()
 #18 0x0000555556082ff0 in smt2::parser::parse_check_sat() ()
 #19 0x0000555556084dc0 in smt2::parser::parse_cmd() ()
 #20 0x00005555560861b6 in smt2::parser::operator()() ()
 #21 0x00005555560757e6 in parse_smt2_commands(cmd_context&, std::basic_istream<char, std::char_traits<char> >&, bool, params_ref const&, char const*) ()
 #22 0x00005555555e8f68 in read_smtlib2_commands(char const*) ()
 Z3Prover#23 0x00005555555ee6f6 in main ()
 (gdb)
```
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.

2 participants