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

What happens to data_type after unhandled exception is being run? #3233

Closed
dragostis opened this issue Nov 4, 2016 · 7 comments
Closed

What happens to data_type after unhandled exception is being run? #3233

dragostis opened this issue Nov 4, 2016 · 7 comments

Comments

@dragostis
Copy link

Does free get called on mrb_close for all GC'd objects?

I'm currently having an issue in mrusty where running into an unhandled exception leaks all the Rust objects that were lended to mruby because free doesn't seem to get called. There's a good chance that the bug might be in my code, but I haven't been able to figure it out so what I'm trying to ask is what is the best way to handle this use scenario?

@matz
Copy link
Member

matz commented Nov 4, 2016

All malloced regions should be deallocated by free. If not, we consider it as a bug.
Please submit an issue, preferably with bug reproducing info.

@dragostis
Copy link
Author

dragostis commented Nov 7, 2016

@matz I was able to track down the problem: it arises when mruby calls Rust code that calls back mruby which causes an unhandled exception.

mruby -> rust -> mruby (unhandled exception)

This breaks the first mruby context and interrupts the Rust code from finishing which should normally decrement a reference counter. Because the reference counter doesn't get decremented (it should get, even if mruby has an exception), this breaks the reference counter which will never call mrb_close which leads to the memory leak.

@dragostis
Copy link
Author

dragostis commented Nov 11, 2016

@matz Here's a minimal example:

#include <stdlib.h>

#include <mruby.h>
#include <mruby/compile.h>

mrbc_context* ctx;

void c1(mrb_state* mrb) {
  printf("entering c1\n");

  mrb_load_nstring_cxt(mrb, "c2", 2, ctx);

  printf("exiting c1\n");
}

void c2(mrb_state* mrb, mrb_value self) {
  printf("entering c2\n");

  void* p = malloc(10);

  mrb_load_nstring_cxt(mrb, "raise 'hi'", 2, ctx);

  free(p); // we're not freeing here

  printf("exiting c2\n");
}

int main(void) {
  mrb_state* mrb = mrb_open();
  ctx = mrbc_context_new(mrb);
  ctx->capture_errors = TRUE;

  mrb_define_singleton_method(mrb, mrb->top_self, "c2", (mrb_func_t) c2, MRB_ARGS_NONE());

  c1(mrb);

  mrbc_context_free(mrb, ctx);
  mrb_close(mrb);

  return 0;
}

The output is:

entering c1
entering c2
exiting c1

The issue is that c2 gets interrupted. Is there a way to catch the exception?

@dragostis
Copy link
Author

@matz Any update on the issue? I still haven't figured out how to prevent the memory leak.

@Asmod4n
Copy link
Contributor

Asmod4n commented Nov 22, 2016

You have to put those c methods in a try/catch block and manually free the just allocated memory. Take a look at https://github.com/mruby/mruby/tree/master/mrbgems/mruby-error

@dragostis
Copy link
Author

dragostis commented Nov 23, 2016

@Asmod4n I'm trying to run mrb_load_nstring_cxt inside of a function that is run from mrb_protect. If I raise manually from C, state changes and it captures exceptions, but it fails to captures exceptions from mrb_load_nstring_cxt with state always being false.

mrb_value foo(mrb_state* mrb, mrb_value data) {
    mrb_load_nstring_ctx(..., "raise 'exc'");
}

...

mrb_protect(mrb, foo, ..., state); // state is ALWAYS false

However, it does protect.

@dragostis
Copy link
Author

I solved the issue by raising the caught exception within foo. Not the most elegant solution, but it works.

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

No branches or pull requests

3 participants