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

Bus error / Segfault when calling finalizer of an actor #274

Closed
kikofernandez opened this issue Nov 9, 2015 · 11 comments
Closed

Bus error / Segfault when calling finalizer of an actor #274

kikofernandez opened this issue Nov 9, 2015 · 11 comments
Assignees
Milestone

Comments

@kikofernandez
Copy link
Contributor

It seems that there's a bug in upstream / master.
I am using plain active / passive objects in the following test and get segfaults and bus errors.
It would be good if someone else could check that the test doesn't do anything that should fail (cyclic dependency, e.g.).

(This has been taken from the extract.enc in the parallel combinators and I have removed everything that has to do with them)

def inc(number: int): int
  number + 1

def dec(number: int): int
  number - 1

def switchTag(p: PassiveObject): PassiveObject
  if p.tag == "pass" then p.update("fail", p.life)
  else p.update("pass", p.life)

def switchLife(p: PassiveObject): PassiveObject
  if p.life == 42 then p.update(p.tag, 1)
  else p.update(p.tag, 42)

def from_string_to_int(sentence: string): int
  if (sentence == "Liftv String") then 7
  else 1

passive class PassiveObject
  tag: string
  life: int

  def init(tag: string, life: int): void {
    this.tag = tag;
    this.life = life;
  }

  def update(tag: string, life: int): PassiveObject {
    this.tag = tag;
    this.life = life;
    this
  }

class Test
  def number(number: int): int number
  def stringf(sentence: string): string sentence
  def passiveObject(pObject: PassiveObject): PassiveObject pObject

  --
  -- test user defined functions
  --
  def test_number(): void
    let test = new Test
        expected = 34
        value = get test.number(expected)
    in
      assertTrue(value == expected,
                 "ERROR in 'test_number', found {} but expecting {}", value, expected)

  def test_stringf(): void
    let test = new Test
        expected = "string"
        value = get test.stringf(expected)
    in assertTrue(value == expected,
                  "ERROR in 'test_stringf', found {} but expecting {}", value, expected)

  def test_int_and_dec(): void
    let value = 42
        expected = 43
    in
      assertTrue(inc(inc(dec(value))) == expected, "ERROR in 'test_int_and_dec'")

  def test_from_string_to_int(): void
    let seven = from_string_to_int("Liftv String")
        one = from_string_to_int("Something else")
        expected1 = 7
        expected2 = 1
    in {
        assertTrue(seven == expected1, "ERROR in 'test_from_string_to_int'");
        assertTrue(one == expected2, "ERROR in 'test_from_string_to_int'");
    }

  def test_switch_test(): void
    let switchTest = switchLife(switchTag(new PassiveObject("pass", 42)))
        expectedTag = "fail"
        expectedLife = 1
    in {
       assertTrue(switchTest.tag == expectedTag,
                  "ERROR in 'test_switch_life', wrong tag");
       assertTrue(switchTest.life == expectedLife,
                  "ERROR in 'test_switch_life', wrong life");
    }

  def test_passive_object(): void
    let test = new Test
        pObject = new PassiveObject("pass", 42)
        pObjectFulfilled = get test.passiveObject(pObject)
    in {
       assertTrue(pObjectFulfilled.tag == "pass");
       assertTrue(pObjectFulfilled.life == 42);
    }

  --
  -- test parallel combinator extract
  --
  def test_liftv_primitive_int(): void{
    let expectedInt = 34
    in ()
  }

  def test_liftv_primitive_string(): void {
    let expectedString = "Liftv String"
    in ()
  }

  def test_liftv_passive(): void {
    let expectedLife = 42
        expectedTag = "pass"
    in ()
  }

  def test_liftv_par_primitive(): void {
    let expectedInt = 34
        expectedString = "Liftv String"
    in { ()
    }
  }

  def test_liftv_par_passive(): void {
    let expectedTag = "pass"
        expectedLife = 42
    in ()
  }

class Main
  def main(): void {
    let test = new Test in {
      --
      -- Test user defined functions for simple values
      --

      get test.test_number();
      get test.test_stringf();
      get test.test_int_and_dec();
      get test.test_from_string_to_int();
      get test.test_switch_test();
      get test.test_passive_object();

      --
      -- Test with parallel combinators
      --

      test.test_liftv_primitive_int();
      test.test_liftv_primitive_string();
      test.test_liftv_passive();
      test.test_liftv_par_primitive();
      -- test.test_liftv_par_passive();
      }
  }

This code crashes 145 / 10.000 times:

  • 57 are Bus error
  • 88 are Segfault

If we use the single threaded approach, it does not crash.

@kikofernandez kikofernandez changed the title Bus error when calling finalizer of an actor Bus error / Segfault when calling finalizer of an actor Nov 9, 2015
@albertnetymk
Copy link
Contributor

I can confirm this bug on my Linux box. Only encounterred segfault, though. Spent some time on this, but no clue why it occurs.

@kikofernandez
Copy link
Contributor Author

I'll send an email to Sylvan today. I know where it happens and why. There's just a detail that I don't understand and therefore I cannot fix (for now)

@kikofernandez
Copy link
Contributor Author

The proposed solution is to move to the new ponyrt and contact him if it's not fixed.

@albertnetymk
Copy link
Contributor

Spent some time on debugging this problem, my understanding is that it's a bug from upstream. The following is the comparatively shorter program to reproduce the bug.

class Test {
  def f1() : void {
    let c = new Test in ()
  }

  def f2() : void {
    let arr = new [int](1000*1000) in {
      ();
    }
  }

  def f3() : void {
    let i = 0 in {
      while (i<=1000*1000*10) {
        i = i+1;
      };
    }
  }

}

class Main
  def count_down(n:int) : void {
    if n > 0 then {
      this!count_down(n-1);
      -- exploit the memset in actor construction
      new Test;
      ();
    } else {
      ();
    }
  }

  def cont(p:Test) : void {
    let i = 0 in {
      -- wait until p sends block msg to CD
      while (i<=1000*1000*10) {
        i = i+1;
      };
      -- force p to run gc so that c is released by p
      p.f2();
      -- wait until c is collected
      p.f3();
      -- wait until p is collected
      this!count_down(50);
    }
  }

  def main(): void {
    let
      arr = new [int](1000*1000)
      p = new Test
    in {
      p.f1();
      this!cont(p);
    }
  }

I don't fully understand cycle.c, so my hypothesis is possibly wrong; the bug (calling actor finaliser twice) occurs because the delta set used by cycle detector (CD) is behind one message processing. If we swap those two line in cycle.c, it seems to be working:

view->delta = map;

apply_delta(d, view);

It would be good to get some comments from someone who's more knowledgeable on this.

@kikofernandez
Copy link
Contributor Author

@albertnetymk I can try to swap those two lines and test 100.000 times but I doubt that there is anyone with more knowledge on the subject than you, me and Sylvan.

BTW, when you say upstream, does that mean the PonyRT or our master branch?

Could you check if the latest version of the PonyRT has changed those lines to something else? We need to update the EncoreRT and it would be good to know if that could solve everything or if the PonyRT has changed that part and we may not rely on that change.

@albertnetymk
Copy link
Contributor

I meant PonyRT. The relevant code is the same in master of ponyc.

@jupvfranco
Copy link

I just ran a benchmark with an old version of pony and I also got a seg fault (probably because of a different reason).
However I could fix it by pulling the most recent version of pony.
Maybe this bug will be fixed by getting a new version of the PonyRT for Encore, as well.
WRT memory management, I believe Sylvan made some changes in the past months.

@kikofernandez
Copy link
Contributor Author

@jupvfranco it might... the problem is that the new PonyRT modified the header files, scheduler and some other parts that we were relying on. This is short list of things that I have detected to cause troubles in Encore (issues with new PonyRT).

@kikofernandez
Copy link
Contributor Author

@albertnetymk could you test this on Linux to confirm that the issue has been solved?

@albertnetymk
Copy link
Contributor

The larger example doesn't compile; the smaller one seem to work on my box using development. I think it's fixed by ponylang/ponyc@ae156f8

@kikofernandez
Copy link
Contributor Author

tested on my Mac in development and master. This bug has been fixed!

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