Created
January 1, 2009 16:31
-
-
Save rkh/42307 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
konstantin:~/Workspace/misc/rubinius-copy (git: master) $ uname --all | |
Linux localtoast 2.6.27-10-generic #1 SMP Fri Nov 21 12:00:22 UTC 2008 i686 GNU/Linux | |
konstantin:~/Workspace/misc/rubinius-copy (git: master) $ gdb --args bin/rbx -e "puts ObjectSpace.each_object { }" | |
GNU gdb 6.8-debian | |
Copyright (C) 2008 Free Software Foundation, Inc. | |
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> | |
This is free software: you are free to change and redistribute it. | |
There is NO WARRANTY, to the extent permitted by law. Type "show copying" | |
and "show warranty" for details. | |
This GDB was configured as "i486-linux-gnu"... | |
(gdb) run | |
Starting program: /home/konstantin/Workspace/misc/rubinius-copy/bin/rbx -e puts\ ObjectSpace.each_object\ \{\ \} | |
[Thread debugging using libthread_db enabled] | |
[New Thread 0xb7c3f6c0 (LWP 31539)] | |
[New Thread 0xb793bb90 (LWP 31542)] | |
Program received signal SIGSEGV, Segmentation fault. | |
[Switching to Thread 0xb7c3f6c0 (LWP 31539)] | |
rubinius::LookupTable::store (this=0x0, state=0x99de428, key=0x34e, val=0x1ba9) at vm/builtin/lookuptable.cpp:102 | |
102 num_entries = entries_->to_native(); | |
(gdb) bt | |
#0 rubinius::LookupTable::store (this=0x0, state=0x99de428, key=0x34e, val=0x1ba9) at vm/builtin/lookuptable.cpp:102 | |
#1 0x080d551d in rubinius::Object::set_ivar (this=0xa8e0f2c, state=0x99de428, sym=0x34e, val=0x1ba9) at vm/builtin/object.cpp:396 | |
#2 0x080d5aa3 in rubinius::Object::id (this=0xa8e0f2c, state=0x99de428) at vm/builtin/object.cpp:240 | |
#3 0x080ad665 in rubinius::Primitives::object_id (state=0x99de428, task=0xa8845ec, msg=@0xa2a9940) at vm/gen/primitives_glue.gen.cpp:4865 | |
#4 0x080d6679 in rubinius::performer::basic_performer (state=0x99de428, task=0xa8845ec, msg=@0xa2a9940) at vm/builtin/sendsite.cpp:82 | |
#5 0x081118b9 in rubinius::VMMethod::interpreter (vmm=0x9ab6170, task=0xa8845ec, ctx=0xb79dd3e4) at vm/gen/instructions.cpp:2532 | |
#6 0x080db324 in rubinius::Task::execute (this=0xa8845ec) at vm/builtin/task.cpp:575 | |
#7 0x080b8f85 in rubinius::VM::run_and_monitor (this=0x99de428) at vm/vm.cpp:371 | |
#8 0x080a6e5b in rubinius::CompiledFile::execute (this=0xa4111e8, state=0x99de428) at vm/compiled_file.cpp:58 | |
#9 0x080c122c in rubinius::Environment::run_file (this=0xbff591cc, file= | |
{static npos = 4294967295, _M_dataplus = {<std::allocator<char>> = {<__gnu_cxx::new_allocator<char>> = {<No data fields>}, <No data fields>}, _M_p = 0xbff591bc "\214\232�t�N�\t�N�\t�N�\t(�235\t"}}) at vm/environment.cpp:91 | |
#10 0x08114da8 in main (argc=180292172, argv=0x5c0a) at vm/drivers/cli.cpp:62 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
the important part is line 167 - 169 in objectspace.rb |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# depends on: module.rb | |
module ObjectSpace | |
# HACK: Check out ObjectSpace.known_to | |
# Set this to false to have a not correctly working, | |
# but not segfaulting version of each_object. | |
# | |
# Imagine: | |
# a_string = "foo" | |
# an_array = ["bar"] | |
# ObjectSpace.each_object(String) { ... } | |
# | |
# If FIND_ALL_OBJECTS_BUT_SEGFAULT is false, each_object will | |
# only find "foo". If it is set to true, it will find "bar", too | |
# but segfault at some point. (You can uncomment the part where | |
# each_object(nil) is traveling all constants to see that this | |
# realy works). | |
FIND_ALL_OBJECTS_BUT_SEGFAULT = true | |
# This does loop through all instances of what. | |
# FIXME: In most cases this method will be horribly slow. | |
def self.each_object(what = nil, &block) | |
raise TypeError, "class or module required" if what and not what.is_a? Module | |
# This loops through all objects by looping through all constants, class vars, ivars | |
# and local vars (the latter by looping through all contexts). | |
# We will only find "living" objects that way, so this is not exactly what MRI does. | |
if what == nil | |
skip_list = [] | |
recursive_loop(Object, block, skip_list) { |an_object| known_to(an_object)} | |
each_object(MethodContext) do |context| | |
unless context.current_scope == ObjectSpace | |
local_vars = [] | |
if list = context.method.local_names | |
local_vars += list.collect { |lvar| context.get_eval_local lvar } | |
end | |
local_vars += list.values if list = context.dynamic_locals | |
local_vars.each do |lvar| | |
recursive_loop(lvar, block, skip_list) { |an_object| known_to(an_object) } | |
end | |
end | |
end | |
return skip_list.size | |
end | |
# Finds all classes by recursivly looping through subclasses. | |
if what == Class | |
return recursive_loop(Object, block) { |a_class| a_class.__subclasses__ } | |
end | |
# Finds all modules by looping through the constants of all classes (hence | |
# __subclasses__) and checking whether those are modules. | |
if what == Module | |
return recursive_loop(Object, block) do |a_module| | |
# Get all modules that are helt in a_module.constants plus | |
# subclasses of a_module if a_module is a class. | |
list = get_constants(a_module).find_all { |const| const.is_a? Module } | |
list += a_module.__subclasses__ if a_module.is_a? Class | |
list | |
end | |
end | |
# This is what MRI does: | |
return 0 if [Fixnum, Symbol, TrueClass, FalseClass, NilClass].include? what | |
# All threads are kept in Thread.list. | |
if what == Thread | |
Thread.list.each(&block) | |
return Thread.list.size | |
end | |
# Looping through all methods by looping through all modules and | |
# their meta classes. | |
if what == CompiledMethod | |
count = 0 | |
each_object(Module) do |o| | |
[o, o.__metaclass__].each do |a_module| | |
a_module.method_table.values.each do |a_method| | |
if a_method.is_a? CompiledMethod | |
yield a_method | |
count += 1 | |
end | |
end | |
end | |
end | |
return count | |
end | |
# This is used by ObjectSpace.each_object(nil)- | |
if what == MethodContext | |
list = [] | |
Thread.list.each { |t| list += t.current_context.context_stack } | |
list.uniq! | |
list.each(&block) | |
return list.size | |
end | |
# This is a singelton pattern, too. | |
if what.is_a? MetaClass | |
yield what.attached_instance | |
return 1 | |
end | |
# If this is a Singelton, check whether it already has an instance. | |
if defined?(Singleton) and what.ancestors.include?(Singleton) | |
return 0 unless what.instance_eval { _instantiate? } | |
yield what.instance | |
return 1 | |
end | |
# Simply loop through all instances of superclass and check. | |
count = 0 | |
each_object(what.superclass) do |obj| | |
if obj.is_a? what | |
count += 1 | |
yield obj | |
end | |
end | |
count | |
end | |
protected | |
# This is a helper method for ObjectSpace.each_object. It does expect the grepper block | |
# to return an array of objects and will call the each block for each one of those, than calling | |
# recursive_loop on each of those with the same grepper block. It will maintain a skip list to | |
# not call it for any object twice (and thus prevent loops). | |
# Note: This is no longer recursive, for speed reason. | |
def self.recursive_loop(start, each_block, skip = []) | |
list = yield start | |
count = 0 | |
until list.empty? | |
an_object = list.shift | |
unless skip.include? an_object | |
each_block.call(an_object) | |
skip << an_object | |
yld = yield(an_object) | |
list += yld | |
count += 1 | |
end | |
end | |
count | |
end | |
# This is a helper method for ObjectSpace.each_object returning an Array of objects | |
# known to an_object. | |
def self.known_to(an_object) | |
known_objects = [] | |
case an_object | |
when nil, true, false, Symbol, Fixnum | |
return [] | |
# I assume that any other Enumerable will be based on those two or otherwise | |
# use ivars / cvars / ... to reference it's values. Thus I keep the skip | |
# list shorter and do not get trapped in infinite lists. | |
when Array, Tuple | |
# FIXME: HACK HACK HACK HACK HACK | |
if FIND_ALL_OBJECTS_BUT_SEGFAULT | |
# HACK: Tuple#to_a does raise a VM Assertion error from time to time. | |
# As we'll probably meet a lot of Tuples from time to time turns out | |
# to be very often. | |
an_object.each do |value| | |
known_objects << value | |
end | |
end | |
return known_objects | |
when Module | |
known_objects += get_constants(an_object) | |
known_objects += an_object.__subclasses__ if an_object.is_a? Class | |
end | |
known_objects << an_object | |
known_objects << an_object.instance_variables.collect { |ivar| an_object.instance_variable_get(ivar) rescue nil } | |
known_objects << an_object.class_variables.collect { |cvar| an_object.class_variable_get(cvar) rescue nil } | |
end | |
# This is a helper method for ObjectSpace.each_object and ObjectSpace.known_to. | |
def self.get_constants(a_module) | |
a_module.constants.inject([]) do |list, const_name| | |
begin | |
list << a_module.const_get(const_name) unless const_name == "fatal" | |
rescue LoadError # Handles malicious autoloading. | |
end | |
list | |
end | |
end | |
public | |
# Finalizer support. Uses WeakRef to detect object death. | |
# WeakRef uses the GC to do all the real work. | |
@finalizers = Hash.new | |
def self.define_finalizer(obj, prc=nil, &block) | |
prc ||= block | |
if prc.nil? or !prc.respond_to?(:call) | |
raise ArgumentError, "action must respond to call" | |
end | |
@finalizers[obj.object_id] = [WeakRef.new(obj), prc] | |
return nil | |
end | |
def self.undefine_finalizer(obj) | |
@finalizers.delete(obj.object_id) | |
end | |
def self.run_finalizers | |
@finalizers.each_pair do |key, val| | |
unless val[0].weakref_alive? | |
@finalizers.delete key | |
val[1].call(key) | |
end | |
end | |
end | |
def self.garbage_collect | |
GC.start | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment