WSL/test/linux/unit_tests/eventfd.c

290 lines
7.5 KiB
C
Raw Permalink Normal View History

/*++
Copyright (c) Microsoft. All rights reserved.
Module Name:
EventFd.c
Abstract:
This file is a eventfd test.
--*/
#include "lxtcommon.h"
#include "unittests.h"
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/epoll.h>
#include <sys/eventfd.h>
#define LXT_NAME "EventFd"
#define DEFAULT_USLEEP 5000
int EventFdVariationReadWrite(PLXT_ARGS Args);
//
// Global constants
//
static const LXT_VARIATION g_LxtVariations[] = {{"EventFdVariation - read, write", EventFdVariationReadWrite}};
int EventfdTestEntry(int Argc, char* Argv[])
/*++
--*/
{
LXT_ARGS Args;
int Result;
LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));
LXT_SYNCHRONIZATION_POINT_INIT();
LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));
ErrorExit:
LXT_SYNCHRONIZATION_POINT_DESTROY();
LxtUninitialize();
return !LXT_SUCCESS(Result);
}
int EventFdVariationReadWrite(PLXT_ARGS Args)
/*++
--*/
{
pid_t ChildPid;
struct epoll_event EpollEvent;
int EpollFd;
int Fd;
int Index;
fd_set ReadFds;
int Result;
ssize_t Size;
int Status;
struct timeval Timeout;
uint64_t Value;
uint64_t Values[] = {1, 2, 10, 0xFFFFFFFF, 0xFFFFFFFFFFFFFFFE};
struct
{
uint64_t Value;
char Buffer[32];
} LargeValue;
EpollFd = -1;
LXT_SYNCHRONIZATION_POINT_START();
//
// FIXME: Add 0 varaition
//
//
// Write the values to the event and read them back out single threaded.
//
LxtCheckErrno(Fd = eventfd(0, 0));
for (Index = 0; Index < LXT_COUNT_OF(Values); Index += 1)
{
//
// Verify that the event is not ready for read (value is zero).
//
memset(&Timeout, 0, sizeof(Timeout));
FD_ZERO(&ReadFds);
FD_SET(Fd, &ReadFds);
LxtCheckErrno(select((Fd + 1), &ReadFds, NULL, NULL, &Timeout));
LxtCheckEqual(Result, 0, "%d");
Value = Values[Index];
Size = write(Fd, &Value, sizeof(Value));
LxtCheckEqual(Size, sizeof(Value), "%lld");
LxtCheckEqual(Value, Values[Index], "%llu");
//
// Verify that the event is ready for read (value is NOT zero).
//
memset(&Timeout, 0, sizeof(Timeout));
FD_ZERO(&ReadFds);
FD_SET(Fd, &ReadFds);
LxtCheckErrno(select((Fd + 1), &ReadFds, NULL, NULL, &Timeout));
LxtCheckEqual(Result, 1, "%d");
//
// Check again to verify nothing has changed.
//
memset(&Timeout, 0, sizeof(Timeout));
FD_ZERO(&ReadFds);
FD_SET(Fd, &ReadFds);
LxtCheckErrno(select((Fd + 1), &ReadFds, NULL, NULL, &Timeout));
LxtCheckEqual(Result, 1, "%d");
Value = 0;
Size = read(Fd, &Value, sizeof(Value));
LxtCheckEqual(Size, sizeof(Value), "%lld");
LxtCheckEqual(Value, Values[Index], "%llu");
}
//
// Verify that the event is not ready for read (value is zero).
//
memset(&Timeout, 0, sizeof(Timeout));
FD_ZERO(&ReadFds);
FD_SET(Fd, &ReadFds);
LxtCheckErrno(select((Fd + 1), &ReadFds, NULL, NULL, &Timeout));
LxtCheckEqual(Result, 0, "%d");
//
// Write the values to the event and read them back out on another thread.
//
LxtCheckErrno(ChildPid = fork());
if (ChildPid == 0)
{
LxtCheckErrno(EpollFd = epoll_create(1));
EpollEvent.events = EPOLLIN | EPOLLET;
EpollEvent.data.fd = Fd;
LxtCheckErrno(epoll_ctl(EpollFd, EPOLL_CTL_ADD, Fd, &EpollEvent));
}
LXT_SYNCHRONIZATION_POINT();
for (Index = 0; Index < LXT_COUNT_OF(Values); Index += 1)
{
if (ChildPid == 0)
{
//
// Ignore the first value completely and don't read the second
// value to verify that epoll edge-triggered behavior is working as
// expected.
//
if (Index > 0)
{
if (Index == 1)
{
//
// Since the first write was ignored, need to make sure
// that the second has completed so that the edge-triggered
// epoll logic works as expected instead of racing with the
// next write.
//
LXT_SYNCHRONIZATION_POINT();
}
LxtLogInfo("Waiting from child thread...");
LxtCheckErrno(epoll_wait(EpollFd, &EpollEvent, 1, -1));
LxtCheckEqual(Result, 1, "%d") LxtCheckEqual(EpollEvent.data.fd, Fd, "%d");
//
// The epoll should be blocked.
//
LxtCheckErrno(epoll_wait(EpollFd, &EpollEvent, 1, 0));
LxtCheckEqual(Result, 0, "%d");
//
// Skip the first two values to verify epoll edge-triggered
// behavior and then start verifying the value data.
//
if (Index > 1)
{
Value = 0;
Size = read(Fd, &Value, sizeof(Value));
LxtCheckEqual(Size, sizeof(Value), "%lld");
if (Index == 2)
{
LxtCheckEqual(Value, (Values[0] + Values[1] + Values[2]), "%llu");
}
else
{
LxtCheckEqual(Value, Values[Index], "%llu");
}
}
//
// The epoll should still be blocked.
//
LxtCheckErrno(epoll_wait(EpollFd, &EpollEvent, 1, 0));
LxtCheckEqual(Result, 0, "%d")
}
}
else
{
LxtLogInfo("Writing 0x%llx from parent thread...", Values[Index]);
Value = Values[Index];
Size = write(Fd, &Value, sizeof(Value));
LxtCheckEqual(Size, sizeof(Value), "%lld");
LxtCheckEqual(Value, Values[Index], "%llu");
if (Index == 1)
{
LXT_SYNCHRONIZATION_POINT();
}
}
LXT_SYNCHRONIZATION_POINT();
}
if (ChildPid == 0)
{
goto ErrorExit;
}
//
// Read and write out with a buffer larger than the default.
//
memset(&LargeValue, 0, sizeof(LargeValue));
LargeValue.Value = 1;
Size = write(Fd, &LargeValue, sizeof(LargeValue));
LxtCheckEqual(Size, sizeof(LargeValue.Value), "%lld");
LxtCheckEqual(LargeValue.Value, 1, "%llu");
LargeValue.Value = 0;
Size = read(Fd, &LargeValue, sizeof(LargeValue));
LxtCheckEqual(Size, sizeof(LargeValue.Value), "%lld");
LxtCheckEqual(LargeValue.Value, 1, "%llu");
//
// Check the error code for writing the maximum value.
//
Value = 0xFFFFFFFFFFFFFFFF;
LxtCheckErrnoFailure(write(Fd, &Value, sizeof(Value)), EINVAL);
//
// Check the error code for reading and writing a size of 0.
//
LxtCheckErrnoFailure(read(Fd, &Value, 0), EINVAL);
LxtCheckErrnoFailure(write(Fd, &Value, 0), EINVAL);
Result = LXT_RESULT_SUCCESS;
ErrorExit:
if (Fd > 0)
{
close(Fd);
}
if (EpollFd > 0)
{
close(EpollFd);
}
LXT_SYNCHRONIZATION_POINT_END();
return Result;
}