Skip to content

Commit

Permalink
Add thread wait with timeout API (#28)
Browse files Browse the repository at this point in the history
Adding this functionality to Windows platforms since the XDP test code
waits on threads with a timeout.
Added test case.
  • Loading branch information
nigriMSFT authored May 24, 2024
1 parent c1230c6 commit b159a2e
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 12 deletions.
2 changes: 1 addition & 1 deletion inc/cxplat_posix.h
Original file line number Diff line number Diff line change
Expand Up @@ -500,7 +500,7 @@ CxPlatThreadDelete(
);

void
CxPlatThreadWait(
CxPlatThreadWaitForever(
_Inout_ CXPLAT_THREAD* Thread
);

Expand Down
17 changes: 16 additions & 1 deletion inc/cxplat_winkernel.h
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,19 @@ typedef struct _ETHREAD *CXPLAT_THREAD;

#define CXPLAT_THREAD_RETURN(Status) PsTerminateSystemThread(Status)

inline
NTSTATUS
CxPlatInternalThreadWaitWithTimeout(
_In_ CXPLAT_THREAD* Thread,
_In_ uint32_t TimeoutMs
)
{
LARGE_INTEGER Timeout100Ns;
CXPLAT_DBG_ASSERT(TimeoutMs != UINT32_MAX);
Timeout100Ns.QuadPart = -1 * UInt32x32To64(TimeoutMs, 10000);
return KeWaitForSingleObject(*(Thread), Executive, KernelMode, FALSE, &Timeout100Ns);
}

inline
CXPLAT_STATUS
CxPlatThreadCreate(
Expand Down Expand Up @@ -520,13 +533,15 @@ CxPlatThreadCreate(
return Status;
}
#define CxPlatThreadDelete(Thread) ObDereferenceObject(*(Thread))
#define CxPlatThreadWait(Thread) \
#define CxPlatThreadWaitForever(Thread) \
KeWaitForSingleObject( \
*(Thread), \
Executive, \
KernelMode, \
FALSE, \
NULL)
#define CxPlatThreadWaitWithTimeout(Thread, TimeoutMs) \
(STATUS_SUCCESS == CxPlatInternalThreadWaitWithTimeout(Thread, TimeoutMs))
typedef ULONG_PTR CXPLAT_THREAD_ID;
#define CxPlatCurThreadID() ((CXPLAT_THREAD_ID)PsGetCurrentThreadId())

Expand Down
12 changes: 11 additions & 1 deletion inc/cxplat_winuser.h
Original file line number Diff line number Diff line change
Expand Up @@ -538,7 +538,17 @@ CxPlatThreadCreate(
return CXPLAT_STATUS_SUCCESS;
}
#define CxPlatThreadDelete(Thread) CxPlatCloseHandle(*(Thread))
#define CxPlatThreadWait(Thread) WaitForSingleObject(*(Thread), INFINITE)
#define CxPlatThreadWaitForever(Thread) WaitForSingleObject(*(Thread), INFINITE)
inline
BOOLEAN
CxPlatThreadWaitWithTimeout(
_In_ CXPLAT_THREAD* Thread,
_In_ uint32_t TimeoutMs
)
{
CXPLAT_DBG_ASSERT(TimeoutMs != UINT32_MAX);
return WAIT_OBJECT_0 == WaitForSingleObject(*Thread, TimeoutMs);
}
typedef uint32_t CXPLAT_THREAD_ID;
#define CxPlatCurThreadID() GetCurrentThreadId()

Expand Down
2 changes: 1 addition & 1 deletion src/lib/cxplat_posix.c
Original file line number Diff line number Diff line change
Expand Up @@ -482,7 +482,7 @@ CxPlatThreadDelete(
}

void
CxPlatThreadWait(
CxPlatThreadWaitForever(
_Inout_ CXPLAT_THREAD* Thread
)
{
Expand Down
8 changes: 7 additions & 1 deletion src/test/CxPlatTests.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ void CxPlatTestProcBasic();
//

void CxPlatTestThreadBasic();
#if defined(CX_PLATFORM_WINUSER) || defined(CX_PLATFORM_WINKERNEL)
void CxPlatTestThreadWaitTimeout();
#endif

//
// Platform Specific Functions
Expand Down Expand Up @@ -127,4 +130,7 @@ static const GUID CXPLAT_TEST_DEVICE_INSTANCE =
#define IOCTL_CXPLAT_RUN_THREAD_BASIC \
CXPLAT_CTL_CODE(6, METHOD_BUFFERED, FILE_WRITE_DATA)

#define CXPLAT_MAX_IOCTL_FUNC_CODE 6
#define IOCTL_CXPLAT_RUN_THREAD_WAIT_TIMEOUT \
CXPLAT_CTL_CODE(7, METHOD_BUFFERED, FILE_WRITE_DATA)

#define CXPLAT_MAX_IOCTL_FUNC_CODE 7
11 changes: 11 additions & 0 deletions src/test/bin/cxplat_gtest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,17 @@ TEST(ThreadSuite, Basic) {
}
}

#if defined(CX_PLATFORM_WINUSER) || defined(CX_PLATFORM_WINKERNEL)
TEST(ThreadSuite, WithTimeout) {
TestLogger Logger("CxPlatTestThreadWaitTimeout");
if (TestingKernelMode) {
ASSERT_TRUE(DriverClient.Run(IOCTL_CXPLAT_RUN_THREAD_WAIT_TIMEOUT));
} else {
CxPlatTestThreadWaitTimeout();
}
}
#endif

int main(int argc, char** argv) {
for (int i = 0; i < argc; ++i) {
if (strcmp("--kernel", argv[i]) == 0) {
Expand Down
5 changes: 5 additions & 0 deletions src/test/bin/winkernel/control.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,7 @@ size_t CXPLAT_IOCTL_BUFFER_SIZES[] =
0,
0,
0,
0,
};

static_assert(
Expand Down Expand Up @@ -498,6 +499,10 @@ CxPlatTestCtlEvtIoDeviceControl(
CxPlatTestCtlRun(CxPlatTestThreadBasic());
break;

case IOCTL_CXPLAT_RUN_THREAD_WAIT_TIMEOUT:
CxPlatTestCtlRun(CxPlatTestThreadWaitTimeout());
break;

default:
Status = STATUS_NOT_IMPLEMENTED;
break;
Expand Down
60 changes: 53 additions & 7 deletions src/test/lib/ThreadTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,24 @@

#include "precomp.h"

#define INITIAL_CONTEXT_VALUE ((CXPLAT_THREAD_ID)-1)
typedef struct THREAD_CONTEXT {
CXPLAT_THREAD_ID ThreadId;
uint32_t DelayMs;
} THREAD_CONTEXT;

CXPLAT_THREAD_CALLBACK(ThreadFn, Context)
#define INITIAL_THREAD_ID_VALUE ((CXPLAT_THREAD_ID)-1)

CXPLAT_THREAD_CALLBACK(ThreadFn, Ctx)
{
TEST_EQUAL_GOTO(*(CXPLAT_THREAD_ID*)Context, INITIAL_CONTEXT_VALUE);
THREAD_CONTEXT* Context = (THREAD_CONTEXT*)Ctx;

if (Context->DelayMs > 0) {
CxPlatSleep(Context->DelayMs);
}

*(CXPLAT_THREAD_ID*)Context = CxPlatCurThreadID();
TEST_EQUAL_GOTO(Context->ThreadId, INITIAL_THREAD_ID_VALUE);

*(CXPLAT_THREAD_ID*)Ctx = CxPlatCurThreadID();

Failure:

Expand All @@ -28,7 +39,10 @@ void CxPlatTestThreadBasic()
{
CXPLAT_THREAD Thread;
CXPLAT_THREAD_CONFIG ThreadConfig;
CXPLAT_THREAD_ID Context = INITIAL_CONTEXT_VALUE;
THREAD_CONTEXT Context;

Context.ThreadId = INITIAL_THREAD_ID_VALUE;
Context.DelayMs = 0;

ThreadConfig.Flags = 0;
ThreadConfig.IdealProcessor = 0;
Expand All @@ -38,9 +52,41 @@ void CxPlatTestThreadBasic()

TEST_CXPLAT(CxPlatThreadCreate(&ThreadConfig, &Thread));

CxPlatThreadWait(&Thread);
CxPlatThreadWaitForever(&Thread);

TEST_TRUE_GOTO(Context.ThreadId != INITIAL_THREAD_ID_VALUE);

Failure:

CxPlatThreadDelete(&Thread);
}

#if defined(CX_PLATFORM_WINUSER) || defined(CX_PLATFORM_WINKERNEL)
void CxPlatTestThreadWaitTimeout()
{
CXPLAT_THREAD Thread;
CXPLAT_THREAD_CONFIG ThreadConfig;
THREAD_CONTEXT Context;

Context.ThreadId = INITIAL_THREAD_ID_VALUE;
Context.DelayMs = 1000;

ThreadConfig.Flags = 0;
ThreadConfig.IdealProcessor = 0;
ThreadConfig.Name = "CxPlatTestThreadWaitTimeout";
ThreadConfig.Callback = ThreadFn;
ThreadConfig.Context = (void*)&Context;

TEST_CXPLAT(CxPlatThreadCreate(&ThreadConfig, &Thread));

TEST_FALSE_GOTO(CxPlatThreadWaitWithTimeout(&Thread, 50));

TEST_TRUE(Context != INITIAL_CONTEXT_VALUE);
TEST_TRUE_GOTO(CxPlatThreadWaitWithTimeout(&Thread, 5000));

TEST_TRUE_GOTO(Context.ThreadId != INITIAL_THREAD_ID_VALUE);

Failure:

CxPlatThreadDelete(&Thread);
}
#endif

0 comments on commit b159a2e

Please sign in to comment.