[squeak-dev] Error: infinite recursion in doesNotUnderstand:

mail at jaromir.net mail at jaromir.net
Tue Jan 11 21:07:36 UTC 2022


Hi Christoph,

> On 2022-01-10T12:29:26+00:00, christoph.thiede at student.hpi.uni-potsdam.de wrote:
> If I just evaluate "nil foo" and press Proceed two consecutive times, I get this "infinite recursion" error and my iterative development cycle is broken. :-( Can we fix this?
> I would really appreciate it if we could keep the existing workflows intact. 

I've been thinking about the idea of "restricting" the MNU 'infinite recursion' fix; from my perspective the minimum new #terminate needs is to prevent the infinite MNU loop *during termination*, which would keep your workflow intact and the new #terminate resilient; here's a suggestion:

doesNotUnderstand: aMessage 

	| exception resumeValue |
	(exception := MessageNotUnderstood new)
		message: aMessage;
		receiver: self.
	resumeValue := exception signal.
	^exception reachedDefaultHandler "i.e. exception was not caught..."
		ifTrue:
			[[aMessage sentTo: self]
				on: MessageNotUnderstood
				do: [:ex | thisContext isTerminating ifFalse: [ex pass]]]     "<------------------ here's the needed minimum"
		ifFalse: [resumeValue]

where #isTerminating checks the context stack's bottom context for a mark left by #terminate, and reads:

isTerminating
	"Answer true if the receiver's stack is being unwound by #terminate."

	| bottom |
	bottom := self bottomContext.
	^bottom stackPtr > 0 and: [(bottom at: bottom stackPtr) = #unwindingInTerminate]

For this to work I'd only sneak the following line at the beginning of #terminate: to mark the receiver's stack as being terminated:

terminate
...
		self suspendedContext bottomContext push: #unwindingInTerminate.     "<------- this marks receiver's stack as being terminated"
...

Please let me know if anybody has any concerns about borrowing the bottom context's stack top during termination :)

Thanks!

best,
~~~
^[^    Jaromir

Sent from Squeak Inbox Talk

On 2022-01-10T18:36:42+01:00, mail at jaromir.net wrote:

> Hi Christoph,
> 
> Eliot's fix raises the 'infinite recursion' only when you Proceed the debugger without any change, i.e. attempting to evaluate the same unknown message; what's the scenario you've had in mind and what am I missing here? :) 
> 
> Oh, wait - did you mean you'd defined the unknown method in the wrong class so it only looked like nothing changed?
> 
> In that case yes, #dnu will give you the 'infinite recursion' error because from it's point of view 'nothing changed'.
> 
> The 'infinite recursion' guard is nice to have to prevent infinite recursion when you work with an MNU inside the ensure argument block. #terminateAggressively avoids this recursion by skipping halfway unwound blocks; at a price though: I've recently noticed when you abandon the debugger with a process inside a critical section of a conditional variable (Mutex, Semaphore) it wouldn't release the critical section: it would leave the Mutex/Semaphore locked.
> 
> Try this example: 
> 
> Mutex new inspect critical: [1/0]
> 
> do-it, a debugger opens, step into a few times until you stand right before #primitiveExitCriticalSection, hit Abandon - and watch the owner of the Mutex
> 
> The Mutex will remain *locked* by a terminated process.
> 
> The reason is #releaseCriticalSection: assumes #terminate will finish unwinding the innermost unwind context regardless whether it has already started or not yet - i.e. #releaseCriticalSection: expects #teminate to release the Mutex or signal the Semaphore.
> 
> Unfortunately this is a general scenario: such behavior will happen whenever you Abandon a debugger inside an ensure argument block.
> 
> So back to the MNU problem; we may need the classic terminate to try to correctly release critical sections and then the MNU 'infinite recursion' fix needs to be present;
> 
> or, maybe just restrict Eliot's fix to only kick in for MNU errors inside ensure argument blocks... I haven't thought this through yet though :) 
> 
> Just FYI, the above scenario inspired this test (in the Inbox):
> 
> testMutexInCriticalEnsureArgument "self run: #testMutexInCriticalEnsureArgument"
> 	"This tests whether a process that is in the ensure argument block in critical: but has yet to evaluate the primitiveExitCriticalSection
> 	leaves it with the mutex unlocked."
> 	
> 	| terminatee mutex |
> 	mutex := Mutex new.
> 	terminatee := [mutex critical: []] newProcess.
> 	self assert: terminatee isSuspended.
> 	terminatee runUntil: [:ctx | ctx selectorToSendOrSelf = #primitiveExitCriticalSection].
> 	self assert: terminatee isSuspended.
> 	terminatee terminate.
> 	self deny: mutex isOwned.
> 	self assert: mutex isEmpty
> 
> Thanks for any comments,
> 
> best,
> ~~~
> ^[^    Jaromir
> 
> Sent from Squeak Inbox Talk
> 
> On 2022-01-10T12:29:26+00:00, christoph.thiede at student.hpi.uni-potsdam.de wrote:
> 
> > Hi all, hi Eliot, hi Jaromir,
> > 
> > 
> > in the last time, I observed a number of "Error: infinite recursion in doesNotUnderstand:" messages when proceeding from a DNU in the debugger. Would it be possible to disarm this again? I admit that I have not yet followed up the full background on this change, but ...:
> > 
> > 
> > My usual workflow looks like this: Observe a DNU, create a new message in order to *probably* fix the issue, and press Proceed to give it a second try. If my fix was incorrect, observe a second DNU, approach a second fix, and proceed again for a third try, etc. With the recent changes to #doesNotUnderstand:, I have only got one chance to make the right fix, i.e., implement the missing method on the right class in the first attempt. If I just evaluate "nil foo" and press Proceed two consecutive times, I get this "infinite recursion" error and my iterative development cycle is broken. :-( Can we fix this?
> > 
> > 
> > Long-term note: Sometime after the release(tm) I would like to revise the debugger's buttons to handle exceptions anyway, e.g., we could integrate a "retry" button for DNUs. But until then, I would really appreciate it if we could keep the existing workflows intact. Anyway, IMHO proceeding from a non-resumable exception should either) retry the operation recursively or) ignore the exception and resume right after it. At the moment, I have the feeling that we have reached neither of these directions for #doesNotUnderstand:. See also the analogous behavior in Object >> #at:. :-)
> > 
> > 
> > Thanks in advance!
> > 
> > 
> > Best,
> > 
> > Christoph
> > -------------- next part --------------
> > An HTML attachment was scrubbed...
> > URL: <http://lists.squeakfoundation.org/pipermail/squeak-dev/attachments/20220110/58ec15da/attachment.html>
> > 
> >
> 
> 


More information about the Squeak-dev mailing list