Exemplo n.º 1
0
// 'lockNow == false' may only be used during unit tests. Normally we
// should never call the callback while holding the lock.
void
Pool::asyncGet(const Options &options, const GetCallback &callback, bool lockNow) {
	DynamicScopedLock lock(syncher, lockNow);

	assert(lifeStatus == ALIVE || lifeStatus == PREPARED_FOR_SHUTDOWN);
	verifyInvariants();
	P_TRACE(2, "asyncGet(appGroupName=" << options.getAppGroupName() << ")");
	boost::container::vector<Callback> actions;

	Group *existingGroup = findMatchingGroup(options);

	if (OXT_LIKELY(existingGroup != NULL)) {
		/* Best case: the app group is already in the pool. Let's use it. */
		P_TRACE(2, "Found existing Group");
		existingGroup->verifyInvariants();
		SessionPtr session = existingGroup->get(options, callback, actions);
		existingGroup->verifyInvariants();
		verifyInvariants();
		P_TRACE(2, "asyncGet() finished");
		if (lockNow) {
			lock.unlock();
		}
		if (session != NULL) {
			callback(session, ExceptionPtr());
		}

	} else if (!atFullCapacityUnlocked()) {
		/* The app super group isn't in the pool and we have enough free
		 * resources to make a new one.
		 */
		P_DEBUG("Spawning new Group");
		GroupPtr group = createGroupAndAsyncGetFromIt(options,
			callback, actions);
		group->verifyInvariants();
		verifyInvariants();
		P_DEBUG("asyncGet() finished");

	} else {
		/* Uh oh, the app super group isn't in the pool but we don't
		 * have the resources to make a new one. The sysadmin should
		 * configure the system to let something like this happen
		 * as least as possible, but let's try to handle it as well
		 * as we can.
		 */
		ProcessPtr freedProcess = forceFreeCapacity(NULL, actions);
		if (freedProcess == NULL) {
			/* No process is eligible for killing. This could happen if, for example,
			 * all (super)groups are currently initializing/restarting/spawning/etc.
			 * We have no choice but to satisfy this get() action later when resources
			 * become available.
			 */
			P_DEBUG("Could not free a process; putting request to top-level getWaitlist");
			getWaitlist.push_back(GetWaiter(
				options.copyAndPersist(),
				callback));
		} else {
			/* Now that a process has been trashed we can create
			 * the missing Group.
			 */
			P_DEBUG("Creating new Group");
			GroupPtr group = createGroup(options);
			SessionPtr session = group->get(options, callback,
				actions);
			/* The Group is now spawning a process so the callback
			 * should now have been put on the wait list,
			 * unless something has changed and we forgot to update
			 * some code here or if options.noop...
			 */
			if (session != NULL) {
				assert(options.noop);
				actions.push_back(boost::bind(GetCallback::call,
					callback, session, ExceptionPtr()));
			}
			freedProcess->getGroup()->verifyInvariants();
			group->verifyInvariants();
		}

		assert(atFullCapacityUnlocked());
		verifyInvariants();
		verifyExpensiveInvariants();
		P_TRACE(2, "asyncGet() finished");
	}

	if (!actions.empty()) {
		if (lockNow) {
			if (lock.owns_lock()) {
				lock.unlock();
			}
			runAllActions(actions);
		} else {
			// This state is not allowed. If we reach
			// here then it probably indicates a bug in
			// the test suite.
			abort();
		}
	}
}
Exemplo n.º 2
0
// 'lockNow == false' may only be used during unit tests. Normally we
// should never call the callback while holding the lock.
void
Pool::asyncGet(const Options &options, const GetCallback &callback, bool lockNow, UnionStation::StopwatchLog **stopwatchLog) {
	DynamicScopedLock lock(syncher, lockNow);

	assert(lifeStatus == ALIVE || lifeStatus == PREPARED_FOR_SHUTDOWN);
	verifyInvariants();
	P_TRACE(2, "asyncGet(appGroupName=" << options.getAppGroupName() << ")");
	boost::container::vector<Callback> actions;

	Group *existingGroup = findMatchingGroup(options);
	if (stopwatchLog != NULL) {
		// Log some essentials stats about what this request is facing in its upcoming journey through the queue:
		// 1) position in the queue upon entry, and 2) whether spawning activity is occurring (which takes cycles
		// but also indicates the server has headroom to handle the load).
		Json::Value data;
		if (!existingGroup) {
			data["message"] = "spawning.."; // the first of this group, so keep it simple (also: we don't know maxQ yet)
		} else {
			char queueMaxStr[10];
			int queueMax = existingGroup->options.maxRequestQueueSize;
			if (queueMax > 0) {
				snprintf(queueMaxStr, 10, "%d", queueMax);
			}
			char message[50];
			snprintf(message, 100, "queue: %zu / %s, spawning: %s", existingGroup->getWaitlist.size(),
					(queueMax == 0 ? "inf" : queueMaxStr),
					(existingGroup->processesBeingSpawned == 0 ? "no" : "yes"));
			data["message"] = message;
		}
		Json::Value json;
		json["data"] = data;
		json["data_type"] = "generic";
		json["name"] = "Await available process";

		*stopwatchLog = new UnionStation::StopwatchLog(options.transaction, "Pool::asyncGet", stringifyJson(json).c_str());
	}

	if (OXT_LIKELY(existingGroup != NULL)) {
		/* Best case: the app group is already in the pool. Let's use it. */
		P_TRACE(2, "Found existing Group");
		existingGroup->verifyInvariants();
		SessionPtr session = existingGroup->get(options, callback, actions);
		existingGroup->verifyInvariants();
		verifyInvariants();
		P_TRACE(2, "asyncGet() finished");
		if (lockNow) {
			lock.unlock();
		}
		if (session != NULL) {
			callback(session, ExceptionPtr());
		}

	} else if (!atFullCapacityUnlocked()) {
		/* The app super group isn't in the pool and we have enough free
		 * resources to make a new one.
		 */
		P_DEBUG("Spawning new Group");
		GroupPtr group = createGroupAndAsyncGetFromIt(options,
			callback, actions);
		group->verifyInvariants();
		verifyInvariants();
		P_DEBUG("asyncGet() finished");

	} else {
		/* Uh oh, the app super group isn't in the pool but we don't
		 * have the resources to make a new one. The sysadmin should
		 * configure the system to let something like this happen
		 * as least as possible, but let's try to handle it as well
		 * as we can.
		 */
		ProcessPtr freedProcess = forceFreeCapacity(NULL, actions);
		if (freedProcess == NULL) {
			/* No process is eligible for killing. This could happen if, for example,
			 * all (super)groups are currently initializing/restarting/spawning/etc.
			 * We have no choice but to satisfy this get() action later when resources
			 * become available.
			 */
			P_DEBUG("Could not free a process; putting request to top-level getWaitlist");
			getWaitlist.push_back(GetWaiter(
				options.copyAndPersist().detachFromUnionStationTransaction(),
				callback));
		} else {
			/* Now that a process has been trashed we can create
			 * the missing Group.
			 */
			P_DEBUG("Creating new Group");
			GroupPtr group = createGroup(options);
			SessionPtr session = group->get(options, callback,
				actions);
			/* The Group is now spawning a process so the callback
			 * should now have been put on the wait list,
			 * unless something has changed and we forgot to update
			 * some code here or if options.noop...
			 */
			if (session != NULL) {
				assert(options.noop);
				actions.push_back(boost::bind(GetCallback::call,
					callback, session, ExceptionPtr()));
			}
			freedProcess->getGroup()->verifyInvariants();
			group->verifyInvariants();
		}

		assert(atFullCapacityUnlocked());
		verifyInvariants();
		verifyExpensiveInvariants();
		P_TRACE(2, "asyncGet() finished");
	}

	if (!actions.empty()) {
		if (lockNow) {
			if (lock.owns_lock()) {
				lock.unlock();
			}
			runAllActions(actions);
		} else {
			// This state is not allowed. If we reach
			// here then it probably indicates a bug in
			// the test suite.
			abort();
		}
	}
}