mirror of
https://github.com/microsoft/WSL.git
synced 2025-12-10 16:30:30 -06:00
* link: Collect WSL logs (recommended method) Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * link: Advanced Authoring Tests in C++ Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * link: CMake Documentation and Community Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * link: Collect WSL logs for networking issues Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * link: Collect WSL logs (recommended method) Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: ; otherwise, Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: a Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: access Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: accessible Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: across Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: actively Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: adapters Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: address Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: addresses Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: and Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: appropriate Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: argument Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: associated Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: attach Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: available Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: beginning Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: between Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: binaries Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: bound Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: buffer Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: buffers Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: cannot Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: canonical Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: capabilities Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: case-insensitive Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: case-sensitive Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: certified Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: command Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: committer Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: communication Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: complains Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: configuration Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: consumed Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: continue Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: converted Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: currently Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: customers Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: daemon Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: deferred Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: definitions Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: delimiter Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: delivered Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: dellink Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: derived Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: descriptor Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: destined Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: destruct Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: destructible Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: destructor Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: detach Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: differentiate Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: directories Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: disassociate Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: disposition Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: distribution Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: distro Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: duping Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: emitted Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: empty Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: environment Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: every time Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: exclusive Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: expected Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: expire Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: explicitly Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: fall back Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: false Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: fastfail Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: filesystem Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: first Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: followed Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: for Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: functionality Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: functionally Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: github Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: greater Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: guarantee Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: guaranteed Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: handles Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: hangup Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: hierarchy Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: hogwarts Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: hydrated Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: icrnl Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: implementation Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: implementing Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: initialize Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: instance Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: instantiate Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: instantiations Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: intentionally Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: interpret Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: interpreter Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: irreversibly Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: iteration Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: iterator Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: its Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: kernel Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: kmsg Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: knowledge Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: maximum Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: mirrored Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: msftconnecttest Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: multi Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: multiple Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: mutable Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: namespace Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: nonexistent Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: notifications Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: occurred Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: occurring Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: otherwise, Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: outstanding Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: overridden Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: partition Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: pass through Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: passthrough Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: performs Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: periodically Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: positional Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: precedence Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: preexisting Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: preferring Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: prepopulate Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: previous Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: privileges Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: process Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: processes Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: programmatically Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: protection Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: provided Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: reasonable Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: receive Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: received Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: red hat Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: reentrant Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: registered Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: regularly Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: relay Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: release Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: representing Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: requests Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: response Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: resurrect Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: retention Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: returned Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: security Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: semaphore Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: separate Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: separator Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: service Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: set up Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: setup Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: severely Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: should Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: signal Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: similarly Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: simple Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: simplified Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: single Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: specified Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: splitting Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: standard Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: stress Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: succeed Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: success Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: successfully Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: supplementary Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: synced Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: system Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: take Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: than Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: that opening Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: the Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: threadpool Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: to Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: true Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: truncate Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: tunneling Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: unexpected Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: uninitialize Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: unique Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: unprivileged Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: unregistered Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: untrusted Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: upgrade Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: utility Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: validating Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: variant Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: variation Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: variations Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: verify Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: visible Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: whether Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: winget Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: worker Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: written Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: wslservice Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * format source --------- Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> Co-authored-by: Ben Hillis <benhillis@gmail.com> Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com>
3400 lines
90 KiB
C
3400 lines
90 KiB
C
/*++
|
|
|
|
Copyright (c) Microsoft. All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
epoll.c
|
|
|
|
Abstract:
|
|
|
|
This file is the epoll test.
|
|
|
|
--*/
|
|
|
|
#include "lxtcommon.h"
|
|
#include "unittests.h"
|
|
#include <sys/epoll.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
#include <poll.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
#include <netinet/in.h>
|
|
#include <netdb.h>
|
|
#include "common.h"
|
|
|
|
#include <sys/wait.h>
|
|
|
|
#define LXT_NAME "Epoll"
|
|
#define SOCKET_NAME "PartyInTheUsa"
|
|
#define EPOLL_DUP2_FD_COUNT 100
|
|
|
|
typedef struct _EPOLL_DUP2_CONTEXT
|
|
{
|
|
int EpollFd;
|
|
int Fd[EPOLL_DUP2_FD_COUNT];
|
|
} EPOLL_DUP2_CONTEXT, *PEPOLL_DUP2_CONTEXT;
|
|
|
|
LXT_VARIATION_HANDLER EpollAddTest;
|
|
LXT_VARIATION_HANDLER EpollBasic;
|
|
|
|
int EpollBasicVariation(unsigned short ReadFlags, unsigned short WriteFlags);
|
|
|
|
LXT_VARIATION_HANDLER EpollDeleteCloseFdLoop;
|
|
LXT_VARIATION_HANDLER EpollDeleteTest;
|
|
LXT_VARIATION_HANDLER EpollDup2FdLoop;
|
|
LXT_VARIATION_HANDLER EpollHangupTestSimple;
|
|
LXT_VARIATION_HANDLER EpollHangupTestUnix;
|
|
LXT_VARIATION_HANDLER PPollInvalidArgument;
|
|
LXT_VARIATION_HANDLER EpollModifyWhilePollingTest;
|
|
LXT_VARIATION_HANDLER EpollModTest;
|
|
LXT_VARIATION_HANDLER EpollPhantomEventsTest;
|
|
LXT_VARIATION_HANDLER EpollRecursionTest;
|
|
LXT_VARIATION_HANDLER EpollRecursionLimitTest;
|
|
LXT_VARIATION_HANDLER EpollRelatedFileStress;
|
|
LXT_VARIATION_HANDLER EpollSequenceTestUnix;
|
|
LXT_VARIATION_HANDLER EpollSocketAcceptTest;
|
|
LXT_VARIATION_HANDLER EpollSocketReadTest;
|
|
LXT_VARIATION_HANDLER EpollUnalignedTest;
|
|
LXT_VARIATION_HANDLER EpollVariation0;
|
|
|
|
//
|
|
// Global constants
|
|
//
|
|
|
|
static const LXT_VARIATION g_LxtVariations[] = {
|
|
{"Basic_Variations", EpollBasic},
|
|
{"Epoll", EpollVariation0},
|
|
{"Epoll_Read", EpollSocketReadTest},
|
|
{"Epoll_Hangup", EpollHangupTestSimple},
|
|
{"Epoll_Accept", EpollSocketAcceptTest},
|
|
{"Epoll_Add", EpollAddTest},
|
|
{"Epoll_Delete", EpollDeleteTest},
|
|
{"Epoll_Modify_WhilePolling", EpollModifyWhilePollingTest},
|
|
{"Epoll_Related_File_Stress", EpollRelatedFileStress},
|
|
{"Epoll_Mod", EpollModTest},
|
|
{"Epoll_PhantomEvents", EpollPhantomEventsTest},
|
|
{"Ppoll invalid argument", PPollInvalidArgument},
|
|
{"Epoll unaligned", EpollUnalignedTest},
|
|
{"Epoll delete, close FD loop", EpollDeleteCloseFdLoop},
|
|
{"Epoll dup2 FD loop", EpollDup2FdLoop},
|
|
{"Epoll basic recursion", EpollRecursionTest},
|
|
{"Epoll recursion limit", EpollRecursionLimitTest}};
|
|
|
|
int EpollTestEntry(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);
|
|
}
|
|
|
|
#ifndef EPOLLONESHOT
|
|
#define EPOLLONESHOT (1 << 30)
|
|
#endif
|
|
|
|
int EpollBasic(PLXT_ARGS Args)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
unsigned short ReadFlags[] = {EPOLLIN, EPOLLRDNORM, (EPOLLIN | EPOLLRDNORM)};
|
|
int ReadVariation;
|
|
int Result;
|
|
unsigned short WriteFlags[] = {EPOLLOUT, EPOLLWRNORM, (EPOLLOUT | EPOLLWRNORM)};
|
|
int WriteVariation;
|
|
|
|
for (ReadVariation = 0; ReadVariation < LXT_COUNT_OF(ReadFlags); ReadVariation += 1)
|
|
{
|
|
|
|
for (WriteVariation = 0; WriteVariation < LXT_COUNT_OF(ReadFlags); WriteVariation += 1)
|
|
{
|
|
|
|
Result = EpollBasicVariation(ReadFlags[ReadVariation], WriteFlags[WriteVariation]);
|
|
|
|
if (Result < 0)
|
|
{
|
|
LxtLogError("Failed basic variation (%d, %d)", ReadVariation, WriteVariation);
|
|
|
|
goto cleanup;
|
|
}
|
|
}
|
|
}
|
|
|
|
cleanup:
|
|
return Result;
|
|
}
|
|
|
|
int EpollBasicVariation(unsigned short ReadFlags, unsigned short WriteFlags)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
struct epoll_event EpollControlEvent;
|
|
int EpollFileDescriptor;
|
|
struct epoll_event EpollWaitEvent[2];
|
|
struct epoll_event* InputEvent;
|
|
struct epoll_event* OutputEvent;
|
|
int PipeFileDescriptors[2] = {};
|
|
int Result;
|
|
|
|
//
|
|
// Initialize locals.
|
|
//
|
|
|
|
EpollFileDescriptor = -1;
|
|
|
|
//
|
|
// Open a pipe to test epoll.
|
|
//
|
|
|
|
LxtCheckErrnoZeroSuccess(pipe(PipeFileDescriptors));
|
|
|
|
//
|
|
// Pend a write.
|
|
//
|
|
|
|
LxtCheckErrno(write(PipeFileDescriptors[1], "\n", 1));
|
|
|
|
//
|
|
// Create an epoll.
|
|
//
|
|
|
|
LxtCheckErrno(EpollFileDescriptor = epoll_create(1));
|
|
|
|
//
|
|
// Add the file to the epoll.
|
|
//
|
|
|
|
EpollControlEvent.events = ReadFlags;
|
|
EpollControlEvent.data.fd = PipeFileDescriptors[0];
|
|
LxtCheckErrno(epoll_ctl(EpollFileDescriptor, EPOLL_CTL_ADD, PipeFileDescriptors[0], &EpollControlEvent));
|
|
|
|
EpollControlEvent.events = WriteFlags | EPOLLPRI;
|
|
EpollControlEvent.data.fd = PipeFileDescriptors[1];
|
|
LxtCheckErrno(epoll_ctl(EpollFileDescriptor, EPOLL_CTL_ADD, PipeFileDescriptors[1], &EpollControlEvent));
|
|
|
|
//
|
|
// Verify the epoll is triggered.
|
|
//
|
|
|
|
LxtCheckErrno(epoll_wait(EpollFileDescriptor, EpollWaitEvent, 2, 0));
|
|
if (Result != 2)
|
|
{
|
|
LxtLogError("Waiting on epoll returned %d events (expecting 2)!", Result);
|
|
Result = -1;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
if (EpollWaitEvent[0].data.fd == PipeFileDescriptors[1])
|
|
{
|
|
InputEvent = &EpollWaitEvent[1];
|
|
OutputEvent = &EpollWaitEvent[0];
|
|
}
|
|
else
|
|
{
|
|
InputEvent = &EpollWaitEvent[0];
|
|
OutputEvent = &EpollWaitEvent[1];
|
|
}
|
|
|
|
LxtCheckEqual(InputEvent->data.fd, PipeFileDescriptors[0], "%d");
|
|
LxtCheckEqual(InputEvent->events, ReadFlags, "%hd");
|
|
LxtCheckEqual(OutputEvent->data.fd, PipeFileDescriptors[1], "%d");
|
|
LxtCheckEqual(OutputEvent->events, WriteFlags, "%hd");
|
|
|
|
ErrorExit:
|
|
if (EpollFileDescriptor != -1)
|
|
{
|
|
close(EpollFileDescriptor);
|
|
}
|
|
|
|
if (PipeFileDescriptors[1] != -1)
|
|
{
|
|
close(PipeFileDescriptors[1]);
|
|
}
|
|
|
|
if (PipeFileDescriptors[0] != -1)
|
|
{
|
|
close(PipeFileDescriptors[0]);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
int EpollCreateClientSocket(void)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
int Result;
|
|
struct sockaddr_in ServerAddress = {0};
|
|
int Socket;
|
|
|
|
//
|
|
// Create a socket.
|
|
//
|
|
|
|
Socket = socket(AF_INET, SOCK_STREAM, 0);
|
|
if (Socket < 0)
|
|
{
|
|
LxtLogError("socket(AF_INET, SOCK_STREAM, 0) - %s", strerror(errno));
|
|
Result = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Connect to the server.
|
|
//
|
|
|
|
ServerAddress.sin_family = AF_INET;
|
|
ServerAddress.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
|
ServerAddress.sin_port = htons(LXT_SOCKET_DEFAULT_PORT);
|
|
|
|
Result = connect(Socket, (struct sockaddr*)&ServerAddress, sizeof(ServerAddress));
|
|
|
|
if (Result < 0)
|
|
{
|
|
LxtLogError("connect(%d) - %s", Socket, strerror(errno));
|
|
Result = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
Result = Socket;
|
|
Socket = 0;
|
|
|
|
cleanup:
|
|
if (Socket > 0)
|
|
{
|
|
if (close(Socket) != 0)
|
|
{
|
|
LxtLogError("close(%d) - %s", Socket, strerror(errno));
|
|
Result = LXT_RESULT_FAILURE;
|
|
}
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
int EpollCreateClientUnixSocket(int SocketType)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
int Result;
|
|
struct sockaddr_un ServerAddress = {0};
|
|
int Socket;
|
|
|
|
//
|
|
// Create a socket.
|
|
//
|
|
|
|
Socket = socket(AF_UNIX, SocketType, 0);
|
|
if (Socket < 0)
|
|
{
|
|
LxtLogError("socket(AF_UNIX, SocketType, 0) - %s", strerror(errno));
|
|
Result = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Connect to the server.
|
|
//
|
|
|
|
ServerAddress.sun_family = AF_UNIX;
|
|
strcpy(ServerAddress.sun_path, SOCKET_NAME);
|
|
|
|
Result = connect(Socket, (struct sockaddr*)&ServerAddress, sizeof(ServerAddress));
|
|
|
|
if (Result < 0)
|
|
{
|
|
LxtLogError("connect(%d) - %s", Socket, strerror(errno));
|
|
Result = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
Result = Socket;
|
|
Socket = 0;
|
|
|
|
cleanup:
|
|
if (Socket > 0)
|
|
{
|
|
if (close(Socket) != 0)
|
|
{
|
|
LxtLogError("close(%d) - %s", Socket, strerror(errno));
|
|
Result = LXT_RESULT_FAILURE;
|
|
}
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
int EpollCreateListenSocket(void)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
struct sockaddr_in ServerAddress = {0};
|
|
int Result;
|
|
int Socket;
|
|
|
|
//
|
|
// Create a socket.
|
|
//
|
|
|
|
Socket = socket(AF_INET, SOCK_STREAM, 0);
|
|
if (Socket < 0)
|
|
{
|
|
LxtLogError("socket - %s", strerror(errno));
|
|
Result = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Bind the socket to an ipv4 socket.
|
|
//
|
|
|
|
ServerAddress.sin_family = AF_INET;
|
|
ServerAddress.sin_addr.s_addr = INADDR_ANY;
|
|
ServerAddress.sin_port = htons(LXT_SOCKET_DEFAULT_PORT);
|
|
|
|
Result = bind(Socket, (struct sockaddr*)&ServerAddress, sizeof(ServerAddress));
|
|
|
|
if (Result < 0)
|
|
{
|
|
LxtLogError("bind(%d) - %s", Socket, strerror(errno));
|
|
Result = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Mark the socket as a listen socket.
|
|
//
|
|
|
|
Result = listen(Socket, LXT_SOCKET_SERVER_MAX_BACKLOG_NUM);
|
|
if (Result < 0)
|
|
{
|
|
LxtLogError("listen(%d) - %s", Socket, strerror(errno));
|
|
Result = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
Result = Socket;
|
|
Socket = 0;
|
|
|
|
cleanup:
|
|
|
|
if (Socket > 0)
|
|
{
|
|
if (close(Socket) != 0)
|
|
{
|
|
LxtLogError("close(%d) - %s", Socket, strerror(errno));
|
|
}
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
int EpollCreateListenUnixSocket(int SocketType)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
struct sockaddr_un ServerAddress = {0};
|
|
int Result;
|
|
int Socket;
|
|
|
|
//
|
|
// Create a socket.
|
|
//
|
|
|
|
Socket = socket(AF_UNIX, SocketType, 0);
|
|
if (Socket < 0)
|
|
{
|
|
LxtLogError("socket - %s", strerror(errno));
|
|
Result = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Bind the socket to an ipv4 socket.
|
|
//
|
|
|
|
ServerAddress.sun_family = AF_UNIX;
|
|
strcpy(ServerAddress.sun_path, SOCKET_NAME);
|
|
unlink(SOCKET_NAME);
|
|
Result = bind(Socket, (struct sockaddr*)&ServerAddress, sizeof(ServerAddress));
|
|
|
|
if (Result < 0)
|
|
{
|
|
LxtLogError("bind(%d) - %s", Socket, strerror(errno));
|
|
Result = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Mark the socket as a listen socket.
|
|
//
|
|
|
|
Result = listen(Socket, LXT_SOCKET_SERVER_MAX_BACKLOG_NUM);
|
|
if (Result < 0)
|
|
{
|
|
LxtLogError("listen(%d) - %s", Socket, strerror(errno));
|
|
Result = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
Result = Socket;
|
|
Socket = 0;
|
|
|
|
cleanup:
|
|
|
|
if (Socket > 0)
|
|
{
|
|
if (close(Socket) != 0)
|
|
{
|
|
LxtLogError("close(%d) - %s", Socket, strerror(errno));
|
|
}
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
int EpollHandleClientAccept(int Socket)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
struct sockaddr_in ClientAddress = {0};
|
|
socklen_t ClientLength;
|
|
int ClientSocket;
|
|
|
|
ClientLength = sizeof(ClientAddress);
|
|
ClientSocket = accept(Socket, (struct sockaddr*)&ClientAddress, &ClientLength);
|
|
if (ClientSocket < 0)
|
|
{
|
|
LxtLogError("accept(%d) - %s", Socket, strerror(errno));
|
|
ClientSocket = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
cleanup:
|
|
|
|
return ClientSocket;
|
|
}
|
|
|
|
const char* DataToWrite[] = {
|
|
"<This is the first message> ",
|
|
"<This is another message> ",
|
|
"<Dumbledore is dead> ",
|
|
"<Harry Potter must not go back to Hogwarts> ",
|
|
"<There must always be a stark in Winterfell>",
|
|
};
|
|
|
|
const int WriteItemCount = sizeof(DataToWrite) / sizeof(DataToWrite[0]);
|
|
|
|
int EpollSocketReadTest(PLXT_ARGS Args)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
char Buffer[256];
|
|
int Result;
|
|
int EpollFileDescriptor;
|
|
int FileDescriptor1;
|
|
int FileDescriptor2;
|
|
struct epoll_event EpollControlEvent;
|
|
struct epoll_event EpollWaitEvent[2];
|
|
int ChildPid;
|
|
int Index;
|
|
int ChildStatus;
|
|
|
|
//
|
|
// Initialize locals.
|
|
//
|
|
|
|
FileDescriptor1 = -1;
|
|
FileDescriptor2 = -1;
|
|
EpollFileDescriptor = -1;
|
|
ChildPid = -1;
|
|
|
|
//
|
|
// Create the server socket.
|
|
//
|
|
|
|
LxtLogInfo("[Setup] About to create server socket...");
|
|
|
|
FileDescriptor1 = EpollCreateListenSocket();
|
|
if (FileDescriptor1 == -1)
|
|
{
|
|
Result = errno;
|
|
LxtLogError("[Setup] Could not create socket! %d", Result);
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Fork to create a server and a client.
|
|
//
|
|
|
|
LxtLogInfo("[Setup] About to fork...");
|
|
|
|
ChildPid = fork();
|
|
|
|
if (ChildPid == -1)
|
|
{
|
|
Result = errno;
|
|
LxtLogError("[Setup] Fork failed! %d", Result);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (ChildPid == 0)
|
|
{
|
|
|
|
LxtLogInfo("[Client] Waiting 2 seconds to let server block...");
|
|
|
|
usleep(2 * 1000 * 1000);
|
|
|
|
LxtLogInfo("[Client] Connecting to server...");
|
|
|
|
FileDescriptor2 = EpollCreateClientSocket();
|
|
|
|
LxtLogInfo("[Client] Connected to server, fd = %d", FileDescriptor2);
|
|
|
|
LxtLogInfo("[Client] Sleeping for 5 seconds with open socket");
|
|
|
|
usleep(5 * 1000 * 1000);
|
|
|
|
//
|
|
// Create an epoll container.
|
|
//
|
|
|
|
EpollFileDescriptor = epoll_create(1);
|
|
|
|
if (EpollFileDescriptor == -1)
|
|
{
|
|
Result = errno;
|
|
LxtLogError("[Client] Could not create Epoll! %d", Result);
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Add the connected socket to the epoll.
|
|
//
|
|
|
|
EpollControlEvent.events = EPOLLIN;
|
|
EpollControlEvent.data.fd = FileDescriptor2;
|
|
|
|
Result = epoll_ctl(EpollFileDescriptor, EPOLL_CTL_ADD, FileDescriptor2, &EpollControlEvent);
|
|
|
|
if (Result == -1)
|
|
{
|
|
Result = errno;
|
|
LxtLogError("[Client] Could not add file to epoll! %d", Result);
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Wait for data to be available with a timeout.
|
|
//
|
|
|
|
while (1)
|
|
{
|
|
|
|
LxtLogInfo("[Client] Waiting on epoll with 15 second timeout ...");
|
|
|
|
Result = epoll_wait(EpollFileDescriptor, EpollWaitEvent, 2, 15000);
|
|
|
|
LxtLogInfo("[Client] Epoll returned %d events", Result);
|
|
|
|
if (Result != 1)
|
|
{
|
|
LxtLogError("[Client] Wait on epoll failed! %d", Result);
|
|
Result = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
LxtLogInfo("[Client] Event: {%d, %x} ", EpollWaitEvent[0].data.fd, EpollWaitEvent[0].events);
|
|
|
|
if (EpollWaitEvent[0].data.fd != FileDescriptor2)
|
|
{
|
|
LxtLogError("[Client] Epoll wait satisfied with wrong user data! %d", EpollWaitEvent[0].data.fd);
|
|
Result = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
if (EpollWaitEvent[0].events != EPOLLIN)
|
|
{
|
|
LxtLogError("[Client] Epoll wait satisfied with wrong events! 0x%x", EpollWaitEvent[0].events);
|
|
Result = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
memset(Buffer, 0, sizeof(Buffer));
|
|
Result = read(FileDescriptor2, Buffer, sizeof(Buffer));
|
|
if (Result < 0)
|
|
{
|
|
Result = -1;
|
|
LxtLogError("[Client] Read on socket failed! %d", Result);
|
|
goto cleanup;
|
|
}
|
|
|
|
LxtLogInfo("[Client] read %d bytes: %s ...", Result, Buffer);
|
|
|
|
if (Result == 0)
|
|
{
|
|
LxtLogInfo("[Client] exiting ...");
|
|
goto cleanup;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Accept an incoming connection.
|
|
//
|
|
|
|
FileDescriptor2 = EpollHandleClientAccept(FileDescriptor1);
|
|
|
|
LxtLogInfo("[Server] Writing to socket %d times!", WriteItemCount);
|
|
|
|
for (Index = 0; Index < WriteItemCount; Index += 1)
|
|
{
|
|
|
|
Result = write(FileDescriptor2, DataToWrite[Index], strlen(DataToWrite[Index]));
|
|
|
|
if (Result < 0)
|
|
{
|
|
LxtLogError("[Server] Write %d failed %d", Index, Result);
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
LxtLogInfo("[Server] Write (%d, %s, %d) -> %d!", FileDescriptor2, DataToWrite[Index], strlen(DataToWrite[Index]) + (Index == WriteItemCount - 1), Result);
|
|
|
|
if ((Index % 5) == 0)
|
|
{
|
|
usleep(5 * 1000 * 1000);
|
|
}
|
|
}
|
|
|
|
usleep(5 * 1000 * 1000);
|
|
|
|
LxtLogInfo("[Server] Closing client fd = %d", FileDescriptor2);
|
|
if (FileDescriptor2 != -1)
|
|
{
|
|
close(FileDescriptor2);
|
|
FileDescriptor2 = -1;
|
|
}
|
|
|
|
LxtLogInfo("[Server] Waiting for child to exit");
|
|
|
|
ChildStatus = 0;
|
|
wait(&ChildStatus);
|
|
|
|
LxtLogInfo("[Server] Child WIFEXITED=%d WEXITSTATUS=%d", WIFEXITED(ChildStatus), WEXITSTATUS(ChildStatus));
|
|
|
|
//
|
|
// Determine if the test passed or failed.
|
|
//
|
|
|
|
if ((Result < 0) || (WIFEXITED(ChildStatus) == 0) || (WEXITSTATUS(ChildStatus) != 0))
|
|
{
|
|
|
|
LxtLogInfo("[Server] Test failed!");
|
|
Result = -1;
|
|
}
|
|
|
|
LxtLogInfo("[Server] Done");
|
|
|
|
cleanup:
|
|
|
|
if (FileDescriptor1 != -1)
|
|
{
|
|
close(FileDescriptor1);
|
|
}
|
|
|
|
if (EpollFileDescriptor != -1)
|
|
{
|
|
close(EpollFileDescriptor);
|
|
}
|
|
|
|
if (FileDescriptor2 != -1)
|
|
{
|
|
close(FileDescriptor2);
|
|
}
|
|
|
|
if (ChildPid == 0)
|
|
{
|
|
LxtLogInfo("[Child] Exit with %d!", Result);
|
|
_exit(Result);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
int EpollHangupTestSimple(PLXT_ARGS Args)
|
|
{
|
|
|
|
char Buffer[256];
|
|
int Result;
|
|
int EpollFileDescriptor;
|
|
int FileDescriptor1;
|
|
int FileDescriptor2;
|
|
struct epoll_event EpollControlEvent;
|
|
struct epoll_event EpollWaitEvent[2];
|
|
int ChildPid;
|
|
int Index;
|
|
int ChildStatus;
|
|
int ReadAttempts;
|
|
|
|
//
|
|
// Initialize locals.
|
|
//
|
|
|
|
FileDescriptor1 = -1;
|
|
FileDescriptor2 = -1;
|
|
EpollFileDescriptor = -1;
|
|
ChildPid = -1;
|
|
LxtLogInfo("[Setup] Starting simple hangup test");
|
|
|
|
//
|
|
// Create the server socket.
|
|
//
|
|
|
|
LxtLogInfo("[Setup] About to create server socket");
|
|
|
|
FileDescriptor1 = EpollCreateListenSocket();
|
|
if (FileDescriptor1 == -1)
|
|
{
|
|
Result = errno;
|
|
LxtLogError("[Setup] Could not create server socket %d!", Result);
|
|
goto cleanup;
|
|
}
|
|
|
|
LxtLogInfo("[Setup] Created server socket successfully");
|
|
|
|
//
|
|
// Fork to create a server and a client.
|
|
//
|
|
|
|
LxtLogInfo("[Setup] About to fork");
|
|
|
|
ChildPid = fork();
|
|
|
|
if (ChildPid == -1)
|
|
{
|
|
Result = errno;
|
|
LxtLogError("[Setup] Fork failed! %d", Result);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (ChildPid == 0)
|
|
{
|
|
|
|
LxtLogInfo("[Client] Connecting to server...");
|
|
|
|
FileDescriptor2 = EpollCreateClientSocket();
|
|
|
|
LxtLogInfo("[Client] Connected to server, fd = %d", FileDescriptor2);
|
|
|
|
LxtLogInfo("[Client] Sleeping for 3 seconds with open socket");
|
|
|
|
usleep(3 * 1000 * 1000);
|
|
|
|
//
|
|
// Create an epoll container.
|
|
//
|
|
|
|
EpollFileDescriptor = epoll_create(1);
|
|
|
|
if (EpollFileDescriptor == -1)
|
|
{
|
|
Result = errno;
|
|
LxtLogError("[Client] Could not create Epoll %d!", Result);
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Add the connected socket to the epoll.
|
|
//
|
|
|
|
EpollControlEvent.events = EPOLLIN;
|
|
EpollControlEvent.data.fd = FileDescriptor2;
|
|
|
|
Result = epoll_ctl(EpollFileDescriptor, EPOLL_CTL_ADD, FileDescriptor2, &EpollControlEvent);
|
|
|
|
if (Result == -1)
|
|
{
|
|
Result = errno;
|
|
LxtLogError("[Client] Could not add file to epoll %d!", Result);
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Wait for data to be available with a timeout.
|
|
//
|
|
|
|
ReadAttempts = 0;
|
|
while (1)
|
|
{
|
|
|
|
LxtLogInfo("[Client] Waiting on epoll with 15 second timeout");
|
|
|
|
Result = epoll_wait(EpollFileDescriptor, EpollWaitEvent, 2, 15000);
|
|
|
|
LxtLogInfo("[Client] Epoll returned %d events", Result);
|
|
|
|
if (Result == 0)
|
|
{
|
|
LxtLogError("[Client] No events returned, exiting!");
|
|
Result = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
if (Result != 1)
|
|
{
|
|
LxtLogError("[Client] Wait on epoll returned too many events, exiting!");
|
|
Result = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
LxtLogInfo("[Client] Event: {%d, %x} ", EpollWaitEvent[0].data.fd, EpollWaitEvent[0].events);
|
|
|
|
if (EpollWaitEvent[0].data.fd != FileDescriptor2)
|
|
{
|
|
LxtLogError("[Client] Epoll wait satisfied with wrong user data! %d", EpollWaitEvent[0].data.fd);
|
|
Result = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
if (EpollWaitEvent[0].events != EPOLLIN)
|
|
{
|
|
LxtLogError("[Client] Epoll wait satisfied with wrong events! 0x%x", EpollWaitEvent[0].events);
|
|
Result = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
memset(Buffer, 0, sizeof(Buffer));
|
|
Result = read(FileDescriptor2, Buffer, sizeof(Buffer));
|
|
if (Result < 0)
|
|
{
|
|
Result = errno;
|
|
LxtLogError("[Client] Read on socket failed! %d", Result);
|
|
goto cleanup;
|
|
}
|
|
|
|
LxtLogInfo("[Client] Read (%d) -> %d bytes: %s", FileDescriptor2, Result, Buffer);
|
|
|
|
if (Result == 0)
|
|
{
|
|
ReadAttempts += 1;
|
|
if (ReadAttempts < 2)
|
|
{
|
|
LxtLogInfo("[Client] Continuing even through read 0 bytes.");
|
|
continue;
|
|
}
|
|
|
|
LxtLogInfo("[Client] Exiting because read returned 0 bytes.");
|
|
goto cleanup;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Accept an incoming connection.
|
|
//
|
|
|
|
LxtLogInfo("[Server] Waiting for incoming connections...");
|
|
|
|
FileDescriptor2 = EpollHandleClientAccept(FileDescriptor1);
|
|
|
|
LxtLogInfo("[Server] Connected to client, fd = %d", FileDescriptor2);
|
|
|
|
Result = write(FileDescriptor2, "Party On", strlen("Party On") + 1);
|
|
|
|
LxtLogInfo("[Server] Write (%d, %s, %d) -> %d", FileDescriptor2, "Party On", strlen("Party On") + 1, Result);
|
|
|
|
if (Result < 0)
|
|
{
|
|
LxtLogError("[Server] Write failed %s", strerror(errno));
|
|
goto cleanup;
|
|
}
|
|
|
|
LxtLogInfo("[Server] Closing client fd = %d", FileDescriptor2);
|
|
if (FileDescriptor2 != -1)
|
|
{
|
|
close(FileDescriptor2);
|
|
FileDescriptor2 = -1;
|
|
}
|
|
|
|
LxtLogInfo("[Server] Waiting for child to exit");
|
|
|
|
wait(&ChildStatus);
|
|
|
|
LxtLogInfo("[Server] Child WIFEXITED=%d WEXITSTATUS=%d", WIFEXITED(ChildStatus), WEXITSTATUS(ChildStatus));
|
|
|
|
//
|
|
// Determine if the test passed or failed.
|
|
//
|
|
|
|
if ((Result < 0) || (WIFEXITED(ChildStatus) == 0) || (WEXITSTATUS(ChildStatus) != 0))
|
|
{
|
|
|
|
LxtLogInfo("[Server] Test failed!");
|
|
Result = -1;
|
|
}
|
|
|
|
LxtLogInfo("[Server] Done");
|
|
|
|
cleanup:
|
|
|
|
if (FileDescriptor1 != -1)
|
|
{
|
|
close(FileDescriptor1);
|
|
}
|
|
|
|
if (EpollFileDescriptor != -1)
|
|
{
|
|
close(EpollFileDescriptor);
|
|
}
|
|
|
|
if (FileDescriptor2 != -1)
|
|
{
|
|
close(FileDescriptor2);
|
|
}
|
|
|
|
if (ChildPid == 0)
|
|
{
|
|
_exit(Result);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
int EpollSocketAcceptTest(PLXT_ARGS Args)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
char Buffer[256];
|
|
int Result;
|
|
int EpollFileDescriptor;
|
|
int FileDescriptor1;
|
|
int FileDescriptor2;
|
|
struct epoll_event EpollControlEvent;
|
|
struct epoll_event EpollWaitEvent[2];
|
|
int ChildPid;
|
|
int Index;
|
|
int ChildStatus;
|
|
|
|
//
|
|
// Initialize locals.
|
|
//
|
|
|
|
FileDescriptor1 = -1;
|
|
FileDescriptor2 = -1;
|
|
EpollFileDescriptor = -1;
|
|
ChildPid = -1;
|
|
|
|
//
|
|
// Create a socket that will be added for epoll.
|
|
//
|
|
|
|
FileDescriptor1 = EpollCreateListenSocket();
|
|
if (FileDescriptor1 == -1)
|
|
{
|
|
Result = errno;
|
|
LxtLogError("Could not create socket! %d", Result);
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Create an epoll container.
|
|
//
|
|
|
|
EpollFileDescriptor = epoll_create(1);
|
|
|
|
if (EpollFileDescriptor == -1)
|
|
{
|
|
Result = errno;
|
|
LxtLogError("Could not create Epoll! %d", Result);
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Add the socket to the epoll.
|
|
//
|
|
|
|
EpollControlEvent.events = EPOLLIN;
|
|
EpollControlEvent.data.fd = FileDescriptor1;
|
|
|
|
Result = epoll_ctl(EpollFileDescriptor, EPOLL_CTL_ADD, FileDescriptor1, &EpollControlEvent);
|
|
|
|
if (Result == -1)
|
|
{
|
|
Result = errno;
|
|
LxtLogError("Could not add file to epoll! %d", Result);
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Wait for data to be available with a timeout. No data should arrive.
|
|
//
|
|
|
|
LxtLogInfo("[Setup] Waiting on epoll to timeout for 5s...");
|
|
|
|
Result = epoll_wait(EpollFileDescriptor, EpollWaitEvent, 2, 5000);
|
|
|
|
if (Result == -1)
|
|
{
|
|
Result = errno;
|
|
LxtLogError("Waiting on epoll failed! %d", Result);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (Result != 0)
|
|
{
|
|
LxtLogError("Waiting on epoll succeeded but returned non-zero events! %d", Result);
|
|
Result = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
LxtLogInfo("[Setup] Wait on epoll returned no data, as expected...");
|
|
|
|
//
|
|
// Fork to create a server and a client.
|
|
//
|
|
|
|
LxtLogInfo("[Setup] About to fork...");
|
|
|
|
ChildPid = fork();
|
|
|
|
if (ChildPid == -1)
|
|
{
|
|
Result = errno;
|
|
LxtLogError("Fork failed! %d", Result);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (ChildPid == 0)
|
|
{
|
|
|
|
LxtLogInfo("[Client] Waiting 2 seconds to let server block...");
|
|
|
|
usleep(2 * 1000 * 1000);
|
|
|
|
LxtLogInfo("[Client] Connecting to server...");
|
|
|
|
FileDescriptor2 = EpollCreateClientSocket();
|
|
|
|
LxtLogInfo("[Client] Connected to server, fd =%d", FileDescriptor2);
|
|
|
|
LxtLogInfo("[Client] Sleeping for 2 seconds with open socket");
|
|
|
|
usleep(2 * 1000 * 1000);
|
|
|
|
Result = write(FileDescriptor2, "Party On", strlen("Party On") + 1);
|
|
|
|
LxtLogInfo("[Client] Write (%d, %s, %d) -> %d", FileDescriptor2, "Party On", strlen("Party On") + 1, Result);
|
|
|
|
if (Result < 0)
|
|
{
|
|
LxtLogError("[Server] Write failed %s", strerror(errno));
|
|
goto cleanup;
|
|
}
|
|
|
|
LxtLogInfo("[Client] Closing socket %d", FileDescriptor2);
|
|
|
|
if (close(FileDescriptor2) != 0)
|
|
{
|
|
LxtLogError("[Client] Closing socket %d failed - %s", FileDescriptor2, strerror(errno));
|
|
}
|
|
|
|
usleep(2 * 1000 * 1000);
|
|
|
|
FileDescriptor2 = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// The server should wait for data to become available on the socket.
|
|
//
|
|
|
|
LxtLogInfo("[Server] Waiting on epoll to timeout for 10s...");
|
|
|
|
Result = epoll_wait(EpollFileDescriptor, EpollWaitEvent, 2, 10000);
|
|
|
|
if (Result == -1)
|
|
{
|
|
Result = errno;
|
|
LxtLogError("[Server] Waiting on epoll failed! %d", Result);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (Result != 1)
|
|
{
|
|
LxtLogError("[Server] Waiting on epoll returned unexpected events: %d!", Result);
|
|
Result = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
if (EpollWaitEvent[0].data.fd != FileDescriptor1)
|
|
{
|
|
LxtLogError("[Server] Epoll wait satisfied with wrong user data! %d", EpollWaitEvent[0].data.fd);
|
|
Result = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
if (EpollWaitEvent[0].events != EPOLLIN)
|
|
{
|
|
LxtLogError("[Server] Epoll wait satisfied with wrong events! 0x%x", EpollWaitEvent[0].events);
|
|
Result = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
FileDescriptor2 = EpollHandleClientAccept(FileDescriptor1);
|
|
if (FileDescriptor2 < 0)
|
|
{
|
|
LxtLogError("[Server] Accept failed!");
|
|
Result = -1;
|
|
}
|
|
|
|
LxtLogInfo("[Server] Accepted a request successfully...");
|
|
|
|
//
|
|
// The server should timeout now if it waits for data again.
|
|
//
|
|
|
|
LxtLogInfo("[Server] Waiting on epoll to timeout for 5s...");
|
|
|
|
Result = epoll_wait(EpollFileDescriptor, EpollWaitEvent, 2, 5000);
|
|
|
|
if (Result == -1)
|
|
{
|
|
Result = errno;
|
|
LxtLogError("Waiting on epoll failed! %d", Result);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (Result != 0)
|
|
{
|
|
LxtLogError("Waiting on epoll succeeded but returned non-zero events! %d", Result);
|
|
Result = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Add the socket to the epoll.
|
|
//
|
|
|
|
EpollControlEvent.events = EPOLLIN;
|
|
EpollControlEvent.data.fd = FileDescriptor2;
|
|
|
|
Result = epoll_ctl(EpollFileDescriptor, EPOLL_CTL_ADD, FileDescriptor2, &EpollControlEvent);
|
|
|
|
if (Result == -1)
|
|
{
|
|
Result = errno;
|
|
LxtLogError("Could not add file to epoll! %d", Result);
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Wait for data to be available with a timeout. No data should arrive.
|
|
//
|
|
|
|
while (1)
|
|
{
|
|
|
|
LxtLogInfo("[Setup] Waiting on epoll to timeout for 5s...");
|
|
|
|
Result = epoll_wait(EpollFileDescriptor, EpollWaitEvent, 2, 5000);
|
|
|
|
if (Result == -1)
|
|
{
|
|
Result = errno;
|
|
LxtLogError("Waiting on epoll failed! %d", Result);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (Result == 0)
|
|
{
|
|
LxtLogError("Waiting on epoll succeeded but returned zero events!");
|
|
Result = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
LxtLogInfo("[Server] Event: {%d, %x} ", EpollWaitEvent[0].data.fd, EpollWaitEvent[0].events);
|
|
|
|
if (EpollWaitEvent[0].data.fd != FileDescriptor2)
|
|
{
|
|
LxtLogError("[Server] Epoll wait satisfied with wrong user data! %d", EpollWaitEvent[0].data.fd);
|
|
Result = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
if (EpollWaitEvent[0].events != EPOLLIN)
|
|
{
|
|
LxtLogError("[Server] Epoll wait satisfied with wrong events! 0x%x", EpollWaitEvent[0].events);
|
|
Result = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
memset(Buffer, 0, sizeof(Buffer));
|
|
Result = read(FileDescriptor2, Buffer, sizeof(Buffer));
|
|
if (Result < 0)
|
|
{
|
|
Result = errno;
|
|
LxtLogError("[Client] Read on socket failed! %d", Result);
|
|
goto cleanup;
|
|
}
|
|
|
|
LxtLogInfo("[Server] read %d bytes: %s ...", Result, Buffer);
|
|
|
|
if (Result == 0)
|
|
{
|
|
LxtLogInfo("[Server] exiting ...");
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
LxtLogInfo("[Server] Waiting on child");
|
|
|
|
wait(&ChildStatus);
|
|
|
|
LxtLogInfo("[Server] Child WIFEXITED=%d WEXITSTATUS=%d", WIFEXITED(ChildStatus), WEXITSTATUS(ChildStatus));
|
|
|
|
//
|
|
// Determine if the test passed or failed.
|
|
//
|
|
|
|
if ((Result < 0) || (WIFEXITED(ChildStatus) == 0) || (WEXITSTATUS(ChildStatus) != 0))
|
|
{
|
|
|
|
LxtLogInfo("[Server] Test failed!");
|
|
Result = -1;
|
|
}
|
|
|
|
LxtLogInfo("[Server] Done");
|
|
|
|
cleanup:
|
|
|
|
if (FileDescriptor1 != -1)
|
|
{
|
|
close(FileDescriptor1);
|
|
}
|
|
|
|
if (EpollFileDescriptor != -1)
|
|
{
|
|
close(EpollFileDescriptor);
|
|
}
|
|
|
|
if (FileDescriptor2 != -1)
|
|
{
|
|
close(FileDescriptor2);
|
|
}
|
|
|
|
if (ChildPid == 0)
|
|
{
|
|
_exit(Result);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
int EpollVariation0(PLXT_ARGS Args)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
char Buffer[10];
|
|
int BytesReadWrite;
|
|
int ChildPid;
|
|
struct epoll_event EpollControlEvent;
|
|
int FileDescriptor1;
|
|
int FileDescriptor2;
|
|
int EpollFileDescriptor;
|
|
struct epoll_event EpollWaitEvent[2];
|
|
int Index;
|
|
int Master;
|
|
char PtsDevName[50];
|
|
int Result;
|
|
int Status;
|
|
|
|
//
|
|
// Initialize locals.
|
|
//
|
|
|
|
Master = -1;
|
|
FileDescriptor1 = -1;
|
|
FileDescriptor2 = -1;
|
|
EpollFileDescriptor = -1;
|
|
ChildPid = -1;
|
|
|
|
//
|
|
// Open a file that will be added to the epoll.
|
|
//
|
|
|
|
LxtCheckErrno(Master = open("/dev/ptmx", O_RDWR));
|
|
LxtCheckErrno(grantpt(Master));
|
|
LxtCheckErrno(unlockpt(Master));
|
|
LxtCheckErrno(ptsname_r(Master, PtsDevName, sizeof(PtsDevName)));
|
|
LxtLogInfo("Subordinate Device is:%s", PtsDevName);
|
|
LxtCheckErrno(FileDescriptor1 = open(PtsDevName, O_RDWR));
|
|
LxtCheckErrno(FileDescriptor2 = open(PtsDevName, O_RDWR));
|
|
|
|
//
|
|
// Create an epoll.
|
|
//
|
|
|
|
LxtCheckErrno(EpollFileDescriptor = epoll_create(1));
|
|
|
|
//
|
|
// Add the file to the epoll.
|
|
//
|
|
|
|
EpollControlEvent.events = EPOLLIN;
|
|
EpollControlEvent.data.fd = FileDescriptor1;
|
|
LxtCheckErrno(epoll_ctl(EpollFileDescriptor, EPOLL_CTL_ADD, FileDescriptor1, &EpollControlEvent));
|
|
|
|
//
|
|
// Add the file to the epoll again and it should fail.
|
|
//
|
|
|
|
LxtCheckErrnoFailure(epoll_ctl(EpollFileDescriptor, EPOLL_CTL_ADD, FileDescriptor1, &EpollControlEvent), EEXIST);
|
|
|
|
//
|
|
// Add the epoll file descriptor to itself and it should fail.
|
|
//
|
|
|
|
LxtCheckErrnoFailure(epoll_ctl(EpollFileDescriptor, EPOLL_CTL_ADD, EpollFileDescriptor, &EpollControlEvent), EINVAL);
|
|
|
|
//
|
|
// Add the second file descriptor to the epoll.
|
|
//
|
|
|
|
EpollControlEvent.events = EPOLLOUT | EPOLLPRI;
|
|
EpollControlEvent.data.fd = FileDescriptor2;
|
|
LxtCheckErrno(epoll_ctl(EpollFileDescriptor, EPOLL_CTL_ADD, FileDescriptor2, &EpollControlEvent));
|
|
|
|
//
|
|
// Remove the second file from the epoll.
|
|
//
|
|
|
|
LxtCheckErrno(epoll_ctl(EpollFileDescriptor, EPOLL_CTL_DEL, FileDescriptor2, NULL));
|
|
|
|
//
|
|
// Try adding back the first file descriptor as it should still be there.
|
|
//
|
|
|
|
LxtCheckErrnoFailure(epoll_ctl(EpollFileDescriptor, EPOLL_CTL_ADD, FileDescriptor1, &EpollControlEvent), EEXIST);
|
|
|
|
//
|
|
// Add the second file descriptor back to the epoll.
|
|
//
|
|
|
|
EpollControlEvent.events = EPOLLOUT | EPOLLPRI;
|
|
EpollControlEvent.data.fd = FileDescriptor2;
|
|
LxtCheckErrno(epoll_ctl(EpollFileDescriptor, EPOLL_CTL_ADD, FileDescriptor2, &EpollControlEvent));
|
|
|
|
//
|
|
// Modify the second file descriptor in the epoll.
|
|
//
|
|
|
|
EpollControlEvent.events = EPOLLIN | EPOLLERR | EPOLLPRI | EPOLLET;
|
|
EpollControlEvent.data.fd = FileDescriptor2;
|
|
LxtCheckErrno(epoll_ctl(EpollFileDescriptor, EPOLL_CTL_MOD, FileDescriptor2, &EpollControlEvent));
|
|
|
|
//
|
|
// Wait for the epoll with a timeout.
|
|
//
|
|
|
|
LxtLogInfo("Waiting on epoll to timeout for 1s...");
|
|
|
|
LxtCheckErrnoZeroSuccess(epoll_wait(EpollFileDescriptor, EpollWaitEvent, 2, 200));
|
|
|
|
//
|
|
// Fork to create another thread to signal the epoll.
|
|
//
|
|
|
|
LXT_SYNCHRONIZATION_POINT_START();
|
|
LxtCheckErrno(ChildPid = fork());
|
|
if (ChildPid == 0)
|
|
{
|
|
|
|
//
|
|
// Wait to allow parent to block on epoll.
|
|
//
|
|
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
LxtLogInfo("T2: Waiting to make read data available...");
|
|
usleep(200 * 1000);
|
|
LxtLogInfo("T2: Making data available for read...");
|
|
LxtCheckErrno((BytesReadWrite = write(Master, "\n", 1)));
|
|
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
usleep(200 * 1000);
|
|
LxtLogInfo("T2: Making data available for read...");
|
|
LxtCheckErrno(BytesReadWrite = read(FileDescriptor1, Buffer, sizeof(Buffer)));
|
|
LxtCheckEqual(BytesReadWrite, 1, "%d");
|
|
LxtCheckErrno((BytesReadWrite = write(Master, "\n", 1)));
|
|
|
|
LxtLogInfo("T2: Waiting to allow T1 to wake, consume edge trigger, and wait again...");
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
usleep(200 * 1000);
|
|
LxtLogInfo("T2: Clearing edge-trigger on descriptor2...");
|
|
LxtCheckErrno(BytesReadWrite = read(FileDescriptor1, Buffer, sizeof(Buffer)));
|
|
LxtCheckEqual(BytesReadWrite, 1, "%d");
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
LxtLogInfo("T2: Making data available for read...");
|
|
LxtCheckErrno((BytesReadWrite = write(Master, "\n", 1)));
|
|
|
|
Result = LXT_RESULT_SUCCESS;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
//
|
|
// Wait on epoll to be woken by the child. Do this twice and the second time
|
|
// should immediately return since the first epoll is still signalled.
|
|
//
|
|
|
|
for (Index = 0; Index < 2; Index += 1)
|
|
{
|
|
|
|
LxtLogInfo("T1: Waiting for epoll to be signaled for first descriptor...");
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
LxtCheckErrno(epoll_wait(EpollFileDescriptor, EpollWaitEvent, 2, -1));
|
|
LxtCheckEqual(Result, (2 - Index), "%d");
|
|
if (EpollWaitEvent[0].data.fd == FileDescriptor1)
|
|
{
|
|
LxtCheckEqual(EpollWaitEvent[0].data.fd, FileDescriptor1, "%d");
|
|
LxtCheckEqual(EpollWaitEvent[0].events, EPOLLIN, "%d");
|
|
if (Result > 1)
|
|
{
|
|
LxtCheckEqual(EpollWaitEvent[1].data.fd, FileDescriptor2, "%d");
|
|
LxtCheckEqual(EpollWaitEvent[1].events, EPOLLIN, "%d");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LxtCheckEqual(EpollWaitEvent[0].data.fd, FileDescriptor2, "%d");
|
|
LxtCheckEqual(EpollWaitEvent[0].events, EPOLLIN, "%d");
|
|
if (Result > 1)
|
|
{
|
|
LxtCheckEqual(EpollWaitEvent[1].data.fd, FileDescriptor1, "%d");
|
|
LxtCheckEqual(EpollWaitEvent[1].events, EPOLLIN, "%d");
|
|
}
|
|
}
|
|
|
|
// LxtCheckErrno(BytesReadWrite = read(FileDescriptor1, Buffer, sizeof(Buffer)));
|
|
// LxtCheckEqual(BytesReadWrite, 1, "%d");
|
|
if (Index == 0)
|
|
{
|
|
|
|
//
|
|
// Modify the first file descriptor in the epoll to be one shot. This
|
|
// way, when it's waited on again in the next iteration of the loop,
|
|
// it will be disabled.
|
|
//
|
|
|
|
EpollControlEvent.events = EPOLLIN | EPOLLONESHOT;
|
|
EpollControlEvent.data.fd = FileDescriptor1;
|
|
LxtCheckErrno(epoll_ctl(EpollFileDescriptor, EPOLL_CTL_MOD, FileDescriptor1, &EpollControlEvent));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now wait for the epoll to be signalled by the child for the second
|
|
// descriptor. That registration was with an edge trigger.
|
|
//
|
|
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
LxtLogInfo("T1: Waiting for epoll to be signaled for second descriptor...");
|
|
LxtCheckErrno(epoll_wait(EpollFileDescriptor, EpollWaitEvent, 2, -1));
|
|
LxtCheckEqual(Result, 1, "%d");
|
|
LxtCheckEqual(EpollWaitEvent[0].data.fd, FileDescriptor2, "%d");
|
|
LxtCheckEqual(EpollWaitEvent[0].events, EPOLLIN, "%d");
|
|
|
|
//
|
|
// Wait for the epoll again and it should timeout this time due to edge
|
|
// trigger.
|
|
//
|
|
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
LxtLogInfo("Waiting on epoll to timeout...");
|
|
LxtCheckErrnoZeroSuccess(epoll_wait(EpollFileDescriptor, EpollWaitEvent, 2, 200));
|
|
|
|
//
|
|
// Signal the event again, but descriptor 1 is marked oneshot so it still
|
|
// won't deliver any notifications.
|
|
//
|
|
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
LxtLogInfo("Waiting on epoll (T1 to ready data) indefinitely...");
|
|
LxtCheckErrno(epoll_wait(EpollFileDescriptor, EpollWaitEvent, 2, -1));
|
|
LxtCheckEqual(Result, 1, "%d");
|
|
LxtCheckEqual(EpollWaitEvent[0].data.fd, FileDescriptor2, "%d");
|
|
LxtCheckEqual(EpollWaitEvent[0].events, EPOLLIN, "%d");
|
|
|
|
//
|
|
// Making data unavailable for both descriptors. And generating error on
|
|
// second descriptor.
|
|
//
|
|
|
|
LxtCheckClose(Master);
|
|
LxtLogInfo("Waiting on epoll for error indefinitely...");
|
|
LxtCheckErrno(epoll_wait(EpollFileDescriptor, EpollWaitEvent, 2, -1));
|
|
LxtCheckEqual(Result, 1, "%d");
|
|
LxtCheckEqual(EpollWaitEvent[0].data.fd, FileDescriptor2, "%d");
|
|
|
|
//
|
|
// TODO_LX: Currently only signalling EPOLLHUP.
|
|
//
|
|
// LxtCheckEqual(EpollWaitEvent[0].events, (EPOLLHUP | EPOLLERR | EPOLLIN), "%d");
|
|
//
|
|
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
ErrorExit:
|
|
|
|
//
|
|
// Close the file descriptors in a specific order to exercise both code
|
|
// paths where a file is closed while in an epoll and epoll is closed while
|
|
// having files in it.
|
|
//
|
|
|
|
if (FileDescriptor2 != -1)
|
|
{
|
|
close(FileDescriptor2);
|
|
}
|
|
|
|
if (EpollFileDescriptor != -1)
|
|
{
|
|
close(EpollFileDescriptor);
|
|
}
|
|
|
|
if (FileDescriptor1 != -1)
|
|
{
|
|
close(FileDescriptor1);
|
|
}
|
|
|
|
if (Master != -1)
|
|
{
|
|
close(Master);
|
|
}
|
|
|
|
LXT_SYNCHRONIZATION_POINT_END();
|
|
return Result;
|
|
}
|
|
|
|
typedef struct _READD_TEST_DATA
|
|
{
|
|
int Fd;
|
|
int EpollFd;
|
|
} READD_TEST_DATA, *PREADD_TEST_DATA;
|
|
|
|
static READD_TEST_DATA ReAddTestData;
|
|
|
|
static int EpollReAddTestClone(void* Parameter)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
struct epoll_event EpollControlEvent;
|
|
int Result = -1;
|
|
|
|
//
|
|
// Try to close file descriptor already added to epoll.
|
|
//
|
|
|
|
EpollControlEvent.events = EPOLLIN | EPOLLET;
|
|
EpollControlEvent.data.fd = ReAddTestData.Fd;
|
|
LxtLogInfo("[Cloned] Trying to add existing fd (%d) to epoll context", ReAddTestData.Fd);
|
|
|
|
LxtCheckErrnoFailure(epoll_ctl(ReAddTestData.EpollFd, EPOLL_CTL_ADD, ReAddTestData.Fd, &EpollControlEvent), EEXIST);
|
|
|
|
LxtLogInfo("[Cloned] Closing fd (%d) in shared file descriptor table", ReAddTestData.Fd);
|
|
|
|
LxtClose(ReAddTestData.Fd);
|
|
Result = 0;
|
|
|
|
ErrorExit:
|
|
LxtLogInfo("[Cloned] Exiting...");
|
|
exit(Result);
|
|
}
|
|
|
|
int EpollAddTest(PLXT_ARGS Args)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
int Result;
|
|
int ChildPid;
|
|
struct epoll_event EpollControlEvent;
|
|
int EpollFd;
|
|
int SocketFd1;
|
|
int SocketFdCopy;
|
|
int SocketFd2;
|
|
int SocketFd3;
|
|
int SocketFd4;
|
|
int Status;
|
|
LXT_CLONE_ARGS CloneArgs;
|
|
|
|
SocketFd1 = -1;
|
|
SocketFd2 = -1;
|
|
SocketFd3 = -1;
|
|
EpollFd = -1;
|
|
int Flags;
|
|
|
|
LxtCheckErrno(EpollFd = epoll_create1(EPOLL_CLOEXEC));
|
|
LxtCheckErrno(SocketFd1 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
|
|
LxtCheckErrno(SocketFd3 = dup(SocketFd1));
|
|
LxtLogInfo("Created fd1 (%d), duplicated into fd3 (%d)", SocketFd1, SocketFd3);
|
|
EpollControlEvent.events = EPOLLIN | EPOLLET;
|
|
EpollControlEvent.data.fd = SocketFd1;
|
|
LxtLogInfo("Adding fd1 (%d) file descriptor to epoll context", SocketFd1);
|
|
LxtCheckErrno(epoll_ctl(EpollFd, EPOLL_CTL_ADD, SocketFd1, &EpollControlEvent));
|
|
|
|
LxtLogInfo("Adding fd3 (%d) file descriptor to epoll context", SocketFd3);
|
|
EpollControlEvent.data.fd = SocketFd3;
|
|
LxtCheckErrno(epoll_ctl(EpollFd, EPOLL_CTL_ADD, SocketFd3, &EpollControlEvent));
|
|
|
|
LxtLogInfo("Closing fd1 (%d) file descriptor", SocketFd1);
|
|
LxtClose(SocketFd1);
|
|
SocketFdCopy = SocketFd1;
|
|
SocketFd1 = -1;
|
|
LxtCheckErrno(SocketFd2 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
|
|
LxtLogInfo("Created fd2 (%d) file descriptor, that should be the same as closed fd1 (%d)", SocketFd2, SocketFdCopy);
|
|
|
|
LxtCheckEqual(SocketFdCopy, SocketFd2, "%d");
|
|
LxtCheckNotEqual(SocketFd1, SocketFd3, "%d");
|
|
EpollControlEvent.data.fd = SocketFd2;
|
|
LxtLogInfo("Adding fd2 (%d) file descriptor to epoll context", SocketFd2);
|
|
LxtCheckErrno(epoll_ctl(EpollFd, EPOLL_CTL_ADD, SocketFd2, &EpollControlEvent));
|
|
|
|
LxtLogInfo("Trying to add fd3 (%d) file descriptor, expecting EEXIST", SocketFd3);
|
|
EpollControlEvent.data.fd = SocketFd3;
|
|
LxtCheckErrnoFailure(epoll_ctl(EpollFd, EPOLL_CTL_ADD, SocketFd3, &EpollControlEvent), EEXIST);
|
|
|
|
LxtLogInfo("Forking...");
|
|
LXT_SYNCHRONIZATION_POINT_START();
|
|
LxtCheckErrno(ChildPid = fork());
|
|
if (ChildPid == 0)
|
|
{
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
|
|
LxtLogInfo("[Forked] Try to add fd3 (%d) already added to epoll context", SocketFd3);
|
|
LxtCheckErrnoFailure(epoll_ctl(EpollFd, EPOLL_CTL_ADD, SocketFd3, &EpollControlEvent), EEXIST);
|
|
|
|
LxtLogInfo("[Forked] Closing fd3 (%d), should not affect parent", SocketFd3);
|
|
LxtClose(SocketFd3);
|
|
SocketFd3 = -1;
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
|
|
//
|
|
// Let the parent try to add already existing file descriptor, verifying
|
|
// that it hasn't been removed in both file descriptor tables.
|
|
//
|
|
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
|
|
LxtCheckErrno(SocketFd3 = dup(SocketFd2));
|
|
LxtLogInfo("[Forked] Duplicated fd2 (%d) to fd3 (%d) and adding fd3 to epoll context", SocketFd2, SocketFd3);
|
|
EpollControlEvent.data.fd = SocketFd3;
|
|
LxtCheckErrno(epoll_ctl(EpollFd, EPOLL_CTL_ADD, SocketFd3, &EpollControlEvent));
|
|
|
|
goto ErrorExit;
|
|
}
|
|
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
|
|
//
|
|
// Let the child process close the file descriptor in the cloned file
|
|
// descriptor table.
|
|
//
|
|
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
|
|
LxtLogInfo("Try to add fd3 (%d) already added to epoll context", SocketFd3);
|
|
EpollControlEvent.data.fd = SocketFd3;
|
|
LxtCheckErrnoFailure(epoll_ctl(EpollFd, EPOLL_CTL_ADD, SocketFd3, &EpollControlEvent), EEXIST);
|
|
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
|
|
ErrorExit:
|
|
if (SocketFd1 != -1)
|
|
{
|
|
close(SocketFd1);
|
|
}
|
|
|
|
if (SocketFd2 != -1)
|
|
{
|
|
close(SocketFd2);
|
|
}
|
|
|
|
if (SocketFd3 != -1)
|
|
{
|
|
close(SocketFd3);
|
|
}
|
|
|
|
if (EpollFd != -1)
|
|
{
|
|
close(EpollFd);
|
|
}
|
|
|
|
LXT_SYNCHRONIZATION_POINT_END();
|
|
return Result;
|
|
}
|
|
|
|
int EpollDeleteTest(PLXT_ARGS Args)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
int Result;
|
|
int ChildPid;
|
|
struct epoll_event EpollControlEvent;
|
|
int EpollFd;
|
|
int SocketFd;
|
|
int SocketFdCopy;
|
|
int SocketFdDup;
|
|
int Status;
|
|
LXT_CLONE_ARGS CloneArgs;
|
|
|
|
SocketFd = -1;
|
|
EpollFd = -1;
|
|
int Flags;
|
|
|
|
LxtCheckErrno(EpollFd = epoll_create1(EPOLL_CLOEXEC));
|
|
LxtCheckErrno(SocketFd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
|
|
LxtLogInfo("Created socket file descriptor (%d)", SocketFd);
|
|
EpollControlEvent.events = EPOLLIN | EPOLLET;
|
|
EpollControlEvent.data.fd = SocketFd;
|
|
LxtLogInfo("Adding socket file descriptor (%d) to epoll context", SocketFd);
|
|
LxtCheckErrno(epoll_ctl(EpollFd, EPOLL_CTL_ADD, SocketFd, &EpollControlEvent));
|
|
|
|
LxtLogInfo("1. Forking...");
|
|
LXT_SYNCHRONIZATION_POINT_START();
|
|
LxtCheckErrno(ChildPid = fork());
|
|
if (ChildPid == 0)
|
|
{
|
|
LxtLogInfo("[Forked] Try to add socket file descriptor (%d) already added to epoll context", SocketFd);
|
|
LxtCheckErrnoFailure(epoll_ctl(EpollFd, EPOLL_CTL_ADD, SocketFd, &EpollControlEvent), EEXIST);
|
|
|
|
LxtCheckErrno(epoll_ctl(EpollFd, EPOLL_CTL_DEL, SocketFd, NULL));
|
|
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
|
|
//
|
|
// Let the parent re-add socket file descriptor.
|
|
//
|
|
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
goto ErrorExit;
|
|
}
|
|
|
|
//
|
|
// Let the child process remove the file descriptor in the cloned file
|
|
// descriptor table.
|
|
//
|
|
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
LxtLogInfo("Try to add socket file descriptor (%d)", SocketFd);
|
|
LxtCheckErrno(epoll_ctl(EpollFd, EPOLL_CTL_ADD, SocketFd, &EpollControlEvent));
|
|
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
LXT_SYNCHRONIZATION_POINT_END();
|
|
ChildPid = -1;
|
|
LxtCheckResult(Result);
|
|
|
|
LxtLogInfo("2. Forking...");
|
|
LxtCheckErrno(ChildPid = fork());
|
|
if (ChildPid == 0)
|
|
{
|
|
LxtLogInfo("[Forked] Closing original socket fd and creating a new socket fd, should be equal");
|
|
SocketFdCopy = SocketFd;
|
|
LxtClose(SocketFd);
|
|
LxtCheckErrno(SocketFd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
|
|
LxtCheckEqual(SocketFd, SocketFdCopy, "%d");
|
|
LxtLogInfo("[Forked] Trying to delete socket file descriptor (%d), should fail", SocketFd);
|
|
LxtCheckErrnoFailure(epoll_ctl(EpollFd, EPOLL_CTL_DEL, SocketFd, NULL), ENOENT);
|
|
|
|
LxtLogInfo("[Forked] Try to add socket file descriptor (%d)", SocketFd);
|
|
EpollControlEvent.data.fd = SocketFd;
|
|
LxtCheckErrno(epoll_ctl(EpollFd, EPOLL_CTL_ADD, SocketFd, &EpollControlEvent));
|
|
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
|
|
//
|
|
// Let parent run.
|
|
//
|
|
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
|
|
LxtLogInfo("[Forked] Trying to delete socket file descriptor (%d) again", SocketFd);
|
|
LxtCheckErrno(epoll_ctl(EpollFd, EPOLL_CTL_DEL, SocketFd, NULL));
|
|
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
|
|
//
|
|
// Let the parent re-add socket file descriptor.
|
|
//
|
|
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
goto ErrorExit;
|
|
}
|
|
|
|
//
|
|
// Let the child process remove the file descriptor in the cloned file
|
|
// descriptor table.
|
|
//
|
|
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
|
|
LxtLogInfo("Try to add socket file descriptor (%d), should fail", SocketFd);
|
|
LxtCheckErrnoFailure(epoll_ctl(EpollFd, EPOLL_CTL_ADD, SocketFd, &EpollControlEvent), EEXIST);
|
|
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
|
|
//
|
|
// Let child run.
|
|
//
|
|
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
|
|
LxtLogInfo("Try to add socket file descriptor (%d), should fail again", SocketFd);
|
|
LxtCheckErrnoFailure(epoll_ctl(EpollFd, EPOLL_CTL_ADD, SocketFd, &EpollControlEvent), EEXIST);
|
|
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
LXT_SYNCHRONIZATION_POINT_END();
|
|
ChildPid = -1;
|
|
LxtCheckResult(Result);
|
|
|
|
LxtLogInfo("3.Forking...");
|
|
LxtCheckErrno(ChildPid = fork());
|
|
if (ChildPid == 0)
|
|
{
|
|
LxtLogInfo("[Forked] Duplicating original socket fd and creating a new socket fd, should be equal");
|
|
LxtCheckErrno(SocketFdDup = dup(SocketFd));
|
|
LxtLogInfo("[Forked] Closing original socket fd and creating a new socket fd, should be equal");
|
|
SocketFdCopy = SocketFd;
|
|
LxtClose(SocketFd);
|
|
LxtLogInfo("[Forked] Duplicating original socket fd into new socket fd, should be equal as the closed socket fd");
|
|
LxtCheckErrno(SocketFd = dup(SocketFdDup));
|
|
LxtCheckEqual(SocketFd, SocketFdCopy, "%d");
|
|
LxtLogInfo("[Forked] Trying to delete socket file descriptor (%d)", SocketFd);
|
|
LxtCheckErrno(epoll_ctl(EpollFd, EPOLL_CTL_DEL, SocketFd, NULL));
|
|
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
|
|
//
|
|
// Let the parent run.
|
|
//
|
|
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
goto ErrorExit;
|
|
}
|
|
|
|
//
|
|
// Let the child process remove the file descriptor in the cloned file
|
|
// descriptor table.
|
|
//
|
|
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
|
|
LxtLogInfo("Try to add socket file descriptor (%d)", SocketFd);
|
|
LxtCheckErrno(epoll_ctl(EpollFd, EPOLL_CTL_ADD, SocketFd, &EpollControlEvent));
|
|
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
|
|
//
|
|
// Let the child process finish.
|
|
//
|
|
|
|
ErrorExit:
|
|
if (SocketFd != -1)
|
|
{
|
|
close(SocketFd);
|
|
}
|
|
|
|
if (EpollFd != -1)
|
|
{
|
|
close(EpollFd);
|
|
}
|
|
|
|
LXT_SYNCHRONIZATION_POINT_END();
|
|
return Result;
|
|
}
|
|
|
|
int EpollModifyWhilePollingTest(PLXT_ARGS Args)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
int ChildPid;
|
|
struct epoll_event EpollControlEvent;
|
|
int EpollFileDescriptor;
|
|
struct epoll_event EpollWaitEvent[2];
|
|
struct epoll_event* InputEvent;
|
|
struct epoll_event* OutputEvent;
|
|
int PipeFileDescriptors[2] = {-1, -1};
|
|
int Result;
|
|
int Status;
|
|
|
|
//
|
|
// Initialize locals.
|
|
//
|
|
|
|
ChildPid = -1;
|
|
EpollFileDescriptor = -1;
|
|
|
|
LxtCheckResult(LxtSignalInitialize());
|
|
LxtCheckResult(LxtSignalSetupHandler(SIGPIPE, SA_SIGINFO));
|
|
|
|
//
|
|
// Open a pipe to test epoll.
|
|
//
|
|
|
|
LxtCheckErrnoZeroSuccess(pipe(PipeFileDescriptors));
|
|
|
|
//
|
|
// Create an epoll.
|
|
//
|
|
|
|
LxtCheckErrno(EpollFileDescriptor = epoll_create(1));
|
|
|
|
//
|
|
// Add the file to the epoll.
|
|
//
|
|
|
|
LXT_SYNCHRONIZATION_POINT_START();
|
|
LxtCheckErrno(ChildPid = fork());
|
|
if (ChildPid == 0)
|
|
{
|
|
LxtCheckClose(PipeFileDescriptors[0]);
|
|
LxtCheckClose(PipeFileDescriptors[1]);
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
LxtLogInfo("Waiting on epoll...", Result);
|
|
LxtCheckErrno(epoll_wait(EpollFileDescriptor, EpollWaitEvent, 2, 2000));
|
|
LxtCheckEqual(Result, 1, "%d");
|
|
LxtCheckEqual(EpollWaitEvent[0].events, EPOLLOUT, "%d");
|
|
LxtCheckEqual(EpollWaitEvent[0].data.fd, 0, "%d");
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
LxtLogInfo("Waiting on epoll...", Result);
|
|
LxtCheckErrnoZeroSuccess(epoll_wait(EpollFileDescriptor, EpollWaitEvent, 2, 2000));
|
|
goto ErrorExit;
|
|
}
|
|
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
sleep(1);
|
|
EpollControlEvent.events = EPOLLIN;
|
|
EpollControlEvent.data.fd = 0;
|
|
LxtCheckErrno(epoll_ctl(EpollFileDescriptor, EPOLL_CTL_ADD, 0, &EpollControlEvent));
|
|
|
|
EpollControlEvent.events = EPOLLIN;
|
|
EpollControlEvent.data.fd = PipeFileDescriptors[0];
|
|
LxtCheckErrno(epoll_ctl(EpollFileDescriptor, EPOLL_CTL_ADD, PipeFileDescriptors[0], &EpollControlEvent));
|
|
|
|
EpollControlEvent.events = EPOLLOUT | EPOLLONESHOT;
|
|
EpollControlEvent.data.fd = 0;
|
|
LxtCheckErrno(epoll_ctl(EpollFileDescriptor, EPOLL_CTL_MOD, 0, &EpollControlEvent));
|
|
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
|
|
//
|
|
// The child consumed the epoll event so it should no longer be available.
|
|
//
|
|
|
|
LxtCheckErrno(epoll_wait(EpollFileDescriptor, EpollWaitEvent, 2, 0));
|
|
LxtCheckEqual(Result, 0, "%d");
|
|
|
|
LxtCheckErrno(epoll_ctl(EpollFileDescriptor, EPOLL_CTL_DEL, 0, NULL));
|
|
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
sleep(1);
|
|
LxtLogInfo("Closing last file descriptor in epoll.", Result);
|
|
LxtCheckClose(PipeFileDescriptors[0]);
|
|
|
|
//
|
|
// The epoll is active, but the pipe it was waiting on is now closed.
|
|
//
|
|
|
|
LxtCheckErrnoFailure(write(PipeFileDescriptors[1], "\n", 1), EPIPE);
|
|
LxtCheckResult(LxtSignalCheckReceived(SIGPIPE));
|
|
LxtSignalResetReceived();
|
|
LxtCheckClose(PipeFileDescriptors[1]);
|
|
|
|
ErrorExit:
|
|
if (EpollFileDescriptor != -1)
|
|
{
|
|
close(EpollFileDescriptor);
|
|
}
|
|
|
|
if (PipeFileDescriptors[1] != -1)
|
|
{
|
|
close(PipeFileDescriptors[1]);
|
|
}
|
|
|
|
if (PipeFileDescriptors[0] != -1)
|
|
{
|
|
close(PipeFileDescriptors[0]);
|
|
}
|
|
|
|
LxtLogInfo("Done, PID=%d, ChildPid=%d", getpid(), ChildPid);
|
|
LXT_SYNCHRONIZATION_POINT_END();
|
|
return Result;
|
|
}
|
|
|
|
int EpollModTest(PLXT_ARGS Args)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
int Result;
|
|
int ChildPid;
|
|
struct epoll_event EpollControlEvent;
|
|
struct epoll_event EpollWaitEvent[2];
|
|
int SocketFd;
|
|
int TmpFd;
|
|
int EpollFd;
|
|
LXT_SOCKET_PAIR SocketPair;
|
|
int Status;
|
|
|
|
SocketPair.Parent = -1;
|
|
SocketPair.Child = -1;
|
|
|
|
LxtCheckErrno(EpollFd = epoll_create1(EPOLL_CLOEXEC));
|
|
LxtCheckResult(LxtSocketPairCreate(&SocketPair));
|
|
LxtLogInfo("Created socket pair (%d, %d)", SocketPair.Parent, SocketPair.Child);
|
|
EpollControlEvent.events = EPOLLET;
|
|
EpollControlEvent.data.fd = SocketPair.Parent;
|
|
LxtLogInfo("Adding socket pair (parent) (%d) file descriptor to epoll context", SocketPair.Parent);
|
|
|
|
LxtCheckErrno(epoll_ctl(EpollFd, EPOLL_CTL_ADD, SocketPair.Parent, &EpollControlEvent));
|
|
|
|
LxtLogInfo("Adding socket pair (child) (%d) file descriptor to epoll context", SocketPair.Child);
|
|
|
|
EpollControlEvent.data.fd = SocketPair.Child;
|
|
LxtCheckErrno(epoll_ctl(EpollFd, EPOLL_CTL_ADD, SocketPair.Child, &EpollControlEvent));
|
|
|
|
LxtLogInfo("Forking...");
|
|
LXT_SYNCHRONIZATION_POINT_START();
|
|
LxtCheckErrno(ChildPid = fork());
|
|
if (ChildPid == 0)
|
|
{
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
|
|
LxtLogInfo("[Child] Waiting on epoll (EPOLLIN), should timeout");
|
|
LxtCheckErrnoZeroSuccess(epoll_wait(EpollFd, EpollWaitEvent, 2, 1000));
|
|
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
|
|
LxtLogInfo("[Child] Waiting on epoll (EPOLLIN)");
|
|
LxtCheckErrno(Result = epoll_wait(EpollFd, EpollWaitEvent, 2, -1));
|
|
LxtLogInfo("[Child] EPOLLIN received");
|
|
LxtCheckEqual(Result, 1, "%d");
|
|
LxtCheckEqual(EpollWaitEvent[0].data.fd, SocketPair.Child, "%d");
|
|
LxtCheckEqual(EpollWaitEvent[0].events, EPOLLIN, "0x%x");
|
|
LxtCheckResult(LxtReceiveMessage(SocketPair.Child, "data"));
|
|
LxtLogInfo("[Child] Received message");
|
|
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
|
|
//
|
|
// Wait for parent to send data.
|
|
//
|
|
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
goto ErrorExit;
|
|
}
|
|
|
|
LxtLogInfo("Sending data over socketpair");
|
|
LxtCheckResult(LxtSendMessage(SocketPair.Parent, "data"));
|
|
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
|
|
//
|
|
// Let the child wait/timeout.
|
|
//
|
|
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
|
|
LxtLogInfo("Modifying the epoll to receive EPOLLIN events");
|
|
EpollControlEvent.events = EPOLLIN | EPOLLET;
|
|
EpollControlEvent.data.fd = SocketPair.Child;
|
|
LxtCheckErrno(epoll_ctl(EpollFd, EPOLL_CTL_MOD, SocketPair.Child, &EpollControlEvent));
|
|
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
|
|
//
|
|
// Wait for child to receive data.
|
|
//
|
|
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
|
|
Result = 0;
|
|
|
|
ErrorExit:
|
|
if (SocketPair.Parent != -1)
|
|
{
|
|
close(SocketPair.Parent);
|
|
}
|
|
|
|
if (SocketPair.Child != -1)
|
|
{
|
|
close(SocketPair.Child);
|
|
}
|
|
|
|
if (EpollFd != -1)
|
|
{
|
|
close(EpollFd);
|
|
}
|
|
|
|
LXT_SYNCHRONIZATION_POINT_END();
|
|
return Result;
|
|
}
|
|
|
|
int EpollPhantomEventsTest(PLXT_ARGS Args)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
int Result;
|
|
int ChildPid;
|
|
struct epoll_event EpollControlEvent;
|
|
struct epoll_event EpollWaitEvent[2];
|
|
int SocketFd;
|
|
int TmpFd;
|
|
int EpollFd;
|
|
LXT_SOCKET_PAIR SocketPair;
|
|
int Status;
|
|
|
|
SocketPair.Parent = -1;
|
|
SocketPair.Child = -1;
|
|
|
|
LxtCheckErrno(EpollFd = epoll_create1(EPOLL_CLOEXEC));
|
|
LxtCheckResult(LxtSocketPairCreate(&SocketPair));
|
|
LxtLogInfo("Created socket pair (%d, %d)", SocketPair.Parent, SocketPair.Child);
|
|
EpollControlEvent.events = EPOLLIN | EPOLLET;
|
|
EpollControlEvent.data.fd = SocketPair.Parent;
|
|
LxtLogInfo("Adding socket pair (parent) (%d) file descriptor to epoll context", SocketPair.Parent);
|
|
|
|
LxtCheckErrno(epoll_ctl(EpollFd, EPOLL_CTL_ADD, SocketPair.Parent, &EpollControlEvent));
|
|
|
|
LxtLogInfo("Adding socket pair (child) (%d) file descriptor to epoll context", SocketPair.Child);
|
|
|
|
EpollControlEvent.data.fd = SocketPair.Child;
|
|
LxtCheckErrno(epoll_ctl(EpollFd, EPOLL_CTL_ADD, SocketPair.Child, &EpollControlEvent));
|
|
|
|
LxtLogInfo("Forking...");
|
|
LXT_SYNCHRONIZATION_POINT_START();
|
|
LxtCheckErrno(ChildPid = fork());
|
|
if (ChildPid == 0)
|
|
{
|
|
LxtLogInfo("[Child] Waiting on epoll (EPOLLIN)");
|
|
LxtCheckErrno(Result = epoll_wait(EpollFd, EpollWaitEvent, 2, -1));
|
|
LxtCheckEqual(Result, 1, "%d");
|
|
LxtCheckEqual(EpollWaitEvent[0].data.fd, SocketPair.Child, "%d");
|
|
LxtCheckEqual(EpollWaitEvent[0].events, EPOLLIN, "0x%x");
|
|
LxtLogInfo("[Child] Waiting on message");
|
|
LxtCheckResult(LxtReceiveMessage(SocketPair.Child, "data"));
|
|
|
|
LxtLogInfo("Closing (%d) file descriptor", SocketPair.Child);
|
|
TmpFd = SocketPair.Child;
|
|
LxtClose(SocketPair.Child);
|
|
|
|
LxtCheckErrno(SocketFd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
|
|
LxtLogInfo("Created (%d) file descriptor, that should be the same as closed fd (%d)", SocketFd, TmpFd);
|
|
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
|
|
//
|
|
// Wait for parent to send data.
|
|
//
|
|
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
|
|
LxtLogInfo("Waiting on epoll phantom event (EPOLLIN)");
|
|
LxtCheckErrno(Result = epoll_wait(EpollFd, EpollWaitEvent, 2, -1));
|
|
LxtCheckEqual(Result, 1, "%d");
|
|
LxtCheckEqual(EpollWaitEvent[0].data.fd, SocketPair.Child, "%d");
|
|
LxtCheckEqual(EpollWaitEvent[0].events, EPOLLIN, "0x%x");
|
|
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
goto ErrorExit;
|
|
}
|
|
|
|
LxtLogInfo("Sending data over socketpair");
|
|
LxtCheckResult(LxtSendMessage(SocketPair.Parent, "data"));
|
|
|
|
//
|
|
// Verify that both only child receives the same EPOLLIN event.
|
|
//
|
|
|
|
sleep(1);
|
|
LxtLogInfo("Waiting on epoll (EPOLLIN) (should fail)");
|
|
LxtCheckErrno(Result = epoll_wait(EpollFd, EpollWaitEvent, 2, 100));
|
|
LxtCheckEqual(Result, 0, "%d");
|
|
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
|
|
LxtCheckResult(LxtSendMessage(SocketPair.Parent, "data"));
|
|
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
|
|
//
|
|
// Wait for child to receive phantom event.
|
|
//
|
|
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
|
|
Result = 0;
|
|
|
|
ErrorExit:
|
|
if (SocketPair.Parent != -1)
|
|
{
|
|
close(SocketPair.Parent);
|
|
}
|
|
|
|
if (SocketPair.Child != -1)
|
|
{
|
|
close(SocketPair.Child);
|
|
}
|
|
|
|
if (EpollFd != -1)
|
|
{
|
|
close(EpollFd);
|
|
}
|
|
|
|
LXT_SYNCHRONIZATION_POINT_END();
|
|
return Result;
|
|
}
|
|
|
|
int PPollInvalidArgument(PLXT_ARGS Args)
|
|
|
|
{
|
|
|
|
int FileDescriptor1;
|
|
struct pollfd PollDescriptors[4];
|
|
int Result;
|
|
struct timespec Timeout;
|
|
|
|
LxtCheckResult(FileDescriptor1 = open("/data/test/poll_test.bin", O_RDWR | O_CREAT, S_IRWXU));
|
|
PollDescriptors[0].fd = FileDescriptor1;
|
|
PollDescriptors[0].events = POLLIN;
|
|
PollDescriptors[0].revents = -1;
|
|
|
|
//
|
|
// Invalid argument variations.
|
|
//
|
|
|
|
memset(&Timeout, 0, sizeof(Timeout));
|
|
Timeout.tv_sec = -1;
|
|
LxtCheckErrnoFailure(ppoll(PollDescriptors, 1, &Timeout, NULL), EINVAL);
|
|
memset(&Timeout, 0, sizeof(Timeout));
|
|
Timeout.tv_nsec = 999999999 + 1;
|
|
LxtCheckErrnoFailure(ppoll(PollDescriptors, 1, &Timeout, NULL), EINVAL);
|
|
|
|
ErrorExit:
|
|
if (FileDescriptor1 != -1)
|
|
{
|
|
LxtClose(FileDescriptor1);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
int EpollUnalignedTest(PLXT_ARGS Args)
|
|
|
|
{
|
|
|
|
char Buffer[sizeof(struct pollfd) + 1];
|
|
int FileDescriptor1;
|
|
struct pollfd* PollDescriptors;
|
|
int Result;
|
|
struct timespec Timeout;
|
|
|
|
//
|
|
// Set up the poll descriptors array to be unaligned.
|
|
//
|
|
|
|
PollDescriptors = (struct pollfd*)&Buffer[1];
|
|
LxtLogInfo("PollDescriptors address %p", PollDescriptors);
|
|
|
|
LxtCheckResult(FileDescriptor1 = open("/data/test/poll_test.bin", O_RDWR | O_CREAT, S_IRWXU));
|
|
PollDescriptors[0].fd = FileDescriptor1;
|
|
PollDescriptors[0].events = POLLIN;
|
|
PollDescriptors[0].revents = -1;
|
|
memset(&Timeout, 0, sizeof(Timeout));
|
|
Timeout.tv_sec = 1;
|
|
LxtCheckErrno(ppoll(PollDescriptors, 1, &Timeout, NULL));
|
|
|
|
ErrorExit:
|
|
if (FileDescriptor1 != -1)
|
|
{
|
|
LxtClose(FileDescriptor1);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
int EpollDeleteCloseFdLoop(PLXT_ARGS Args)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine loops\stresses the removal of EPOLL FD and closing of the FD.
|
|
|
|
Arguments:
|
|
|
|
Args - Supplies the command line arguments.
|
|
|
|
Return Value:
|
|
|
|
Returns 0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
char Buffer[10];
|
|
int ChildPid;
|
|
struct epoll_event EpollControlEvent;
|
|
const int NumFd = 100;
|
|
int EpollFd[NumFd];
|
|
int Iterator;
|
|
int Loop;
|
|
int NestedEpollFd[NumFd];
|
|
int PipeFileDescriptors[2] = {-1, -1};
|
|
int Result;
|
|
int SharedEpollFd;
|
|
int SocketFd[NumFd];
|
|
int Status;
|
|
|
|
//
|
|
// Initialize locals.
|
|
//
|
|
|
|
ChildPid = -1;
|
|
for (Iterator = 0; Iterator < NumFd; Iterator++)
|
|
{
|
|
SocketFd[Iterator] = -1;
|
|
EpollFd[Iterator] = -1;
|
|
NestedEpollFd[Iterator] = -1;
|
|
}
|
|
|
|
LxtCheckErrnoZeroSuccess(pipe(PipeFileDescriptors));
|
|
LxtCheckErrno(write(PipeFileDescriptors[1], "\n", 1));
|
|
LxtCheckErrno(SharedEpollFd = epoll_create1(0));
|
|
|
|
//
|
|
// Start a loop to read the pipe, thereby triggering a notification.
|
|
//
|
|
|
|
LxtCheckErrno(ChildPid = fork());
|
|
if (ChildPid == 0)
|
|
{
|
|
LxtCheckClose(PipeFileDescriptors[1]);
|
|
while ((Result = read(PipeFileDescriptors[0], Buffer, sizeof(Buffer))) > 0)
|
|
;
|
|
|
|
//
|
|
// The loop should terminate when the other threads exit and
|
|
// close the write pipe handle.
|
|
//
|
|
|
|
LxtCheckErrno(Result);
|
|
goto ErrorExit;
|
|
}
|
|
|
|
for (Loop = 0; Loop < 50; Loop++)
|
|
{
|
|
for (Iterator = 0; Iterator < NumFd; Iterator++)
|
|
{
|
|
LxtCheckErrno(EpollFd[Iterator] = epoll_create1(0));
|
|
LxtCheckErrno(NestedEpollFd[Iterator] = epoll_create1(0));
|
|
LxtCheckErrno(SocketFd[Iterator] = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0));
|
|
|
|
EpollControlEvent.events = EPOLLIN | EPOLLET;
|
|
EpollControlEvent.data.fd = SocketFd[Iterator];
|
|
LxtCheckErrno(epoll_ctl(EpollFd[Iterator], EPOLL_CTL_ADD, SocketFd[Iterator], &EpollControlEvent));
|
|
|
|
EpollControlEvent.events = EPOLLIN;
|
|
EpollControlEvent.data.fd = PipeFileDescriptors[0];
|
|
LxtCheckErrno(epoll_ctl(NestedEpollFd[Iterator], EPOLL_CTL_ADD, PipeFileDescriptors[0], &EpollControlEvent));
|
|
|
|
EpollControlEvent.events = EPOLLIN;
|
|
EpollControlEvent.data.fd = NestedEpollFd[Iterator];
|
|
LxtCheckErrno(epoll_ctl(EpollFd[Iterator], EPOLL_CTL_ADD, NestedEpollFd[Iterator], &EpollControlEvent));
|
|
|
|
EpollControlEvent.events = EPOLLIN;
|
|
EpollControlEvent.data.fd = EpollFd[Iterator];
|
|
LxtCheckErrno(epoll_ctl(SharedEpollFd, EPOLL_CTL_ADD, EpollFd[Iterator], &EpollControlEvent));
|
|
}
|
|
|
|
LXT_SYNCHRONIZATION_POINT_START();
|
|
LxtCheckErrno(ChildPid = fork());
|
|
if (ChildPid == 0)
|
|
{
|
|
|
|
for (Iterator = 0; Iterator < NumFd; Iterator++)
|
|
{
|
|
LxtClose(NestedEpollFd[Iterator]);
|
|
NestedEpollFd[Iterator] = -1;
|
|
LxtClose(SocketFd[Iterator]);
|
|
SocketFd[Iterator] = -1;
|
|
}
|
|
|
|
//
|
|
// The race is between releasing the last reference to the file
|
|
// descriptor here and releasing the last reference to EPOLL by
|
|
// the child. Synchronize to keep the race as close as possible.
|
|
//
|
|
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
for (Iterator = 0; Iterator < NumFd; Iterator++)
|
|
{
|
|
LxtClose(EpollFd[Iterator]);
|
|
EpollFd[Iterator] = -1;
|
|
}
|
|
|
|
_exit(0);
|
|
}
|
|
|
|
for (Iterator = 0; Iterator < NumFd; Iterator++)
|
|
{
|
|
LxtClose(EpollFd[Iterator]);
|
|
EpollFd[Iterator] = -1;
|
|
}
|
|
|
|
//
|
|
// See the above comment about synchronizing to keep the race close.
|
|
//
|
|
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
for (Iterator = 0; Iterator < NumFd; Iterator++)
|
|
{
|
|
LxtCheckErrno(write(PipeFileDescriptors[1], "\n", 1));
|
|
LxtClose(NestedEpollFd[Iterator]);
|
|
NestedEpollFd[Iterator] = -1;
|
|
LxtClose(SocketFd[Iterator]);
|
|
SocketFd[Iterator] = -1;
|
|
}
|
|
|
|
LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));
|
|
}
|
|
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
ErrorExit:
|
|
if (PipeFileDescriptors[1] != -1)
|
|
{
|
|
close(PipeFileDescriptors[1]);
|
|
}
|
|
|
|
if (PipeFileDescriptors[0] != -1)
|
|
{
|
|
close(PipeFileDescriptors[0]);
|
|
}
|
|
|
|
if (SharedEpollFd != -1)
|
|
{
|
|
close(SharedEpollFd);
|
|
}
|
|
|
|
for (Iterator = 0; Iterator < NumFd; Iterator++)
|
|
{
|
|
|
|
//
|
|
// Only close socket FD's in the parent because socket FD's are created
|
|
// with CLOSE_ON_EXEC and so are only valid in the parent.
|
|
//
|
|
|
|
if ((SocketFd[Iterator] != -1) && (ChildPid != 0))
|
|
{
|
|
close(SocketFd[Iterator]);
|
|
}
|
|
|
|
if ((NestedEpollFd[Iterator] != -1) && (ChildPid != 0))
|
|
{
|
|
close(NestedEpollFd[Iterator]);
|
|
}
|
|
|
|
if (EpollFd[Iterator] != -1)
|
|
{
|
|
close(EpollFd[Iterator]);
|
|
}
|
|
}
|
|
|
|
if (ChildPid == 0)
|
|
{
|
|
_exit(0);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
void* EpollDup2FdLoopThread(void* Parameter)
|
|
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This routine is the thread handler for the EpollDup2FdLoop test.
|
|
|
|
Arguments:
|
|
|
|
Parameter - Supplies the thread parameter.
|
|
|
|
Return Value:
|
|
|
|
Returns the thread id on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PEPOLL_DUP2_CONTEXT Context;
|
|
struct epoll_event EpollControlEvent;
|
|
int Iterator;
|
|
int Loop;
|
|
int Result;
|
|
|
|
Context = (PEPOLL_DUP2_CONTEXT)Parameter;
|
|
for (Loop = 0; Loop < 50; Loop++)
|
|
{
|
|
for (Iterator = 0; Iterator < EPOLL_DUP2_FD_COUNT; Iterator++)
|
|
{
|
|
LXT_SYNCHRONIZATION_POINT_CHILD();
|
|
EpollControlEvent.events = EPOLLIN;
|
|
EpollControlEvent.data.fd = Context->Fd[Iterator];
|
|
if (epoll_ctl(Context->EpollFd, EPOLL_CTL_DEL, Context->Fd[Iterator], &EpollControlEvent) != 0)
|
|
{
|
|
|
|
Result = errno;
|
|
|
|
//
|
|
// Race with parent is expected to cause the fd to be invalid
|
|
// at times (not the same file that was added).
|
|
//
|
|
|
|
if (Result == EINVAL)
|
|
{
|
|
Result = LXT_RESULT_SUCCESS;
|
|
}
|
|
|
|
LxtCheckResult(Result);
|
|
}
|
|
}
|
|
}
|
|
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
ErrorExit:
|
|
LXT_SYNCHRONIZATION_POINT_PTHREAD_END_THREAD();
|
|
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wint-to-pointer-cast"
|
|
|
|
return (void*)Result;
|
|
|
|
#pragma GCC diagnostic pop
|
|
}
|
|
|
|
int EpollDup2FdLoop(PLXT_ARGS Args)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine loops\stresses epoll operations on fd's that are being closed
|
|
by other threads. The close is done via dup2 because dup2 also holds the
|
|
filetable lock, increasing the chances of hitting locking issues.
|
|
|
|
Arguments:
|
|
|
|
Args - Supplies the command line arguments.
|
|
|
|
Return Value:
|
|
|
|
Returns 0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
struct epoll_event EpollControlEvent;
|
|
EPOLL_DUP2_CONTEXT Context;
|
|
int Iterator;
|
|
int Loop;
|
|
int NullFd;
|
|
const int NumFd = EPOLL_DUP2_FD_COUNT;
|
|
int Result;
|
|
void* Status;
|
|
pthread_t Thread = 0;
|
|
|
|
//
|
|
// Initialize locals.
|
|
//
|
|
|
|
Context.EpollFd = -1;
|
|
for (Iterator = 0; Iterator < NumFd; Iterator++)
|
|
{
|
|
Context.Fd[Iterator] = -1;
|
|
}
|
|
|
|
LxtCheckErrno(NullFd = open("/dev/null", O_RDWR));
|
|
LxtCheckErrno(Context.EpollFd = epoll_create1(0));
|
|
LXT_SYNCHRONIZATION_POINT_START();
|
|
LxtCheckResultError(pthread_create(&Thread, NULL, EpollDup2FdLoopThread, &Context));
|
|
|
|
for (Loop = 0; Loop < 50; Loop++)
|
|
{
|
|
for (Iterator = 0; Iterator < NumFd; Iterator++)
|
|
{
|
|
LxtCheckErrno(Context.Fd[Iterator] = open("/dev/null", O_RDWR));
|
|
EpollControlEvent.events = EPOLLIN | EPOLLET;
|
|
EpollControlEvent.data.fd = Context.Fd[Iterator];
|
|
LxtCheckErrno(epoll_ctl(Context.EpollFd, EPOLL_CTL_ADD, Context.Fd[Iterator], &EpollControlEvent));
|
|
}
|
|
|
|
for (Iterator = 0; Iterator < NumFd; Iterator++)
|
|
{
|
|
LXT_SYNCHRONIZATION_POINT_PARENT();
|
|
LxtCheckErrno(dup2(NullFd, Context.Fd[Iterator]));
|
|
}
|
|
|
|
for (Iterator = 0; Iterator < NumFd; Iterator++)
|
|
{
|
|
LxtClose(Context.Fd[Iterator]);
|
|
Context.Fd[Iterator] = -1;
|
|
}
|
|
}
|
|
|
|
pthread_join(Thread, (void**)&Result);
|
|
Thread = 0;
|
|
LxtCheckResult(Result);
|
|
|
|
ErrorExit:
|
|
LXT_SYNCHRONIZATION_POINT_PTHREAD_END_PARENT(Thread);
|
|
for (Iterator = 0; Iterator < NumFd; Iterator++)
|
|
{
|
|
if (Context.Fd[Iterator] != -1)
|
|
{
|
|
close(Context.Fd[Iterator]);
|
|
}
|
|
}
|
|
|
|
if (Context.EpollFd != -1)
|
|
{
|
|
close(Context.EpollFd);
|
|
}
|
|
|
|
if (NullFd != -1)
|
|
{
|
|
close(NullFd);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
int EpollRelatedFileStress(PLXT_ARGS Args)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine loops\stresses the usage of multiple epoll files containing
|
|
the same set of fds.
|
|
|
|
Arguments:
|
|
|
|
Args - Supplies the command line arguments.
|
|
|
|
Return Value:
|
|
|
|
Returns 0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
int AddRemoveLoop;
|
|
char Buffer[10];
|
|
int ChildPid;
|
|
struct epoll_event EpollControlEvent[3];
|
|
int EpollFd[2];
|
|
int Iterator;
|
|
int Loop;
|
|
int MasterEpollFd;
|
|
int Result;
|
|
int SharedEpollFd;
|
|
int Status;
|
|
|
|
//
|
|
// Initialize locals.
|
|
//
|
|
|
|
ChildPid = -1;
|
|
MasterEpollFd = -1;
|
|
for (Iterator = 0; Iterator < LXT_COUNT_OF(EpollFd); Iterator++)
|
|
{
|
|
EpollFd[Iterator] = -1;
|
|
}
|
|
|
|
LxtCheckErrno(MasterEpollFd = epoll_create1(0));
|
|
for (Iterator = 0; Iterator < LXT_COUNT_OF(EpollFd); Iterator++)
|
|
{
|
|
LxtCheckErrno(EpollFd[Iterator] = epoll_create1(0));
|
|
EpollControlEvent[0].events = EPOLLIN;
|
|
EpollControlEvent[0].data.fd = EpollFd[Iterator];
|
|
LxtCheckErrno(epoll_ctl(MasterEpollFd, EPOLL_CTL_ADD, EpollFd[Iterator], EpollControlEvent));
|
|
}
|
|
|
|
LXT_SYNCHRONIZATION_POINT_START();
|
|
LxtCheckErrno(ChildPid = fork());
|
|
for (Loop = 0; Loop < 2000; Loop++)
|
|
{
|
|
if (ChildPid == 0)
|
|
{
|
|
|
|
//
|
|
// Synchronize with the wait loop to try to increase the
|
|
// chances of hitting a race.
|
|
//
|
|
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
for (AddRemoveLoop = 0; AddRemoveLoop < LXT_COUNT_OF(EpollControlEvent); AddRemoveLoop++)
|
|
{
|
|
|
|
for (Iterator = 0; Iterator < LXT_COUNT_OF(EpollFd); Iterator++)
|
|
{
|
|
|
|
EpollControlEvent[0].events = EPOLLIN;
|
|
EpollControlEvent[0].data.fd = 0;
|
|
LxtCheckErrno(epoll_ctl(EpollFd[Iterator], EPOLL_CTL_ADD, 0, EpollControlEvent));
|
|
|
|
EpollControlEvent[0].events = EPOLLOUT;
|
|
EpollControlEvent[0].data.fd = 1;
|
|
LxtCheckErrno(epoll_ctl(EpollFd[Iterator], EPOLL_CTL_ADD, 1, EpollControlEvent));
|
|
|
|
EpollControlEvent[0].events = EPOLLOUT;
|
|
EpollControlEvent[0].data.fd = 2;
|
|
LxtCheckErrno(epoll_ctl(EpollFd[Iterator], EPOLL_CTL_ADD, 2, EpollControlEvent));
|
|
}
|
|
|
|
for (Iterator = 0; Iterator < LXT_COUNT_OF(EpollFd); Iterator++)
|
|
{
|
|
|
|
LxtCheckErrno(epoll_ctl(EpollFd[Iterator], EPOLL_CTL_DEL, 0, NULL));
|
|
|
|
LxtCheckErrno(epoll_ctl(EpollFd[Iterator], EPOLL_CTL_DEL, 1, NULL));
|
|
|
|
LxtCheckErrno(epoll_ctl(EpollFd[Iterator], EPOLL_CTL_DEL, 2, NULL));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
(void)epoll_wait(MasterEpollFd, EpollControlEvent, LXT_COUNT_OF(EpollControlEvent), 1);
|
|
|
|
for (Iterator = 0; Iterator < LXT_COUNT_OF(EpollFd); Iterator++)
|
|
{
|
|
(void)epoll_wait(EpollFd[Iterator], EpollControlEvent, LXT_COUNT_OF(EpollControlEvent), 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
ErrorExit:
|
|
LXT_SYNCHRONIZATION_POINT_END();
|
|
for (Iterator = 0; Iterator < LXT_COUNT_OF(EpollFd); Iterator++)
|
|
{
|
|
if (EpollFd[Iterator] != -1)
|
|
{
|
|
close(EpollFd[Iterator]);
|
|
}
|
|
}
|
|
|
|
if (MasterEpollFd != -1)
|
|
{
|
|
close(MasterEpollFd);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
int EpollRecursionTest(PLXT_ARGS Args)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine verifies epoll file included in epoll file behavior.
|
|
|
|
Arguments:
|
|
|
|
Args - Supplies the command line arguments.
|
|
|
|
Return Value:
|
|
|
|
Returns 0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
char Buffer[10];
|
|
struct epoll_event EpollControlEvent;
|
|
int EpollFileDescriptor;
|
|
int EpollContainerFd;
|
|
int EpollContainer2Fd;
|
|
struct epoll_event EpollWaitEvent;
|
|
struct epoll_event* InputEvent;
|
|
struct epoll_event* OutputEvent;
|
|
int PipeFileDescriptors[2] = {-1, -1};
|
|
int PipeFileDescriptors2[2] = {-1, -1};
|
|
fd_set ReadFds;
|
|
int Result;
|
|
struct timeval Timeout;
|
|
fd_set WriteFds;
|
|
|
|
//
|
|
// Initialize locals.
|
|
//
|
|
|
|
EpollFileDescriptor = -1;
|
|
EpollContainerFd = -1;
|
|
EpollContainer2Fd = -1;
|
|
|
|
//
|
|
// Create two epoll files.
|
|
//
|
|
|
|
LxtCheckErrno(EpollFileDescriptor = epoll_create(1));
|
|
LxtCheckErrno(EpollContainerFd = epoll_create(1));
|
|
|
|
//
|
|
// Open a pipe to test epoll.
|
|
//
|
|
|
|
LxtCheckErrnoZeroSuccess(pipe(PipeFileDescriptors));
|
|
LxtCheckErrnoZeroSuccess(pipe(PipeFileDescriptors2));
|
|
|
|
//
|
|
// Pend a write.
|
|
//
|
|
|
|
LxtCheckErrno(write(PipeFileDescriptors[1], "\n", 1));
|
|
|
|
//
|
|
// Add one epoll file to the other.
|
|
//
|
|
|
|
EpollControlEvent.events = EPOLLIN;
|
|
EpollControlEvent.data.fd = EpollFileDescriptor;
|
|
LxtCheckErrno(epoll_ctl(EpollContainerFd, EPOLL_CTL_ADD, EpollFileDescriptor, &EpollControlEvent));
|
|
|
|
//
|
|
// Now attempt to add them in reverse order to create a simple loop.
|
|
//
|
|
|
|
EpollControlEvent.events = EPOLLIN;
|
|
EpollControlEvent.data.fd = EpollContainerFd;
|
|
LxtCheckErrnoFailure(epoll_ctl(EpollFileDescriptor, EPOLL_CTL_ADD, EpollContainerFd, &EpollControlEvent), ELOOP);
|
|
|
|
//
|
|
// Attempt to modify swapping the container/included descriptors.
|
|
//
|
|
|
|
EpollControlEvent.events = EPOLLIN;
|
|
EpollControlEvent.data.fd = EpollContainerFd;
|
|
LxtCheckErrnoFailure(epoll_ctl(EpollFileDescriptor, EPOLL_CTL_MOD, EpollContainerFd, &EpollControlEvent), ENOENT);
|
|
|
|
//
|
|
// Attempt to delete swapping the container/included descriptors.
|
|
//
|
|
|
|
EpollControlEvent.data.fd = EpollContainerFd;
|
|
LxtCheckErrnoFailure(epoll_ctl(EpollFileDescriptor, EPOLL_CTL_DEL, EpollContainerFd, NULL), ENOENT);
|
|
|
|
//
|
|
// Add the read pipe end to the epoll.
|
|
//
|
|
|
|
EpollControlEvent.events = EPOLLIN;
|
|
EpollControlEvent.data.fd = PipeFileDescriptors[0];
|
|
LxtCheckErrno(epoll_ctl(EpollFileDescriptor, EPOLL_CTL_ADD, PipeFileDescriptors[0], &EpollControlEvent));
|
|
|
|
//
|
|
// Verify the epoll is signalled.
|
|
//
|
|
|
|
memset(&Timeout, 0, sizeof(Timeout));
|
|
FD_ZERO(&ReadFds);
|
|
FD_SET(EpollFileDescriptor, &ReadFds);
|
|
LxtCheckErrno(select((EpollFileDescriptor + 1), &ReadFds, NULL, NULL, &Timeout));
|
|
LxtCheckEqual(Result, 1, "%d");
|
|
|
|
//
|
|
// Add the second pipe, which is not read ready.
|
|
//
|
|
|
|
EpollControlEvent.events = EPOLLIN;
|
|
EpollControlEvent.data.fd = PipeFileDescriptors[0];
|
|
LxtCheckErrno(epoll_ctl(EpollFileDescriptor, EPOLL_CTL_ADD, PipeFileDescriptors2[0], &EpollControlEvent));
|
|
|
|
//
|
|
// Verify the epoll remains signalled.
|
|
//
|
|
|
|
memset(&Timeout, 0, sizeof(Timeout));
|
|
FD_ZERO(&ReadFds);
|
|
FD_SET(EpollFileDescriptor, &ReadFds);
|
|
LxtCheckErrno(select((EpollFileDescriptor + 1), &ReadFds, NULL, NULL, &Timeout));
|
|
LxtCheckEqual(Result, 1, "%d");
|
|
|
|
//
|
|
// Pend a write to the other pipe.
|
|
//
|
|
|
|
LxtCheckErrno(write(PipeFileDescriptors2[1], "\n", 1));
|
|
|
|
//
|
|
// Verify the epoll remains signalled.
|
|
//
|
|
|
|
memset(&Timeout, 0, sizeof(Timeout));
|
|
FD_ZERO(&ReadFds);
|
|
FD_SET(EpollFileDescriptor, &ReadFds);
|
|
LxtCheckErrno(select((EpollFileDescriptor + 1), &ReadFds, NULL, NULL, &Timeout));
|
|
LxtCheckEqual(Result, 1, "%d");
|
|
|
|
//
|
|
// Create another epoll containing the first.
|
|
//
|
|
|
|
LxtCheckErrno(EpollContainerFd = epoll_create(1));
|
|
|
|
//
|
|
// Add the first epoll.
|
|
//
|
|
|
|
EpollControlEvent.events = EPOLLIN;
|
|
EpollControlEvent.data.fd = EpollFileDescriptor;
|
|
LxtCheckErrno(epoll_ctl(EpollContainerFd, EPOLL_CTL_ADD, EpollFileDescriptor, &EpollControlEvent));
|
|
|
|
//
|
|
// Try to add the second back to the first to create a loop.
|
|
//
|
|
|
|
EpollControlEvent.events = EPOLLIN;
|
|
EpollControlEvent.data.fd = EpollContainerFd;
|
|
LxtCheckErrnoFailure(epoll_ctl(EpollFileDescriptor, EPOLL_CTL_ADD, EpollContainerFd, &EpollControlEvent), ELOOP);
|
|
|
|
//
|
|
// The first epoll should trigger the second.
|
|
//
|
|
|
|
LxtCheckErrno(epoll_wait(EpollContainerFd, &EpollWaitEvent, 1, 0));
|
|
LxtCheckEqual(Result, 1, "%d");
|
|
|
|
//
|
|
// Add the second pipe directly to the container epoll.
|
|
//
|
|
|
|
EpollControlEvent.events = EPOLLIN;
|
|
EpollControlEvent.data.fd = PipeFileDescriptors2[0];
|
|
LxtCheckErrno(epoll_ctl(EpollContainerFd, EPOLL_CTL_ADD, PipeFileDescriptors2[0], &EpollControlEvent));
|
|
|
|
//
|
|
// The epoll should remain signalled.
|
|
//
|
|
|
|
LxtCheckErrno(epoll_wait(EpollContainerFd, &EpollWaitEvent, 1, 0));
|
|
LxtCheckEqual(Result, 1, "%d");
|
|
|
|
//
|
|
// Create yet another epoll to test non-read signals.
|
|
//
|
|
|
|
LxtCheckErrno(EpollContainer2Fd = epoll_create(1));
|
|
EpollControlEvent.events = EPOLLOUT;
|
|
EpollControlEvent.data.fd = EpollFileDescriptor;
|
|
LxtCheckErrno(epoll_ctl(EpollContainer2Fd, EPOLL_CTL_ADD, EpollFileDescriptor, &EpollControlEvent));
|
|
|
|
FD_ZERO(&WriteFds);
|
|
FD_SET(EpollContainer2Fd, &WriteFds);
|
|
memset(&Timeout, 0, sizeof(Timeout));
|
|
LxtCheckErrno(select((EpollContainer2Fd + 1), NULL, &WriteFds, NULL, &Timeout));
|
|
LxtCheckEqual(Result, 0, "%d");
|
|
LxtCheckErrno(epoll_wait(EpollContainer2Fd, &EpollWaitEvent, 1, 0));
|
|
LxtCheckEqual(Result, 0, "%d");
|
|
|
|
//
|
|
// Clear the signal for the second pipe and verify signal state.
|
|
//
|
|
|
|
LxtCheckErrno(read(PipeFileDescriptors2[0], Buffer, sizeof(Buffer)));
|
|
FD_SET(EpollFileDescriptor, &ReadFds);
|
|
memset(&Timeout, 0, sizeof(Timeout));
|
|
LxtCheckErrno(select((EpollFileDescriptor + 1), &ReadFds, NULL, NULL, &Timeout));
|
|
LxtCheckEqual(Result, 1, "%d");
|
|
LxtCheckErrno(epoll_wait(EpollContainerFd, &EpollWaitEvent, 1, 0));
|
|
LxtCheckEqual(Result, 1, "%d");
|
|
|
|
//
|
|
// Clear the signal for the first pipe and verify signal state.
|
|
//
|
|
|
|
LxtCheckErrno(read(PipeFileDescriptors[0], Buffer, sizeof(Buffer)));
|
|
FD_SET(EpollFileDescriptor, &ReadFds);
|
|
memset(&Timeout, 0, sizeof(Timeout));
|
|
LxtCheckErrno(select((EpollFileDescriptor + 1), &ReadFds, NULL, NULL, &Timeout));
|
|
LxtCheckEqual(Result, 0, "%d");
|
|
LxtCheckErrno(epoll_wait(EpollContainerFd, &EpollWaitEvent, 1, 0));
|
|
LxtCheckEqual(Result, 0, "%d");
|
|
|
|
//
|
|
// Signal both pipes again.
|
|
//
|
|
|
|
LxtCheckErrno(write(PipeFileDescriptors2[1], "\n", 1));
|
|
LxtCheckErrno(write(PipeFileDescriptors2[1], "\n", 1));
|
|
|
|
//
|
|
// Verify signal.
|
|
//
|
|
|
|
FD_SET(EpollFileDescriptor, &ReadFds);
|
|
memset(&Timeout, 0, sizeof(Timeout));
|
|
LxtCheckErrno(select((EpollFileDescriptor + 1), &ReadFds, NULL, NULL, &Timeout));
|
|
LxtCheckEqual(Result, 1, "%d");
|
|
LxtCheckErrno(epoll_wait(EpollContainerFd, &EpollWaitEvent, 1, 0));
|
|
LxtCheckEqual(Result, 1, "%d");
|
|
|
|
//
|
|
// Remove the second pipe from the container and check signal state again.
|
|
//
|
|
|
|
LxtCheckErrno(epoll_ctl(EpollContainerFd, EPOLL_CTL_DEL, PipeFileDescriptors2[0], NULL));
|
|
|
|
FD_SET(EpollFileDescriptor, &ReadFds);
|
|
memset(&Timeout, 0, sizeof(Timeout));
|
|
LxtCheckErrno(select((EpollFileDescriptor + 1), &ReadFds, NULL, NULL, &Timeout));
|
|
LxtCheckEqual(Result, 1, "%d");
|
|
LxtCheckErrno(epoll_wait(EpollContainerFd, &EpollWaitEvent, 1, 0));
|
|
LxtCheckEqual(Result, 1, "%d");
|
|
|
|
//
|
|
// Close the nested epoll and verify signal state.
|
|
//
|
|
|
|
LxtCheckClose(EpollFileDescriptor);
|
|
LxtCheckErrno(epoll_wait(EpollContainerFd, &EpollWaitEvent, 1, 0));
|
|
LxtCheckEqual(Result, 0, "%d");
|
|
|
|
ErrorExit:
|
|
if (EpollContainer2Fd != -1)
|
|
{
|
|
close(EpollContainer2Fd);
|
|
}
|
|
|
|
if (EpollFileDescriptor != -1)
|
|
{
|
|
close(EpollFileDescriptor);
|
|
}
|
|
|
|
if (EpollContainerFd != -1)
|
|
{
|
|
close(EpollContainerFd);
|
|
}
|
|
|
|
if (PipeFileDescriptors2[1] != -1)
|
|
{
|
|
close(PipeFileDescriptors2[1]);
|
|
}
|
|
|
|
if (PipeFileDescriptors2[0] != -1)
|
|
{
|
|
close(PipeFileDescriptors2[0]);
|
|
}
|
|
|
|
if (PipeFileDescriptors[1] != -1)
|
|
{
|
|
close(PipeFileDescriptors[1]);
|
|
}
|
|
|
|
if (PipeFileDescriptors[0] != -1)
|
|
{
|
|
close(PipeFileDescriptors[0]);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
int EpollRecursionLimitTest(PLXT_ARGS Args)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks for an upper limit of recursive epolls.
|
|
|
|
Arguments:
|
|
|
|
Args - Supplies the command line arguments.
|
|
|
|
Return Value:
|
|
|
|
Returns 0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
#define EPOLL_MAX_RECURSION_COUNT 6
|
|
#define EPOLL_CHAIN_COUNT 50
|
|
|
|
int ChainIndex;
|
|
struct epoll_event EpollControlEvent;
|
|
int EpollFds[EPOLL_CHAIN_COUNT][EPOLL_MAX_RECURSION_COUNT];
|
|
int EpollContentFds[EPOLL_MAX_RECURSION_COUNT];
|
|
struct epoll_event EpollWaitEvent;
|
|
int ExtraEpollFd;
|
|
int Index;
|
|
int PipeFileDescriptors[2] = {-1, -1};
|
|
int Result;
|
|
|
|
//
|
|
// Initialize locals.
|
|
//
|
|
|
|
ExtraEpollFd = -1;
|
|
memset(EpollFds, -1, sizeof(EpollFds));
|
|
memset(EpollContentFds, -1, sizeof(EpollContentFds));
|
|
memset(&EpollControlEvent, 0, sizeof(EpollControlEvent));
|
|
memset(&EpollWaitEvent, 0, sizeof(EpollWaitEvent));
|
|
EpollControlEvent.events = EPOLLIN;
|
|
LxtCheckErrnoZeroSuccess(pipe(PipeFileDescriptors));
|
|
|
|
for (ChainIndex = 0; ChainIndex < EPOLL_CHAIN_COUNT; ChainIndex += 1)
|
|
{
|
|
for (Index = 0; Index < EPOLL_MAX_RECURSION_COUNT; Index += 1)
|
|
{
|
|
|
|
//
|
|
// Create a new epoll.
|
|
//
|
|
|
|
LxtCheckErrno(EpollFds[ChainIndex][Index] = epoll_create(1));
|
|
|
|
//
|
|
// Add the previous epoll to make a chain.
|
|
//
|
|
|
|
if (Index > 0)
|
|
{
|
|
LxtCheckErrno(epoll_ctl(EpollFds[ChainIndex][Index], EPOLL_CTL_ADD, EpollFds[ChainIndex][Index - 1], &EpollControlEvent));
|
|
}
|
|
}
|
|
}
|
|
|
|
LxtCheckErrno(ExtraEpollFd = epoll_create(1));
|
|
|
|
//
|
|
// Attempt to add an epoll exceeding the maximum depth.
|
|
//
|
|
|
|
LxtCheckErrnoFailure(epoll_ctl(ExtraEpollFd, EPOLL_CTL_ADD, EpollFds[0][Index - 1], &EpollControlEvent), ELOOP);
|
|
|
|
//
|
|
// Add a pipe file descriptor.
|
|
//
|
|
|
|
LxtCheckErrno(epoll_ctl(EpollFds[0][5], EPOLL_CTL_ADD, PipeFileDescriptors[0], &EpollControlEvent));
|
|
|
|
//
|
|
// Try creating a chain with a regular file descriptor in it.
|
|
//
|
|
|
|
for (Index = 0; Index < (EPOLL_MAX_RECURSION_COUNT - 1); Index += 1)
|
|
{
|
|
|
|
//
|
|
// Create a new epoll.
|
|
//
|
|
|
|
LxtCheckErrno(EpollContentFds[Index] = epoll_create(1));
|
|
|
|
//
|
|
// Add a regular file descriptor.
|
|
//
|
|
|
|
LxtCheckErrno(epoll_ctl(EpollContentFds[Index], EPOLL_CTL_ADD, PipeFileDescriptors[0], &EpollControlEvent));
|
|
|
|
//
|
|
// Add the previous epoll to make a chain.
|
|
//
|
|
|
|
if (Index > 0)
|
|
{
|
|
LxtCheckErrno(epoll_ctl(EpollContentFds[Index], EPOLL_CTL_ADD, EpollContentFds[Index - 1], &EpollControlEvent));
|
|
}
|
|
}
|
|
|
|
//
|
|
// A non-epoll file descriptor is not allowed to be nested deeper
|
|
// than EPOLL_MAX_RECURSION_COUNT. There is a pipe file descriptor
|
|
// at [0][0] so this add attempt is expected to fail.
|
|
//
|
|
|
|
LxtCheckErrno(EpollContentFds[Index] = epoll_create(1));
|
|
LxtCheckErrnoFailure(epoll_ctl(EpollContentFds[Index], EPOLL_CTL_ADD, EpollContentFds[Index - 1], &EpollControlEvent), EINVAL);
|
|
|
|
//
|
|
// Attempt to link chains together, where each add stays below the limit
|
|
// but the total chain size becomes increasingly large.
|
|
//
|
|
|
|
for (ChainIndex = (EPOLL_CHAIN_COUNT - 1); ChainIndex > 0; ChainIndex -= 1)
|
|
{
|
|
LxtLogInfo("[%d][%d] -> [%d][%d]", ChainIndex, 0, ChainIndex - 1, Index - 2);
|
|
LxtCheckErrno(epoll_ctl(EpollFds[ChainIndex][0], EPOLL_CTL_ADD, EpollFds[ChainIndex - 1][Index - 2], &EpollControlEvent));
|
|
}
|
|
|
|
//
|
|
// Now try to introduce a loop into this extra-large chain.
|
|
//
|
|
|
|
LxtCheckErrnoFailure(epoll_ctl(EpollFds[0][0], EPOLL_CTL_ADD, EpollFds[1][0], &EpollControlEvent), ELOOP);
|
|
|
|
LxtCheckErrnoFailure(epoll_ctl(EpollFds[0][0], EPOLL_CTL_ADD, EpollFds[EPOLL_CHAIN_COUNT - 1][Index - 1], &EpollControlEvent), ELOOP);
|
|
|
|
//
|
|
// The resulting chain is essentially useless. Try to add a file descriptor
|
|
// to a node in the chain.
|
|
//
|
|
|
|
LxtCheckErrnoFailure(epoll_ctl(EpollFds[0][0], EPOLL_CTL_ADD, PipeFileDescriptors[0], &EpollControlEvent), EINVAL);
|
|
|
|
LxtCheckErrnoFailure(epoll_ctl(EpollFds[EPOLL_CHAIN_COUNT - 1][0], EPOLL_CTL_ADD, PipeFileDescriptors[0], &EpollControlEvent), EINVAL);
|
|
|
|
LxtCheckErrno(epoll_ctl(EpollFds[EPOLL_CHAIN_COUNT - 1][1], EPOLL_CTL_ADD, PipeFileDescriptors[0], &EpollControlEvent));
|
|
|
|
LxtCheckErrnoFailure(epoll_ctl(EpollFds[EPOLL_CHAIN_COUNT - 1][0], EPOLL_CTL_ADD, PipeFileDescriptors[0], &EpollControlEvent), EINVAL);
|
|
|
|
//
|
|
// Verify the same file descriptor can be added to two different epolls
|
|
// even when they are linked.
|
|
//
|
|
|
|
LxtCheckErrno(epoll_ctl(EpollFds[EPOLL_CHAIN_COUNT - 1][2], EPOLL_CTL_ADD, PipeFileDescriptors[0], &EpollControlEvent));
|
|
|
|
//
|
|
// Test if the really long chain can be waited on.
|
|
//
|
|
|
|
LxtCheckErrno(epoll_wait(EpollFds[EPOLL_CHAIN_COUNT - 1][Index - 1], &EpollWaitEvent, 1, 0));
|
|
|
|
LxtCheckEqual(Result, 0, "%d");
|
|
|
|
LxtCheckErrno(write(PipeFileDescriptors[1], "\n", 1));
|
|
LxtCheckErrno(epoll_wait(EpollFds[EPOLL_CHAIN_COUNT - 1][Index - 1], &EpollWaitEvent, 1, -1));
|
|
|
|
LxtCheckEqual(Result, 1, "%d");
|
|
|
|
//
|
|
// Try removing a node deep in the chain.
|
|
//
|
|
|
|
LxtCheckErrno(epoll_ctl(
|
|
EpollFds[EPOLL_CHAIN_COUNT / 2][EPOLL_MAX_RECURSION_COUNT / 2],
|
|
EPOLL_CTL_DEL,
|
|
EpollFds[EPOLL_CHAIN_COUNT / 2][EPOLL_MAX_RECURSION_COUNT / 2 - 1],
|
|
NULL));
|
|
|
|
ErrorExit:
|
|
if (ExtraEpollFd != -1)
|
|
{
|
|
close(ExtraEpollFd);
|
|
}
|
|
|
|
for (Index = 0; Index < EPOLL_MAX_RECURSION_COUNT; Index += 1)
|
|
{
|
|
if (EpollContentFds[Index] != -1)
|
|
{
|
|
close(EpollContentFds[Index]);
|
|
}
|
|
}
|
|
|
|
for (ChainIndex = 0; ChainIndex < EPOLL_CHAIN_COUNT; ChainIndex += 1)
|
|
{
|
|
for (Index = 0; Index < EPOLL_MAX_RECURSION_COUNT; Index += 1)
|
|
{
|
|
if (EpollFds[ChainIndex][Index] != -1)
|
|
{
|
|
close(EpollFds[ChainIndex][Index]);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (PipeFileDescriptors[1] != -1)
|
|
{
|
|
close(PipeFileDescriptors[1]);
|
|
}
|
|
|
|
if (PipeFileDescriptors[0] != -1)
|
|
{
|
|
close(PipeFileDescriptors[0]);
|
|
}
|
|
|
|
return Result;
|
|
} |