/*++ Copyright (c) Microsoft. All rights reserved. Module Name: EventFd.c Abstract: This file is a eventfd test. --*/ #include "lxtcommon.h" #include "unittests.h" #include #include #include #include #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; }