ListenerList callback order


#1

Is there a way to call listeners in the order they have been added and not the reversed order that is the default?

I have a singleton object that is the first listener that is added to a ListenerList. Upon callback, it updates itself.

All the other listeners added to the list rely on the state of this singleton for updating their state, but they are added AFTER it and thus they all get their callback BEFORE the singleton.

The result is that they all get called when the singleton has not updated itself yet, because it only does so at the last iteration of the loop in ListenerList::call()

To solve the problem, anything of the following will do:

  • an argument in the call method to tell it in which order to walk the list
  • the possibility to specify whether one wants the listener to be added at the beginning or the end of the list with an argument to ListenerList::add() 
  • adding another method ListenerList::addAtBeginning() to do the same...
  • adding a general purpose ListenerList::insertAt(index) method

#2

I think you'd need to implement your own system for a requirement that's so specific. The order in which the listener list iterates is a totally non-guaranteed implementation detail, and it'd be wrong to write code that relies on it being in either direction.

Surely a better architecture for what you're trying to achieve would be for this singleton to be the holder of the list, or to be called specially by the event source without it being on the list at all?


#3

..and just to clarify why I think that it's wrong for the order to be explicit: The listener list can be mutated and items added/removed while it's being iterated, so its order is inherently unstable, and it may end up iterating in a non-linear way if the list changes while it's part of the way through. It'd always be unwise to write anything that relies on something as unpredictable as this.


#4

yeah, I would have bet my money that you wouldn't be willing to add that, despite none of the above seems like an absurd request.

The last two in particular seem quite sensible to me, or at least they won't hurt at all, since adding a method to a ListenerList doesn't break any existing code at all.

Also, if it is an implementation detail the fact that listener lists are traversed in reverse order, nonetheless I don't think it has changed in the past nor that you foresee reasons for which it should change in the future, so the time could be mature for it to be documented as such.

This would also make more sensible the first two proposals I made, i.e. adding a way to specify the iteration order, so that the client code could decide what's more fit for it (or leave it to the default, whatever it is if it is not relevant).

And your argumentation that ListenerLists are "volatile" and subject to frequent change, I don't think it is so much like that. I suspect that the majority of us developers like our listeners to be quite static anyway. Regardless of that, if a listener list is subject to frequent change, it is quite implicit to me that the order in which it is traversed could be equally chaotic.

For a listenerlist that is quite stable, on the contrary, I don't see a reason for which it cannot be stated clearly that it is traversed in reverse order and, because of that, a way to traverse it in the other order could be provided.

Other frameworks out there explicitly state the order in which listeners are called and have no problem being consistent with that.


#5

yeah, I would have bet my money that you wouldn't be willing to add that, despite none of the above seems like an absurd request.

The last two in particular seem quite sensible to me, or at least they won't hurt at all, since adding a method to a ListenerList doesn't break any existing code at all.

Also, if it is an implementation detail the fact that listener lists are traversed in reverse order, nonetheless I don't think it has changed in the past nor that you foresee reasons for which it should change in the future, so the time could be mature for it to be documented as such.

This would also make more sensible the first proposal I made, i.e. adding a way to specify the iteration order, so that the client code could decide what's more fit for it (or leave it to the default, whatever it is if it is not relevant).

And your argumentation that ListenerLists are "volatile" and subject to frequent change, I don't think it is so much like that. I suspect that the majority of us developers like our listeners to be quite static anyway. Regardless of that, if a listener list is subject to frequent change, it is quite implicit to me that the order in which it is traversed could be equally chaotic.

For a listenerlist that is quite stable, on the contrary, I don't see a reason for which it cannot be stated clearly that it is traversed in reverse order and, because of that, a way to traverse it in the other order could be provided.

Other frameworks out there explicitly state the order in which listeners are called and have no problem being consistent with that.


#6

And the reason why I cannot apply any of your proposed workaround for this, is that the "broadcaster" holding the list is an object part of a module that is shared among some of my projects, while the singleton and all the other listeners are specific to only one of those project.

So I think it is a bad design to add a reference in a general purpose code (the broadcaster) to a specific object that exists only in one of the projects where it is used.


#7

Well, a "stable" listener list is a totally valid request, but that's not what this class was designed for. The reason it's written the way it is is precisely because lists of component listeners tend to be volatile. And although it happens to use an array, that's an implementation detail too - it could store them in a set or other structure where the stored order differs from the order of insertion.


#8

well, I'm f*cked then.

Since neither of my or your proposals are applicable without breaking the other's design choices, I think I'll work around this by adding a second ListenerList "priorityListeners" to my generic broadcaster class, for those listeners to be all notified before all of the regular ones.

The priority listeners will only contain my singleton object, and the regular ones will be all the other instances that depend on the state of the singleton to be up to date.

Do you foresee any shortcomings in this arrangement?


#9

Yeah, that sounds like a good plan, both practically and in terms of good programming style.

Explicitly having a priority list would make it clear to anyone reading your code what the intention is, rather than writing code that relies on the order to work correctly. Doing that would leave your program very brittle both to changes in juce but also to other future changes in your code by other people (or by a future you) who have forgotten or don't realise that the order is important.