odd zl queue behavior - potential bug?

catniptwinz's icon

Alright, so I've traced this a few times, R'd TFM, and searched the forums and could use a few fresh sets of eyes on the problem. The attached patch is a generic example of a problem I've been having when trying to use zl queue in an iterated loop. The process is as follows:

1. Loadbang bangs uzi, which counts to 6 out its right outlet, giving us integers 1-6 in the queue.
2. Bangs from the button SHOULD cause zl queue to output the oldest item in queue (left) and the number of items remaining (right).
3. These outputs are packed and printed.
4. The list is then unpacked and the number of remaining items in queue (from the right out of zl queue) goes to sel 0, with a bang going to the uzi at the top of the patch when the input to sel matches 0.

The idea is that the queue (zl queue) gets refilled (uzi) as soon as it's empty (sel 0), so continuing to click the button SHOULD produce an output (print) of:

1 5
2 4
3 3
4 2
5 1
6 0
1 5
2 4
3 3...

...ad infinitum. INSTEAD, it seems that on the second time through the cycle, the first value to leave zl queue is 2 and not 1. Tracing reveals nothing useful so far as I can tell; the 1 from uzi goes into zl queue and is reflected in the count, but cannot be banged out. What we actually get in the Max window is:

1 5
2 4
3 3
4 2
5 1
6 0
2 5
3 4
4 3
5 2
6 1

...and then nothing, of course, as the count never hits 0, so sel 0 can't bang uzi again. So, is this a legitimate bug, or am I missing something here?

Max Patch
Copy patch and select New From Clipboard in Max.

Thanks for your attention!

Timo Rozendal's icon
Max Patch
Copy patch and select New From Clipboard in Max.

adding a deferlow in the loop solves your issue:

however... I don't fully understand why without the deferlow the first item of the new uzi input dissapears, as if zl queue is still busy cleaning stuff after it sends [6 0] out

xidance's icon
Max Patch
Copy patch and select New From Clipboard in Max.

if you want to have a fixed influence on timing, then instead of [deferlow] a simple [delay] before the retriggering of uzi works fine.

Timo Rozendal's icon

delay is a tricky solution for this kind of problems, I'll try to avoid it when possible.
moreover: keep in mind that if you have overdrive on delay/pipe also means that you move this message (and the outcome) to the high priority thread, this can cause tricky new problems later on in the chain.

broc's icon

I still wonder why the user has to deal with such tricky timing issues.
At least there should be a warning if operations can't be executed due to timing conflicts.

xidance's icon

[uzi] is well known for acting over regular timing, especially in recursive structures.

and @timo: well, i never had any problem with [delay] though using it quite often, but found [deferlow]s timing hard to calculate since it is not fixed but stack-relative.

Timo Rozendal's icon
Max Patch
Copy patch and select New From Clipboard in Max.

here is a solution without deferlow or delay, imho a better solution :-) , with trigger

@xidance: delay without argument puts the new message first in the "scheduler thread" stack, deferlow puts the message last in the "queue thread" stack (and to complete it: "defer" puts the message first in the "queue thread" stack). Maybe you are right that in this case where no output is triggered out of the ZL it is better to use the delay (but I still prefer the trigger solution that I post in this post). However, from my experience it is often best to avoid delays, I did have problems with that when I started doing max. And now when I use a delay I often use a defer(low) at the end of it.

11OLSEN's icon

Delay in such situations is not good coding. how would you know what is the correct delay time, especially on other computers?. so you not know wether you'r wasting milliseconds or it's not working because the time is to short on another maschine.
O.

xidance's icon

probably you both are right, and i´m aware of the technical diferences between delay/defer/low etc. but i just learn by practical experience and remember when having first contact with that recursive [uzi] thing, i measured solutions on large data amounts with [deferlow] and [delay] while delay was much quicker and solid in timing, [deferlow] varied very much - as i said for being stack relative.

but absolutely, sometimes 5ms are not enough. you'll always have to try. and that´s a problem when running patches on different machines.

catniptwinz's icon

Ahh, okay. Thanks everyone for the input! This is what one gets for taking 7-8 years off of Max and then starting up again; minor but characteristic oddities like this tend to slip one's mind.

In response to some of the above solutions, I've always been a bit squeamish about using delays to solve these sorts of problems, and anyway the actual patch that the example was generalized from will be run in overdrive in real-world situations.

@timo, deferlow and trigger would both be viable solutions, though trigger intuitively seems a bit more robust to me. Perhaps I'm overlooking something, but both examples are working for the time being. Thank you!

Max Patch
Copy patch and select New From Clipboard in Max.

@xidance, I think you're correct with regards to issues with uzi in these sorts of patches, but uzi is not actually the problem in this case. I've attached an example in which zl queue is fed with a message box and still behaves strangely.

xidance's icon

that's weird indeed, but yeah, while recursion being so essential in my very past programming habits in max it did drive me mad when discovering sort of this.

Max Patch
Copy patch and select New From Clipboard in Max.

aaand... sorry, but i was feeling much like an idiot about this deferlow/delay thing. so i made up a little practical challenge in the patch. [delay] without argument seems to not delay anything (0ms, i think to remember it once had 5ms per standard?) but fixing the problem. deferlow delays for about 0 to ~2.2ms on my computer. when having had this back then i was doing much midi sequencing on relatively large scale - and therefore decided to choose delay over deferlow due to timing stability. in terms of general coding style it's critical for sure. forgive me ;)

broc's icon

Choosing delay over deferlow is necessary if you want the patch to be executed with high priority.

See also this article, chapter 'Feedback Loops'.

xidance's icon

it affects more particular events than the entire patch i think.

i don´t know the internals, but as there is no measurable delay when delay has no argument it maybe works structural, forcing the max schedular to complete an event down all its connections before it can be retriggered from outside. the result is at least by my experience as solid as [deferlow], but quicker and not stack-related.

broc's icon

So I would conclude that using [delay] makes perfect sense - in theory and practice.

xidance's icon

i´ve read this long text again (did it a year ago but couldn't understand it for 100% back then) and i think it's just like suggested, delay does not nescessarily delay but somehow encapsulates the message flow into one event - and therefore ensures it is finished before it can be triggered again.

"In order to reduce the depth of the network executed per event, we can break up the total execution into separate events once for each iteration of the sub-network. This can be done either using either the delay or pipe objects if it is important that the events are executed at high priority, or the deferlow object if it is important that the events are executed at low priority."

problems with delay occure on slow events like those doing gui stuff oder requesting user input, hardrive access etc.

11OLSEN's icon

ok, it works in feedback loops. what i meant with bad code is when you try in other deferlow-cases like waiting for a file operation to complete to use a delay and guess the time when the thing is not busy anymore.
O.

xidance's icon

sure sir olsen!