odd zl queue behavior – potential bug?
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:
…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:
…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?
Thanks for your attention!
----------begin_max5_patcher---------- 561.3ocuWssiaBCD8Y3qvxOSi7EBA5a86nppx.t65sDCELaSyp8eu3gKIrJK gnP3Ei73A6yblyLFdy0AGmePVgQeE8cjiyatNNfIqAmt4N38hCIYhJvMrV92 73WvdsKYjGLf4rbQZrP+T+B558Jclz.uDsy3ux0lJ0QIXisgbx27ZyGct0j4 eExV3ggsG8itkKDljmU5m9YoLwz5wV9lfcT+cgdHNoY2QAvHqYb30To.baBg uPwmAKsXObN3uUpDYX6Bu65ZG7tOhoRlgHWjUX2BqvljU7P3IYlPx.yry2xI 7foXFFYUnlZcgH42HERc+plOkeTZikdrOlICwB2AhG5jhmnUghljfXKRY0ro ksfxY6jJGZ35PKkPZ8dEMjOOvoMh.5XAA2exH2eYi73ZiIWOYLtT8MYbH7FZ VNQPxWvj3wLzepk0xGXywaowHMJXF56Upy3QEJ394E90tzn+405Nd1MqTBPP 9SRSyrV.dUblR+wOAAff09XpqJutLoOF5NGDa..oxJiRKLpl5lS9XSYnSn7Y UZpTed0eppRDmIAzStXNbtvgN5jtLb7WOzXCb50XmvQN8vwCYN3Yk3G9bvy0 TO6UoE4M0OcJXNayVauDaIIJhLbGBX4zYsbAQ3bBhUTzMK7Dsd3IZFEAraLI Saxx8IVd6Mlzf1uQXzr1++3LUP.4gHBXjGXim1lzhhhWkkUcaIfjlaxdIuzN MvClpzsSgcDWJeU06O20tau69ePdfXTl -----------end_max5_patcher-----------
adding a deferlow in the loop solves your issue:
----------begin_max5_patcher---------- 584.3ocyV1sbaBCDE9Z3oPitlxHI9u20miNY5H.kDkhETPj35L4cuHAXicbq ArhatAFsRrb1OsbDuZaASK2xZffuB9Nvx5UaKKcHU.qgwVvMzsYEzF8xfB1K koOAc5mRx1J0gyY2ypKJeYbh6KExF9NlZRLwEMDVztgKJXRctvCAqnxrG4hG 9QMKS1qErWjaXD1OJ1A34Gz87ffP0UBxEAt6P1JakmlNdtVQcp7KjoxQP2nk C7a0bZw3L8IP96JV+aFBA2ol4Maa0EmqCKEkz7Tp3ACgkv38ToGJgnkCE7Jf htFLIXZXE.zRnB4uSkjf8TINQwCuK0pPNSqBZsXwwv8LshJZ1OAb.2PcMS3i GhnaavKGP3jU.HtPp3i5lIQzZ.z7ZfHgIq0rAG++1sopVyay3.GjbZWim+Ef B5LPw+e.kUT4osRYo.tvhwKVo7dKSxZrL8tcNi6J.+pk0x9.5sw3Y0aetO9W i6n4cF2wAgFhKSNJESzbw+Rbw68bw+JNzX796LG0udXAWb5eloKCU7iQWSYa c1X1GaWAGphbVijKnRd2GNSVD5nE8HOOmIl9U7FddUYm3FDgejafBOQJJgQA t3w1JUnCuLdCMsfoQD5ra8ytJPyoJhuPUXX8fmidv2D8z06AHW6lr4jCYNaW 92tcq34z8j7ISO2P97oZ2hLC0PVngUm6zdOJu39y9w8+HyQihBN0QSE4ivPK YFFHjUx79CMnUUOypaFRoVIcmr9TYsZXnidHWzOTmQXM6Y9358sUY6M6+zWq mBA -----------end_max5_patcher-----------
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
if you want to have a fixed influence on timing, then instead of [deferlow] a simple [delay] before the retriggering of uzi works fine.
----------begin_max5_patcher---------- 537.3ocyV1raiCBEEdsqTeGPr1MBv+lY27bLpphXSaYFGblXrZZpl28AtN3l lpV+yPRmMAkCDx494CW7KWeU.dU8NQCF8MzOPAAuXTB.MqRfSH.uluqnh2.K DqDOUu5m3vCyoE6zfdonh+bu780JcibuvNEksf3zUsqkpJgF1KlScCWW7nT8 vcaEE5N6rL27iPTRlcHZo8SFYAAc6Q6Tcq1sUTmbml94Mht8Auhqd.+5OyZL EeMLK96ak7pdOKKAQS0cSDFz9y0WYGMCg+y7oplWBdYRHh9IHJMeQZFMNKOD EmXATJ4BiIp+wTinBQ7XLJqmQ4LHJkNDiXeNiBQ3IyIFw+fpUsgW7KjDI8Wh ZYROshH.tRoyGWRk1RK6vjCVK8OvlGtXiCWrTnEUR57OANcHkOeHspUqqU3I mPhf1xcsaX9qcywU0Yn269JzuaEshyyidJcbO5+3SJypoB8bzTYuDk5OJczM TzHHyDOHkhFr8qabNMVhOkYca.tRpd2KBAEjchSHYSc61Bmo5dagPzQ0SonQ KUbszb.60EE+1E8nrrT.y2i00xxM0lh5fOP29wOdGs0L27LBuE8k3MSSdStX HucZELr4LAtET20XcwN5gK+ey2xRbuhoa0VE+TZ4iA6..9B39nxqzIycC1Sr msy5vZROVAI+X8QEmOk9WJtRFSdFLG8xat+e6QY+WY9ON9NuYELC+E.r9Xzo -----------end_max5_patcher-----------
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.
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.
[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.
here is a solution without deferlow or delay, imho a better solution :-) , with trigger
----------begin_max5_patcher---------- 597.3oc0WksiaBCE8Y3qvxOmF4EV6a86nZTEKtY7ThgBF0zLZ92qwrjjQDvT FQadAKewXN2y8bOXd01BFmehUAAeF7Ufk0q1VV5PMAr5laAOFcJIKpRuLnf8 q73Wf6ZukjcRpCWTxEx9nEQxjm4hCeqjkHa2bBBsGsCPwt5AmlqDUHvScOCO UuOp89S986y2yExJ9YVycvD0izFVTejKxXRMfvWBlWK6ihtZGDQG06.7Kk7n LXyMdy1t4xt0kyRPLHdhbNzqIKw9N2Ikawq72Er10CiiDGf65FGiYbWOyP1B l4bFnjcXVpgDFzL3flmZZnkQoDxRnDx+NJIKOJss9dWRAiB164ic7CTbhtOw CYnnYLlAudwBdKXlJVF.MEsP8Gnk.htWxy7dowEMnGDUSsnHJ4G.NfOUyj6. +PogZcCddBR6W2NLp5I7Awqw.BBiBGXHhmlgbMPBMNuDrdoyGaaUbsTlKlH8 ostrt8Y7euiBEt.uiOzOm7yZVMyzRLFabI9dND3GFGhybfmgeTAS0Z.GuEdT jIrIbVOMQMhlzubXFW79iqpy4l32xcU40kI8YSeiK3RFmxpjbQjjqZctrH+a Vyy7zTl35ySdjmVjq3hNL.dZzBowPBY.jdGt2DLgMAS3MCSJQFfLGjPKilvJ +PbugAht2UkP3vaCsRX6ZBSh1zpqqABNx1p2nF.I2kAIGec4zq8+uPtCyVIV Ilzu5rozGwDGjvsshFXnHa6LPv+2U2z0j4LGHK1Syev.i52p969ioal4qOFv 0qtIxMYjZxa1+APTl5QL -----------end_max5_patcher-----------
@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.
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.
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.
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!
@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.
----------begin_max5_patcher---------- 508.3ocwU00TiBCE8Y3WQl7LKSRn.UeyeG6riSnjsFGHfjfV0w+6K4FXKshH crVeIL4lK2bNm6G4UeObV0NgFitF8ajm2q9ddfIqAu98d3R9tMEbM3FtTn07 sBbf6LiXmArSQLTDZEJFkLblpsrp0THLveR6s92JkQKeQ.1XgjdyNOMOWKbn AiQ+o+nZtYycR01aaDaLtSWSBSRoqRWGfnTRWPPIo1U55Px++OYNfrpr6+EM FO55U7R3Vv2zH4EifqTMfVl01a991kfEJMJwSc206TFsn.QlVSXmnljwUawA KWaXwwVUIJwtxHSqMreNootQpLSKMj4klI3Mkl9NhuZNhSOQhSOi0DOwqOOk Df.59rzhBVjUUhmsnH8BTTj0ZLUp4mVLcKvbL0wtHx.6lI+G8cmleo.8PqnU bdR0mRmO8pjOOISIegrLDPbgTc76G.vr1OTuzUsMaFnReIFZOrxEZiTwMxtJ h89vNvm6j44B03oC4RMOqP.ThLYd6TPC8SPC8hgF1BzF6aZyBmRYdcU2fg97 yUjP5vHfTntHwUcLdCk.CNG4KXYOTNaT7H3+Abjbwjb6zfuNdNRyiXgw1lQ1 whp0x2gnRV.IRubZJYAcUG14sb33F.wqqeTzn6CIfjtQy2W0X2lD.akJ2VHh 3FwixA+i7sQ6M++AhfaemB -----------end_max5_patcher-----------
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.
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 ;)
----------begin_max5_patcher---------- 919.3oc0XssihBDD8Ymj4efvytFpp45919crYxFT6YF1ffqzj4V1+8su.JJq i.VnNuPmt.wScpSUbfOt+tI1yyekWXa8cqeZMYxGxHSzwTQlTGXh8p3WWjFW nuP6U7hh3m31SqNof+pPeBmYai8XdlnH4ctJNfybpimUtJIKkKz2IrN55Xwh mSxd5Wa3KDFv3xj+H4w.0hmi5HDNyw5gF2o7RQ8sBpCahIdaM2bers28STfJ Kdk9L1+XSRb5V7lrTGLe9u+FB15f+896TqxkoclZx3uHuEsYFQxJ9F5Hm.WE evBXlE0Qz43jCdTx4wz7Xg8zgvRH8rzRdZ7azwR.SKaXrP8RzonoiqglGm8T +YHF8LTwKwqImfvtRPGWGkjoUQpkdSStCmlN5jHvBsXVtVdV9DRWXzL+.vMH bpUntCzO3RNWxaDzS7TKmwggXNZ8Dye3pJca2fFN4SOUsdiVj2GpB9LpJvYG UAflpbOEU4zSZHX3zv7RgHOyt24Eh5rPyI5bgnYtMSqP5qtumZ8mRdIeb5EP SA16L5EFVePzHLaURF..tQaOSZRFeQdYlXuL37IvHiEPzemEPFdQF0B927V. q5ytpd.A2wvC3i7Mo4uP2jVLroMvS2CRoNBu0s.VQNWWKf.7UwBHFbUs.B29 V.axPWUKf27N.YnwAn6H5.D9J3Tpoj4Z4TBbNjoL2As4lC+9X5bPchC3uh7x MKpgQkS0oVMRgk7BQRVrHQ5rd2UI+u26pdNY4Rd1dU6UIKWmKUeUHAYrYdxo NQ5o1XXzLnh+zgrd33Ucpwt6.wNiYdty1Mz.ZVW.s7Mo6InA4SFU3z27gHAi 2q81EpEtpKrtVnhPSV42orhMjrpFrLy2BpNQYNW1JBJuJ3LpHNv1cWP0u78q NqN2lE.B6bi5D1cOG4BxfFxE8N5vNzIr2O4RfeKr6QM1c6Du6OvgOnWKMiND cX+z7dPuwdEIGY9pPx21XK1UgnA6diSu5dcmNXqFVG7xNrAG3vlJxOb2FZPM 1ITC8sU0fSWnAn0aHAzPm.MzaCBJOX6dbD52zfP0NiAgll0nyfPmRJbHI0A9 CpySx7GzwBRuG3ePAwiX+APml0C98F1rlp+l6nA1dizKjD1ZTYqPzj.PWdNE zW7q9RE6lTpT2P0alS27R.5z7xAN4oxcPSVmN2AZAwoY8dOpmgsbkUWIHyU1 gh4OA6CVxWg85Rw+E6p.xk+AZFPos. -----------end_max5_patcher-----------
Choosing delay over deferlow is necessary if you want the patch to be executed with high priority.
See also this article, chapter ‘Feedback Loops’.
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.
So I would conclude that using [delay] makes perfect sense – in theory and practice.
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.
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.
sure sir olsen!