Files

99 lines
2.8 KiB
C++

#pragma once
#include "ExternalChangeFixtures.h"
#include <QDialog>
#include <atomic>
#include <dirent.h>
#include <dlfcn.h>
#include <sys/syscall.h>
#include <unistd.h>
// Shared by the save tests: libc calls held and counted.
namespace {
// Each sync is slow, so overlapping writes show.
std::atomic<int> syncing = 0;
std::atomic<int> mostAtOnce = 0;
std::atomic<int> slowness = 0;
// While set, a package swap waits: a write held mid-save.
std::atomic<bool> holdSwap = false;
std::atomic<int> swaps = 0;
// While it names a folder, listing that folder waits.
QByteArray heldListing;
std::atomic<bool> holdListing = false;
std::atomic<int> listingsHeld = 0;
// Loads the store finished, counted from its log.
std::atomic<int> loads = 0;
QtMessageHandler forward = nullptr;
void countLoads(QtMsgType type, const QMessageLogContext &context, const QString &message)
{
if (message.startsWith(QStringLiteral("loaded ")))
loads += 1;
forward(type, context, message);
}
// Every hold let go and every count cleared.
void resetHooks()
{
mostAtOnce = 0;
slowness = 20'000;
holdSwap = false;
swaps = 0;
holdListing = false;
listingsHeld = 0;
loads = 0;
if (!forward)
forward = qInstallMessageHandler(countLoads);
}
bool lists(DIR *directory, const QByteArray &folder)
{
char path[4096];
const QByteArray link = "/proc/self/fd/" + QByteArray::number(dirfd(directory));
const ssize_t size = readlink(link.constData(), path, sizeof path - 1);
return size > 0 && QByteArray(path, int(size)) == folder;
}
template <typename Entry> Entry *held(DIR *directory, Entry *entry)
{
if (!entry && holdListing && lists(directory, heldListing)) {
listingsHeld += 1;
while (holdListing)
usleep(1000);
}
return entry;
}
}
// These take libc's place in the test executable.
extern "C" struct dirent *readdir(DIR *directory)
{
static const auto real = reinterpret_cast<struct dirent *(*)(DIR *)>(dlsym(RTLD_NEXT, "readdir"));
return held(directory, real(directory));
}
extern "C" struct dirent64 *readdir64(DIR *directory)
{
static const auto real = reinterpret_cast<struct dirent64 *(*)(DIR *)>(dlsym(RTLD_NEXT, "readdir64"));
return held(directory, real(directory));
}
extern "C" int renameat2(int oldDirectory, const char *from, int newDirectory, const char *to, unsigned int flags)
{
while (holdSwap)
usleep(1000);
swaps += 1;
return int(syscall(SYS_renameat2, oldDirectory, from, newDirectory, to, flags));
}
extern "C" int fsync(int descriptor)
{
const int now = ++syncing;
int seen = mostAtOnce;
while (now > seen && !mostAtOnce.compare_exchange_weak(seen, now)) {
}
usleep(useconds_t(slowness.load()));
const int result = int(syscall(SYS_fsync, descriptor));
--syncing;
return result;
}