Re: rb_yield(), break, and C extensions



On 4/7/07, Kent Sibilev <ksruby@xxxxxxxxx> wrote:
On 4/7/07, Noah Easterly <noah.easterly@xxxxxxxxx> wrote:
> On Apr 7, 1:08 pm, "Kent Sibilev" <ksr...@xxxxxxxxx> wrote:
> > You should wrap your rb_yield call with rb_ensure. From README.EXT:
> >
> > VALUE rb_ensure(VALUE (*func1)(), void *arg1, void (*func2)(), void *arg2)
> >
> > Calls the function func1 with arg1 as the argument, then calls func2
> > with arg2 if execution terminated. The return value from
> > rb_ensure() is that of func1.
>
> Hmm.... this doesn't seem to have the desired effect. I'm pretty sure
> rb_ensure() is just for handling exceptions, not breaks.
>
> According to ruby.h and the Pickaxe, the function def'n has changed a
> bit too. From Programming Ruby(2nd ed):
>
> VALUE rb_ensure(VALUE(*body)(), VALUE args, VALUE(*ensure)(), VALUE
> eargs)
>
> Executes body with the given args. Whether or not an exception is
> raised, execute ensure with the given eargs after body has completed.
>
> So it seems that rb_ensure is just for exceptions, not 'break'. I
> tried tweaking my code to use it anyway, but the ensure call is still
> bypassed when there is a break. For example:
>
> >>> lib.c
> void func( int n, int (*callback)(void) )
> {
> printf("before callback...\n");
> if (callback() < 0)
> goto cleanup;
> printf("after callback...\n");
> cleanup:
> printf("in cleanup...\n");
> }
> >>> ext.c
> static void
> my_ensure(void * ptr)
> {
> *(int *)ptr = -1;
> }
> static int
> meth_callback(void)
> {
> int retval = 0;
> rb_ensure(rb_yield, Qnil, my_ensure, &retval);
> return retval;
> }
> // ...
> >>> test.rb
>
> require 'ext'
> YYY.new.meth(100) { } # outputs "before callback...\n", "in cleanup...
> \n"
> YYY.new.meth(100) { break } # outputs "before callback...\n"
>

What I meant was that your cleanup code should be in my_ensure
function and not in func itself. Just to prove my point:

$ cat t.rb
def test
puts 'pre'
yield
puts 'post'
ensure
puts 'ensure'
end

test {break}

$ ruby t.rb
pre
ensure



OK, maybe this code will clean things up:

$ cat t.rb
require 'rubygems'
require 'inline'

class MyTest
inline do |builder|
builder.prefix <<-EOC
static VALUE
func(VALUE arg)
{
printf("pre\\n");
rb_yield(arg);
printf("post\\n");
return Qnil;
}
static VALUE
cleanup(VALUE arg)
{
printf("ensure\\n");
return Qnil;
}
EOC
builder.c <<-EOC
void my_func()
{
rb_ensure(func, Qnil, cleanup, Qnil);
}
EOC
end
end

MyTest.new.my_func {break}

$ ruby t.rb
pre
ensure
$


--
Kent
---
http://www.datanoise.com

.



Relevant Pages

  • Re: Wikipedia vandalism
    ... to int main. ... supplied it on the "stack" with which people with a clue know about. ... The only danger point is when your idiot shell procedure BLINDLY tests ... worse (continued execution based on garbage results). ...
    (comp.lang.c)
  • Re: What if black holes are just a glitch in the matrix?
    ... Computer programs are deterministic... ... char data; ... int f{ ... of course converted into a deterministic machine during execution, ...
    (sci.physics)
  • Re: Wikipedia vandalism
    ... to int main. ... the interface between something and an C program. ... supplied it on the "stack" with which people with a clue know about. ... worse (continued execution based on garbage results). ...
    (comp.lang.c)
  • Re: An ambiguous case of setup_call_cleanup/3
    ... In this manner the choicepoint has to remain open due to 7.8.11.1 c1. ... So if people are that "picky" on the moment when Cleanup is executed, ... Some use it in a way such that the execution of the cleanup is ... The resource can be freed when no such subgoals ...
    (comp.lang.prolog)
  • Re: An ambiguous case of setup_call_cleanup/3
    ... We have here two goals protected independently with cleanup semel and ... likely to use different mechanisms to insert the execution of the cleanup goal ... use a queue or stack of goals to be inserted at the first opportunity, ...
    (comp.lang.prolog)