Skip to content

Commit

Permalink
Add event APIs (#12)
Browse files Browse the repository at this point in the history
Add event APIs and test
  • Loading branch information
nigriMSFT authored May 1, 2024
1 parent 414c176 commit 3798f6c
Show file tree
Hide file tree
Showing 10 changed files with 371 additions and 1 deletion.
198 changes: 198 additions & 0 deletions inc/cxplat_posix.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ extern "C" {

#define UNREFERENCED_PARAMETER(P) (void)(P)

#ifndef FALSE
#define FALSE 0
#define TRUE 1
#endif

typedef unsigned char BOOLEAN;

inline
Expand Down Expand Up @@ -400,6 +405,199 @@ CxPlatSleep(

#define CxPlatSchedulerYield() sched_yield()

//
// Event Interfaces
//

typedef struct CXPLAT_EVENT {

//
// Mutex and condition. The alignas is important, as the perf tanks
// if the event is not aligned.
//
alignas(16) pthread_mutex_t Mutex;
pthread_cond_t Cond;

//
// Denotes if the event object is in signaled state.
//
BOOLEAN Signaled;

//
// Denotes if the event object should be auto reset after it's signaled.
//
BOOLEAN AutoReset;

} CXPLAT_EVENT;

inline
void
CxPlatEventInitialize(
_Out_ CXPLAT_EVENT* Event,
_In_ BOOLEAN ManualReset,
_In_ BOOLEAN InitialState
)
{
pthread_condattr_t Attr;
int Result;

CxPlatZeroMemory(&Attr, sizeof(Attr));
Event->AutoReset = !ManualReset;
Event->Signaled = InitialState;

Result = pthread_mutex_init(&Event->Mutex, NULL);
CXPLAT_FRE_ASSERT(Result == 0);
Result = pthread_condattr_init(&Attr);
CXPLAT_FRE_ASSERT(Result == 0);
#if defined(CX_PLATFORM_LINUX)
Result = pthread_condattr_setclock(&Attr, CLOCK_MONOTONIC);
CXPLAT_FRE_ASSERT(Result == 0);
#endif // CX_PLATFORM_LINUX
Result = pthread_cond_init(&Event->Cond, &Attr);
CXPLAT_FRE_ASSERT(Result == 0);
Result = pthread_condattr_destroy(&Attr);
CXPLAT_FRE_ASSERT(Result == 0);
}

inline
void
CxPlatInternalEventUninitialize(
_Inout_ CXPLAT_EVENT* Event
)
{
int Result;

Result = pthread_cond_destroy(&Event->Cond);
CXPLAT_FRE_ASSERT(Result == 0);
Result = pthread_mutex_destroy(&Event->Mutex);
CXPLAT_FRE_ASSERT(Result == 0);
}

inline
void
CxPlatInternalEventSet(
_Inout_ CXPLAT_EVENT* Event
)
{
int Result;

Result = pthread_mutex_lock(&Event->Mutex);
CXPLAT_FRE_ASSERT(Result == 0);

Event->Signaled = true;

//
// Signal the condition while holding the lock for predictable scheduling,
// better performance and removing possibility of use after free for the
// condition.
//

Result = pthread_cond_broadcast(&Event->Cond);
CXPLAT_FRE_ASSERT(Result == 0);

Result = pthread_mutex_unlock(&Event->Mutex);
CXPLAT_FRE_ASSERT(Result == 0);
}

inline
void
CxPlatInternalEventReset(
_Inout_ CXPLAT_EVENT* Event
)
{
int Result;

Result = pthread_mutex_lock(&Event->Mutex);
CXPLAT_FRE_ASSERT(Result == 0);
Event->Signaled = false;
Result = pthread_mutex_unlock(&Event->Mutex);
CXPLAT_FRE_ASSERT(Result == 0);
}

inline
void
CxPlatInternalEventWaitForever(
_Inout_ CXPLAT_EVENT* Event
)
{
int Result;

Result = pthread_mutex_lock(&Event->Mutex);
CXPLAT_FRE_ASSERT(Result == 0);

//
// Spurious wake ups from pthread_cond_wait can occur. So the function needs
// to be called in a loop until the predicate 'Signaled' is satisfied.
//

while (!Event->Signaled) {
Result = pthread_cond_wait(&Event->Cond, &Event->Mutex);
CXPLAT_FRE_ASSERT(Result == 0);
}

if(Event->AutoReset) {
Event->Signaled = false;
}

Result = pthread_mutex_unlock(&Event->Mutex);
CXPLAT_FRE_ASSERT(Result == 0);
}

inline
BOOLEAN
CxPlatInternalEventWaitWithTimeout(
_Inout_ CXPLAT_EVENT* Event,
_In_ uint32_t TimeoutMs
)
{
BOOLEAN WaitSatisfied = FALSE;
struct timespec Ts = {0, 0};
int Result;

CXPLAT_DBG_ASSERT(TimeoutMs != UINT32_MAX);

//
// Get absolute time.
//

CxPlatGetAbsoluteTime(TimeoutMs, &Ts);

Result = pthread_mutex_lock(&Event->Mutex);
CXPLAT_FRE_ASSERT(Result == 0);

while (!Event->Signaled) {

Result = pthread_cond_timedwait(&Event->Cond, &Event->Mutex, &Ts);

if (Result == ETIMEDOUT) {
WaitSatisfied = FALSE;
goto Exit;
}

CXPLAT_DBG_ASSERT(Result == 0);
UNREFERENCED_PARAMETER(Result);
}

if (Event->AutoReset) {
Event->Signaled = FALSE;
}

WaitSatisfied = TRUE;

Exit:

Result = pthread_mutex_unlock(&Event->Mutex);
CXPLAT_FRE_ASSERT(Result == 0);

return WaitSatisfied;
}

#define CxPlatEventUninitialize(Event) CxPlatInternalEventUninitialize(&Event)
#define CxPlatEventSet(Event) CxPlatInternalEventSet(&Event)
#define CxPlatEventReset(Event) CxPlatInternalEventReset(&Event)
#define CxPlatEventWaitForever(Event) CxPlatInternalEventWaitForever(&Event)
#define CxPlatEventWaitWithTimeout(Event, TimeoutMs) CxPlatInternalEventWaitWithTimeout(&Event, TimeoutMs)

//
// Crypto Interfaces
//
Expand Down
34 changes: 34 additions & 0 deletions inc/cxplat_winkernel.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ typedef UINT16 uint16_t;
typedef UINT32 uint32_t;
typedef UINT64 uint64_t;

#define UINT8_MAX 0xffui8
#define UINT16_MAX 0xffffui16
#define UINT32_MAX 0xffffffffui32
#define UINT64_MAX 0xffffffffffffffffui64

//
// Status Codes
//
Expand Down Expand Up @@ -310,6 +315,35 @@ CxPlatSleep(

#define CxPlatSchedulerYield() // no-op

//
// Event Interfaces
//

typedef KEVENT CXPLAT_EVENT;

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

#define CxPlatEventInitialize(Event, ManualReset, InitialState) \
KeInitializeEvent(Event, ManualReset ? NotificationEvent : SynchronizationEvent, InitialState)
#define CxPlatEventUninitialize(Event) UNREFERENCED_PARAMETER(Event)
#define CxPlatEventSet(Event) KeSetEvent(&(Event), IO_NO_INCREMENT, FALSE)
#define CxPlatEventReset(Event) KeResetEvent(&(Event))
#define CxPlatEventWaitForever(Event) \
KeWaitForSingleObject(&(Event), Executive, KernelMode, FALSE, NULL)
#define CxPlatEventWaitWithTimeout(Event, TimeoutMs) \
(STATUS_SUCCESS == CxPlatInternalEventWaitWithTimeout(&Event, TimeoutMs))

//
// Crypto Interfaces
//
Expand Down
39 changes: 39 additions & 0 deletions inc/cxplat_winuser.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,20 @@ CxPlatLogAssert(
#define CXPLAT_DBG_ASSERTMSG(_exp, _msg) CXPLAT_ASSERT_NOOP(_exp, CXPLAT_WIDE_STRING(_msg))
#endif

//
// Wrapper functions
//

//
// CloseHandle has an incorrect SAL annotation, so call through a wrapper.
//
_IRQL_requires_max_(PASSIVE_LEVEL)
inline
void
CxPlatCloseHandle(_Pre_notnull_ HANDLE Handle) {
CloseHandle(Handle);
}

//
// Allocation/Memory Interfaces
//
Expand Down Expand Up @@ -305,6 +319,31 @@ CxPlatTimeAtOrBefore32(

#define CxPlatSchedulerYield() Sleep(0)


//
// Event Interfaces
//

typedef HANDLE CXPLAT_EVENT;

#define CxPlatEventInitialize(Event, ManualReset, InitialState) \
*(Event) = CreateEvent(NULL, ManualReset, InitialState, NULL); \
CXPLAT_DBG_ASSERT(*Event != NULL)
#define CxPlatEventUninitialize(Event) CxPlatCloseHandle(Event)
#define CxPlatEventSet(Event) SetEvent(Event)
#define CxPlatEventReset(Event) ResetEvent(Event)
#define CxPlatEventWaitForever(Event) WaitForSingleObject(Event, INFINITE)
inline
BOOLEAN
CxPlatEventWaitWithTimeout(
_In_ CXPLAT_EVENT Event,
_In_ uint32_t TimeoutMs
)
{
CXPLAT_DBG_ASSERT(TimeoutMs != UINT32_MAX);
return WAIT_OBJECT_0 == WaitForSingleObject(Event, TimeoutMs);
}

//
// Crypto Interfaces
//
Expand Down
33 changes: 33 additions & 0 deletions src/lib/inline.c
Original file line number Diff line number Diff line change
Expand Up @@ -135,3 +135,36 @@ CxPlatTimeAtOrBefore32(
_In_ uint32_t T1,
_In_ uint32_t T2
);

void
CxPlatEventInitialize(
_Out_ CXPLAT_EVENT* Event,
_In_ BOOLEAN ManualReset,
_In_ BOOLEAN InitialState
);

void
CxPlatInternalEventUninitialize(
_Inout_ CXPLAT_EVENT* Event
);

void
CxPlatInternalEventSet(
_Inout_ CXPLAT_EVENT* Event
);

void
CxPlatInternalEventReset(
_Inout_ CXPLAT_EVENT* Event
);

void
CxPlatInternalEventWaitForever(
_Inout_ CXPLAT_EVENT* Event
);

BOOLEAN
CxPlatInternalEventWaitWithTimeout(
_Inout_ CXPLAT_EVENT* Event,
_In_ uint32_t TimeoutMs
);
11 changes: 10 additions & 1 deletion src/test/CxPlatTests.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ void CxPlatTestMemoryFailureInjection();

void CxPlatTestTimeBasic();

//
// Event Tests
//

void CxPlatTestEventBasic();

//
// Platform Specific Functions
//
Expand Down Expand Up @@ -100,4 +106,7 @@ static const GUID CXPLAT_TEST_DEVICE_INSTANCE =
#define IOCTL_CXPLAT_RUN_TIME_BASIC \
CXPLAT_CTL_CODE(3, METHOD_BUFFERED, FILE_WRITE_DATA)

#define CXPLAT_MAX_IOCTL_FUNC_CODE 3
#define IOCTL_CXPLAT_RUN_EVENT_BASIC \
CXPLAT_CTL_CODE(4, METHOD_BUFFERED, FILE_WRITE_DATA)

#define CXPLAT_MAX_IOCTL_FUNC_CODE 4
9 changes: 9 additions & 0 deletions src/test/bin/cxplat_gtest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,15 @@ TEST(TimeSuite, Basic) {
}
}

TEST(EventSuite, Basic) {
TestLogger Logger("CxPlatTestEventBasic");
if (TestingKernelMode) {
ASSERT_TRUE(DriverClient.Run(IOCTL_CXPLAT_RUN_EVENT_BASIC));
} else {
CxPlatTestEventBasic();
}
}

int main(int argc, char** argv) {
for (int i = 0; i < argc; ++i) {
if (strcmp("--kernel", argv[i]) == 0) {
Expand Down
Loading

0 comments on commit 3798f6c

Please sign in to comment.