Error, sometimes iterating over a collection returns a Frame

Hi all,

While working on some classes recently, I’ve had this unfamiliar (to me) error pop up, inconsistently and in a couple different places, while iterating over elements of a collection inside a class. It seems once in a while a Frame (which is something I haven’t encountered before) turns up instead of the actual collection element.

Here is the latest error dump:
ERROR: Message 'startTime' not understood.
RECEIVER:
Instance of Frame {    (0x2e072d488, gc=7C, fmt=01, flg=00, set=04)
  indexed slots [11]
      0 : instance of Method Set:scanFor (0x121a8a580)
      1 : Integer 0
      2 : Frame (0x2e072d488) of Set:scanFor
      3 : Frame (0x2e072d488) of Set:scanFor
      4 : RawPointer 0x0
      5 : instance of Set (0x2e07b7e58, size=2, set=2)
      6 : Float 19200.000000   00000000 40D2C000
      7 : nil
      8 : Integer 265
      9 : Integer 318
     10 : Integer 265
}
ARGS:
PATH: /Users/ericsluyter/Documents/sc temp/Off Hours/off hours test 5.scd
CALL STACK:
	DoesNotUnderstandError:reportError
		arg this = <instance of DoesNotUnderstandError>
	Nil:handleError
		arg this = nil
		arg error = <instance of DoesNotUnderstandError>
	Thread:handleError
		arg this = <instance of Thread>
		arg error = <instance of DoesNotUnderstandError>
	Object:throw
		arg this = <instance of DoesNotUnderstandError>
	Object:doesNotUnderstand
		arg this = Frame (0x2e072d488) of Set:scanFor
		arg selector = 'startTime'
		arg args = [*0]
	< FunctionDef in closed FunctionDef >
		arg clip = Frame (0x2e072d488) of Set:scanFor
	< FunctionDef in Method Collection:selectAs >
		arg elem = Frame (0x2e072d488) of Set:scanFor
		arg i = 660
	ArrayedCollection:do
		arg this = [*721]
		arg function = <instance of Function>
		var i = 660
	Collection:selectAs
		arg this = [*721]
		arg function = <instance of Function>
		arg class = <instance of Meta_Array>
		var res = [*660]
	< FunctionDef in closed FunctionDef >  (no arguments or variables)
	< closed FunctionDef >
		var program = <instance of OHProgram>
		var win = nil
		var view = nil
		var timeline = nil
		var startTime = 0
		var duration = 54000
		var clips = nil
		var deletedClips = nil
		var birdClips = nil
		var refreshClips = <instance of Function>
		var refreshView = <instance of Function>
		var secsToPixels = <instance of Function>
		var pixelsToSecs = <instance of Function>
		var pixelsToPercent = <instance of Function>
		var secsToPercent = <instance of Function>
		var clickPoint = nil
		var clickTime = nil
		var originalDuration = nil
		var prDrawSpectrograms = true
		var scrollRefreshRout = nil
	Interpreter:interpretPrintCmdLine
		arg this = <instance of Interpreter>
		var res = nil
		var func = <instance of Function>
		var code = "( /////////  ---------------..."
		var doc = nil
		var ideClass = <instance of Meta_ScIDE>
	Process:interpretPrintCmdLine
		arg this = <instance of Main>
^^ ERROR: Message 'startTime' not understood.
RECEIVER: Frame (0x2e072d488) of Set:scanFor

But when evaluating the exact same code a second time it all worked as expected, no errors.

In a different place, where this was happening a lot, I fixed the issue by testing the class of the element like so:

while ({ clipsPile.size > 0 }) {
      thisClip = clipsPile.removeAt(0);
      thisClips = [];

      if (thisClip.class == OHClip) {
....

with seemingly no repercussions. (i.e. clipsPile.removeAt(0) doesn’t seem to ever skip over an element, just sometimes returns an erroneous Frame.)

And as you can see in the error dump, the code that threw this error today was iterating over a different collection in a different manner altogether.

I wonder if anyone has insight into what might be causing this?

(edit: I’m using 3.13.0 universal binary on M1 mac)

Ooh, haven’t seen one like this in a loooong time…

If I recall, the most likely culprit is a buggy primitive that does something wrong with garbage collection. If that’s the case, then the GC corruption likely occurs sometime before you hit the error… so it’s extremely difficult to track down.

I see there’s a large array being iterated, but 700+ elements is in no way too large an array.

One way to narrow it down is differential testing: either 1/ remove or simplify operations one by one until it stops breaking (such as, if the data are important, try saving intermediate results to disk, then load from disk in your tests rather than calculating fresh – skipping processing steps would have an effect on the state of the GC), or 2/ start from the simplest minimal case, and add processing steps until it breaks. Either way, the last thing that changed would be the place to focus attention.

Unfortunately bugs like this are just… hard… Maybe someone else has another idea.

hjh

Hi James, thanks for the hints. I will try to dedicate some time to tracking down the culprit primitive… it’s difficult even to make a reproducer because of the apparent randomness with which the error comes up.

(I had thought it was only happening when the iteration was taking place inside one of my methods, because when I would reconstruct the same process directly in the interpreter the error wouldn’t happen… but looking at last night’s example again I realize it was in an interpreted code block. sigh)

If anyone else has more ideas or pointers please don’t hold back…

(unfortunately / of course, this pops up on a piece that I will be installing overseas to run for many months without human intervention…)

May I also ask: What are the contents of the array being selectAs-ed in the first error dump? And how is that array being built initially?

hjh

In that first error dump, I think the “FunctionDef in closed FunctionDef” is:

{
  clips = program.clips.select { |clip| ((clip.startTime >= startTime) or: ((clip.startTime + clip.duration) >= startTime)) and: (clip.startTime < ((startTime + duration))) };
  deletedClips = program.deletedClips.select { |clip| ((clip.startTime >= startTime) or: ((clip.startTime + clip.duration) >= startTime)) and: (clip.startTime < ((startTime + duration))) };
  birdClips = program.birdClips.select { |clip| ((clip.startTime >= (startTime)) or: ((clip.startTime + clip.duration) >= (startTime))) and: (clip.startTime < ((startTime + duration))) }; 
}

(sorry, this is all a bit untidy still…)

The program contains three Arrays of OHClips (https://github.com/esluyter/TheOffHours/blob/main/OHClip.sc) which are being filtered in this function

and these arrays are assembled in the init function of OHProgram, where they are filtered from yet another array of OHClips and which is the other place this error has popped up (https://github.com/esluyter/TheOffHours/blob/main/OHProgram.sc)

Edit: and they are originally assembled from four instances of OHCycle copying template clips and giving them different start times (https://github.com/esluyter/TheOffHours/blob/875dfc9fef20b26344a938aa485a55522cfc2b28/OHCycle.sc#L29) via OffHoursScore’s clips method (https://github.com/esluyter/TheOffHours/blob/875dfc9fef20b26344a938aa485a55522cfc2b28/OffHoursScore.sc#L39)