//------------------------------------------------------------------------------
// File: AsynchronousFileReader.hh
// Author: Georgios Bitzes, CERN
//------------------------------------------------------------------------------
/************************************************************************
* EOS - the CERN Disk Storage System *
* Copyright (C) 2011 CERN/Switzerland *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see .*
************************************************************************/
#ifndef __ENVIRONMENT_READER_HH__
#define __ENVIRONMENT_READER_HH__
#include
#include
#include
#include
#include "CredentialFinder.hh"
struct FutureEnvironment {
std::shared_future contents;
std::chrono::high_resolution_clock::time_point queuedSince;
//----------------------------------------------------------------------------
//! Returns the Environemnt object.
//!
//! Always call wait first with a timeout! This could block indefinitely
//! causing a kernel deadlock.
//----------------------------------------------------------------------------
Environment get() {
return contents.get();
}
//----------------------------------------------------------------------------
//! Wait until the deadline, which is, t milliseconds after queuedSince. If
//! this time has elapsed since submitting the request, give up and unblock.
//!
//! Returns whether the result is available or not. If false, it means the
//! deadline has certainly passed.
//----------------------------------------------------------------------------
bool waitUntilDeadline(std::chrono::milliseconds t) {
std::chrono::high_resolution_clock::time_point deadline =
queuedSince + t;
return contents.wait_until(deadline) == std::future_status::ready;
}
};
//------------------------------------------------------------------------------
//! This contraption is used to safely read /proc/pid/environ in a separate
//! thread, without risk of deadlocking.
//!
//!
//! We return a future to all requests. Never block on it, always wait with a
//! timeout.
//!
//! If we receive a request for the same file again, and the other is still
//! pending, we should tell the caller for how long the other request has been
//! pending for.
//!
//! This is because a single execve() will typically issue many requests to
//! fuse - we only want to pay the wait penalty once.
//------------------------------------------------------------------------------
class EnvironmentReader
{
public:
//----------------------------------------------------------------------------
//! Constructor - launch a thread pool with the specified number of threads
//----------------------------------------------------------------------------
EnvironmentReader(size_t threads);
//----------------------------------------------------------------------------
//! Destructor
//----------------------------------------------------------------------------
~EnvironmentReader();
//----------------------------------------------------------------------------
//! Request to retrieve the environmnet variables for the given pid.
//!
//! Returns a FutureEnvironment object, which _might_ be kernel-deadlocked,
//! and must be waited-for with a timeout.
//----------------------------------------------------------------------------
FutureEnvironment stageRequest(pid_t pid, uid_t uid = -1);
//----------------------------------------------------------------------------
//! Inject fake data into this class. _All_ responses will be faked if there's
//! at least one injection active. Used in testing.
//----------------------------------------------------------------------------
void inject(pid_t pid, const Environment& env,
std::chrono::milliseconds artificialDelay = std::chrono::milliseconds(0));
//----------------------------------------------------------------------------
//! Remove fake data injection for given pid.
//----------------------------------------------------------------------------
void removeInjection(pid_t pid);
private:
//----------------------------------------------------------------------------
//! Fill fake data for a request.
//----------------------------------------------------------------------------
void fillFromInjection(pid_t pid, Environment& env);
//----------------------------------------------------------------------------
//! Stores a simulated response, served from fake data.
//----------------------------------------------------------------------------
struct SimulatedResponse {
Environment env;
std::chrono::milliseconds artificialDelay;
};
//----------------------------------------------------------------------------
//! For each pending, still-unfulfilled request we keep a QueuedRequest
//! object with the corresponding promise.
//----------------------------------------------------------------------------
struct QueuedRequest {
pid_t pid;
uid_t uid;
std::promise promise;
};
//----------------------------------------------------------------------------
//! Each worker loops on the queue, waiting for pending requests to fulfill.
//----------------------------------------------------------------------------
void worker();
//----------------------------------------------------------------------------
//! Thread synchronization, request queue
//----------------------------------------------------------------------------
std::atomic shutdown{false};
std::atomic threadsAlive{0};
std::vector threads;
std::mutex mtx;
std::condition_variable queueCV;
std::queue requestQueue;
std::map pendingRequests;
std::mutex injectionMtx;
std::map injections;
};
#endif