Page 1 of 1

How to make sure timer is run again instead of reset

Posted: 01 Aug 2018, 22:51
by SAbboushi
Is there any way to determine in script whether a timer is enabled or not? Only way I can think of is maybe to use Lexikos' ScriptInfo and extract "Enabled Timers"... or maybe set a global variable in the timer function. Anyone with experience that can suggest what they think is the best way?

I want to make sure that the second SetTimer "MsgBox1", -1 creates a new timer instead of resetting the existing timer, or is it not possible to have two timers that use the same function (i.e. two enabled "MsgBox1" timers)?

Code: Select all

#SingleInstance Force

...
SetTimer "MsgBox1", -1

...

SetTimer "MsgBox1", -1 ; how to make sure this command creates a new timer instead of 

MsgBox1()
{
	static j:=0
	j++
	MsgBox j
}

Re: How to make sure timer is run again instead of reset

Posted: 02 Aug 2018, 00:24
by Helgef
Use boundfuncs (or closures). You can store your active callbacks in an array to keep track of your timers.

Cheers.

Re: How to make sure timer is run again instead of reset

Posted: 02 Aug 2018, 11:31
by SAbboushi
Thanks - I'll look into that.

I'm curious though: documentation says
By default, a given hotkey or hotstring subroutine cannot be run a second time if it is already running. Use #MaxThreadsPerHotkey to change this behavior.
Wondering if this might also apply to functions called by SetTimer (relates to my question
... or is it not possible to have two timers that use the same function (i.e. two enabled "MsgBox1" timers)?

Re: How to make sure timer is run again instead of reset

Posted: 02 Aug 2018, 11:46
by Helgef
It doesn't apply to timers.

There is only one timer for every unique Callback, that is why, if you pass two unique boundfuncs (func('MsgBox1').bind()), you can have two calls to the function. If MsgBox1 is a closure, you can just refer to it by name, then a new closure is created for every call to settimer.

Cheers.

Re: How to make sure timer is run again instead of reset

Posted: 02 Aug 2018, 11:47
by SAbboushi
My conclusion is that it is not possible to have two enabled "MsgBox1" timers because a SetTimer statement either creates a new timer or resets it (a "timer" being identified by the SetTimer's callback parameter which in this case is MsgBox1).

So if I want to have multiple timers that all use the same function callback, I need to learn about bound functions

Re: How to make sure timer is run again instead of reset

Posted: 02 Aug 2018, 11:48
by SAbboushi
Oops - you posted while I was still writing, so I didn't see your post. Thanks - I think you've given me enough info to proceed.

Re: How to make sure timer is run again instead of reset

Posted: 02 Aug 2018, 16:46
by lexikos
The quoted documentation clearly only applies to hotkeys and hotstrings, but timers work the same way:
Reliability: A timer might not be able to run as often as specified under the following conditions:
[...]
2.The timer subroutine itself takes longer than its own period to run, [...]
3.The timer has been interrupted by another thread, [...]
But this has nothing to do with the issue.

Re: How to make sure timer is run again instead of reset

Posted: 04 Aug 2018, 16:14
by SAbboushi
lexikos wrote: 2.The timer subroutine itself takes longer than its own period to run, [...]
I'm assuming that #2 is not relevant to a period of -1 or to my post... but if I'm missing something, would you please point it out?
lexikos wrote:But this has nothing to do with the issue.
I assume you're referring to my question
SAbboushi wrote:Is there any way to determine in script whether a timer is enabled or not?
So is there a better way?

Re: How to make sure timer is run again instead of reset

Posted: 05 Aug 2018, 01:46
by lexikos
I'm assuming that #2 is not relevant to a period of -1 or to my post...
The entire quote is only indirectly relevant; "as often as specified" implies that the timer is a repeating one. It was most likely written before run-once timers were added, but it also fails to take into account that an existing timer's period can be reset to a smaller period (probably because it is less common or less likely to be a problem).

It does not matter what the period is relative to how long the subroutine runs, only whether or not the subroutine is still running when the period expires.
  • If the timer is a repeating one and the subroutine duration exceeds the period, the subroutine will not be relaunched at the expected time because it is still running.
  • If the timer is a repeating one but the subroutine finishes before the period expires, the subroutine may be relaunched.
  • Whether or not the timer is a repeating one, if the period is reset and the new period expires while the subroutine is still running (regardless of how long ago it started), the subroutine will not be relaunched at the expected time.
I assume you're referring to my question
No, I was referring to the titular issue; whether "the second SetTimer "MsgBox1", -1 creates a new timer instead of resetting the existing timer" has nothing to do with whether the subroutine is still running. As I think you had already concluded, if you pass the same function name in both calls (and that function is not a closure), the second call will never create a new timer. (However, if the timer subroutine has already been called and returned, you probably won't notice the difference.)

KeyHistory is the only way to determine which timers are enabled, unless you keep track of them yourself.

Re: How to make sure timer is run again instead of reset

Posted: 08 Aug 2018, 01:18
by SAbboushi
Helgef wrote:There is only one timer for every unique Callback, that is why, if you pass two unique boundfuncs (func('MsgBox1').bind()), you can have two calls to the function. If MsgBox1 is a closure, you can just refer to it by name, then a new closure is created for every call to settimer.
Thanks for that. I think I finally figured out how to do boundfuncs and closures. Since I want to use the same name and have SetTimer always create a new timer, I think that means I need to use a closure, right? The documentation for SetTimersays
If Callback is a closure or an object created by the script (not an actual function, of type Func)...
which leads me to believe there's possibly another way other than using a closure? Since I believe boundfuncs are of type Func that it's referring to something other than boundfuncs?

Is there a way to have the SetTimer call a closure in the global scope instead of within a function as I've done below? I know I can return the closure from the function, but if I do that, I don't see a way to call it in a SetTimer statement:

Code: Select all

msg := "my message!"

displayMsg(msg)
displayMsg("msg")

displayMsg(message)
{
	;Critical
	static x:=-1
	y:= Func("displayMsg1") ; closure

	displayMsg1()
	{
		static x:=0
			Gui := GuiCreate("+AlwaysOnTop +Owner")
			Gui.SetFont("s20") ; font size 20
			Gui.Add("Text",, message . ++x)
			gui.Show
	}
	
	;SetTimer "displayMsg1", (x++, x ? -1000 : -1)
	SetTimer "displayMsg1", -1 ; will always create a new timer instead of updating an enabled "displayMsg1" timer
}

return

Re: How to make sure timer is run again instead of reset

Posted: 08 Aug 2018, 01:30
by Helgef
Boundfuncs are of type Boundfunc. You can return y in your function and pass it to settimer. You can also pass a custom object which handles call.

Cheers.

Re: How to make sure timer is run again instead of reset

Posted: 08 Aug 2018, 02:02
by lexikos
Func("MsgBox1").Bind() is sufficient to give you a unique object.

Re: How to make sure timer is run again instead of reset  Topic is solved

Posted: 30 Oct 2018, 20:27
by SAbboushi
Sorry - daughter's wedding out of country in August... took awhile to get back to this.

Thanks very much for your help Helgef & lexikos. Took me awhile but it's finally making sense!