phidgets-ffi icon indicating copy to clipboard operation
phidgets-ffi copied to clipboard

on_position_change stops firing

Open mjskier opened this issue 12 years ago • 9 comments

Could you explain the meaning of this warning with all the event callbacks for stepper.rb? "As this runs in it's own thread, be sure that all errors are properly handled or the thread will halt and not fire any more."

I have a simple ruby/tk interface for a 1262 (4 stepper board) As I start moving the motor back and forth a few times, my "on_position_change" callback works fine. But eventually it stops being called. I get no error, so I'm not sure how to debug this. My callback looks like this (just like your example, but I added a rescue clause to see if anything was happening. I get nothing

@sc.on_position_change do |device, stepper, position, obj|
  puts "Position: #{position}"
  begin
@vars['actual_position'].value = position
  rescue Exception => e
puts "Exception caught: #{e.message}"
  end
end

All the callback does is to update a TkVariable that is used to display the new position value. I'm thinking that maybe I'm running into some sort of race condition between the phidget stuff and the Tk main loop.

Who creates the thread. The phidget library, the ruby interface?

mjskier avatar Mar 14 '12 05:03 mjskier

After adding some trace, the read thread doesn't die. What happens is that eventually the @on_position_change Proc never comes back after the yield statement. @on_position_change = Proc.new { |device, obj_ptr, index, position| puts ">>>>>>>>>>>>>>>>>>>>>>> Before yielding" yield self, @steppers[index], position, object_for(obj_ptr) puts "<<<<<<<<<<<<<<<<<<<<<<< Back from yielding" }

everything works fine for a while then I just get the "Before yielding" message, and the puts I have in my block never gets printed. I don't get the "Back from yielding" and the read thread is just in limbo. I'm not sure how to debug that...

mjskier avatar Mar 17 '12 03:03 mjskier

Yuck! It looks like it is related to garbage collection.

If I GC.disable, I can keep moving the motor back and forth as often as I want and the on_position_change callback works like a charm. Once I GC.enable back (after having moved the motor a few times) either the program crashes, or the callback stops happening as described earlier. So it looks like the GC is screwing up some stuff he shouldn't (like the block passed to on_position_change)

mjskier avatar Mar 20 '12 05:03 mjskier

Does the problem persist if you GC.disable before the yield and GC.enable after? Does this problem happen with all versions of ruby or just one of them?

kreynolds avatar Mar 20 '12 14:03 kreynolds

Yes, I tried that and that's when I got a core dump. If I do the GC.enable later, (after a few times the callback is called) then sometimes I get a core dump and sometimes I get the thread freezing. So far I only tried with that one version of Ruby that comes with OpenSuse 12.1. (1.8.7 I believe, I'll check tonight) I was going to try with a self compiled 1.9 version on an 10.04 Lucid Lynx distribution but I'm running from one issue to the next (ancient Tk that doesn't support scales, failure in require statements because it can't find openssl...)

mjskier avatar Mar 20 '12 15:03 mjskier

Looks like I found a solution, (or got lucky and masked the error) This is what my on_position_change looks like, working on the assumption that the issue was garbage collection of a object that gets reused later...

I added 2 lines to save the block and pointer_for(obj). Saving the block didn't do anything. But saving the pointer_for(obj) before calling the phidget call back registration seems to have done the trick.

def on_position_change(obj=nil, &block)
  @on_position_change_obj = obj
  @on_block = block                          # this didn't do anything        
  @obj_ptr = pointer_for(obj)                # but this did
  @on_position_change = Proc.new { |device, obj_ptr, index, position|
yield self, @steppers[index], position, object_for(obj_ptr)
  }
  #Klass.set_OnPositionChange_Handler(@handle, @on_position_change, pointer_for(obj))
  Klass.set_OnPositionChange_Handler(@handle, @on_position_change, @obj_ptr)
end

Here's what I don't understand:

  • "obj" is nil, so I'm not sure why that makes a difference.
  • How is the block argument accessible to the yield call since it isn't being saved, or passed down to the C function for later use when the callback is fired? Is it bound to the proc when Proc.new is called, so anytime you call that proc you have access to the block?

mjskier avatar Mar 21 '12 02:03 mjskier

Forget my last question. This pretty much answers it:

def register(&blk) $foo = Proc.new { yield } end

register { puts "I'm in here" } $foo.call

mjskier avatar Mar 21 '12 16:03 mjskier

For what it's worth - this 'issue' affects many/all of the event driven modules. It's not actually a bug in the phidget-ffi, it's clearly a 'feature' of ffi, and is well documented on a number of the ffi pages. (Say https://github.com/ffi/ffi/wiki/Callbacks)

I do think this should go noted in the README though somewhere, because I spent a bunch of time figuring this out, only to come here to post the issue, and finding this thread.

Thanks for this wonderful library kreynolds! It's saving me a bunch of work on a project (now that it's not crashing after a minute :) )

brighton36 avatar Jun 01 '13 16:06 brighton36

For anyone who reaches this thread who is having the same problems we were - consider using the phidgets_native gem. It supports the accelerometer, is much faster, stable, and supports arm architectures (in case you intend to deploy onto a raspberry pi)

brighton36 avatar Aug 27 '13 01:08 brighton36

Thanks for these comments - I'm having issues on raspberry pi and was tearing my hair out trying to figure out what's going on. This gives me a direction to work in.

joshuasiler avatar Apr 11 '14 00:04 joshuasiler