I already used a global limit of rule's firing with success to prevent these kind of
problems (with a big number, eventually calculated from rules as Wolfgang said).
This works, at least to detect infinite loops, because usually, when this happend, the
number of firing increases very fast so the big number is hit quite early.
Of course, if you have many rules, and/or many facts, an absolute number could be
problematic to set (generally solved by making it easily modifiable so the rules'
writer can change this quickly if it find it too low).
And if you session is a living daemon thread, you have to find a way to reset the
counter... And I agree with Wolfgang, 'most of time', a single rule is looping.
May be two... 3 or 4 is rare ...
May be you can maintain (in a Session's AgendaListener) a map that count firing for
each rule and put a global max number of activation, but by rule.
Time duration is a good way too, but needs a additionnal thread to monitor time consumed
so far, and the value could be hard to set.
Another safer way is to force your users to execute some kind of unit tests before
deploying their rules (and these tests are executed with a time and/or rule firing global
limit).
You can also (if you have some kind of deployment procedure by code), execute these tests
as a condition to accept the deployment.