I'm looking for advice on how to achieve rather precise delay timing (ballpark several milliseconds) for my key sends but do so in a way that doesn't hurt concurrency and responsiveness. Let me give some context.
I am writing scripts for games to automate small sequences of input so the the actual time the script is performing useful work is rather small, 5ms at most, the rest is sleep intervals to space the send commands apart. While sleep is in effect for one hotkey others can be launched and since the workload per hotkey is small AHK's concurrency model works well for my case. The issue is that AHK's Sleep command, which I used initially, is terribly inaccurate and can give a spread of as much as 40 ms especially at lower sleep intervals. So I switched to using DllCall("Sleep", "UInt", NrMs) from the Win32 API. It is affected by the current system timer resolution so it gives 1-2ms accuracy when the system timer resolution is set to 1ms, which is usually in effect while a game is running anyway because it requests it but otherwise I can always set it manually in the script using DllCall("NTDLL\NtSetTimerResolution", ...) or DllCall("Winmm\timeBeginPeriod", ...).
However after I started using longer sleep intervals I realized that DllCall("Sleep", "UInt", ...) is actually effectively a blocking call for AHK, because I somewhat foolishly thought that AHK operated like a thread pool with parallelism set to 1 and it would see that the thread is in "waiting state" and schedule another one from it's queue (thread is such an unfortunate choice of a name). But it really operates like a single thread in an event loop backed up by a task queue. So it doesn't detect that DllCall("Sleep", "UInt", ...) intends to sleep and doesn't switch tasks but can do so in case of it's own Sleep, NrMillis command which it controls, so it makes sense.
As I add more hotkeys I can't afford to use Win32's Sleep API especially for longer sleep intervals because of responsiveness concerns but I also need the superior timing it provides. So what are my options here?
I can't use a busy wait loop like below
Code: Select all
msTimestampStart := getQPCTimeStampMilliseconds()
while(getQPCTimeStampMilliseconds() - msTimestampStart < 500) {
Sleep -1
}
The only option I can think of with AutoHotKey 1.1.x is to split the DllCall("Sleep", "UInt", ...) with larger sleep intervals into smaller ones like this and manually force an interrupt. This way the script might be able to squeeze another hotkey or timer in between.
Code: Select all
interruptibleWin32Sleep(millis, uninterruptibleSleepStep) {
millis := Ceil(Abs(millis))
uninterruptibleSleepStep := Ceil(Abs(uninterruptibleSleepStep))
if(millis > 1 && uninterruptibleSleepStep > 1) {
msTimestampStart := getQPCTimeStampMilliseconds()
elapsedDelay := 0
loop {
nextUninterruptibleSleepStep := Min(Ceil(millis - elapsedDelay), uninterruptibleSleepStep)
DllCall("Sleep", "UInt", nextUninterruptibleSleepStep)
Sleep -1 ; force pending interruption
elapsedDelay := getQPCTimeStampMilliseconds() - msTimestampStart
} Until elapsedDelay >= millis
}
}
Alternatively maybe at this point I should start looking into using AutoHotKey_H or AutoHotKey 2 (or are they the same thing?!). Doing a very quick search of the forum I learned that they have some kind of multithreading capabilities so I could potentially afford to have a thread blocked while others are running? Would I be able to solve this issue easier by migrating to v2?
Thanks!