void unlock() { int ret; do { ret = pthread_spin_unlock(&spin); } while (OXT_UNLIKELY(ret == EINTR)); if (OXT_UNLIKELY(ret != 0)) { throw boost::thread_resource_error(ret, "Cannot unlock spin lock"); } }
Group::~Group() { LifeStatus lifeStatus = getLifeStatus(); if (OXT_UNLIKELY(lifeStatus == ALIVE)) { P_BUG("You must call Group::shutdown() before destroying a Group."); } assert(lifeStatus == SHUT_DOWN); assert(!detachedProcessesCheckerActive); }
static struct mbuf_block * _mbuf_block_get(struct mbuf_pool *pool) { struct mbuf_block *mbuf_block; char *buf; if (!STAILQ_EMPTY(&pool->free_mbuf_blockq)) { assert(pool->nfree_mbuf_blockq > 0); mbuf_block = STAILQ_FIRST(&pool->free_mbuf_blockq); pool->nfree_mbuf_blockq--; STAILQ_REMOVE_HEAD(&pool->free_mbuf_blockq, next); assert(mbuf_block->magic == MBUF_BLOCK_MAGIC); goto done; } buf = (char *) malloc(pool->mbuf_block_chunk_size); if (OXT_UNLIKELY(buf == NULL)) { return NULL; } /* * mbuf_block header is at the tail end of the mbuf_block. This enables us to catch * buffer overrun early by asserting on the magic value during get or * put operations * * <------------- mbuf_block_chunk_size -------------------> * +-------------------------------------------------------+ * | mbuf_block data | mbuf_block header | * | (mbuf_block_offset) | (struct mbuf_block) | * +-------------------------------------------------------+ * ^ ^^ * | || * \ |\ * block->start | block->end (one byte past valid bound) * \ * block * */ mbuf_block = (struct mbuf_block *)(buf + pool->mbuf_block_offset); mbuf_block->magic = MBUF_BLOCK_MAGIC; mbuf_block->pool = pool; mbuf_block->refcount = 1; done: STAILQ_NEXT(mbuf_block, next) = NULL; #ifdef MBUF_ENABLE_DEBUGGING TAILQ_INSERT_HEAD(&pool->active_mbuf_blockq, mbuf_block, active_q); #endif #ifdef MBUF_ENABLE_BACKTRACES mbuf_block->backtrace = strdup(oxt::thread::current_backtrace().c_str()); #endif pool->nactive_mbuf_blockq++; return mbuf_block; }
mbuf mbuf_get(struct mbuf_pool *pool) { struct mbuf_block *block = mbuf_block_get(pool); if (OXT_UNLIKELY(block == NULL)) { return mbuf(); } assert(block->refcount == 1); block->refcount--; return mbuf(block, 0, block->end - block->start); }
bool try_lock() { int ret; do { ret = pthread_spin_trylock(&spin); } while (OXT_UNLIKELY(ret == EINTR)); if (ret == 0) { return true; } else if (ret == EBUSY) { return false; } else { throw boost::thread_resource_error(ret, "Cannot lock spin lock"); } }
// The 'self' parameter is for keeping the current Group object alive void Group::lockAndMaybeInitiateOobw(const ProcessPtr &process, DisableResult result, GroupPtr self) { TRACE_POINT(); // Standard resource management boilerplate stuff... PoolPtr pool = getPool(); boost::unique_lock<boost::mutex> lock(pool->syncher); if (OXT_UNLIKELY(!process->isAlive() || !isAlive())) { return; } assert(process->oobwStatus == Process::OOBW_IN_PROGRESS); if (result == DR_SUCCESS) { if (process->enabled == Process::DISABLED) { P_DEBUG("Process " << process->inspect() << " disabled; proceeding " << "with out-of-band work"); process->oobwStatus = Process::OOBW_REQUESTED; if (shouldInitiateOobw(process)) { initiateOobw(process); } else { // We do not re-enable the process because it's likely that the // administrator has explicitly changed the state. P_DEBUG("Out-of-band work for process " << process->inspect() << " aborted " "because the process no longer requests out-of-band work"); process->oobwStatus = Process::OOBW_NOT_ACTIVE; } } else { // We do not re-enable the process because it's likely that the // administrator has explicitly changed the state. P_DEBUG("Out-of-band work for process " << process->inspect() << " aborted " "because the process was reenabled after disabling"); process->oobwStatus = Process::OOBW_NOT_ACTIVE; } } else { P_DEBUG("Out-of-band work for process " << process->inspect() << " aborted " "because the process could not be disabled"); process->oobwStatus = Process::OOBW_NOT_ACTIVE; } }
struct mbuf_block * mbuf_block_get(struct mbuf_pool *pool) { struct mbuf_block *mbuf_block; char *buf; mbuf_block = _mbuf_block_get(pool); if (OXT_UNLIKELY(mbuf_block == NULL)) { return NULL; } buf = (char *)mbuf_block - pool->mbuf_block_offset; mbuf_block->start = buf; mbuf_block->end = buf + pool->mbuf_block_offset; assert(mbuf_block->end - mbuf_block->start == (int)pool->mbuf_block_offset); assert(mbuf_block->start < mbuf_block->end); #ifdef MBUF_DEBUG printf("[%p] mbuf_block get %p\n", oxt::thread_signature, mbuf_block); #endif return mbuf_block; }
// The 'self' parameter is for keeping the current Group object alive while this thread is running. void Group::spawnThreadOOBWRequest(GroupPtr self, ProcessPtr process) { TRACE_POINT(); this_thread::disable_interruption di; this_thread::disable_syscall_interruption dsi; Socket *socket; Connection connection; PoolPtr pool = getPool(); Pool::DebugSupportPtr debug = pool->debugSupport; UPDATE_TRACE_POINT(); P_DEBUG("Performing OOBW request for process " << process->inspect()); if (debug != NULL && debug->oobw) { debug->debugger->send("OOBW request about to start"); debug->messages->recv("Proceed with OOBW request"); } UPDATE_TRACE_POINT(); { // Standard resource management boilerplate stuff... boost::unique_lock<boost::mutex> lock(pool->syncher); if (OXT_UNLIKELY(!process->isAlive() || process->enabled == Process::DETACHED || !isAlive())) { return; } if (process->enabled != Process::DISABLED) { UPDATE_TRACE_POINT(); P_INFO("Out-of-Band Work canceled: process " << process->inspect() << " was concurrently re-enabled."); if (debug != NULL && debug->oobw) { debug->debugger->send("OOBW request canceled"); } return; } assert(process->oobwStatus == Process::OOBW_IN_PROGRESS); assert(process->sessions == 0); socket = process->sessionSockets.top(); assert(socket != NULL); } UPDATE_TRACE_POINT(); unsigned long long timeout = 1000 * 1000 * 60; // 1 min try { this_thread::restore_interruption ri(di); this_thread::restore_syscall_interruption rsi(dsi); // Grab a connection. The connection is marked as fail in order to // ensure it is closed / recycled after this request (otherwise we'd // need to completely read the response). connection = socket->checkoutConnection(); connection.fail = true; ScopeGuard guard(boost::bind(&Socket::checkinConnection, socket, connection)); // This is copied from RequestHandler when it is sending data using the // "session" protocol. char sizeField[sizeof(uint32_t)]; SmallVector<StaticString, 10> data; data.push_back(StaticString(sizeField, sizeof(uint32_t))); data.push_back(makeStaticStringWithNull("REQUEST_METHOD")); data.push_back(makeStaticStringWithNull("OOBW")); data.push_back(makeStaticStringWithNull("PASSENGER_CONNECT_PASSWORD")); data.push_back(makeStaticStringWithNull(process->connectPassword)); uint32_t dataSize = 0; for (unsigned int i = 1; i < data.size(); i++) { dataSize += (uint32_t) data[i].size(); } Uint32Message::generate(sizeField, dataSize); gatheredWrite(connection.fd, &data[0], data.size(), &timeout); // We do not care what the actual response is ... just wait for it. UPDATE_TRACE_POINT(); waitUntilReadable(connection.fd, &timeout); } catch (const SystemException &e) { P_ERROR("*** ERROR: " << e.what() << "\n" << e.backtrace()); } catch (const TimeoutException &e) { P_ERROR("*** ERROR: " << e.what() << "\n" << e.backtrace()); } UPDATE_TRACE_POINT(); vector<Callback> actions; { // Standard resource management boilerplate stuff... PoolPtr pool = getPool(); boost::unique_lock<boost::mutex> lock(pool->syncher); if (OXT_UNLIKELY(!process->isAlive() || !isAlive())) { return; } process->oobwStatus = Process::OOBW_NOT_ACTIVE; if (process->enabled == Process::DISABLED) { enable(process, actions); assignSessionsToGetWaiters(actions); } pool->fullVerifyInvariants(); initiateNextOobwRequest(); } UPDATE_TRACE_POINT(); runAllActions(actions); actions.clear(); UPDATE_TRACE_POINT(); P_DEBUG("Finished OOBW request for process " << process->inspect()); if (debug != NULL && debug->oobw) { debug->debugger->send("OOBW request finished"); } }
void SuperGroup::realDoRestart(const Options &options, unsigned int generation) { TRACE_POINT(); vector<ComponentInfo> componentInfos = loadComponentInfos(options); vector<ComponentInfo>::const_iterator it; PoolPtr pool = getPool(); Pool::DebugSupportPtr debug = pool->debugSupport; if (debug != NULL && debug->superGroup) { debug->debugger->send("About to finish SuperGroup restart"); debug->messages->recv("Proceed with restarting SuperGroup"); } boost::unique_lock<boost::mutex> lock(getPoolSyncher(pool)); if (OXT_UNLIKELY(this->generation != generation)) { return; } assert(state == RESTARTING); verifyInvariants(); vector<GroupPtr> allGroups; vector<GroupPtr> updatedGroups; vector<GroupPtr> newGroups; vector<GroupPtr>::const_iterator g_it; vector<Callback> actions; this->options = options; // Update the component information for existing groups. UPDATE_TRACE_POINT(); for (it = componentInfos.begin(); it != componentInfos.end(); it++) { const ComponentInfo &info = *it; pair<GroupPtr, unsigned int> result = findGroupCorrespondingToComponent(groups, info); GroupPtr group = result.first; if (group != NULL) { unsigned int index = result.second; group->componentInfo = info; updatedGroups.push_back(group); groups[index].reset(); } else { // This is not an existing group but a new one, // so create it. group = boost::make_shared<Group>(shared_from_this(), options, info); newGroups.push_back(group); } // allGroups must be in the same order as componentInfos. allGroups.push_back(group); } // Some components might have been deleted, so delete the // corresponding groups. detachAllGroups(groups, actions); // Tell all previous existing groups to restart. for (g_it = updatedGroups.begin(); g_it != updatedGroups.end(); g_it++) { GroupPtr group = *g_it; group->restart(options); } groups = allGroups; defaultGroup = findDefaultGroup(allGroups); setState(READY); assignGetWaitlistToGroups(actions); UPDATE_TRACE_POINT(); verifyInvariants(); lock.unlock(); runAllActions(actions); }
void SuperGroup::realDoInitialize(const Options &options, unsigned int generation) { vector<ComponentInfo> componentInfos; vector<ComponentInfo>::const_iterator it; ExceptionPtr exception; P_TRACE(2, "Initializing SuperGroup " << inspect() << " in the background..."); try { componentInfos = loadComponentInfos(options); } catch (const tracable_exception &e) { exception = copyException(e); } if (componentInfos.empty() && exception == NULL) { string message = "The directory " + options.appRoot + " does not seem to contain a web application."; exception = boost::make_shared<SpawnException>( message, message, false); } PoolPtr pool = getPool(); Pool::DebugSupportPtr debug = pool->debugSupport; vector<Callback> actions; { if (debug != NULL && debug->superGroup) { debug->debugger->send("About to finish SuperGroup initialization"); debug->messages->recv("Proceed with initializing SuperGroup"); } boost::unique_lock<boost::mutex> lock(getPoolSyncher(pool)); this_thread::disable_interruption di; this_thread::disable_syscall_interruption dsi; NOT_EXPECTING_EXCEPTIONS(); if (OXT_UNLIKELY(getPool() == NULL || generation != this->generation)) { return; } P_TRACE(2, "Initialization of SuperGroup " << inspect() << " almost done; grabbed lock"); assert(state == INITIALIZING); verifyInvariants(); if (componentInfos.empty()) { /* Somehow initialization failed. Maybe something has deleted * the supergroup files while we're working. */ assert(exception != NULL); setState(DESTROYED); actions.reserve(getWaitlist.size()); while (!getWaitlist.empty()) { const GetWaiter &waiter = getWaitlist.front(); actions.push_back(boost::bind(waiter.callback, SessionPtr(), exception)); getWaitlist.pop_front(); } } else { for (it = componentInfos.begin(); it != componentInfos.end(); it++) { const ComponentInfo &info = *it; GroupPtr group = boost::make_shared<Group>(shared_from_this(), options, info); groups.push_back(group); if (info.isDefault) { defaultGroup = group.get(); } } setState(READY); assignGetWaitlistToGroups(actions); } verifyInvariants(); P_TRACE(2, "Done initializing SuperGroup " << inspect()); } this_thread::disable_interruption di; this_thread::disable_syscall_interruption dsi; runAllActions(actions); runInitializationHooks(); }
void Controller::createNewPoolOptions(Client *client, Request *req, const HashedStaticString &appGroupName) { ServerKit::HeaderTable &secureHeaders = req->secureHeaders; Options &options = req->options; SKC_TRACE(client, 2, "Creating new pool options: app group name=" << appGroupName); options = Options(); const LString *scriptName = secureHeaders.lookup("!~SCRIPT_NAME"); const LString *appRoot = secureHeaders.lookup("!~PASSENGER_APP_ROOT"); if (scriptName == NULL || scriptName->size == 0) { if (appRoot == NULL || appRoot->size == 0) { const LString *documentRoot = secureHeaders.lookup("!~DOCUMENT_ROOT"); if (OXT_UNLIKELY(documentRoot == NULL || documentRoot->size == 0)) { disconnectWithError(&client, "client did not send a !~PASSENGER_APP_ROOT or a !~DOCUMENT_ROOT header"); return; } documentRoot = psg_lstr_make_contiguous(documentRoot, req->pool); appRoot = psg_lstr_create(req->pool, extractDirNameStatic(StaticString(documentRoot->start->data, documentRoot->size))); } else { appRoot = psg_lstr_make_contiguous(appRoot, req->pool); } options.appRoot = HashedStaticString(appRoot->start->data, appRoot->size); } else { if (appRoot == NULL || appRoot->size == 0) { const LString *documentRoot = secureHeaders.lookup("!~DOCUMENT_ROOT"); if (OXT_UNLIKELY(documentRoot == NULL || documentRoot->size == 0)) { disconnectWithError(&client, "client did not send a !~DOCUMENT_ROOT header"); return; } documentRoot = psg_lstr_null_terminate(documentRoot, req->pool); documentRoot = resolveSymlink(StaticString(documentRoot->start->data, documentRoot->size), req->pool); appRoot = psg_lstr_create(req->pool, extractDirNameStatic(StaticString(documentRoot->start->data, documentRoot->size))); } else { appRoot = psg_lstr_make_contiguous(appRoot, req->pool); } options.appRoot = HashedStaticString(appRoot->start->data, appRoot->size); scriptName = psg_lstr_make_contiguous(scriptName, req->pool); options.baseURI = StaticString(scriptName->start->data, scriptName->size); } fillPoolOptionsFromConfigCaches(options, req->pool, req->config); const LString *appType = secureHeaders.lookup("!~PASSENGER_APP_TYPE"); if (appType == NULL || appType->size == 0) { AppTypeDetector detector; PassengerAppType type = detector.checkAppRoot(options.appRoot); if (type == PAT_NONE || type == PAT_ERROR) { disconnectWithError(&client, "client did not send a recognized !~PASSENGER_APP_TYPE header"); return; } options.appType = getAppTypeName(type); } else { fillPoolOption(req, options.appType, "!~PASSENGER_APP_TYPE"); } options.appGroupName = appGroupName; fillPoolOption(req, options.appType, "!~PASSENGER_APP_TYPE"); fillPoolOption(req, options.environment, "!~PASSENGER_APP_ENV"); fillPoolOption(req, options.ruby, "!~PASSENGER_RUBY"); fillPoolOption(req, options.python, "!~PASSENGER_PYTHON"); fillPoolOption(req, options.nodejs, "!~PASSENGER_NODEJS"); fillPoolOption(req, options.meteorAppSettings, "!~PASSENGER_METEOR_APP_SETTINGS"); fillPoolOption(req, options.user, "!~PASSENGER_USER"); fillPoolOption(req, options.group, "!~PASSENGER_GROUP"); fillPoolOption(req, options.minProcesses, "!~PASSENGER_MIN_PROCESSES"); fillPoolOption(req, options.maxProcesses, "!~PASSENGER_MAX_PROCESSES"); fillPoolOption(req, options.spawnMethod, "!~PASSENGER_SPAWN_METHOD"); fillPoolOption(req, options.startCommand, "!~PASSENGER_START_COMMAND"); fillPoolOptionSecToMsec(req, options.startTimeout, "!~PASSENGER_START_TIMEOUT"); fillPoolOption(req, options.maxPreloaderIdleTime, "!~PASSENGER_MAX_PRELOADER_IDLE_TIME"); fillPoolOption(req, options.maxRequestQueueSize, "!~PASSENGER_MAX_REQUEST_QUEUE_SIZE"); fillPoolOption(req, options.abortWebsocketsOnProcessShutdown, "!~PASSENGER_ABORT_WEBSOCKETS_ON_PROCESS_SHUTDOWN"); fillPoolOption(req, options.forceMaxConcurrentRequestsPerProcess, "!~PASSENGER_FORCE_MAX_CONCURRENT_REQUESTS_PER_PROCESS"); fillPoolOption(req, options.restartDir, "!~PASSENGER_RESTART_DIR"); fillPoolOption(req, options.startupFile, "!~PASSENGER_STARTUP_FILE"); fillPoolOption(req, options.loadShellEnvvars, "!~PASSENGER_LOAD_SHELL_ENVVARS"); fillPoolOption(req, options.fileDescriptorUlimit, "!~PASSENGER_APP_FILE_DESCRIPTOR_ULIMIT"); fillPoolOption(req, options.raiseInternalError, "!~PASSENGER_RAISE_INTERNAL_ERROR"); fillPoolOption(req, options.lveMinUid, "!~PASSENGER_LVE_MIN_UID"); /******************/ boost::shared_ptr<Options> optionsCopy = boost::make_shared<Options>(options); optionsCopy->persist(options); optionsCopy->clearPerRequestFields(); optionsCopy->detachFromUnionStationTransaction(); poolOptionsCache.insert(options.getAppGroupName(), optionsCopy); }