The difference between callcc/Continuation#call and setjmp/longjmp is that callcc allows goto to forward, backward in the stack and even to a side stack while setjmp only allows jump backward. This imply that while setjmp/longjmp works fine with linear stack, callcc will not work in that way and will need a non-linear stack, a complex tree dynamic struct instead a chunk of memory growing when needed (the native stack)
Maybe something easier: lambdas
For example, a code like this, on released version 0.0.7 shouldn't work:
require "fastruby"
fastruby '
class X
def foo
a = 32
lambda {|x|
a+x
}
end
end
'
p X.new.foo.call(16) # it will return 48, or it will fail?
But it works! why?... the current implementation allocs scopes for ruby local variables as a local variable of a struct type, in C, the local variables live in native stack in a fixed address which is passed as block parameter when calling lambda (using rb_funcall, etc..), this memory address remains associated to the block passed to lambda and then that address is used from inside the block like a normal block invocation. The difference here is that the scope struct used from the lambda block is deprecated and not longer valid since that stack space is below the stack pointer
To obtain the example of failure (in the grand tradition of TDD ;) ), we must call any another method after calling the method returning the lambda and before calling the lambda
require "fastruby"
fastruby '
class X
def foo
a = 32
lambda {|x|
a+x
}
end
def bar
end
end
'
lambda_object = X.new.foo # it will return 48, or it will fail?
X.new.bar # this should not affect the lambda object since it is not related
lambda_object.call(16) # but it does, and this does not return 48 as expected
The call to lambda does not return 48 as expected, in fact, it could return any unpredictable value even an invalid object causing a segmentation fault. The reason is that the call to X#bar overwrites the deprecated stack space used by the local scope associated to the lambda block; in this situation, the value of the "local variable" may be any unpredictable random garbage in the stack
Make it pass: alloc locals on the heap using malloc
And it pass, simply by moving the allocation of local variable scopes to heap instead of stack. Memory allocated on the heap never will be deprecated... but it must be de-allocated or the ruby process will die young of cancer. It is late at night and I will not write a test to assert ruby does not die with cancer when six billon of objects are allocated, but surely the de-allocation of local variable scopes will be performed as a refactor task.
And the scope deallocation is the real key issue here
But it is a topic for another post...
Links
- Callcc at Wikipedia: http://en.wikipedia.org/wiki/Call-with-current-continuation
- Callcc in ruby, the pragmatic programmer guide: http://www.ruby-doc.org/docs/ProgrammingRuby/html/ref_c_continuation.html
- Extensive explanation of closure, blocks, lambdas at Wikibooks: http://en.wikibooks.org/wiki/Ruby_Programming/Syntax/Method_Calls
No hay comentarios:
Publicar un comentario