I am building an extension that uses observers to see what the user is doing. When certain model changes are made, the extension makes further changes to the model. I understand that this is a risky thing to do, and I have read in detail about Safer Observer Events, and followed the advice of using a timer.
Essentially, my extension has an events queue that listens for various changes to the model, adds it to the queue (not making any changes), then uses the onTransactionCommit observer, with the UI.timer hack as suggested in the Safer Observer Events example.
I have spent many hours checking my code for any errors, but I keep getting a crash.
After getting a ruby debugger up and running, I have managed to get the following ruby back trace.
from /Users/username/Library/Application Support/SketchUp 2019/SketchUp/Plugins/SkinUp/ruby/CommitObserver.rb:139:in `block in onTransactionCommit'
from /Users/username/Library/Application Support/SketchUp 2019/SketchUp/Plugins/SkinUp/ruby/CommitObserver.rb:139:in `commit_operation'
from /users/username/library/application support/sketchup 2019/sketchup/plugins/su_dynamiccomponents/ruby/dcobservers.rbe:829:in `onComponentAdded'
from /users/username/library/application support/sketchup 2019/sketchup/plugins/su_dynamiccomponents/ruby/dcobservers.rbe:829:in `instances'
I should note that it doesn’t crash every time - more like once every 6 operations. It always crashes when model.commit_operation is called from my plugin. It appears to crash deep in the SketchUp code on SketchUp CComponentDefinition::GetInstance:
, but I can’t get much info other than that.
I have checked my code for bad behaviour, and I am pretty sure that I have checked that an entity exists before doing anything to it throughout my code. It is always the Dynamic Components plugin that seems to be giving me issues with this. The plugin does delete and create components (but not DC ones).
-
Am I doing something wrong? Or is this more likely to be a bug in Dynamic Components?
-
Is the approach of making an events queue and only making model changes on onTransactionCommit a bad idea for any reason?
In case you want to inspect the code, the problem function is below:
def onTransactionCommit(model)
# The following snippet from SaferObserverEvents: https://github.com/SketchUp/sketchup-safe-observer-events/blob/master/src/safer_observer_events.rb
executed = false
timer_id = UI.start_timer(0, false) {
break if executed
executed = true
next if BuildQueue.length == 0
if (!TK::SkinUp.deactivate_observers)
TK::SkinUp.deactivate_observers = true
@@deactivate_commit = true
# Ensure that the operation is transparent so the Undo stack isn't
# cluttered.
status = model.start_operation('SkinUp Entities', true, false, true)
raise LogicError, "Start Operation Error" if status == false
new_templates = []
begin
new_templates = BuildQueue.process_queue()
if new_templates.length == 0
puts caller
end
rescue
# Note: Never abort a transparent operation, it will abort the operation
# it chains to. Instead, try to clean up and simply commit in order to
# make sure the operation is closed.
# https://assets.sketchup.com/files/ewh/Observers2016.pdf
# TODO: the commit operation triggers a crash in the observers in DCobservers. Stack trace following commit:
# /users/tomkaneko/library/application support/sketchup 2019/sketchup/plugins/su_dynamiccomponents/ruby/dcobservers.rbe:829:in `instances'
#/users/tomkaneko/library/application support/sketchup 2019/sketchup/plugins/su_dynamiccomponents/ruby/dcobservers.rbe:829:in `onComponentAdded'
model.commit_operation
@@deactivate_commit = false
TK::SkinUp.deactivate_observers = false
raise LogicError, 'SkinUp Build Queue processing failed'
end
puts "DEBUG: events processed: " + new_templates.length.to_s
status = model.commit_operation
puts "DEBUG: transaction commited" # crashes before it gets called
@@deactivate_commit = false
TK::SkinUp.deactivate_observers = false
end
}
end