Is there a Hash-like data structure that's not referencing its own elements?

Thanks, the “one off” shouldn’t really hurt, you’re right.
But it doesn’t seem to work for me (Crystal 1.8.1 on Ubuntu)…

class X
    def initialize
        GC.before_collect do
        end
    end
end

X.new
GC.collect

… leads to…

Stack overflow (e.g., infinite or very deep recursion)
[0x55c2e81c47c6] *Exception::CallStack::print_backtrace:Nil +118 in .../snap/crystal/common/.cache/crystal/crystal-run-gc.tmp
[0x55c2e81b3ff6] ~procProc(Int32, Pointer(LibC::SiginfoT), Pointer(Void), Nil) +310 in .../snap/crystal/common/.cache/crystal/crystal-run-gc.tmp
[0x7fdd94d39980] ?? +140589661395328 in /lib/x86_64-linux-gnu/libpthread.so.0
[0x55c2e81b13c1] ~procProc(Nil) +17 in .../snap/crystal/common/.cache/crystal/crystal-run-gc.tmp
[0x55c2e81b14c7] ~procProc(Nil) +279 in .../snap/crystal/common/.cache/crystal/crystal-run-gc.tmp (140587164552344 times)
[0x55c2e823dd10] GC_mark_some +1392 in .../snap/crystal/common/.cache/crystal/crystal-run-gc.tmp
[0x55c2e823693d] ?? +94295606651197 in .../snap/crystal/common/.cache/crystal/crystal-run-gc.tmp
[0x55c2e8236719] GC_try_to_collect_inner +329 in .../snap/crystal/common/.cache/crystal/crystal-run-gc.tmp
[0x55c2e82374a8] ?? +94295606654120 in .../snap/crystal/common/.cache/crystal/crystal-run-gc.tmp
[0x55c2e823751a] GC_gcollect +10 in .../snap/crystal/common/.cache/crystal/crystal-run-gc.tmp
[0x55c2e81d9346] *GC::collect:Nil +6 in .../snap/crystal/common/.cache/crystal/crystal-run-gc.tmp
[0x55c2e81a3d2b] __crystal_main +1051 in .../snap/crystal/common/.cache/crystal/crystal-run-gc.tmp
[0x55c2e8235c96] *Crystal::main_user_code<Int32, Pointer(Pointer(UInt8))>:Nil +6 in .../snap/crystal/common/.cache/crystal/crystal-run-gc.tmp
[0x55c2e8235c0a] *Crystal::main<Int32, Pointer(Pointer(UInt8))>:Int32 +58 in .../snap/crystal/common/.cache/crystal/crystal-run-gc.tmp
[0x55c2e81b12d6] main +6 in .../snap/crystal/common/.cache/crystal/crystal-run-gc.tmp
[0x7fdd940e2c87] __libc_start_main +231 in /lib/x86_64-linux-gnu/libc.so.6
[0x55c2e81a382a] _start +42 in .../snap/crystal/common/.cache/crystal/crystal-run-gc.tmp

Hm… right, sorry. GC.before_collect might not be ready for this. It seems it does not accept an arbitrary number of calls, only the one that is currently in place. Procs passed end up calling itself because of limitation on how before_collect is currently written to avoid generating closures.

If we need to hook on before collect we need to offer a better API for it.

it just occurred to me that this is also the proper general (long-living) caching data structure (vs. Hash). :smiley:
Because the GC cleans the cache if the objects are no longer existing.
At least for class instance related data…

We should just remove any enumeration-/indexing-like methods from this class (can lead to non-deterministic behavior), to have it just serve as a mapper.

I just recently realised that the cleaning approach above with a Fiber per instance is a bad thing. If your applications runs for a while you end up with 32768 fibers and then chokes…

Is 32767 the max number of fibers a program can have? Is the scheduler limited to an Int16 range of fibers?

I’m not sure, where it really comes from; the console tells something about out-of-memory, but that’s not true.
For this scenario I was actually lucky that the error showed up as early as ~32k fibers; otherwise I would have realized much later that the number of fibers in my program is/was unbounded.

count = 0
40000.times do |i|
    p i
    spawn do
        while(true)
        end
    end
end

# [...]
# 32734
# 32735
# 32736
# 32737
# 32738
# 32739
# Unhandled exception: Cannot allocate new fiber stack: Cannot allocate memory (RuntimeError)
# GC Warning: Out of memory - trying to allocate requested amount (512 bytes)...
# GC Warning: Out of memory - trying to allocate requested amount (336 bytes)...
# GC Warning: Out of memory - trying to allocate requested amount (336 bytes)...
# GC Warning: Header allocation failed: dropping block
# GC Warning: Failed to expand heap by 2031616 bytes
# GC Warning: Failed to expand heap by 262144 bytes
# GC Warning: Out of Memory! Heap size: 5 MiB. Returning NULL!
# Invalid memory access (signal 11) at address 0xc0
# [0x559772385726] *Exception::CallStack::print_backtrace:Nil +118 in /home/wolfgang/snap/crystal/common/.cache/crystal/crystal-run-fibers.tmp
# [0x5597723741f6] ~procProc(Int32, Pointer(LibC::SiginfoT), Pointer(Void), Nil) +310 in /home/wolfgang/snap/crystal/common/.cache/crystal/crystal-run-fibers.tmp
# [0x7f1fe2f42980] ?? +139774928365952 in /lib/x86_64-linux-gnu/libpthread.so.0
# [0x559772410f2a] *Pointer(Crystal::DWARF::LineNumbers::Row) +186 in /home/wolfgang/snap/crystal/common/.cache/crystal/crystal-run-fibers.tmp
# [0x55977240ffba] *Array(Crystal::DWARF::LineNumbers::Row) +218 in /home/wolfgang/snap/crystal/common/.cache/crystal/crystal-run-fibers.tmp
# [0x55977240fed1] *Array(Crystal::DWARF::LineNumbers::Row) +145 in /home/wolfgang/snap/crystal/common/.cache/crystal/crystal-run-fibers.tmp
# [0x55977240bea4] *Crystal::DWARF::LineNumbers#register_to_matrix<Crystal::DWARF::LineNumbers::Sequence, Crystal::DWARF::LineNumbers::Register>:Nil +1876 in /home/wolfgang/snap/crystal/common/.cache/crystal/crystal-run-fibers.tmp
# [0x559772409d63] *Crystal::DWARF::LineNumbers#read_statement_program<Crystal::DWARF::LineNumbers::Sequence>:Nil +3187 in /home/wolfgang/snap/crystal/common/.cache/crystal/crystal-run-fibers.tmp
# [0x559772407534] *Crystal::DWARF::LineNumbers#decode_sequences<(UInt32 | UInt64)>:Nil +6644 in /home/wolfgang/snap/crystal/common/.cache/crystal/crystal-run-fibers.tmp
# [0x559772405b20] *Crystal::DWARF::LineNumbers#initialize<IO::FileDescriptor+, (UInt32 | UInt64), UInt64, (Crystal::DWARF::Strings | Nil), (Crystal::DWARF::Strings | Nil)>:Nil +592 in /home/wolfgang/snap/crystal/common/.cache/crystal/crystal-run-fibers.tmp
# [0x5597724057bc] *Crystal::DWARF::LineNumbers::new<IO::FileDescriptor+, (UInt32 | UInt64), UInt64, (Crystal::DWARF::Strings | Nil), (Crystal::DWARF::Strings | Nil)>:Crystal::DWARF::LineNumbers +1020 in /home/wolfgang/snap/crystal/common/.cache/crystal/crystal-run-fibers.tmp
# [0x55977237ef02] *Exception::CallStack::read_dwarf_sections<String, UInt64>:(Array(Tuple(UInt64, UInt64, String)) | Nil) +14290 in /home/wolfgang/snap/crystal/common/.cache/crystal/crystal-run-fibers.tmp
# [0x55977237b725] *Exception::CallStack::load_debug_info_impl:Nil +277 in /home/wolfgang/snap/crystal/common/.cache/crystal/crystal-run-fibers.tmp
# [0x55977237b51c] *Exception::CallStack::load_debug_info:Nil +156 in /home/wolfgang/snap/crystal/common/.cache/crystal/crystal-run-fibers.tmp
# [0x55977237b20b] *Exception::CallStack::decode_line_number<UInt64>:Tuple(String, Int32, Int32) +27 in /home/wolfgang/snap/crystal/common/.cache/crystal/crystal-run-fibers.tmp
# [0x55977237aa36] *Exception::CallStack#decode_backtrace:Array(String) +310 in /home/wolfgang/snap/crystal/common/.cache/crystal/crystal-run-fibers.tmp
# [0x55977237a8e2] *Exception::CallStack#printable_backtrace:Array(String) +50 in /home/wolfgang/snap/crystal/common/.cache/crystal/crystal-run-fibers.tmp
# [0x559772419b3b] *Exception+ +75 in /home/wolfgang/snap/crystal/common/.cache/crystal/crystal-run-fibers.tmp
# [0x559772419bd9] *Exception+ +105 in /home/wolfgang/snap/crystal/common/.cache/crystal/crystal-run-fibers.tmp
# [0x55977242882a] *Crystal::exit<Int32, (Exception+ | Nil)>:Int32 +90 in /home/wolfgang/snap/crystal/common/.cache/crystal/crystal-run-fibers.tmp
# [0x559772428769] *Crystal::main<Int32, Pointer(Pointer(UInt8))>:Int32 +105 in /home/wolfgang/snap/crystal/common/.cache/crystal/crystal-run-fibers.tmp
# [0x559772371d36] main +6 in /home/wolfgang/snap/crystal/common/.cache/crystal/crystal-run-fibers.tmp
# [0x7f1fe22ebc87] __libc_start_main +231 in /lib/x86_64-linux-gnu/libc.so.6
# [0x5597723642ba] _start +42 in /home/wolfgang/snap/crystal/common/.cache/crystal/crystal-run-fibers.tmp
# [0x0] ???