objeck / objeck-lang

Objeck is a modern object-oriented programming language with functional features tailored for machine learning. It emphasizes expression, simplicity, portability, and scalability. The programming environment consists of a compiler, virtual machine, REPL shell, and command line debugger with IDE plugins.
https://objeck.org
Other
157 stars 11 forks source link

The `obr` process crashes when loop insert into Collection.Hash or Collection.Hash/Map in native function #503

Closed forchid closed 1 month ago

forchid commented 1 month ago

Version info

Version: 2024.9.0 (x86_64 Windows)

Test case

The test source

use Collection;
use System.Time;

class Mc {

    function: Main(args: String[]) ~ Nil {
        @I := 100;
        @J := 100;
        @K := 10;
        NormlRun(@I, @J, @K);
        CrashRun(@I, @J, @K);
    }

    function: NormlRun(@I: Int, @J: Int, @K: Int) ~ Nil {
        "NormlRun(): Started."->PrintLine();
        timer := Timer->New(true);

        for (k := 0; k < @K; k++) {
            tm := Timer->New(true);
            #m := Hash->New()<IntRef, String>; # Also crash when using Hash!
            m := Map->New()<IntRef, String>;
            for (i := 0; i < @I; i++) {
                for (j := 0; j < @J; j++) {
                    #"test{$i}{$j}"->PrintLine();
                    m->Insert(i * k, "test{$i}{$j}");
                }
                #"i {$i}, k {$k}"->PrintLine();
            }
            t := tm->GetElapsedTime();
            "k#{$k}: {$t}s"->PrintLine();
        }

        t := timer->GetElapsedTime();
        timer->End();
        "NormlRun(): ok({$t}s)."->PrintLine();
    }

    function: native: CrashRun(@I: Int, @J: Int, @K: Int) ~ Nil {
        "CrashRun(): Started."->PrintLine();
        timer := Timer->New(true);

        for (k := 0; k < @K; k++) {
            tm := Timer->New(true);
            #m := Hash->New()<IntRef, String>; # Crash when using Hash!
            m := Map->New()<IntRef, String>;   # Crash when using Map too!
            for (i := 0; i < @I; i++) {
                for (j := 0; j < @J; j++) {
                    #"test{$i}{$j}"->PrintLine();
                    m->Insert(i * k, "test{$i}{$j}");
                }
                #"i {$i}, k {$k}"->PrintLine();
            }
            t := tm->GetElapsedTime();
            "k#{$k}: {$t}s"->PrintLine();
        }

        t := timer->GetElapsedTime();
        timer->End();
        "CrashRun()(): ok({$t}s)"->PrintLine();
    }

}

The test result

mc>obc mc.obs
Compiled 1 class.
Linked 23 library classes.
Wrote target file: 'mc.obe'.
---

mc>obr mc.obe
NormlRun(): Started.
k#0: 0.240000s
k#1: 0.365000s
k#2: 0.379000s
k#3: 0.361000s
k#4: 0.366000s
k#5: 0.362000s
k#6: 0.350000s
k#7: 0.358000s
k#8: 0.364000s
k#9: 0.359000s
NormlRun(): ok(3.539000s).
CrashRun(): Started.
..................... Crash here!
objeck commented 1 month ago

Thank you! I can reproduce the crash and will look at it today.

objeck commented 1 month ago

An interesting finding in the object allocation stress test code.

There were two issues with the code due to the number of objects that were being allocated.

The first issue was allocated objects not being found on the JIT processor stack due to nested function calls. Those roots were not accounted for when that happened, and their in-use memory was deallocated.

The second issue was due to the size of the backing array for the Hash Objeck class. When hashtables spilled, and linked lists became extremely long, the memory manager would blow the stack when marking memory. An interim fix is in place; a longer-term fix is to remove recursion from the memory manager.

objeck commented 1 month ago

Fixed in 2024.10.0