Problems with throw in __Delete meta function

Report problems with documented functionality
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Problems with throw in __Delete meta function

24 Jun 2017, 05:09

There is inconsistent behaviour of the error message generated by the throw command when used in the __Delete meta function.
Example 1, no error message, (unexpected)

Code: Select all

new a
class a {
	__Delete(){	
		throw
	}
}
Example 2, error message is shown, (expected, hadn't it been for the above)

Code: Select all

new a
return
class a {
	__Delete(){	
		throw
	}
}
Example 3, error message is shown after the msgbox has shown, (even less expected)

Code: Select all

new a
msgbox
return
class a {
	__Delete(){	
		throw
	}
}
Note, one can still catch the errors using try - catch in all of the above cases, either by try new a or try throw.

Tested on 64U 1.1.26.00, win7

☕
lexikos
Posts: 9583
Joined: 30 Sep 2013, 04:07
Contact:

Re: Problems with throw in __Delete meta function

24 Jun 2017, 19:11

It's not meaningful or safe to throw an exception from a destructor. Even if you throw an exception, if the object has no references it must continue to be deleted. Other objects which are being released (such as while a function's local variables are being freed or the script exits) must continue to be released and possibly deleted, calling more destructors. This process cannot be aborted (which would be what an exception should do), and in fact, the thread may already be aborting due to a thrown exception.

The reason for the strange behaviour is that destructors are never called directly by the script; they are called indirectly by the object's Release() implementation. Checking for this condition after every call to Release() is impractical, especially because of the reasons I noted above - Release() is called during cleanup, which cannot be aborted.

If an exception is thrown at an unexpected point, it will not be detected until the next point at which the exception status is checked. Usually that's when the thread exits or (in v1) you call a built-in function (not a command).

In example 1, the script reaches the Exit which is implied at the end of the script, and because it is not persistent, the entire program immediately exits.

In examples 2 and 3, return ends the thread and causes the exception status to be checked before the program exits.

Something similar happens with fast-mode callbacks created by RegisterCallback. When the callback function throws, AutoHotkey only has control up to the entry point of the callback. It has no control over the code which called the callback. When the callback returns to script, the script may or may not check the exception status and then propagate the exception. For instance, if you call a callback via DllCall, DllCall will detect the exception and cause the thread to abort (try aside), but the called function may do other things (such as call more callbacks) between the exception being thrown and control returning to DllCall.

Note that if __delete is being called after an exception is thrown (i.e. while the thread is aborting), any exception thrown by __delete is ignored .

__Delete should probably report all exceptions immediately (acting as if called from a new thread).

For callbacks it is less clear. In some cases the current behaviour allows the script to correctly abort the whole thread, instead of just one callback. "Fast" mode is supposed to mean "don't start a new thread".
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: Problems with throw in __Delete meta function

25 Jun 2017, 09:20

Thank you for your reply. Lots of good info.
__Delete should probably report all exceptions immediately (acting as if called from a new thread).
To further motivate this, consider a more accurate representation of what I was doing when I got problems with this in the first place,

Code: Select all

loop 1000 {
	tooltip % "Everything seems fine at line: " A_Index
	if (A_Index=400){
		new a
		;abs(0) ; compare to when this is uncommented. (lol)
	}
}
Msgbox Done!
return
class a{
	__Delete(){
		a()
	}
}
; Some function library
a(){
	b()
}
b(){
	c()
}
c(){
	d()
}
d(){
	throw Exception("Something happened",-3)
}

I will continue as if this post was in the ask for help section. Regarding meaningfulness (word?) of throwing in __Delete, the program does it, eg

Code: Select all

__Delete(){
	gui, %invalidHandle%: Destroy
}
The error message says the thread will exit, but it seems it only makes the __Delete() function return/stop. for example,

Code: Select all

f::
	SendMode, Play
	; gui %invalidHandle%: destroy ; <- This will exit the thread (expected)
	new a
	msgbox
return
class a{
	__Delete(){
		msgbox, % A_SendMode
		gui %invalidHandle%: destroy ; <- this says it will exit the thread, expected, but which thread is it exiting?
	}
}
(where the ask-for-help is in the last comment)
Thanks for your time. Cheers.

Return to “Bug Reports”

Who is online

Users browsing this forum: niCode and 20 guests