Preview: InternalUtils.cpp
Size: 10.39 KB
//opt/cpanel/ea-ruby27/src/passenger-release-6.1.2/src/agent/Core/ApplicationPool/Group/InternalUtils.cpp
/*
* Phusion Passenger - https://www.phusionpassenger.com/
* Copyright (c) 2011-2025 Asynchronous B.V.
*
* "Passenger", "Phusion Passenger" and "Union Station" are registered
* trademarks of Asynchronous B.V.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef _PASSENGER_APPLICATION_POOL_GROUP_INTERNAL_UTILS_CPP_
#define _PASSENGER_APPLICATION_POOL_GROUP_INTERNAL_UTILS_CPP_
#ifdef INTELLISENSE
#include <Core/ApplicationPool/Pool.h>
#endif
#include <Core/ApplicationPool/Group.h>
/*************************************************************************
*
* Internal utility functions for ApplicationPool2::Group
*
*************************************************************************/
namespace Passenger {
namespace ApplicationPool2 {
using namespace std;
using namespace boost;
/****************************
*
* Private methods
*
****************************/
void
Group::runAllActions(const boost::container::vector<Callback> &actions) {
boost::container::vector<Callback>::const_iterator it, end = actions.end();
for (it = actions.begin(); it != end; it++) {
(*it)();
}
}
void
Group::interruptAndJoinAllThreads(GroupPtr self) {
self->interruptableThreads.interrupt_and_join_all();
}
void
Group::doCleanupSpawner(SpawningKit::SpawnerPtr spawner) {
spawner->cleanup();
}
/**
* Persists options into this Group. Called at creation time and at restart time.
* Values will be persisted into `destination`. Or if it's NULL, into `this->options`.
*/
void
Group::resetOptions(const Options &newOptions, Options *destination) {
if (destination == NULL) {
destination = &this->options;
}
*destination = newOptions;
destination->persist(newOptions);
destination->clearPerRequestFields();
destination->apiKey = getApiKey().toStaticString();
destination->groupUuid = uuid;
}
/**
* Merges some of the new options from the latest get() request into this Group.
*/
void
Group::mergeOptions(const Options &other) {
options.maxRequests = other.maxRequests;
options.minProcesses = other.minProcesses;
options.statThrottleRate = other.statThrottleRate;
options.maxPreloaderIdleTime = other.maxPreloaderIdleTime;
}
/* Given a hook name like "queue_full_error", we return HookScriptOptions filled in with this name and a spec
* (user settings that can be queried from agentsOptions using the external hook name that is prefixed with "hook_")
*
* @return false if the user parameters (agentsOptions) are not available (e.g. during ApplicationPool2_PoolTest)
*/
bool
Group::prepareHookScriptOptions(HookScriptOptions &hsOptions, const char *name) {
Context *context = getPool()->getContext();
LockGuard l(context->agentConfigSyncher);
if (context->agentConfig.isNull()) {
return false;
}
hsOptions.name = name;
string hookName = string("hook_") + name;
hsOptions.spec = context->agentConfig.get(hookName, Json::Value()).asString();
return true;
}
// 'process' is not a reference so that bind(runAttachHooks, ...) causes the shared
// pointer reference to increment.
void
Group::runAttachHooks(const ProcessPtr process) const {
getPool()->runHookScripts("attached_process",
boost::bind(&Group::setupAttachOrDetachHook, this, process, boost::placeholders::_1));
}
void
Group::runDetachHooks(const ProcessPtr process) const {
getPool()->runHookScripts("detached_process",
boost::bind(&Group::setupAttachOrDetachHook, this, process, boost::placeholders::_1));
}
void
Group::setupAttachOrDetachHook(const ProcessPtr process, HookScriptOptions &options) const {
options.environment.push_back(make_pair("PASSENGER_PROCESS_PID", toString(process->getPid())));
options.environment.push_back(make_pair("PASSENGER_APP_ROOT", this->options.appRoot));
}
unsigned int
Group::generateStickySessionId() {
unsigned int result;
while (true) {
result = (unsigned int) rand();
if (result != 0 && findProcessWithStickySessionId(result) == NULL) {
return result;
}
}
// Never reached; shut up compiler warning.
return 0;
}
ProcessPtr
Group::createNullProcessObject() {
struct Guard {
Context *context;
Process *process;
Guard(Context *c, Process *s)
: context(c),
process(s)
{ }
~Guard() {
if (process != NULL) {
context->processObjectPool.free(process);
}
}
void clear() {
process = NULL;
}
};
Json::Value args;
args["pid"] = 0;
args["gupid"] = "0";
args["spawner_creation_time"] = 0;
args["spawn_start_time"] = 0;
args["dummy"] = true;
args["sockets"] = Json::Value(Json::arrayValue);
Context *context = getContext();
LockGuard l(context->memoryManagementSyncher);
Process *process = context->processObjectPool.malloc();
Guard guard(context, process);
process = new (process) Process(&info, info.group->restartsInitiated, args);
process->shutdownNotRequired();
guard.clear();
return ProcessPtr(process, false);
}
ProcessPtr
Group::createProcessObject(const SpawningKit::Spawner &spawner,
const SpawningKit::Result &spawnResult)
{
struct Guard {
Context *context;
Process *process;
Guard(Context *c, Process *s)
: context(c),
process(s)
{ }
~Guard() {
if (process != NULL) {
context->processObjectPool.free(process);
}
}
void clear() {
process = NULL;
}
};
Json::Value args;
args["spawner_creation_time"] = (Json::UInt64) spawner.creationTime;
Context *context = getContext();
LockGuard l(context->memoryManagementSyncher);
Process *process = context->processObjectPool.malloc();
Guard guard(context, process);
process = new (process) Process(&info, info.group->restartsInitiated, spawnResult, args);
guard.clear();
return ProcessPtr(process, false);
}
bool
Group::poolAtFullCapacity() const {
return getPool()->atFullCapacityUnlocked();
}
ProcessPtr
Group::poolForceFreeCapacity(const Group *exclude,
boost::container::vector<Callback> &postLockActions)
{
return getPool()->forceFreeCapacity(exclude, postLockActions);
}
void
Group::wakeUpGarbageCollector() {
getPool()->garbageCollectionCond.notify_all();
}
bool
Group::anotherGroupIsWaitingForCapacity() const {
return findOtherGroupWaitingForCapacity() != NULL;
}
Group *
Group::findOtherGroupWaitingForCapacity() const {
Pool *pool = getPool();
if (pool->groups.size() == 1) {
return NULL;
}
GroupMap::ConstIterator g_it(pool->groups);
while (*g_it != NULL) {
const GroupPtr &group = g_it.getValue();
if (group.get() != this && group->isWaitingForCapacity()) {
return group.get();
}
g_it.next();
}
return NULL;
}
bool
Group::pushGetWaiter(const Options &newOptions, const GetCallback &callback,
boost::container::vector<Callback> &postLockActions)
{
if (OXT_LIKELY(!testOverflowRequestQueue()
&& (newOptions.maxRequestQueueSize == 0
|| getWaitlist.size() < newOptions.maxRequestQueueSize)))
{
getWaitlist.push_back(GetWaiter(
newOptions.copyAndPersist(),
callback));
return true;
} else {
postLockActions.push_back(boost::bind(GetCallback::call,
callback, SessionPtr(), boost::make_shared<RequestQueueFullException>(newOptions.maxRequestQueueSize)));
HookScriptOptions hsOptions;
if (prepareHookScriptOptions(hsOptions, "queue_full_error")) {
// TODO <Feb 17, 2015] DK> should probably rate limit this, since we are already at heavy load
postLockActions.push_back(boost::bind(runHookScripts, hsOptions));
}
return false;
}
}
template<typename Lock>
void
Group::assignSessionsToGetWaitersQuickly(Lock &lock) {
if (getWaitlist.empty()) {
verifyInvariants();
lock.unlock();
return;
}
boost::container::small_vector<GetAction, 8> actions;
unsigned int i = 0;
bool done = false;
actions.reserve(getWaitlist.size());
while (!done && i < getWaitlist.size()) {
const GetWaiter &waiter = getWaitlist[i];
RouteResult result = route(waiter.options);
if (result.process != NULL) {
GetAction action;
action.callback = waiter.callback;
action.session = newSession(result.process);
getWaitlist.erase(getWaitlist.begin() + i);
actions.push_back(action);
} else {
done = result.finished;
if (!result.finished) {
i++;
}
}
}
verifyInvariants();
lock.unlock();
boost::container::small_vector<GetAction, 50>::const_iterator it, end = actions.end();
for (it = actions.begin(); it != end; it++) {
it->callback(it->session, ExceptionPtr());
}
}
void
Group::assignSessionsToGetWaiters(boost::container::vector<Callback> &postLockActions) {
unsigned int i = 0;
bool done = false;
while (!done && i < getWaitlist.size()) {
const GetWaiter &waiter = getWaitlist[i];
RouteResult result = route(waiter.options);
if (result.process != NULL) {
postLockActions.push_back(boost::bind(
GetCallback::call,
waiter.callback,
newSession(result.process),
ExceptionPtr()));
getWaitlist.erase(getWaitlist.begin() + i);
} else {
done = result.finished;
if (!result.finished) {
i++;
}
}
}
}
bool
Group::testOverflowRequestQueue() const {
// This has a performance penalty, although I'm not sure whether the penalty is
// any greater than a hash table lookup if I were to implement it in Options.
Pool::DebugSupportPtr debug = getPool()->debugSupport;
if (debug) {
return debug->testOverflowRequestQueue;
} else {
return false;
}
}
void
Group::callAbortLongRunningConnectionsCallback(const ProcessPtr &process) {
Pool::AbortLongRunningConnectionsCallback callback =
getPool()->abortLongRunningConnectionsCallback;
if (callback) {
callback(process);
}
}
} // namespace ApplicationPool2
} // namespace Passenger
#endif // _PASSENGER_APPLICATION_POOL_GROUP_INTERNAL_UTILS_CPP_
Directory Contents
Dirs: 0 × Files: 10