My code doesn’t work because WeakRef can not ref the module HookProcessTerminate. Is there any good way to solve this problem?
code
module Internal
module HookInitialize
abstract def after_initialize
macro included
macro finished
\{% for method in @type.methods %}
\{% if method.name == "initialize" %}
def initialize(\{{ method.args.join(",").id }})
previous_def
after_initialize
end
\{% end %}
\{% end %}
end
end
end
module HookProcessTerminate
include HookInitialize
@@hooks : Array(WeakRef(HookProcessTerminate)) = [] of WeakRef(HookProcessTerminate)
abstract def on_process_terminate
def after_initialize
@@hooks << WeakRef.new(self)
end
def self.hook
Process.on_terminate do |reason|
case reason
when .interrupted?
puts "Signal : interrupted"
when .terminal_disconnected?
puts "Signal : terminal disconnected"
when .session_ended?
puts "Signal : session ended"
end
@@hooks.each &.value.try &.on_process_terminate
Process.exit
end
end
end
end
error
In /usr/share/crystal/src/weak_ref.cr:35:5
35 | @target.as(T?)
^
Error: can't cast Pointer(Void) to (Internal::HookProcessTerminate | Nil)
not use WeakRef, use custom WeakRef something like this?
struct MyWeakRef
@value : Void*
@typ : Class
end
Another question is, if the pointer is updated when GC is performing memory folding, will WeakRef also be invalid in this case (but the referenced object is actually alive)?
Could you switch to using structs to store the callbacks and then just don’t use WeakRef?
EDIT: Or why do you want to use WeakRef at all? You’re storing exit handlers which will be referenced the entire lifetime of the program, just to exit out of the program entirely which would free all memory related to said program.
Using callback still keep a strong ref to target, maybe i need a unregister method to remove it from @@hooks when target is finished.
I feel that using WeakRef is more convenient but unfortunatly not supported in crystal
But that doesn’t answer my question. If the entire program is going to exit after these handlers run, the OS will reclaim the memory used by the program when that happens. I.e. I’m pretty sure the GC will never even have a chance to reclaim it itself even if you were using WeakRef.
There are many tasks, each task registers a process terminate handle after initialization to save the task progress when the program exits abnormally.
If the task has been completed before, there is no need to execute this handle (I assume it may be GCed if WeakRef is used)
My English is not very good, I may not be able to describe my problem well and answer your questions
In that case, assuming the types that are stored in the array are actually classes (if they’re structs idt it would matter anyway since they wouldn’t be allocated by the GC), I think you could get away with just clearing the array.
For example:
class Foo
def finalize
puts "Going away"
end
end
arr = Array(Foo).new 100
arr << Foo.new
arr << Foo.new
arr << Foo.new
arr.clear
GC.collect
Because the array is the only thing holding onto the references of Foo, clearing it and forcing a collection will result in Going away being printed three times as each of the three objects are collected.
Yes, WeakRef should not be able to achieve what I want, even if I use a custom WeakRef (because of the uncertainty of GC) so I may choose to use storage strong reference and manually remove it from the array after the task is completed.
Thank you for your patient answer ;-)
Just add a require "weak_ref" at the beginning of code and include it into a new class, then crystal run it.
code
require "weak_ref"
module Internal
module HookInitialize
abstract def after_initialize
macro included
macro finished
\{% for method in @type.methods %}
\{% if method.name == "initialize" %}
def initialize(\{{ method.args.join(",").id }})
previous_def
after_initialize
end
\{% end %}
\{% end %}
end
end
end
module HookProcessTerminate
include HookInitialize
@@hooks : Array(WeakRef(HookProcessTerminate)) = [] of WeakRef(HookProcessTerminate)
abstract def on_process_terminate
def after_initialize
@@hooks << WeakRef.new(self)
end
def self.hook
Process.on_terminate do |reason|
case reason
when .interrupted?
puts "Signal : interrupted"
when .terminal_disconnected?
puts "Signal : terminal disconnected"
when .session_ended?
puts "Signal : session ended"
end
@@hooks.each &.value.try &.on_process_terminate
Process.exit
end
end
end
end
class A
include Internal::HookProcessTerminate
def on_process_terminate
end
def self.hooks
@@hooks
end
end
p! typeof(A.hooks) # typeof(A.hooks) # => Array(WeakRef(Internal::HookProcessTerminate))
Without any driver code it’s a no-op and compiles to an empty program.
the doc said:
WARNING: The referenced object cannot be a module.
So, I thought it should give a compile-time when run it.