News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests

Main Menu

Apc_Waitable Timers

Started by six_L, March 23, 2021, 11:41:00 AM

Previous topic - Next topic

six_L

The following example associates an asynchronous procedure call (APC) function, also known as a completion routine, with a waitable timer when the timer is set. The address of the completion routine is the fourth parameter to the SetWaitableTimer function. The fifth parameter is a void pointer that you can use to pass arguments to the completion routine.

The completion routine will be executed by the same thread that called SetWaitableTimer. This thread must be in an alertable state to execute the completion routine. It accomplishes this by calling the SleepEx function, which is an alertable function.

Each thread has an APC queue. If there is an entry in the thread's APC queue at the time that one of the alertable functions is called, the thread is not put to sleep. Instead, the entry is removed from the APC queue and the completion routine is called.

If no entry exists in the APC queue, the thread is suspended until the wait is satisfied. The wait can be satisfied by adding an entry to the APC queue, by a timeout, or by a handle becoming signaled. If the wait is satisfied by an entry in the APC queue, the thread is awakened and the completion routine is called. In this case, the return value of the function is WAIT_IO_COMPLETION.

After the completion routine is executed, the system checks for another entry in the APC queue to process. An alertable function will return only after all APC entries have been processed. Therefore, if entries are being added to the APC queue faster than they can be processed, it is possible that a call to an alertable function will never return. This is especially possible with waitable timers, if the period is shorter than the amount of time required to execute the completion routine.

When you are using a waitable timer with an APC, the thread that sets the timer should not wait on the handle of the timer. By doing so, you would cause the thread to wake up as a result of the timer becoming signaled rather than as the result of an entry being added to the APC queue. As a result, the thread is no longer in an alertable state and the completion routine is not called. In the following code, the call to SleepEx awakens the thread when an entry is added to the thread's APC queue after the timer is set to the signaled state.

option casemap:none
option win64:7

include \UASM64\include\windows.inc

includelib \UASM64\Lib\user32.lib
includelib \UASM64\Lib\kernel32.lib

MYDATA STRUCT
szText byte 32 dup(?)
dwValue         dword ?
MYDATA ENDS

return  MACRO arg
        mov rax, arg
        ret
ENDM

ICO_MAIN equ 1000
DLG_MAIN equ 100
IDC_STAR equ 101
IDC_EXIT equ 102
IDC_OUTPUT equ 103
IDC_CLS equ 105

_SECOND equ 10000000

.data?
hInstance dq ?
hMain dq ?
hTimer HANDLE ?
qwDueTime dq ?
liDueTime LARGE_INTEGER <{?}>
MyData MYDATA <{?}>

.data
szBuffer db 4096 dup (0)

.code

ErrorMessage Proc USES RBX lpCaption:qword
Local lpErrorMessage:QWORD

call GetLastError
lea rbx,lpErrorMessage
invoke FormatMessage, FORMAT_MESSAGE_ALLOCATE_BUFFER or FORMAT_MESSAGE_FROM_SYSTEM, NULL, eax, LANG_NEUTRAL,rbx,0,NULL

invoke MessageBox, 0, lpErrorMessage, lpCaption, MB_OK
invoke LocalFree, lpErrorMessage
ret   

ErrorMessage EndP

TimerAPCProc PROC USES RBX lpArg:LPVOID,dwTimerLowValue:DWORD,dwTimerHighValue:DWORD
Local szBuf[64]:BYTE

invoke RtlZeroMemory,addr szBuf,sizeof szBuf
mov rbx,lpArg
invoke wsprintf,ADDR szBuf, CStr("Message: %s, Value: %d",13,10),addr (MYDATA PTR [rbx]).szText,(MYDATA PTR [rbx]).dwValue
invoke lstrcat,ADDR szBuffer,ADDR szBuf
invoke SetDlgItemText,hMain,IDC_OUTPUT,addr szBuffer
invoke MessageBeep,MB_OK
return  TRUE

TimerAPCProc ENDP

StartThread PROC USES RBX Parameters:QWORD

invoke RtlZeroMemory,addr MyData,sizeof MyData
invoke lstrcat,ADDR MyData.szText,CStr("This is my data")
mov eax,100
mov MyData.dwValue,eax
       
;// Default security attributes
        ;// Create auto-reset timer
        ;// Name of waitable timer
invoke CreateWaitableTimer,NULL,FALSE,CStr("MyTimer")
.if rax != NULL
mov hTimer,rax

;// Create an integer that will be used to signal the timer
;// 5 seconds from now.
xor rdx,rdx
mov rax,-5
mov rcx,_SECOND
mul rcx
mov qwDueTime,rax

;// Copy the relative time into a LARGE_INTEGER.
mov eax,DWORD PTR qwDueTime
mov liDueTime.LowPart,eax
mov eax,DWORD PTR qwDueTime + 4
mov liDueTime.HighPart,eax

;// Handle to the timer object
;// When timer will become signaled
;// Periodic timer interval of 2 seconds
;// Completion routine
;// Argument to the completion routine
;// Do not restore a suspended system
invoke SetWaitableTimer,hTimer,addr liDueTime,2000,offset TimerAPCProc,addr MyData,FALSE
.if rax != NULL
mov ebx,1000
.while MyData.dwValue < ebx
add MyData.dwValue,100
invoke SleepEx,INFINITE,TRUE ;Wait forever. Put thread in an alertable state
.endw
invoke lstrcat,ADDR szBuffer,CStr(">>> GAME OVER! <<<")
invoke SetDlgItemText,hMain,IDC_OUTPUT,addr szBuffer
.else
invoke ErrorMessage,CStr("SetWaitableTimer failed with error")
.endif
invoke CloseHandle,hTimer

.else
invoke ErrorMessage,CStr("CreateWaitableTimer failed with error")
return  FALSE
.endif

return TRUE

StartThread ENDP

_ProcDlgMain proc  hWnd:qword,wMsg:dword,wParam:qword,lParam:qword
Local dwThreadID:DWORD

mov eax,wMsg
.if eax == WM_INITDIALOG
invoke LoadIcon,hInstance,ICO_MAIN
invoke SendMessage,hWnd,WM_SETICON,ICON_BIG,eax
mov rax,hWnd
mov hMain,rax

.elseif eax == WM_COMMAND
mov rax,wParam
.if ax == IDC_EXIT
invoke SendMessage,hWnd,WM_CLOSE,NULL,NULL

.elseif ax == IDC_STAR
invoke CreateThread, NULL, 0, offset StartThread,NULL,NULL, addr dwThreadID
invoke CloseHandle,rax

.elseif ax == IDC_CLS
invoke RtlZeroMemory,addr szBuffer,sizeof szBuffer
invoke SetDlgItemText,hWnd,IDC_OUTPUT,NULL
.endif
.elseif eax == WM_CLOSE
invoke EndDialog,hWnd,NULL
.else
mov rax,FALSE
ret
.endif
mov rax,TRUE
ret

_ProcDlgMain endp

WinMainCRTStartup Proc
invoke GetModuleHandle,NULL
mov hInstance,rax
invoke DialogBoxParam,hInstance,DLG_MAIN,NULL,offset _ProcDlgMain,NULL
invoke ExitProcess,NULL
WinMainCRTStartup Endp


end

#include <\UASM64\include\resource.h>
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
#define ICO_MAIN 1000
#define DLG_MAIN 100
#define IDC_STAR 101
#define IDC_EXIT 102
#define IDC_OUTPUT 103
#define IDC_CLS 105
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
DLG_MAIN DIALOG 0, 0, 357, 164
STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
CAPTION "Apc_Waitable Timers"
FONT 10, "Consolas"
{
EDITTEXT IDC_OUTPUT, 2, 2, 352, 145,ES_MULTILINE | ES_AUTOVSCROLL | ES_READONLY | WS_BORDER | WS_TABSTOP | WS_VSCROLL
PUSHBUTTON "Start", IDC_STAR,2, 148, 40, 14
PUSHBUTTON "Cls", IDC_CLS,46, 148, 40, 14
PUSHBUTTON "Exit", IDC_EXIT, 314, 148, 40, 14, WS_TABSTOP
}
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
ICO_MAIN ICON "MICON.ico"
Say you, Say me, Say the codes together for ever.

jj2007

It works fine - interesting, thanks :thumbsup:
For some reason, it does not work as 32-bit code, though :sad: