2008/4/27 bryce@kampjes.demon.co.uk:
Good point, but I can reproduce the bug without using MessageTally or any class variables. ExuperyProfiler doesn't use class variables.
The following will reproduce it: TestProfiler>>spyOn: aBlock | myDelay millisecs timer | millisecs := 1. (aBlock isMemberOf: BlockContext) ifFalse: [self error: 'spy needs a block here']. myDelay := Delay forMilliseconds: millisecs. timer := [[true] whileTrue: [myDelay wait]. nil] newProcess. timer priority: Processor userInterruptPriority. "activate the probe and evaluate the block" timer resume. aBlock ensure: [timer terminate]. ^ self
Ok, here we see a scheduler stress test. Given that infinite loop running at higher priority, and you having 10 concurrent loops, there is a good chance that processes with lower priorities will starve forever (never gain control to evaluate anything).
Lets analyze why it can be. The Delay interrupt watcher uses single semaphore and single primitive to tell VM when to signal it. So, whenever this semaphore get signaled, a #timerInterruptWatcher gains control, takes active delay and signals delay's semaphore. Then it takes another delay and if its not yet elapsed, waits for it .. and so on.
So, what we having : - 10 processes with highest priority, each waiting for 1 msec delay - 1 process with highest priority (a #timerInterruptWatcher) - 10 processes with user priority each waiting 500 msec delay.
Squeak's scheduler never passing control to lower priority process if there are higher priority process waiting to be executed, right? To have a chance for process at user priority to be executed, we should have 10+1 processes waiting for semaphore at some point of time, which is very unlikely (consider time, which interpreter need to spend to switch processes and reschedule 10 delays - it may be more than 1 msec), so this leads to an infinite loop which bounces between 11 topmost priority processes.
In ideal environment, where all processes running in parallel, your code SHOULD work. But we not in ideal environment, and squeak's scheduler design having own limits in this regard :)
100 timesRepeat: [processes := (1 to: 10) collect: [:each| [[(TestProfiler new spyOn: [(Delay forMilliseconds: 500) wait]) ] repeat] fork ]. (Delay forSeconds: 1) wait. processes do: [:each| each terminate]]
TestProfiler is a new subclass of Object without any variables or any other methods.
I'm not convinced this is a new bug with 3.10. It feels similar to something I've encountered earlier. It is happening frequently in 3.10 but not in 3.9.
Bryce