<commit_before>/* Copyright 2007-2015 QReal Research Group
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. */

#include "metaEditorSupportPlugin.h"

#include <QtCore/QProcess>
#include <QtWidgets/QApplication>
#include <QtWidgets/QFileDialog>
#include <QtWidgets/QMessageBox>
#include <QtWidgets/QProgressBar>
#include <QtWidgets/QDesktopWidget>

#include <qrkernel/settingsManager.h>
#include <qrmc/metaCompiler.h>

#include "editorGenerator.h"
#include "xmlParser.h"

using namespace qReal;
using namespace metaEditor;

MetaEditorSupportPlugin::MetaEditorSupportPlugin()
		: mGenerateEditorForQrxcAction(NULL)
		, mGenerateEditorWithQrmcAction(NULL)
		, mParseEditorXmlAction(NULL)
		, mRepoControlApi(NULL)
		, mCompilerSettingsPage(new PreferencesCompilerPage())
{
}

MetaEditorSupportPlugin::~MetaEditorSupportPlugin()
{
}

void MetaEditorSupportPlugin::init(PluginConfigurator const &configurator)
{
	mMainWindowInterface = &configurator.mainWindowInterpretersInterface();
	mLogicalRepoApi = &configurator.logicalModelApi().mutableLogicalRepoApi();
	mRepoControlApi = &configurator.repoControlInterface();
}

QList<ActionInfo> MetaEditorSupportPlugin::actions()
{
	mGenerateEditorForQrxcAction.setText(tr("Generate editor"));
	ActionInfo generateEditorForQrxcActionInfo(&mGenerateEditorForQrxcAction, "generators", "tools");
	connect(&mGenerateEditorForQrxcAction, SIGNAL(triggered()), this, SLOT(generateEditorForQrxc()));

	mGenerateEditorWithQrmcAction.setText(tr("Generate editor (qrmc)"));
	ActionInfo generateEditorWithQrmcActionInfo(&mGenerateEditorWithQrmcAction, "generators", "tools");
	connect(&mGenerateEditorWithQrmcAction, SIGNAL(triggered()), this, SLOT(generateEditorWithQrmc()));

	/*
	mParseEditorXmlAction.setText(tr("Parse editor xml")); // button for parsing xml, doesn't work
	ActionInfo parseEditorXmlActionInfo(&mParseEditorXmlAction, "generators", "tools");
	connect(&mParseEditorXmlAction, SIGNAL(triggered()), this, SLOT(parseEditorXml()));
	*/

	return QList<ActionInfo>() << generateEditorForQrxcActionInfo
	<< generateEditorWithQrmcActionInfo;
	//<< parseEditorXmlActionInfo;
}

QPair<QString, gui::PreferencesPage *> MetaEditorSupportPlugin::preferencesPage()
{
	return qMakePair(QObject::tr("Compiler"), static_cast<gui::PreferencesPage *>(mCompilerSettingsPage));
}

void MetaEditorSupportPlugin::generateEditorForQrxc()
{
	EditorGenerator editorGenerator(*mLogicalRepoApi, *mMainWindowInterface->errorReporter());

	QDir dir(".");

	QHash<Id, QPair<QString, QString> > metamodelList = editorGenerator.getMetamodelList();
	foreach (Id const &key, metamodelList.keys()) {
		QString const nameOfTheDirectory = metamodelList[key].first;
		QString const pathToQRealRoot = metamodelList[key].second;
		dir.mkpath(nameOfTheDirectory);
		QPair<QString, QString> const metamodelNames = editorGenerator.generateEditor(key, nameOfTheDirectory, pathToQRealRoot);

		if (!mMainWindowInterface->errorReporter()->wereErrors()) {
			if (QMessageBox::question(mMainWindowInterface->windowWidget()
					, tr("loading.."), QString(tr("Do you want to load generated editor %1?")).arg(metamodelNames.first),
					QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
			{
				return;
			}
			loadNewEditor(nameOfTheDirectory, metamodelNames
					, SettingsManager::value("pathToQmake").toString()
					, SettingsManager::value("pathToMake").toString()
					, SettingsManager::value("pluginExtension").toString()
					, SettingsManager::value("prefix").toString()
					, mLogicalRepoApi->stringProperty(key, "buildConfiguration"));
		}
	}
	if (metamodelList.isEmpty()) {
		mMainWindowInterface->errorReporter()->addError(tr("There is nothing to generate"));
	}
}

void MetaEditorSupportPlugin::generateEditorWithQrmc()
{
	qrmc::MetaCompiler metaCompiler(qApp->applicationDirPath() + "/../../qrmc", mLogicalRepoApi);

	IdList const metamodels = mLogicalRepoApi->children(Id::rootId());

	QProgressBar *progress = new QProgressBar(mMainWindowInterface->windowWidget());
	progress->show();
	int const progressBarWidth = 240;
	int const progressBarHeight = 20;

	QApplication::processEvents();
	QRect const screenRect = qApp->desktop()->availableGeometry();
	progress->move(screenRect.width() / 2 - progressBarWidth / 2, screenRect.height() / 2 - progressBarHeight / 2);
	progress->setFixedWidth(progressBarWidth);
	progress->setFixedHeight(progressBarHeight);
	progress->setRange(0, 100);

	int forEditor = 60 / metamodels.size();

	foreach (Id const &key, metamodels) {
		QString const objectType = key.element();
	if (objectType == "MetamodelDiagram" && mLogicalRepoApi->isLogicalElement(key)) {
			QString nameOfTheDirectory = mLogicalRepoApi->stringProperty(key, "name of the directory");
			QString nameOfMetamodel = mLogicalRepoApi->stringProperty(key, "name");
			QString nameOfPlugin = nameOfTheDirectory.split("/").last();

			if (QMessageBox::question(mMainWindowInterface->windowWidget()
					, tr("loading..")
					, QString(tr("Do you want to compile and load editor %1?")).arg(nameOfPlugin)
					, QMessageBox::Yes, QMessageBox::No)
					== QMessageBox::No)
			{
				continue;
			}

			progress->setValue(5);

			const QString normalizedName = nameOfMetamodel.at(0).toUpper() + nameOfMetamodel.mid(1);
			const bool stateOfLoad = mMainWindowInterface->pluginLoaded(normalizedName);
			if (!mMainWindowInterface->unloadPlugin(normalizedName)) {
				progress->close();
				delete progress;
				return;
			}

			if (!metaCompiler.compile(nameOfMetamodel)) { // generating source code for all metamodels
				QMessageBox::warning(mMainWindowInterface->windowWidget()
						, tr("error")
						, tr("Cannot generate source code for editor ") + nameOfPlugin);
				continue;
			}
			progress->setValue(20);

			QStringList qmakeArgs;
			qmakeArgs.append("CONFIG+=" + mLogicalRepoApi->stringProperty(key, "buildConfiguration"));
			qmakeArgs.append(nameOfMetamodel + ".pro");

			QProcess builder;
			builder.setWorkingDirectory(nameOfTheDirectory);
			const QStringList environment = QProcess::systemEnvironment();
			builder.setEnvironment(environment);
			builder.start(SettingsManager::value("pathToQmake").toString(), qmakeArgs);

			qDebug()  << "qmake";
			if ((builder.waitForFinished()) && (builder.exitCode() == 0)) {
				progress->setValue(40);
				builder.start(SettingsManager::value("pathToMake").toString());

				bool finished = builder.waitForFinished(100000);
				qDebug()  << "make";

				if (finished && (builder.exitCode() == 0)) {
					if (stateOfLoad) {
						QMessageBox::warning(mMainWindowInterface->windowWidget(), tr("Attention!"), tr("Please restart QReal."));
						progress->close();
						delete progress;
						return;
					}
					qDebug()  << "make ok";
					progress->setValue(progress->value() + forEditor / 2);

					if (!nameOfMetamodel.isEmpty()) {
						if (!mMainWindowInterface->unloadPlugin(normalizedName)) {
							QMessageBox::warning(mMainWindowInterface->windowWidget()
									, tr("error")
									, tr("cannot unload plugin ") + normalizedName);
							progress->close();
							delete progress;
							continue;
						}
					}

					QString suffix = "";
					if (mLogicalRepoApi->stringProperty(key, "buildConfiguration") == "debug") {
						suffix = "-d";
					}

					QString const generatedPluginFileName = SettingsManager::value("prefix").toString()
							+ nameOfMetamodel
							+ suffix
							+ "."
							+ SettingsManager::value("pluginExtension").toString()
							;

					if (mMainWindowInterface->loadPlugin(generatedPluginFileName, normalizedName)) {
						progress->setValue(progress->value() + forEditor / 2);
					}
				}
				progress->setValue(100);
			}
		}
	}
	if (progress->value() != 100) {
		QMessageBox::warning(mMainWindowInterface->windowWidget(), tr("error"), tr("cannot load new editor"));
	}
	progress->setValue(100);
	progress->close();
	delete progress;
}

void MetaEditorSupportPlugin::parseEditorXml()
{
	if (!mMainWindowInterface->pluginLoaded("MetaEditor")) {
		QMessageBox::warning(mMainWindowInterface->windowWidget(), tr("error"), tr("required plugin (MetaEditor) is not loaded"));
		return;
	}
	QDir dir(".");
	QString directoryName = ".";
	while (dir.cdUp()) {
		QFileInfoList const infoList = dir.entryInfoList(QDir::Dirs);
		foreach (QFileInfo const &directory, infoList){
			if (directory.baseName() == "qrxml") {
				directoryName = directory.absolutePath() + "/qrxml";
			}
		}
	}
	QString const fileName = QFileDialog::getOpenFileName(mMainWindowInterface->windowWidget()
			, tr("Select xml file to parse")
			, directoryName
			, "XML files (*.xml)");

	if (fileName.isEmpty())
		return;

	XmlParser parser(*mLogicalRepoApi);

	parser.parseFile(fileName);

	parser.loadIncludeList(fileName);

	mMainWindowInterface->reinitModels();
}

void MetaEditorSupportPlugin::loadNewEditor(QString const &directoryName
		, QPair<QString, QString> const &metamodelNames
		, QString const &commandFirst
		, QString const &commandSecond
		, QString const &extension
		, QString const &prefix
		, QString const &buildConfiguration
		)
{
	int const progressBarWidth = 240;
	int const progressBarHeight = 20;

	QString const metamodelName = metamodelNames.first;
	QString const normalizerMetamodelName = metamodelNames.second;

	if ((commandFirst == "") || (commandSecond == "") || (extension == "")) {
		QMessageBox::warning(mMainWindowInterface->windowWidget(), tr("error"), tr("please, fill compiler settings"));
		return;
	}

	QString const normalizeDirName = metamodelName.at(0).toUpper() + metamodelName.mid(1);

	QProgressBar * const progress = new QProgressBar(mMainWindowInterface->windowWidget());
	progress->show();

	QApplication::processEvents();

	QRect const screenRect = qApp->desktop()->availableGeometry();
	progress->move(screenRect.width() / 2 - progressBarWidth / 2, screenRect.height() / 2 - progressBarHeight / 2);
	progress->setFixedWidth(progressBarWidth);
	progress->setFixedHeight(progressBarHeight);
	progress->setRange(0, 100);
	progress->setValue(5);

	const bool stateOfLoad = mMainWindowInterface->pluginLoaded(normalizeDirName);
	if (!mMainWindowInterface->unloadPlugin(normalizeDirName)) {
		progress->close();
		delete progress;
		return;
	}

	progress->setValue(20);
	QStringList qmakeArgs;
	qmakeArgs.append("CONFIG+=" + buildConfiguration);
	qmakeArgs.append(metamodelName + ".pro");

	QProcess builder;
	builder.setWorkingDirectory(directoryName);
	const QStringList environment = QProcess::systemEnvironment();
	builder.setEnvironment(environment);
	builder.start(commandFirst, qmakeArgs);

	if ((builder.waitForFinished()) && (builder.exitCode() == 0)) {
		progress->setValue(60);
		builder.start(commandSecond);

		if (builder.waitForFinished(60000) && (builder.exitCode() == 0)) {
			progress->setValue(80);

			if (stateOfLoad) {
				QMessageBox::warning(mMainWindowInterface->windowWidget(), tr("Attention!"), tr("Please restart QReal."));
				progress->close();
				delete progress;
				return;
			} else if (buildConfiguration == "debug") {
				if (mMainWindowInterface->loadPlugin(prefix + metamodelName + "-d"+ "." + extension, normalizeDirName)) {
					progress->setValue(100);
				}
			} else {
				if (mMainWindowInterface->loadPlugin(prefix + metamodelName + "." + extension, normalizeDirName)) {
					progress->setValue(100);
				}
			}
		}
	}

	if (progress->value() == 20) {
		QMessageBox::warning(mMainWindowInterface->windowWidget(), tr("error"), tr("cannot qmake new editor"));
	} else if (progress->value() == 60) {
		QMessageBox::warning(mMainWindowInterface->windowWidget(), tr("error"), tr("cannot make new editor"));
	}

	progress->setValue(100);
	progress->close();
	delete progress;
}
<commit_msg>- I hope travis will like it<commit_after>/* Copyright 2007-2015 QReal Research Group
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. */

#include "metaEditorSupportPlugin.h"

#include <QtCore/QProcess>
#include <QtWidgets/QApplication>
#include <QtWidgets/QFileDialog>
#include <QtWidgets/QMessageBox>
#include <QtWidgets/QProgressBar>
#include <QtWidgets/QDesktopWidget>

#include <qrkernel/settingsManager.h>
#include <qrmc/metaCompiler.h>

#include "editorGenerator.h"
#include "xmlParser.h"

using namespace qReal;
using namespace metaEditor;

MetaEditorSupportPlugin::MetaEditorSupportPlugin()
		: mGenerateEditorForQrxcAction(NULL)
		, mGenerateEditorWithQrmcAction(NULL)
		, mParseEditorXmlAction(NULL)
		, mRepoControlApi(NULL)
		, mCompilerSettingsPage(new PreferencesCompilerPage())
{
}

MetaEditorSupportPlugin::~MetaEditorSupportPlugin()
{
}

void MetaEditorSupportPlugin::init(PluginConfigurator const &configurator)
{
	mMainWindowInterface = &configurator.mainWindowInterpretersInterface();
	mLogicalRepoApi = &configurator.logicalModelApi().mutableLogicalRepoApi();
	mRepoControlApi = &configurator.repoControlInterface();
}

QList<ActionInfo> MetaEditorSupportPlugin::actions()
{
	mGenerateEditorForQrxcAction.setText(tr("Generate editor"));
	ActionInfo generateEditorForQrxcActionInfo(&mGenerateEditorForQrxcAction, "generators", "tools");
	connect(&mGenerateEditorForQrxcAction, SIGNAL(triggered()), this, SLOT(generateEditorForQrxc()));

	mGenerateEditorWithQrmcAction.setText(tr("Generate editor (qrmc)"));
	ActionInfo generateEditorWithQrmcActionInfo(&mGenerateEditorWithQrmcAction, "generators", "tools");
	connect(&mGenerateEditorWithQrmcAction, SIGNAL(triggered()), this, SLOT(generateEditorWithQrmc()));

	/*
	mParseEditorXmlAction.setText(tr("Parse editor xml")); // button for parsing xml, doesn't work
	ActionInfo parseEditorXmlActionInfo(&mParseEditorXmlAction, "generators", "tools");
	connect(&mParseEditorXmlAction, SIGNAL(triggered()), this, SLOT(parseEditorXml()));
	*/

	return QList<ActionInfo>() << generateEditorForQrxcActionInfo
	<< generateEditorWithQrmcActionInfo;
	//<< parseEditorXmlActionInfo;
}

QPair<QString, gui::PreferencesPage *> MetaEditorSupportPlugin::preferencesPage()
{
	return qMakePair(QObject::tr("Compiler"), static_cast<gui::PreferencesPage *>(mCompilerSettingsPage));
}

void MetaEditorSupportPlugin::generateEditorForQrxc()
{
	EditorGenerator editorGenerator(*mLogicalRepoApi, *mMainWindowInterface->errorReporter());

	QDir dir(".");

	QHash<Id, QPair<QString, QString> > metamodelList = editorGenerator.getMetamodelList();
	foreach (Id const &key, metamodelList.keys()) {
		QString const nameOfTheDirectory = metamodelList[key].first;
		QString const pathToQRealRoot = metamodelList[key].second;
		dir.mkpath(nameOfTheDirectory);
		QPair<QString, QString> const metamodelNames = editorGenerator.generateEditor(key, nameOfTheDirectory, pathToQRealRoot);

		if (!mMainWindowInterface->errorReporter()->wereErrors()) {
			if (QMessageBox::question(mMainWindowInterface->windowWidget()
					, tr("loading.."), QString(tr("Do you want to load generated editor %1?")).arg(metamodelNames.first),
					QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
			{
				return;
			}
			loadNewEditor(nameOfTheDirectory, metamodelNames
					, SettingsManager::value("pathToQmake").toString()
					, SettingsManager::value("pathToMake").toString()
					, SettingsManager::value("pluginExtension").toString()
					, SettingsManager::value("prefix").toString()
					, mLogicalRepoApi->stringProperty(key, "buildConfiguration"));
		}
	}
	if (metamodelList.isEmpty()) {
		mMainWindowInterface->errorReporter()->addError(tr("There is nothing to generate"));
	}
}

void MetaEditorSupportPlugin::generateEditorWithQrmc()
{
	qrmc::MetaCompiler metaCompiler(qApp->applicationDirPath() + "/../../qrmc", mLogicalRepoApi);

	IdList const metamodels = mLogicalRepoApi->children(Id::rootId());

	QProgressBar *progress = new QProgressBar(mMainWindowInterface->windowWidget());
	progress->show();
	int const progressBarWidth = 240;
	int const progressBarHeight = 20;

	QApplication::processEvents();
	QRect const screenRect = qApp->desktop()->availableGeometry();
	progress->move(screenRect.width() / 2 - progressBarWidth / 2, screenRect.height() / 2 - progressBarHeight / 2);
	progress->setFixedWidth(progressBarWidth);
	progress->setFixedHeight(progressBarHeight);
	progress->setRange(0, 100);

	int forEditor = 60 / metamodels.size();

	foreach (Id const &key, metamodels) {
		QString const objectType = key.element();
	if (objectType == "MetamodelDiagram" && mLogicalRepoApi->isLogicalElement(key)) {
			QString nameOfTheDirectory = mLogicalRepoApi->stringProperty(key, "name of the directory");
			QString nameOfMetamodel = mLogicalRepoApi->stringProperty(key, "name");
			QString nameOfPlugin = nameOfTheDirectory.split("/").last();

			if (QMessageBox::question(mMainWindowInterface->windowWidget()
					, tr("loading..")
					, QString(tr("Do you want to compile and load editor %1?")).arg(nameOfPlugin)
					, QMessageBox::Yes, QMessageBox::No)
					== QMessageBox::No)
			{
				continue;
			}

			progress->setValue(5);

			const QString normalizedName = nameOfMetamodel.at(0).toUpper() + nameOfMetamodel.mid(1);
			const bool stateOfLoad = mMainWindowInterface->pluginLoaded(normalizedName);
			if (!mMainWindowInterface->unloadPlugin(normalizedName)) {
				progress->close();
				delete progress;
				return;
			}

			if (!metaCompiler.compile(nameOfMetamodel)) { // generating source code for all metamodels
				QMessageBox::warning(mMainWindowInterface->windowWidget()
						, tr("error")
						, tr("Cannot generate source code for editor ") + nameOfPlugin);
				continue;
			}
			progress->setValue(20);

			QStringList qmakeArgs;
			qmakeArgs.append("CONFIG+=" + mLogicalRepoApi->stringProperty(key, "buildConfiguration"));
			qmakeArgs.append(nameOfMetamodel + ".pro");

			QProcess builder;
			builder.setWorkingDirectory(nameOfTheDirectory);
			const QStringList environment = QProcess::systemEnvironment();
			builder.setEnvironment(environment);
			builder.start(SettingsManager::value("pathToQmake").toString(), qmakeArgs);

			qDebug()  << "qmake";
			if ((builder.waitForFinished()) && (builder.exitCode() == 0)) {
				progress->setValue(40);
				builder.start(SettingsManager::value("pathToMake").toString());

				bool finished = builder.waitForFinished(100000);
				qDebug()  << "make";

				if (finished && (builder.exitCode() == 0)) {
					if (stateOfLoad) {
						QMessageBox::warning(mMainWindowInterface->windowWidget()
								, tr("Attention!"), tr("Please restart QReal."));
						progress->close();
						delete progress;
						return;
					}
					qDebug()  << "make ok";
					progress->setValue(progress->value() + forEditor / 2);

					if (!nameOfMetamodel.isEmpty()) {
						if (!mMainWindowInterface->unloadPlugin(normalizedName)) {
							QMessageBox::warning(mMainWindowInterface->windowWidget()
									, tr("error")
									, tr("cannot unload plugin ") + normalizedName);
							progress->close();
							delete progress;
							continue;
						}
					}

					QString suffix = "";
					if (mLogicalRepoApi->stringProperty(key, "buildConfiguration") == "debug") {
						suffix = "-d";
					}

					QString const generatedPluginFileName = SettingsManager::value("prefix").toString()
							+ nameOfMetamodel
							+ suffix
							+ "."
							+ SettingsManager::value("pluginExtension").toString()
							;

					if (mMainWindowInterface->loadPlugin(generatedPluginFileName, normalizedName)) {
						progress->setValue(progress->value() + forEditor / 2);
					}
				}
				progress->setValue(100);
			}
		}
	}
	if (progress->value() != 100) {
		QMessageBox::warning(mMainWindowInterface->windowWidget(), tr("error"), tr("cannot load new editor"));
	}
	progress->setValue(100);
	progress->close();
	delete progress;
}

void MetaEditorSupportPlugin::parseEditorXml()
{
	if (!mMainWindowInterface->pluginLoaded("MetaEditor")) {
		QMessageBox::warning(mMainWindowInterface->windowWidget(), tr("error"), tr("required plugin (MetaEditor) is not loaded"));
		return;
	}
	QDir dir(".");
	QString directoryName = ".";
	while (dir.cdUp()) {
		QFileInfoList const infoList = dir.entryInfoList(QDir::Dirs);
		foreach (QFileInfo const &directory, infoList){
			if (directory.baseName() == "qrxml") {
				directoryName = directory.absolutePath() + "/qrxml";
			}
		}
	}
	QString const fileName = QFileDialog::getOpenFileName(mMainWindowInterface->windowWidget()
			, tr("Select xml file to parse")
			, directoryName
			, "XML files (*.xml)");

	if (fileName.isEmpty())
		return;

	XmlParser parser(*mLogicalRepoApi);

	parser.parseFile(fileName);

	parser.loadIncludeList(fileName);

	mMainWindowInterface->reinitModels();
}

void MetaEditorSupportPlugin::loadNewEditor(QString const &directoryName
		, QPair<QString, QString> const &metamodelNames
		, QString const &commandFirst
		, QString const &commandSecond
		, QString const &extension
		, QString const &prefix
		, QString const &buildConfiguration
		)
{
	int const progressBarWidth = 240;
	int const progressBarHeight = 20;

	QString const metamodelName = metamodelNames.first;
	QString const normalizerMetamodelName = metamodelNames.second;

	if ((commandFirst == "") || (commandSecond == "") || (extension == "")) {
		QMessageBox::warning(mMainWindowInterface->windowWidget(), tr("error"), tr("please, fill compiler settings"));
		return;
	}

	QString const normalizeDirName = metamodelName.at(0).toUpper() + metamodelName.mid(1);

	QProgressBar * const progress = new QProgressBar(mMainWindowInterface->windowWidget());
	progress->show();

	QApplication::processEvents();

	QRect const screenRect = qApp->desktop()->availableGeometry();
	progress->move(screenRect.width() / 2 - progressBarWidth / 2, screenRect.height() / 2 - progressBarHeight / 2);
	progress->setFixedWidth(progressBarWidth);
	progress->setFixedHeight(progressBarHeight);
	progress->setRange(0, 100);
	progress->setValue(5);

	const bool stateOfLoad = mMainWindowInterface->pluginLoaded(normalizeDirName);
	if (!mMainWindowInterface->unloadPlugin(normalizeDirName)) {
		progress->close();
		delete progress;
		return;
	}

	progress->setValue(20);
	QStringList qmakeArgs;
	qmakeArgs.append("CONFIG+=" + buildConfiguration);
	qmakeArgs.append(metamodelName + ".pro");

	QProcess builder;
	builder.setWorkingDirectory(directoryName);
	const QStringList environment = QProcess::systemEnvironment();
	builder.setEnvironment(environment);
	builder.start(commandFirst, qmakeArgs);

	if ((builder.waitForFinished()) && (builder.exitCode() == 0)) {
		progress->setValue(60);
		builder.start(commandSecond);

		if (builder.waitForFinished(60000) && (builder.exitCode() == 0)) {
			progress->setValue(80);

			if (stateOfLoad) {
				QMessageBox::warning(mMainWindowInterface->windowWidget()
						, tr("Attention!"), tr("Please restart QReal."));
				progress->close();
				delete progress;
				return;
			} else if (buildConfiguration == "debug") {
				if (mMainWindowInterface->loadPlugin(prefix + metamodelName
						+ "-d"+ "." + extension, normalizeDirName)) {
					progress->setValue(100);
				}
			} else {
				if (mMainWindowInterface->loadPlugin(prefix + metamodelName + "." + extension, normalizeDirName)) {
					progress->setValue(100);
				}
			}
		}
	}

	if (progress->value() == 20) {
		QMessageBox::warning(mMainWindowInterface->windowWidget(), tr("error"), tr("cannot qmake new editor"));
	} else if (progress->value() == 60) {
		QMessageBox::warning(mMainWindowInterface->windowWidget(), tr("error"), tr("cannot make new editor"));
	}

	progress->setValue(100);
	progress->close();
	delete progress;
}
<|endoftext|>


<commit_before>#include "intermediateNode.h"

#include <QQueue>

using namespace generatorBase;
using namespace myUtils;

SimpleNode::SimpleNode(const qReal::Id &id, QObject *parent)
	: IntermediateNode(parent)
	, mId(id)
{
}

IntermediateNode::Type SimpleNode::type() const
{
	return Type::simple;
}

qReal::Id SimpleNode::firstId() const
{
	return mId;
}

bool SimpleNode::analyzeBreak()
{
	mHasBreakInside = false;
	return mHasBreakInside;
}

QList<IntermediateNode *> SimpleNode::childrenNodes() const
{
	return {};
}

qReal::Id SimpleNode::id() const
{
	return mId;
}

IfNode::IfNode(IntermediateNode *condition
							, IntermediateNode *thenBranch
							, IntermediateNode *elseBranch
							, QObject *parent)
	: IntermediateNode(parent)
	, mCondition(condition)
	, mThenBranch(thenBranch)
	, mElseBranch(elseBranch)
	, mIsIfThenForm(elseBranch == nullptr)
{
}

IntermediateNode *IfNode::condition() const
{
	return mCondition;
}

IntermediateNode *IfNode::thenBranch() const
{
	return mThenBranch;
}

IntermediateNode *IfNode::elseBranch() const
{
	return mElseBranch;
}

bool IfNode::analyzeBreak()
{
	mHasBreakInside = mThenBranch->analyzeBreak();

	if (mElseBranch) {
		mHasBreakInside |= mElseBranch->analyzeBreak();
	}

	return mHasBreakInside;
}

QList<IntermediateNode *> IfNode::childrenNodes() const
{
	QList<IntermediateNode *> chilrendNodes = {mCondition, mThenBranch};
	if (mElseBranch) {
		chilrendNodes.append(mElseBranch);
	}

	return childrenNodes();
}

IntermediateNode::Type IfNode::type() const
{
	return Type::ifThenElseCondition;
}

qReal::Id IfNode::firstId() const
{
	return mCondition->firstId();
}

IntermediateNode::IntermediateNode(QObject *parent)
	: QObject(parent)
	, mHasBreakInside(false)
	, mBreakWasAnalyzed(false)
{
}

QString IntermediateNode::currentThread() const
{
	return mCurrentThread;
}

void IntermediateNode::setCurrentThread(const QString &thread)
{
	mCurrentThread = thread;
}

//bool IntermediateNode::analyzeBreak()
//{
//	if (mBreakWasAnalyzed) {
//		return mHasBreakInside;
//	}

//	mBreakWasAnalyzed = true;
//	return analyzeBreak();
//}

bool IntermediateNode::hasBreakInside() const
{
	return mHasBreakInside;
}

SwitchNode::SwitchNode(IntermediateNode *condition, const QList<IntermediateNode *> &branches, IntermediateNode *exit, QObject *parent)
	: IntermediateNode(parent)
	, mCondition(condition)
	, mBranches(QList<IntermediateNode *>(branches))
	, mExit(exit)
{
}

IntermediateNode *SwitchNode::condition() const
{
	return mCondition;
}

QList<IntermediateNode *> SwitchNode::branches() const
{
	return mBranches;
}

IntermediateNode *SwitchNode::exit() const
{
	return mExit;
}

bool SwitchNode::analyzeBreak()
{
	if (mBreakWasAnalyzed) {
		return mHasBreakInside;
	}

	mHasBreakInside = false;
	for (IntermediateNode *node : mBranches) {
		mHasBreakInside |= node->analyzeBreak();
	}
	mBreakWasAnalyzed = true;

	return mHasBreakInside;
}

QList<IntermediateNode *> SwitchNode::childrenNodes() const
{
	QList<IntermediateNode *> childrenNodes = mBranches;
	childrenNodes.append(mCondition);

	return childrenNodes;
}

IntermediateNode::Type SwitchNode::type() const
{
	return Type::switchCondition;
}

qReal::Id SwitchNode::firstId() const
{
	return mCondition->firstId();
}

BlockNode::BlockNode(IntermediateNode *firstNode, IntermediateNode *secondNode, QObject *parent)
	: IntermediateNode(parent)
	, mFirstNode(firstNode)
	, mSecondNode(secondNode)
{
}

IntermediateNode *BlockNode::firstNode() const
{
	return mFirstNode;
}

IntermediateNode *BlockNode::secondNode() const
{
	return mSecondNode;
}

bool BlockNode::analyzeBreak()
{
	if (mBreakWasAnalyzed) {
		return mHasBreakInside;
	}

	mHasBreakInside = mFirstNode->analyzeBreak() || mSecondNode->analyzeBreak();
	mBreakWasAnalyzed = true;
	return mHasBreakInside;
}

QList<IntermediateNode *> BlockNode::childrenNodes() const
{
	return { mFirstNode, mSecondNode };
}

IntermediateNode::Type BlockNode::type() const
{
	return Type::block;
}

qReal::Id BlockNode::firstId() const
{
	return firstNode()->firstId();
}

WhileNode::WhileNode(IntermediateNode *headNode, IntermediateNode *bodyNode, IntermediateNode *exitNode, QObject *parent)
	: IntermediateNode(parent)
	, mHeadNode(headNode)
	, mBodyNode(bodyNode)
	, mExitNode(exitNode)
{
}

IntermediateNode *WhileNode::headNode() const
{
	return mHeadNode;
}

IntermediateNode *WhileNode::bodyNode() const
{
	return mBodyNode;
}

IntermediateNode *WhileNode::exitNode() const
{
	return mExitNode;
}

bool WhileNode::analyzeBreak()
{
	if (mBreakWasAnalyzed) {
		return mHasBreakInside;
	}

	mHasBreakInside = mHeadNode->analyzeBreak() || mBodyNode->analyzeBreak();
	mBreakWasAnalyzed = true;
	return mHasBreakInside;
}

QList<IntermediateNode *> WhileNode::childrenNodes() const
{
	return { mHeadNode, mBodyNode };
}

IntermediateNode::Type WhileNode::type() const
{
	return Type::whileloop;
}

qReal::Id WhileNode::firstId() const
{
	return mHeadNode->firstId();
}

SelfLoopNode::SelfLoopNode(IntermediateNode *bodyNode, QObject *parent)
	: IntermediateNode(parent)
	, mBodyNode(bodyNode)
{
}

IntermediateNode *SelfLoopNode::bodyNode() const
{
	return mBodyNode;
}

bool SelfLoopNode::analyzeBreak()
{
	if (mBreakWasAnalyzed) {
		return mHasBreakInside;
	}

	mHasBreakInside = mBodyNode->analyzeBreak();
	mBreakWasAnalyzed = true;
	return mHasBreakInside;
}

QList<IntermediateNode *> SelfLoopNode::childrenNodes() const
{
	return { mBodyNode };
}

IntermediateNode::Type SelfLoopNode::type() const
{
	return Type::infiniteloop;
}

qReal::Id SelfLoopNode::firstId() const
{
	return mBodyNode->firstId();
}

BreakNode::BreakNode(const qReal::Id &id, QObject *parent)
	: IntermediateNode(parent)
	, mId(id)
{
}

IntermediateNode::Type BreakNode::type() const
{
	return breakNode;
}

qReal::Id BreakNode::firstId() const
{
	return mId;
}

bool BreakNode::analyzeBreak()
{
	mHasBreakInside = true;
	mBreakWasAnalyzed = true;
	return mHasBreakInside;
}

QList<IntermediateNode *> BreakNode::childrenNodes() const
{
	return {};
}

FakeCycleHeadNode::FakeCycleHeadNode(const qReal::Id &id, QObject *parent)
	: IntermediateNode(parent)
	, mId(id)
{
}

IntermediateNode::Type FakeCycleHeadNode::type() const
{
	return fakeCycleHead;
}

qReal::Id FakeCycleHeadNode::firstId() const
{
	return mId;
}

bool FakeCycleHeadNode::analyzeBreak()
{
	mHasBreakInside = false;
	mBreakWasAnalyzed = true;
	return mHasBreakInside;
}

QList<IntermediateNode *> FakeCycleHeadNode::childrenNodes() const
{
	return {};
}

NodeWithBreaks::NodeWithBreaks(IntermediateNode *condition, QList<IntermediateNode *> &exitBranches, QObject *parent)
	: IntermediateNode(parent)
	, mCondition(condition)
	, mExitBranches(exitBranches)
{
}

IntermediateNode *NodeWithBreaks::condition() const
{
	return mCondition;
}

QList<IntermediateNode *> NodeWithBreaks::exitBranches() const
{
	return mExitBranches;
}

QList<IntermediateNode *> NodeWithBreaks::restBranches() const
{
	return mRestBranches;
}

void NodeWithBreaks::setRestBranches(const QList<IntermediateNode *> &restBranches)
{
	mRestBranches = restBranches;
}

bool NodeWithBreaks::analyzeBreak()
{
	mHasBreakInside = true;
	mBreakWasAnalyzed = true;
	return mHasBreakInside;
}

QList<IntermediateNode *> NodeWithBreaks::childrenNodes() const
{
	QList<IntermediateNode *> childrenNodes = mExitBranches;
	childrenNodes.append(mCondition);

	return childrenNodes;
}

IntermediateNode::Type NodeWithBreaks::type() const
{
	return nodeWithBreaks;
}

qReal::Id NodeWithBreaks::firstId() const
{
	return mCondition->firstId();
}
<commit_msg>Bug fix<commit_after>#include "intermediateNode.h"

#include <QQueue>

using namespace generatorBase;
using namespace myUtils;

SimpleNode::SimpleNode(const qReal::Id &id, QObject *parent)
	: IntermediateNode(parent)
	, mId(id)
{
}

IntermediateNode::Type SimpleNode::type() const
{
	return Type::simple;
}

qReal::Id SimpleNode::firstId() const
{
	return mId;
}

bool SimpleNode::analyzeBreak()
{
	mHasBreakInside = false;
	return mHasBreakInside;
}

QList<IntermediateNode *> SimpleNode::childrenNodes() const
{
	return {};
}

qReal::Id SimpleNode::id() const
{
	return mId;
}

IfNode::IfNode(IntermediateNode *condition
							, IntermediateNode *thenBranch
							, IntermediateNode *elseBranch
							, QObject *parent)
	: IntermediateNode(parent)
	, mCondition(condition)
	, mThenBranch(thenBranch)
	, mElseBranch(elseBranch)
	, mIsIfThenForm(elseBranch == nullptr)
{
}

IntermediateNode *IfNode::condition() const
{
	return mCondition;
}

IntermediateNode *IfNode::thenBranch() const
{
	return mThenBranch;
}

IntermediateNode *IfNode::elseBranch() const
{
	return mElseBranch;
}

bool IfNode::analyzeBreak()
{
	mHasBreakInside = mThenBranch->analyzeBreak();

	if (mElseBranch) {
		mHasBreakInside |= mElseBranch->analyzeBreak();
	}

	return mHasBreakInside;
}

QList<IntermediateNode *> IfNode::childrenNodes() const
{
	QList<IntermediateNode *> childrenNodes = {mCondition, mThenBranch};
	if (mElseBranch) {
		childrenNodes.append(mElseBranch);
	}

	return childrenNodes;
}

IntermediateNode::Type IfNode::type() const
{
	return Type::ifThenElseCondition;
}

qReal::Id IfNode::firstId() const
{
	return mCondition->firstId();
}

IntermediateNode::IntermediateNode(QObject *parent)
	: QObject(parent)
	, mHasBreakInside(false)
	, mBreakWasAnalyzed(false)
{
}

QString IntermediateNode::currentThread() const
{
	return mCurrentThread;
}

void IntermediateNode::setCurrentThread(const QString &thread)
{
	mCurrentThread = thread;
}

//bool IntermediateNode::analyzeBreak()
//{
//	if (mBreakWasAnalyzed) {
//		return mHasBreakInside;
//	}

//	mBreakWasAnalyzed = true;
//	return analyzeBreak();
//}

bool IntermediateNode::hasBreakInside() const
{
	return mHasBreakInside;
}

SwitchNode::SwitchNode(IntermediateNode *condition, const QList<IntermediateNode *> &branches, IntermediateNode *exit, QObject *parent)
	: IntermediateNode(parent)
	, mCondition(condition)
	, mBranches(QList<IntermediateNode *>(branches))
	, mExit(exit)
{
}

IntermediateNode *SwitchNode::condition() const
{
	return mCondition;
}

QList<IntermediateNode *> SwitchNode::branches() const
{
	return mBranches;
}

IntermediateNode *SwitchNode::exit() const
{
	return mExit;
}

bool SwitchNode::analyzeBreak()
{
	if (mBreakWasAnalyzed) {
		return mHasBreakInside;
	}

	mHasBreakInside = false;
	for (IntermediateNode *node : mBranches) {
		mHasBreakInside |= node->analyzeBreak();
	}
	mBreakWasAnalyzed = true;

	return mHasBreakInside;
}

QList<IntermediateNode *> SwitchNode::childrenNodes() const
{
	QList<IntermediateNode *> childrenNodes = mBranches;
	childrenNodes.append(mCondition);

	return childrenNodes;
}

IntermediateNode::Type SwitchNode::type() const
{
	return Type::switchCondition;
}

qReal::Id SwitchNode::firstId() const
{
	return mCondition->firstId();
}

BlockNode::BlockNode(IntermediateNode *firstNode, IntermediateNode *secondNode, QObject *parent)
	: IntermediateNode(parent)
	, mFirstNode(firstNode)
	, mSecondNode(secondNode)
{
}

IntermediateNode *BlockNode::firstNode() const
{
	return mFirstNode;
}

IntermediateNode *BlockNode::secondNode() const
{
	return mSecondNode;
}

bool BlockNode::analyzeBreak()
{
	if (mBreakWasAnalyzed) {
		return mHasBreakInside;
	}

	mHasBreakInside = mFirstNode->analyzeBreak() || mSecondNode->analyzeBreak();
	mBreakWasAnalyzed = true;
	return mHasBreakInside;
}

QList<IntermediateNode *> BlockNode::childrenNodes() const
{
	return { mFirstNode, mSecondNode };
}

IntermediateNode::Type BlockNode::type() const
{
	return Type::block;
}

qReal::Id BlockNode::firstId() const
{
	return firstNode()->firstId();
}

WhileNode::WhileNode(IntermediateNode *headNode, IntermediateNode *bodyNode, IntermediateNode *exitNode, QObject *parent)
	: IntermediateNode(parent)
	, mHeadNode(headNode)
	, mBodyNode(bodyNode)
	, mExitNode(exitNode)
{
}

IntermediateNode *WhileNode::headNode() const
{
	return mHeadNode;
}

IntermediateNode *WhileNode::bodyNode() const
{
	return mBodyNode;
}

IntermediateNode *WhileNode::exitNode() const
{
	return mExitNode;
}

bool WhileNode::analyzeBreak()
{
	if (mBreakWasAnalyzed) {
		return mHasBreakInside;
	}

	mHasBreakInside = mHeadNode->analyzeBreak() || mBodyNode->analyzeBreak();
	mBreakWasAnalyzed = true;
	return mHasBreakInside;
}

QList<IntermediateNode *> WhileNode::childrenNodes() const
{
	return { mHeadNode, mBodyNode };
}

IntermediateNode::Type WhileNode::type() const
{
	return Type::whileloop;
}

qReal::Id WhileNode::firstId() const
{
	return mHeadNode->firstId();
}

SelfLoopNode::SelfLoopNode(IntermediateNode *bodyNode, QObject *parent)
	: IntermediateNode(parent)
	, mBodyNode(bodyNode)
{
}

IntermediateNode *SelfLoopNode::bodyNode() const
{
	return mBodyNode;
}

bool SelfLoopNode::analyzeBreak()
{
	if (mBreakWasAnalyzed) {
		return mHasBreakInside;
	}

	mHasBreakInside = mBodyNode->analyzeBreak();
	mBreakWasAnalyzed = true;
	return mHasBreakInside;
}

QList<IntermediateNode *> SelfLoopNode::childrenNodes() const
{
	return { mBodyNode };
}

IntermediateNode::Type SelfLoopNode::type() const
{
	return Type::infiniteloop;
}

qReal::Id SelfLoopNode::firstId() const
{
	return mBodyNode->firstId();
}

BreakNode::BreakNode(const qReal::Id &id, QObject *parent)
	: IntermediateNode(parent)
	, mId(id)
{
}

IntermediateNode::Type BreakNode::type() const
{
	return breakNode;
}

qReal::Id BreakNode::firstId() const
{
	return mId;
}

bool BreakNode::analyzeBreak()
{
	mHasBreakInside = true;
	mBreakWasAnalyzed = true;
	return mHasBreakInside;
}

QList<IntermediateNode *> BreakNode::childrenNodes() const
{
	return {};
}

FakeCycleHeadNode::FakeCycleHeadNode(const qReal::Id &id, QObject *parent)
	: IntermediateNode(parent)
	, mId(id)
{
}

IntermediateNode::Type FakeCycleHeadNode::type() const
{
	return fakeCycleHead;
}

qReal::Id FakeCycleHeadNode::firstId() const
{
	return mId;
}

bool FakeCycleHeadNode::analyzeBreak()
{
	mHasBreakInside = false;
	mBreakWasAnalyzed = true;
	return mHasBreakInside;
}

QList<IntermediateNode *> FakeCycleHeadNode::childrenNodes() const
{
	return {};
}

NodeWithBreaks::NodeWithBreaks(IntermediateNode *condition, QList<IntermediateNode *> &exitBranches, QObject *parent)
	: IntermediateNode(parent)
	, mCondition(condition)
	, mExitBranches(exitBranches)
{
}

IntermediateNode *NodeWithBreaks::condition() const
{
	return mCondition;
}

QList<IntermediateNode *> NodeWithBreaks::exitBranches() const
{
	return mExitBranches;
}

QList<IntermediateNode *> NodeWithBreaks::restBranches() const
{
	return mRestBranches;
}

void NodeWithBreaks::setRestBranches(const QList<IntermediateNode *> &restBranches)
{
	mRestBranches = restBranches;
}

bool NodeWithBreaks::analyzeBreak()
{
	mHasBreakInside = true;
	mBreakWasAnalyzed = true;
	return mHasBreakInside;
}

QList<IntermediateNode *> NodeWithBreaks::childrenNodes() const
{
	QList<IntermediateNode *> childrenNodes = mExitBranches;
	childrenNodes.append(mCondition);

	return childrenNodes;
}

IntermediateNode::Type NodeWithBreaks::type() const
{
	return nodeWithBreaks;
}

qReal::Id NodeWithBreaks::firstId() const
{
	return mCondition->firstId();
}
<|endoftext|>


<commit_before>/*****************************************************************************

YASK: Yet Another Stencil Kernel
Copyright (c) 2014-2017, Intel Corporation

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.

*****************************************************************************/

// Finite-differences coefficients code.
// Contributed by Jeremy Tillay.

#include <iostream>
#include <cstring>
#include "fd_coeff.hpp"
#define MIN(x, y) (((x) < (y)) ? (x): (y))
#define MAX(x, y) (((x) > (y)) ? (x): (y))

using namespace std;

int main()
{
    //set the order of the derivative to approximate
    //e.g. we want to approximate d^m/dx^m
    const int order = 2;

    //set the evaluation point e.g. we want to approximate some derivative f^(m)[eval_point]
    //for most application, this is 0
    float eval_point = 0;

    const int radius = 2;
    const int num_points = 2*radius+1;
    float coeff[num_points];
    memset(coeff, 0.0, sizeof(coeff));

    //float* coeff = (float*) malloc(num_points*sizeof(float));
    //memset(coeff, 0.0, num_points*sizeof(float));

    //Construct a set of points (-h*radius, -h*(radius-1), .. 0, h,..., h*radius)
    //Could pass any arbitrary array grid_points = {x_0, x_1, ... x_n}

    //float* grid_points = (float*) malloc(num_points*sizeof(float));
    float grid_points[num_points];
    cout << "Approximating derivative from grid points: " ;
    for(int i=0; i<num_points; i++){
        grid_points[i] = (-radius + i);
        cout << grid_points[i]<< ", ";
    }

    cout << endl;

    fd_coeff(coeff, eval_point, order, grid_points, num_points);

    string suffix = (order == 1) ? "st" : (order == 2) ? "nd" : (order == 3) ? "rd" : "th";
    cout << "The " << order << suffix << " derivative of f("<< eval_point <<
        ") is approximated by this " << num_points << "-point FD formula:" << endl;
    cout << "f^(" << order << ")(" << eval_point << ") ~= ";

    for(int i=0; i<num_points; i++) {
        if (i)
            cout << " + ";
            cout << coeff[i] << "*f[" << grid_points[i] << "]";
    }
    cout << endl;

    cout << "Therefore, the coefficients are: ";
    for(int i=0; i<num_points; i++) {
    	cout << coeff[i] << ", ";
    }
    cout << endl;

free(grid_points);
free(coeff);
return 0;
}
<commit_msg>Removed extraneous frees<commit_after>/*****************************************************************************

YASK: Yet Another Stencil Kernel
Copyright (c) 2014-2017, Intel Corporation

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.

*****************************************************************************/

// Finite-differences coefficients code.
// Contributed by Jeremy Tillay.

#include <iostream>
#include <cstring>
#include "fd_coeff.hpp"
#define MIN(x, y) (((x) < (y)) ? (x): (y))
#define MAX(x, y) (((x) > (y)) ? (x): (y))

using namespace std;

int main()
{
    //set the order of the derivative to approximate
    //e.g. we want to approximate d^m/dx^m
    const int order = 2;

    //set the evaluation point e.g. we want to approximate some derivative f^(m)[eval_point]
    //for most application, this is 0
    float eval_point = 0;

    const int radius = 2;
    const int num_points = 2*radius+1;
    float coeff[num_points];
    memset(coeff, 0.0, sizeof(coeff));

    //float* coeff = (float*) malloc(num_points*sizeof(float));
    //memset(coeff, 0.0, num_points*sizeof(float));

    //Construct a set of points (-h*radius, -h*(radius-1), .. 0, h,..., h*radius)
    //Could pass any arbitrary array grid_points = {x_0, x_1, ... x_n}

    //float* grid_points = (float*) malloc(num_points*sizeof(float));
    float grid_points[num_points];
    cout << "Approximating derivative from grid points: " ;
    for(int i=0; i<num_points; i++){
        grid_points[i] = (-radius + i);
        cout << grid_points[i]<< ", ";
    }

    cout << endl;

    fd_coeff(coeff, eval_point, order, grid_points, num_points);

    string suffix = (order == 1) ? "st" : (order == 2) ? "nd" : (order == 3) ? "rd" : "th";
    cout << "The " << order << suffix << " derivative of f("<< eval_point <<
        ") is approximated by this " << num_points << "-point FD formula:" << endl;
    cout << "f^(" << order << ")(" << eval_point << ") ~= ";

    for(int i=0; i<num_points; i++) {
        if (i)
            cout << " + ";
            cout << coeff[i] << "*f[" << grid_points[i] << "]";
    }
    cout << endl;

    cout << "Therefore, the coefficients are: ";
    for(int i=0; i<num_points; i++) {
    	cout << coeff[i] << ", ";
    }
    cout << endl;

//free(grid_points);
//free(coeff);
return 0;
}
<|endoftext|>


<commit_before>#include "condor_common.h"
#include <set>
#include "MyString.h"
#include "condor_sockaddr.h"
#include "condor_netdb.h"
#include "condor_config.h"
#include "condor_sockfunc.h"
#include "ipv6_hostname.h"
#include "ipv6_addrinfo.h"

static condor_sockaddr local_ipaddr;
static MyString local_hostname;
static MyString local_fqdn;
static bool hostname_initialized = false;

static bool nodns_enabled()
{
	return param_boolean("NO_DNS", false);
}

void init_local_hostname()
{
		// [m.]
		// initializing local hostname, ip address, fqdn was
		// super complex.
		//
		// implementation was scattered over condor_netdb and
		// my_hostname, get_full_hostname.
		//
		// above them has duplicated code in many ways.
		// so I aggregated all of them into here.

	bool ipaddr_inited = false;
	char hostname[MAXHOSTNAMELEN];
	int ret;

		// [TODO:IPV6] condor_gethostname is not IPv6 safe.
		// reimplement it.
	ret = condor_gethostname(hostname, sizeof(hostname));
	if (ret) {
		dprintf(D_HOSTNAME, "condor_gethostname() failed. Cannot initialize "
				"local hostname, ip address, FQDN.\n");
		return;
	}

		// if NETWORK_INTERFACE is defined, we use that as a local ip addr.
	MyString network_interface;
	if (param(network_interface, "NETWORK_INTERFACE")) {
		if (local_ipaddr.from_ip_string(network_interface))
			ipaddr_inited = true;
	}

		// now initialize hostname and fqdn
	if (nodns_enabled()) { // if nodns is enabled, we can cut some slack.
			// condor_gethostname() returns a hostname with
			// DEFAULT_DOMAIN_NAME. Thus, it is always fqdn
		local_hostname = hostname;
		local_fqdn = hostname;
		if (!ipaddr_inited) {
			local_ipaddr = convert_hostname_to_ipaddr(local_hostname);
		}
		return;
	}

	addrinfo_iterator ai;
	ret = ipv6_getaddrinfo(hostname, NULL, ai);
	if (ret) {
			// write some error message
		dprintf(D_HOSTNAME, "hostname %s cannot be resolved by getaddrinfo\n",
				hostname);
		return;
	}

	bool got_fqdn = false;
	while (addrinfo* info = ai.next()) {
		const char* name = info->ai_canonname;
		if (!name)
			continue;
		const char* dotpos = strchr(name, '.');
		condor_sockaddr addr(info->ai_addr);

		if (addr.is_loopback() || addr.is_private_network())
			continue;

		if (dotpos) {
				// consider it as a FQDN
			local_fqdn = name;
			local_hostname = local_fqdn.Substr(0, dotpos-name-1);
			if (!ipaddr_inited)
				local_ipaddr = addr;
			got_fqdn = true;
			break;
		}
		else {
			local_hostname = name;
			if (!ipaddr_inited)
				local_ipaddr = addr;
		}
	}

	if (!got_fqdn) {
		local_fqdn = local_hostname;
		MyString default_domain;
		if (param(default_domain, "DEFAULT_DOMAIN_NAME")) {
			if (default_domain[0] != '.')
				local_fqdn += ".";
			local_fqdn += default_domain;
		}
	}
	hostname_initialized = true;
}

condor_sockaddr get_local_ipaddr()
{
	if (!hostname_initialized)
		init_local_hostname();
	return local_ipaddr;
}

MyString get_local_hostname()
{
	if (!hostname_initialized)
		init_local_hostname();
	return local_hostname;
}

MyString get_local_fqdn()
{
	if (!hostname_initialized)
		init_local_hostname();
	return local_fqdn;
}

MyString get_hostname(const condor_sockaddr& addr)
{
	MyString ret;
	if (nodns_enabled())
		return convert_ipaddr_to_hostname(addr);

	condor_sockaddr targ_addr;

	// just like sin_to_string(), if given address is 0.0.0.0 or equivalent,
	// it changes to local IP address.
	if (addr.is_addr_any())
		targ_addr = get_local_ipaddr();
	else
		targ_addr = addr;

	int e;
	char hostname[NI_MAXHOST];

	e = condor_getnameinfo(targ_addr, hostname, sizeof(hostname), NULL, 0, 0);
	if (e)
		return ret;

	ret = hostname;
	return ret;
}

std::vector<MyString> get_hostname_with_alias(const condor_sockaddr& addr)
{
	std::vector<MyString> ret;
	MyString hostname = get_hostname(addr);
	if (hostname.IsEmpty())
		return ret;
	ret.push_back(hostname);

	if (nodns_enabled())
		return ret; // no need to call further DNS functions.

	hostent* ent;
		//int aftype = addr.get_aftype();
		//ent = gethostbyname2(hostname.Value(), addr.get_aftype());

		// really should call gethostbyname2() however most platforms do not
		// support. (Solaris, HP-UX, IRIX)
		// complete DNS aliases can be only obtained by gethostbyname.
		// however, what happens if we call it in IPv6-only system?
		// can we get DNS aliases for the hostname that only contains
		// IPv6 addresses?
	ent = gethostbyname(hostname.Value());

	if (!ent)
		return ret;

	char** alias = ent->h_aliases;
	for (; *alias; ++alias) {
		ret.push_back(MyString(*alias));
	}
	return ret;
}

// look up FQDN for hostname and aliases.
// if not, it adds up DEFAULT_DOMAIN_NAME
MyString get_full_hostname(const condor_sockaddr& addr)
{
		// this function will go smooth even with NODNS.
	MyString ret;
	std::vector<MyString> hostnames = get_hostname_with_alias(addr);
	if (hostnames.empty()) return ret;
	std::vector<MyString>::iterator iter;
	for (iter = hostnames.begin(); iter != hostnames.end(); ++iter) {
		MyString& str = *iter;
		if (str.FindChar('.') != -1) {
			return str;
		}
	}

	MyString default_domain;
	if (param(default_domain, "DEFAULT_DOMAIN_NAME")) {
			// first element is the hostname got by gethostname()
		ret = *hostnames.begin();
		if (default_domain[0] != '.')
			ret += ".";
		ret += default_domain;
	}
	return ret;
}

std::vector<condor_sockaddr> resolve_hostname(const char* hostname)
{
	MyString host(hostname);
	return resolve_hostname(host);
}

std::vector<condor_sockaddr> resolve_hostname(const MyString& hostname)
{
	std::vector<condor_sockaddr> ret;
	if (nodns_enabled()) {
		condor_sockaddr addr = convert_hostname_to_ipaddr(hostname);
		if (addr == condor_sockaddr::null)
			return ret;
		ret.push_back(addr);
		return ret;
	}

	addrinfo_iterator ai;
	bool res  = ipv6_getaddrinfo(hostname.Value(), NULL, ai);
	if (res) {
		return ret;
	}

		// To eliminate duplicate address, here we use std::set
	std::set<condor_sockaddr> s;
	while (addrinfo* info = ai.next()) {
		s.insert(condor_sockaddr(info->ai_addr));
	}

	ret.insert(ret.begin(), s.begin(), s.end());
	return ret;
}

MyString convert_ipaddr_to_hostname(const condor_sockaddr& addr)
{
	MyString ret;
	MyString default_domain;
	if (!param(default_domain, "DEFAULT_DOMAIN_NAME")) {
		dprintf(D_HOSTNAME,
				"NO_DNS: DEFAULT_DOMAIN_NAME must be defined in your "
				"top-level config file\n");
		return ret;
	}

	ret = addr.to_ip_string();
	for (int i = 0; i < ret.Length(); ++i) {
		if (ret[i] == '.' || ret[i] == ':')
			ret.setChar(i, '-');
	}
	ret += ".";
	ret += default_domain;

	return ret;
}

condor_sockaddr convert_hostname_to_ipaddr(const MyString& fullname)
{
	MyString hostname;
	MyString default_domain;
	bool truncated = false;
	if (param(default_domain, "DEFAULT_DOMAIN_NAME")) {
		MyString dotted_domain = ".";
		dotted_domain += default_domain;
		int pos = fullname.find(dotted_domain.Value());
		if (pos != -1) {
			truncated = true;
			hostname = fullname.Substr(0, pos - 1);
		}
	}
	if (!truncated)
		hostname = fullname;

	char target_char = '.';
		// [TODO] Implement a way to detect IPv6 address
		//if (hostname.Length() > 15) { // assume we have IPv6 address
		//target_char = ':';
		//}

		// converts hostname to IP address string
	for (int i = 0; i < hostname.Length(); ++i) {
		if (hostname[i] == '-')
			hostname.setChar(i, target_char);
	}

	condor_sockaddr ret;
	ret.from_ip_string(hostname);
	return ret;
}
<commit_msg>Temporarily disable new functions for determining local hostname/IP. #2216<commit_after>#include "condor_common.h"
#include <set>
#include "MyString.h"
#include "condor_sockaddr.h"
#include "condor_netdb.h"
#include "condor_config.h"
#include "condor_sockfunc.h"
#include "ipv6_hostname.h"
#include "ipv6_addrinfo.h"
#include "my_hostname.h"

static condor_sockaddr local_ipaddr;
static MyString local_hostname;
static MyString local_fqdn;
static bool hostname_initialized = false;

static bool nodns_enabled()
{
	return param_boolean("NO_DNS", false);
}

void init_local_hostname()
{
		// [m.]
		// initializing local hostname, ip address, fqdn was
		// super complex.
		//
		// implementation was scattered over condor_netdb and
		// my_hostname, get_full_hostname.
		//
		// above them has duplicated code in many ways.
		// so I aggregated all of them into here.

		// Temporarily use the old functions to determine the local
		// hostname and IP address. The code below needs several
		// fixes. See gittrac #2216 for details.
	condor_sockaddr addr( *my_sin_addr() );
	local_ipaddr = addr;;
	local_hostname = my_hostname();
	local_fqdn = my_full_hostname();
#if 0
	bool ipaddr_inited = false;
	char hostname[MAXHOSTNAMELEN];
	int ret;

		// [TODO:IPV6] condor_gethostname is not IPv6 safe.
		// reimplement it.
	ret = condor_gethostname(hostname, sizeof(hostname));
	if (ret) {
		dprintf(D_HOSTNAME, "condor_gethostname() failed. Cannot initialize "
				"local hostname, ip address, FQDN.\n");
		return;
	}

		// if NETWORK_INTERFACE is defined, we use that as a local ip addr.
	MyString network_interface;
	if (param(network_interface, "NETWORK_INTERFACE")) {
		if (local_ipaddr.from_ip_string(network_interface))
			ipaddr_inited = true;
	}

		// now initialize hostname and fqdn
	if (nodns_enabled()) { // if nodns is enabled, we can cut some slack.
			// condor_gethostname() returns a hostname with
			// DEFAULT_DOMAIN_NAME. Thus, it is always fqdn
		local_hostname = hostname;
		local_fqdn = hostname;
		if (!ipaddr_inited) {
			local_ipaddr = convert_hostname_to_ipaddr(local_hostname);
		}
		return;
	}

	addrinfo_iterator ai;
	ret = ipv6_getaddrinfo(hostname, NULL, ai);
	if (ret) {
			// write some error message
		dprintf(D_HOSTNAME, "hostname %s cannot be resolved by getaddrinfo\n",
				hostname);
		return;
	}

	bool got_fqdn = false;
	while (addrinfo* info = ai.next()) {
		const char* name = info->ai_canonname;
		if (!name)
			continue;
		const char* dotpos = strchr(name, '.');
		condor_sockaddr addr(info->ai_addr);

		if (addr.is_loopback() || addr.is_private_network())
			continue;

		if (dotpos) {
				// consider it as a FQDN
			local_fqdn = name;
			local_hostname = local_fqdn.Substr(0, dotpos-name-1);
			if (!ipaddr_inited)
				local_ipaddr = addr;
			got_fqdn = true;
			break;
		}
		else {
			local_hostname = name;
			if (!ipaddr_inited)
				local_ipaddr = addr;
		}
	}

	if (!got_fqdn) {
		local_fqdn = local_hostname;
		MyString default_domain;
		if (param(default_domain, "DEFAULT_DOMAIN_NAME")) {
			if (default_domain[0] != '.')
				local_fqdn += ".";
			local_fqdn += default_domain;
		}
	}
#endif
	hostname_initialized = true;
}

condor_sockaddr get_local_ipaddr()
{
	if (!hostname_initialized)
		init_local_hostname();
	return local_ipaddr;
}

MyString get_local_hostname()
{
	if (!hostname_initialized)
		init_local_hostname();
	return local_hostname;
}

MyString get_local_fqdn()
{
	if (!hostname_initialized)
		init_local_hostname();
	return local_fqdn;
}

MyString get_hostname(const condor_sockaddr& addr)
{
	MyString ret;
	if (nodns_enabled())
		return convert_ipaddr_to_hostname(addr);

	condor_sockaddr targ_addr;

	// just like sin_to_string(), if given address is 0.0.0.0 or equivalent,
	// it changes to local IP address.
	if (addr.is_addr_any())
		targ_addr = get_local_ipaddr();
	else
		targ_addr = addr;

	int e;
	char hostname[NI_MAXHOST];

	e = condor_getnameinfo(targ_addr, hostname, sizeof(hostname), NULL, 0, 0);
	if (e)
		return ret;

	ret = hostname;
	return ret;
}

std::vector<MyString> get_hostname_with_alias(const condor_sockaddr& addr)
{
	std::vector<MyString> ret;
	MyString hostname = get_hostname(addr);
	if (hostname.IsEmpty())
		return ret;
	ret.push_back(hostname);

	if (nodns_enabled())
		return ret; // no need to call further DNS functions.

	hostent* ent;
		//int aftype = addr.get_aftype();
		//ent = gethostbyname2(hostname.Value(), addr.get_aftype());

		// really should call gethostbyname2() however most platforms do not
		// support. (Solaris, HP-UX, IRIX)
		// complete DNS aliases can be only obtained by gethostbyname.
		// however, what happens if we call it in IPv6-only system?
		// can we get DNS aliases for the hostname that only contains
		// IPv6 addresses?
	ent = gethostbyname(hostname.Value());

	if (!ent)
		return ret;

	char** alias = ent->h_aliases;
	for (; *alias; ++alias) {
		ret.push_back(MyString(*alias));
	}
	return ret;
}

// look up FQDN for hostname and aliases.
// if not, it adds up DEFAULT_DOMAIN_NAME
MyString get_full_hostname(const condor_sockaddr& addr)
{
		// this function will go smooth even with NODNS.
	MyString ret;
	std::vector<MyString> hostnames = get_hostname_with_alias(addr);
	if (hostnames.empty()) return ret;
	std::vector<MyString>::iterator iter;
	for (iter = hostnames.begin(); iter != hostnames.end(); ++iter) {
		MyString& str = *iter;
		if (str.FindChar('.') != -1) {
			return str;
		}
	}

	MyString default_domain;
	if (param(default_domain, "DEFAULT_DOMAIN_NAME")) {
			// first element is the hostname got by gethostname()
		ret = *hostnames.begin();
		if (default_domain[0] != '.')
			ret += ".";
		ret += default_domain;
	}
	return ret;
}

std::vector<condor_sockaddr> resolve_hostname(const char* hostname)
{
	MyString host(hostname);
	return resolve_hostname(host);
}

std::vector<condor_sockaddr> resolve_hostname(const MyString& hostname)
{
	std::vector<condor_sockaddr> ret;
	if (nodns_enabled()) {
		condor_sockaddr addr = convert_hostname_to_ipaddr(hostname);
		if (addr == condor_sockaddr::null)
			return ret;
		ret.push_back(addr);
		return ret;
	}

	addrinfo_iterator ai;
	bool res  = ipv6_getaddrinfo(hostname.Value(), NULL, ai);
	if (res) {
		return ret;
	}

		// To eliminate duplicate address, here we use std::set
	std::set<condor_sockaddr> s;
	while (addrinfo* info = ai.next()) {
		s.insert(condor_sockaddr(info->ai_addr));
	}

	ret.insert(ret.begin(), s.begin(), s.end());
	return ret;
}

MyString convert_ipaddr_to_hostname(const condor_sockaddr& addr)
{
	MyString ret;
	MyString default_domain;
	if (!param(default_domain, "DEFAULT_DOMAIN_NAME")) {
		dprintf(D_HOSTNAME,
				"NO_DNS: DEFAULT_DOMAIN_NAME must be defined in your "
				"top-level config file\n");
		return ret;
	}

	ret = addr.to_ip_string();
	for (int i = 0; i < ret.Length(); ++i) {
		if (ret[i] == '.' || ret[i] == ':')
			ret.setChar(i, '-');
	}
	ret += ".";
	ret += default_domain;

	return ret;
}

condor_sockaddr convert_hostname_to_ipaddr(const MyString& fullname)
{
	MyString hostname;
	MyString default_domain;
	bool truncated = false;
	if (param(default_domain, "DEFAULT_DOMAIN_NAME")) {
		MyString dotted_domain = ".";
		dotted_domain += default_domain;
		int pos = fullname.find(dotted_domain.Value());
		if (pos != -1) {
			truncated = true;
			hostname = fullname.Substr(0, pos - 1);
		}
	}
	if (!truncated)
		hostname = fullname;

	char target_char = '.';
		// [TODO] Implement a way to detect IPv6 address
		//if (hostname.Length() > 15) { // assume we have IPv6 address
		//target_char = ':';
		//}

		// converts hostname to IP address string
	for (int i = 0; i < hostname.Length(); ++i) {
		if (hostname[i] == '-')
			hostname.setChar(i, target_char);
	}

	condor_sockaddr ret;
	ret.from_ip_string(hostname);
	return ret;
}
<|endoftext|>


<commit_before>#include "controller_push.h"
#include "controller_statistics.h"
#include "controller_storage.h"
#include <mist/bitfields.h>
#include <mist/config.h>
#include <mist/json.h>
#include <mist/procs.h>
#include <mist/stream.h>
#include <mist/tinythread.h>
#include <string>

namespace Controller{

  /// Internal list of currently active pushes
  std::map<pid_t, JSON::Value> activePushes;

  /// Internal list of waiting pushes
  std::map<std::string, std::map<std::string, unsigned int> > waitingPushes;

  static bool mustWritePushList = false;
  static bool pushListRead = false;

  /// Immediately starts a push for the given stream to the given target.
  /// Simply calls Util::startPush and stores the resulting PID in the local activePushes map.
  void startPush(const std::string &stream, std::string &target){
    //Cancel if already active
    if (isPushActive(stream, target)){return;}
    std::string originalTarget = target;
    pid_t ret = Util::startPush(stream, target);
    if (ret){
      JSON::Value push;
      push.append((long long)ret);
      push.append(stream);
      push.append(originalTarget);
      push.append(target);
      activePushes[ret] = push;
      mustWritePushList = true;
    }
  }

  /// Returns true if the push is currently active, false otherwise.
  bool isPushActive(const std::string &streamname, const std::string &target){
    while (Controller::conf.is_active && !pushListRead){
      Util::sleep(100);
    }
    std::set<pid_t> toWipe;
    for (std::map<pid_t, JSON::Value>::iterator it = activePushes.begin(); it != activePushes.end(); ++it){
      if (Util::Procs::isActive(it->first)){
        if (it->second[1u].asStringRef() == streamname && it->second[2u].asStringRef() == target){return true;}
      }else{
        toWipe.insert(it->first);
      }
    }
    while (toWipe.size()){
      activePushes.erase(*toWipe.begin());
      mustWritePushList = true;
      toWipe.erase(toWipe.begin());
    }
    return false;
  }

  /// Stops any pushes matching the stream name (pattern) and target
  void stopActivePushes(const std::string &streamname, const std::string &target){
    while (Controller::conf.is_active && !pushListRead){
      Util::sleep(100);
    }
    std::set<pid_t> toWipe;
    for (std::map<pid_t, JSON::Value>::iterator it = activePushes.begin(); it != activePushes.end(); ++it){
      if (Util::Procs::isActive(it->first)){
        if (it->second[2u].asStringRef() == target && (it->second[1u].asStringRef() == streamname || (*streamname.rbegin() == '+' && it->second[1u].asStringRef().substr(0, streamname.size()) == streamname))){
          Util::Procs::Stop(it->first);
        }
      }else{
        toWipe.insert(it->first);
      }
    }
    while (toWipe.size()){
      activePushes.erase(*toWipe.begin());
      mustWritePushList = true;
      toWipe.erase(toWipe.begin());
    }
  }

  /// Immediately stops a push with the given ID
  void stopPush(unsigned int ID){
    if (ID > 1 && activePushes.count(ID)){Util::Procs::Stop(ID);}
  }

  /// Compactly writes the list of pushes to a pointer, assumed to be 8MiB in size
  static void writePushList(char * pwo){
    char * max = pwo + 8*1024*1024 - 4;
    for (std::map<pid_t, JSON::Value>::iterator it = activePushes.begin(); it != activePushes.end(); ++it){
      //check if the whole entry will fit
      unsigned int entrylen = 4+2+it->second[1u].asStringRef().size()+2+it->second[2u].asStringRef().size()+2+it->second[3u].asStringRef().size();
      if (pwo+entrylen >= max){return;}
      //write the pid as a 32 bits unsigned integer
      Bit::htobl(pwo, it->first);
      pwo += 4;
      //write the streamname, original target and target, 2-byte-size-prepended
      for (unsigned int i = 1; i < 4; ++i){
        const std::string &itm = it->second[i].asStringRef();
        Bit::htobs(pwo, itm.size());
        memcpy(pwo+2, itm.data(), itm.size());
        pwo += 2+itm.size();
      }
    }
    //if it fits, write an ending zero to indicate end of page
    if (pwo <= max){
      Bit::htobl(pwo, 0);
    }
  }

  ///Reads the list of pushes from a pointer, assumed to end in four zeroes
  static void readPushList(char * pwo){
    activePushes.clear();
    pid_t p = Bit::btohl(pwo);
    HIGH_MSG("Recovering pushes: %lu", (uint32_t)p);
    while (p > 1){
      JSON::Value push;
      push.append((long long)p);
      pwo += 4;
      for (uint8_t i = 0; i < 3; ++i){
        uint16_t l = Bit::btohs(pwo);
        push.append(std::string(pwo+2, l));
        pwo += 2+l;
      }
      INFO_MSG("Recovered push: %s", push.toString().c_str());
      Util::Procs::remember(p);
      mustWritePushList = true;
      activePushes[p] = push;
      p = Bit::btohl(pwo);
    }
  }

  /// Loops, checking every second if any pushes need restarting.
  void pushCheckLoop(void *np){
    {
      IPC::sharedPage pushReadPage("MstPush", 8*1024*1024, false, false);
      if (pushReadPage.mapped){readPushList(pushReadPage.mapped);}
    }
    pushListRead = true;
    IPC::sharedPage pushPage("MstPush", 8*1024*1024, true, false);
    while (Controller::conf.is_active){
      // this scope prevents the configMutex from being locked constantly
      {
        tthread::lock_guard<tthread::mutex> guard(Controller::configMutex);
        long long maxspeed = Controller::Storage["push_settings"]["maxspeed"].asInt();
        long long waittime = Controller::Storage["push_settings"]["wait"].asInt();
        long long curCount = 0;
        jsonForEach(Controller::Storage["autopushes"], it){
          if (it->size() > 3 && (*it)[3u].asInt() < Util::epoch()){
            INFO_MSG("Deleting autopush from %s to %s because end time passed", (*it)[0u].asStringRef().c_str(), (*it)[1u].asStringRef().c_str());
            stopActivePushes((*it)[0u], (*it)[1u]);
            removePush(*it);
            break;
          }
          if (it->size() > 2 && *((*it)[0u].asStringRef().rbegin()) != '+'){
            if ((*it)[2u].asInt() <= Util::epoch()){
              std::string streamname = (*it)[0u];
              std::string target = (*it)[1u];
              if (!isPushActive(streamname, target)){
                if (waitingPushes[streamname][target]++ >= waittime && (curCount < maxspeed || !maxspeed)){
                  waitingPushes[streamname].erase(target);
                  if (!waitingPushes[streamname].size()){waitingPushes.erase(streamname);}
                  startPush(streamname, target);
                  curCount++;
                }
              }
            }
            continue;
          }
          if (waittime || it->size() > 2){
            const std::string &pStr = (*it)[0u].asStringRef();
            if (activeStreams.size()){
              for (std::map<std::string, uint8_t>::iterator jt = activeStreams.begin(); jt != activeStreams.end(); ++jt){
                std::string streamname = jt->first;
                std::string target = (*it)[1u];
                if (pStr == streamname || (*pStr.rbegin() == '+' && streamname.substr(0, pStr.size()) == pStr)){
                  if (!isPushActive(streamname, target) && Util::getStreamStatus(streamname) == STRMSTAT_READY){
                    if (waitingPushes[streamname][target]++ >= waittime && (curCount < maxspeed || !maxspeed)){
                      waitingPushes[streamname].erase(target);
                      if (!waitingPushes[streamname].size()){waitingPushes.erase(streamname);}
                      startPush(streamname, target);
                      curCount++;
                    }
                  }
                }
              }
            }
          }
          if (it->size() == 3){
            removePush(*it);
            break;
          }
        }
        if (mustWritePushList && pushPage.mapped){
          writePushList(pushPage.mapped);
          mustWritePushList = false;
        }
      }
      Util::wait(1000); // wait at least a second
    }
    //keep the pushPage if we are restarting, so we can restore state from it
    if (Controller::restarting){
      pushPage.master = false;
      //forget about all pushes, so they keep running
      for (std::map<pid_t, JSON::Value>::iterator it = activePushes.begin(); it != activePushes.end(); ++it){
        Util::Procs::forget(it->first);
      }
    }
  }

  /// Gives a list of all currently active pushes
  void listPush(JSON::Value &output){
    output.null();
    std::set<pid_t> toWipe;
    for (std::map<pid_t, JSON::Value>::iterator it = activePushes.begin(); it != activePushes.end(); ++it){
      if (Util::Procs::isActive(it->first)){
        output.append(it->second);
      }else{
        toWipe.insert(it->first);
      }
    }
    while (toWipe.size()){
      activePushes.erase(*toWipe.begin());
      mustWritePushList = true;
      toWipe.erase(toWipe.begin());
    }
  }

  /// Adds a push to the list of auto-pushes.
  /// Auto-starts currently active matches immediately.
  void addPush(JSON::Value &request){
    JSON::Value newPush;
    if (request.isArray()){
      newPush = request;
    }else{
      newPush.append(request["stream"]);
      newPush.append(request["target"]);
      bool startTime = false;
      if (request.isMember("scheduletime") && request["scheduletime"].isInt()){
        newPush.append(request["scheduletime"]);
        startTime = true;
      }
      if (request.isMember("completetime") && request["completetime"].isInt()){
        if (!startTime){newPush.append(0ll);}
        newPush.append(request["completetime"]);
      }
    }
    long long epo = Util::epoch();
    if (newPush.size() > 3 && newPush[3u].asInt() <= epo){
      WARN_MSG("Automatic push not added: removal time is in the past! (%lld <= %lld)", newPush[3u].asInt(), Util::epoch());
      return;
    }
    bool edited = false;
    jsonForEach(Controller::Storage["autopushes"], it){
      if ((*it)[0u] == newPush[0u] && (*it)[1u] == newPush[1u]){
        (*it) = newPush;
        edited = true;
      }
    }
    if (!edited && (newPush.size() != 3 || newPush[2u].asInt() > epo)){
      Controller::Storage["autopushes"].append(newPush);
    }
    if (newPush.size() < 3 || newPush[2u].asInt() <= epo){
      if (newPush.size() > 2 && *(newPush[0u].asStringRef().rbegin()) != '+'){
        std::string streamname = newPush[0u].asStringRef();
        std::string target = newPush[1u].asStringRef();
        startPush(streamname, target);
        return;
      }
      if (activeStreams.size()){
        const std::string &pStr = newPush[0u].asStringRef();
        std::string target = newPush[1u].asStringRef();
        for (std::map<std::string, uint8_t>::iterator it = activeStreams.begin(); it != activeStreams.end(); ++it){
          std::string streamname = it->first;
          if (pStr == streamname || (*pStr.rbegin() == '+' && streamname.substr(0, pStr.size()) == pStr)){
            std::string tmpName = streamname;
            std::string tmpTarget = target;
            startPush(tmpName, tmpTarget);
          }
        }
      }
    }
  }

  /// Removes a push from the list of auto-pushes.
  /// Does not stop currently active matching pushes.
  void removePush(const JSON::Value &request){
    JSON::Value delPush;
    if (request.isString()){
      removeAllPush(request.asStringRef());
      return;
    }
    if (request.isArray()){
      delPush = request;
    }else{
      delPush.append(request["stream"]);
      delPush.append(request["target"]);
    }
    JSON::Value newautopushes;
    jsonForEach(Controller::Storage["autopushes"], it){
      if ((*it) != delPush){newautopushes.append(*it);}
    }
    Controller::Storage["autopushes"] = newautopushes;
  }

  /// Removes a push from the list of auto-pushes.
  /// Does not stop currently active matching pushes.
  void removeAllPush(const std::string &streamname){
    JSON::Value newautopushes;
    jsonForEach(Controller::Storage["autopushes"], it){
      if ((*it)[0u] != streamname){newautopushes.append(*it);}
    }
    Controller::Storage["autopushes"] = newautopushes;
  }

  /// Starts all configured auto pushes for the given stream.
  void doAutoPush(std::string &streamname){
    jsonForEach(Controller::Storage["autopushes"], it){
      if (it->size() > 2 || (*it)[2u].asInt() < Util::epoch()){continue;}
      const std::string &pStr = (*it)[0u].asStringRef();
      if (pStr == streamname || (*pStr.rbegin() == '+' && streamname.substr(0, pStr.size()) == pStr)){
        std::string stream = streamname;
        Util::sanitizeName(stream);
        std::string target = (*it)[1u];
        startPush(stream, target);
      }
    }
  }

  void pushSettings(const JSON::Value &request, JSON::Value &response){
    if (request.isObject()){
      if (request.isMember("wait")){Controller::Storage["push_settings"]["wait"] = request["wait"].asInt();}
      if (request.isMember("maxspeed")){Controller::Storage["push_settings"]["maxspeed"] = request["maxspeed"].asInt();}
    }
    response = Controller::Storage["push_settings"];
  }
}

<commit_msg>Fixed autopush removing themselves when a new push comes in<commit_after>#include "controller_push.h"
#include "controller_statistics.h"
#include "controller_storage.h"
#include <mist/bitfields.h>
#include <mist/config.h>
#include <mist/json.h>
#include <mist/procs.h>
#include <mist/stream.h>
#include <mist/tinythread.h>
#include <string>

namespace Controller{

  /// Internal list of currently active pushes
  std::map<pid_t, JSON::Value> activePushes;

  /// Internal list of waiting pushes
  std::map<std::string, std::map<std::string, unsigned int> > waitingPushes;

  static bool mustWritePushList = false;
  static bool pushListRead = false;

  /// Immediately starts a push for the given stream to the given target.
  /// Simply calls Util::startPush and stores the resulting PID in the local activePushes map.
  void startPush(const std::string &stream, std::string &target){
    //Cancel if already active
    if (isPushActive(stream, target)){return;}
    std::string originalTarget = target;
    pid_t ret = Util::startPush(stream, target);
    if (ret){
      JSON::Value push;
      push.append((long long)ret);
      push.append(stream);
      push.append(originalTarget);
      push.append(target);
      activePushes[ret] = push;
      mustWritePushList = true;
    }
  }

  /// Returns true if the push is currently active, false otherwise.
  bool isPushActive(const std::string &streamname, const std::string &target){
    while (Controller::conf.is_active && !pushListRead){
      Util::sleep(100);
    }
    std::set<pid_t> toWipe;
    for (std::map<pid_t, JSON::Value>::iterator it = activePushes.begin(); it != activePushes.end(); ++it){
      if (Util::Procs::isActive(it->first)){
        if (it->second[1u].asStringRef() == streamname && it->second[2u].asStringRef() == target){return true;}
      }else{
        toWipe.insert(it->first);
      }
    }
    while (toWipe.size()){
      activePushes.erase(*toWipe.begin());
      mustWritePushList = true;
      toWipe.erase(toWipe.begin());
    }
    return false;
  }

  /// Stops any pushes matching the stream name (pattern) and target
  void stopActivePushes(const std::string &streamname, const std::string &target){
    while (Controller::conf.is_active && !pushListRead){
      Util::sleep(100);
    }
    std::set<pid_t> toWipe;
    for (std::map<pid_t, JSON::Value>::iterator it = activePushes.begin(); it != activePushes.end(); ++it){
      if (Util::Procs::isActive(it->first)){
        if (it->second[2u].asStringRef() == target && (it->second[1u].asStringRef() == streamname || (*streamname.rbegin() == '+' && it->second[1u].asStringRef().substr(0, streamname.size()) == streamname))){
          Util::Procs::Stop(it->first);
        }
      }else{
        toWipe.insert(it->first);
      }
    }
    while (toWipe.size()){
      activePushes.erase(*toWipe.begin());
      mustWritePushList = true;
      toWipe.erase(toWipe.begin());
    }
  }

  /// Immediately stops a push with the given ID
  void stopPush(unsigned int ID){
    if (ID > 1 && activePushes.count(ID)){Util::Procs::Stop(ID);}
  }

  /// Compactly writes the list of pushes to a pointer, assumed to be 8MiB in size
  static void writePushList(char * pwo){
    char * max = pwo + 8*1024*1024 - 4;
    for (std::map<pid_t, JSON::Value>::iterator it = activePushes.begin(); it != activePushes.end(); ++it){
      //check if the whole entry will fit
      unsigned int entrylen = 4+2+it->second[1u].asStringRef().size()+2+it->second[2u].asStringRef().size()+2+it->second[3u].asStringRef().size();
      if (pwo+entrylen >= max){return;}
      //write the pid as a 32 bits unsigned integer
      Bit::htobl(pwo, it->first);
      pwo += 4;
      //write the streamname, original target and target, 2-byte-size-prepended
      for (unsigned int i = 1; i < 4; ++i){
        const std::string &itm = it->second[i].asStringRef();
        Bit::htobs(pwo, itm.size());
        memcpy(pwo+2, itm.data(), itm.size());
        pwo += 2+itm.size();
      }
    }
    //if it fits, write an ending zero to indicate end of page
    if (pwo <= max){
      Bit::htobl(pwo, 0);
    }
  }

  ///Reads the list of pushes from a pointer, assumed to end in four zeroes
  static void readPushList(char * pwo){
    activePushes.clear();
    pid_t p = Bit::btohl(pwo);
    HIGH_MSG("Recovering pushes: %lu", (uint32_t)p);
    while (p > 1){
      JSON::Value push;
      push.append((long long)p);
      pwo += 4;
      for (uint8_t i = 0; i < 3; ++i){
        uint16_t l = Bit::btohs(pwo);
        push.append(std::string(pwo+2, l));
        pwo += 2+l;
      }
      INFO_MSG("Recovered push: %s", push.toString().c_str());
      Util::Procs::remember(p);
      mustWritePushList = true;
      activePushes[p] = push;
      p = Bit::btohl(pwo);
    }
  }

  /// Loops, checking every second if any pushes need restarting.
  void pushCheckLoop(void *np){
    {
      IPC::sharedPage pushReadPage("MstPush", 8*1024*1024, false, false);
      if (pushReadPage.mapped){readPushList(pushReadPage.mapped);}
    }
    pushListRead = true;
    IPC::sharedPage pushPage("MstPush", 8*1024*1024, true, false);
    while (Controller::conf.is_active){
      // this scope prevents the configMutex from being locked constantly
      {
        tthread::lock_guard<tthread::mutex> guard(Controller::configMutex);
        long long maxspeed = Controller::Storage["push_settings"]["maxspeed"].asInt();
        long long waittime = Controller::Storage["push_settings"]["wait"].asInt();
        long long curCount = 0;
        jsonForEach(Controller::Storage["autopushes"], it){
          if (it->size() > 3 && (*it)[3u].asInt() < Util::epoch()){
            INFO_MSG("Deleting autopush from %s to %s because end time passed", (*it)[0u].asStringRef().c_str(), (*it)[1u].asStringRef().c_str());
            stopActivePushes((*it)[0u], (*it)[1u]);
            removePush(*it);
            break;
          }
          if (it->size() > 2 && *((*it)[0u].asStringRef().rbegin()) != '+'){
            if ((*it)[2u].asInt() <= Util::epoch()){
              std::string streamname = (*it)[0u];
              std::string target = (*it)[1u];
              if (!isPushActive(streamname, target)){
                if (waitingPushes[streamname][target]++ >= waittime && (curCount < maxspeed || !maxspeed)){
                  waitingPushes[streamname].erase(target);
                  if (!waitingPushes[streamname].size()){waitingPushes.erase(streamname);}
                  startPush(streamname, target);
                  curCount++;
                }
              }
            }
            continue;
          }
          if (waittime || it->size() > 2){
            const std::string &pStr = (*it)[0u].asStringRef();
            if (activeStreams.size()){
              for (std::map<std::string, uint8_t>::iterator jt = activeStreams.begin(); jt != activeStreams.end(); ++jt){
                std::string streamname = jt->first;
                std::string target = (*it)[1u];
                if (pStr == streamname || (*pStr.rbegin() == '+' && streamname.substr(0, pStr.size()) == pStr)){
                  if (!isPushActive(streamname, target) && Util::getStreamStatus(streamname) == STRMSTAT_READY){
                    if (waitingPushes[streamname][target]++ >= waittime && (curCount < maxspeed || !maxspeed)){
                      waitingPushes[streamname].erase(target);
                      if (!waitingPushes[streamname].size()){waitingPushes.erase(streamname);}
                      startPush(streamname, target);
                      curCount++;
                    }
                  }
                }
              }
            }
          }
          if (it->size() == 3){
            removePush(*it);
            break;
          }
        }
        if (mustWritePushList && pushPage.mapped){
          writePushList(pushPage.mapped);
          mustWritePushList = false;
        }
      }
      Util::wait(1000); // wait at least a second
    }
    //keep the pushPage if we are restarting, so we can restore state from it
    if (Controller::restarting){
      pushPage.master = false;
      //forget about all pushes, so they keep running
      for (std::map<pid_t, JSON::Value>::iterator it = activePushes.begin(); it != activePushes.end(); ++it){
        Util::Procs::forget(it->first);
      }
    }
  }

  /// Gives a list of all currently active pushes
  void listPush(JSON::Value &output){
    output.null();
    std::set<pid_t> toWipe;
    for (std::map<pid_t, JSON::Value>::iterator it = activePushes.begin(); it != activePushes.end(); ++it){
      if (Util::Procs::isActive(it->first)){
        output.append(it->second);
      }else{
        toWipe.insert(it->first);
      }
    }
    while (toWipe.size()){
      activePushes.erase(*toWipe.begin());
      mustWritePushList = true;
      toWipe.erase(toWipe.begin());
    }
  }

  /// Adds a push to the list of auto-pushes.
  /// Auto-starts currently active matches immediately.
  void addPush(JSON::Value &request){
    JSON::Value newPush;
    if (request.isArray()){
      newPush = request;
    }else{
      newPush.append(request["stream"]);
      newPush.append(request["target"]);
      bool startTime = false;
      if (request.isMember("scheduletime") && request["scheduletime"].isInt()){
        newPush.append(request["scheduletime"]);
        startTime = true;
      }
      if (request.isMember("completetime") && request["completetime"].isInt()){
        if (!startTime){newPush.append(0ll);}
        newPush.append(request["completetime"]);
      }
    }
    long long epo = Util::epoch();
    if (newPush.size() > 3 && newPush[3u].asInt() <= epo){
      WARN_MSG("Automatic push not added: removal time is in the past! (%lld <= %lld)", newPush[3u].asInt(), Util::epoch());
      return;
    }
    bool edited = false;
    jsonForEach(Controller::Storage["autopushes"], it){
      if ((*it)[0u] == newPush[0u] && (*it)[1u] == newPush[1u]){
        (*it) = newPush;
        edited = true;
      }
    }
    if (!edited && (newPush.size() != 3 || newPush[2u].asInt() > epo)){
      Controller::Storage["autopushes"].append(newPush);
    }
    if (newPush.size() < 3 || newPush[2u].asInt() <= epo){
      if (newPush.size() > 2 && *(newPush[0u].asStringRef().rbegin()) != '+'){
        std::string streamname = newPush[0u].asStringRef();
        std::string target = newPush[1u].asStringRef();
        startPush(streamname, target);
        return;
      }
      if (activeStreams.size()){
        const std::string &pStr = newPush[0u].asStringRef();
        std::string target = newPush[1u].asStringRef();
        for (std::map<std::string, uint8_t>::iterator it = activeStreams.begin(); it != activeStreams.end(); ++it){
          std::string streamname = it->first;
          if (pStr == streamname || (*pStr.rbegin() == '+' && streamname.substr(0, pStr.size()) == pStr)){
            std::string tmpName = streamname;
            std::string tmpTarget = target;
            startPush(tmpName, tmpTarget);
          }
        }
      }
    }
  }

  /// Removes a push from the list of auto-pushes.
  /// Does not stop currently active matching pushes.
  void removePush(const JSON::Value &request){
    JSON::Value delPush;
    if (request.isString()){
      removeAllPush(request.asStringRef());
      return;
    }
    if (request.isArray()){
      delPush = request;
    }else{
      delPush.append(request["stream"]);
      delPush.append(request["target"]);
    }
    JSON::Value newautopushes;
    jsonForEach(Controller::Storage["autopushes"], it){
      if ((*it) != delPush){newautopushes.append(*it);}
    }
    Controller::Storage["autopushes"] = newautopushes;
  }

  /// Removes a push from the list of auto-pushes.
  /// Does not stop currently active matching pushes.
  void removeAllPush(const std::string &streamname){
    JSON::Value newautopushes;
    jsonForEach(Controller::Storage["autopushes"], it){
      if ((*it)[0u] != streamname){newautopushes.append(*it);}
    }
    Controller::Storage["autopushes"] = newautopushes;
  }

  /// Starts all configured auto pushes for the given stream.
  void doAutoPush(std::string &streamname){
    jsonForEach(Controller::Storage["autopushes"], it){
      if (it->size() > 2 && (*it)[2u].asInt() < Util::epoch()){continue;}
      const std::string &pStr = (*it)[0u].asStringRef();
      if (pStr == streamname || (*pStr.rbegin() == '+' && streamname.substr(0, pStr.size()) == pStr)){
        std::string stream = streamname;
        Util::sanitizeName(stream);
        std::string target = (*it)[1u];
        startPush(stream, target);
      }
    }
  }

  void pushSettings(const JSON::Value &request, JSON::Value &response){
    if (request.isObject()){
      if (request.isMember("wait")){Controller::Storage["push_settings"]["wait"] = request["wait"].asInt();}
      if (request.isMember("maxspeed")){Controller::Storage["push_settings"]["maxspeed"] = request["maxspeed"].asInt();}
    }
    response = Controller::Storage["push_settings"];
  }
}

<|endoftext|>


<commit_before>/*
 * Copyright 2015, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include <grpc++/completion_queue.h>

#include <memory>

#include <grpc/grpc.h>
#include <grpc/support/log.h>
#include "src/cpp/util/time.h"

namespace grpc {

CompletionQueue::CompletionQueue() { cq_ = grpc_completion_queue_create(); }

CompletionQueue::CompletionQueue(grpc_completion_queue* take) : cq_(take) {}

CompletionQueue::~CompletionQueue() { grpc_completion_queue_destroy(cq_); }

void CompletionQueue::Shutdown() { grpc_completion_queue_shutdown(cq_); }

// Helper class so we can declare a unique_ptr with grpc_event
class EventDeleter {
 public:
  void operator()(grpc_event* ev) {
    if (ev) grpc_event_finish(ev);
  }
};

CompletionQueue::NextStatus CompletionQueue::AsyncNextInternal(
    void** tag, bool* ok, gpr_timespec deadline) {
  std::unique_ptr<grpc_event, EventDeleter> ev;

  for (;;) {
    ev.reset(grpc_completion_queue_next(cq_, deadline));
    if (!ev) { /* got a NULL back because deadline passed */
      return TIMEOUT;
    }
    if (ev->type == GRPC_QUEUE_SHUTDOWN) {
      return SHUTDOWN;
    }
    auto cq_tag = static_cast<CompletionQueueTag*>(ev->tag);
    *ok = ev->data.op_complete == GRPC_OP_OK;
    *tag = cq_tag;
    if (cq_tag->FinalizeResult(tag, ok)) {
      return GOT_EVENT;
    }
  }
}

CompletionQueue::NextStatus CompletionQueue::AsyncNext(
    void** tag, bool* ok, std::chrono::system_clock::time_point deadline) {
  gpr_timespec gpr_deadline;
  Timepoint2Timespec(deadline, &gpr_deadline);
  return AsyncNextInternal(tag, ok, gpr_deadline);
}

bool CompletionQueue::Pluck(CompletionQueueTag* tag) {
  std::unique_ptr<grpc_event, EventDeleter> ev;

  ev.reset(grpc_completion_queue_pluck(cq_, tag, gpr_inf_future));
  bool ok = ev->data.op_complete == GRPC_OP_OK;
  void* ignored = tag;
  GPR_ASSERT(tag->FinalizeResult(&ignored, &ok));
  GPR_ASSERT(ignored == tag);
  return ok;
}

void CompletionQueue::TryPluck(CompletionQueueTag* tag) {
  std::unique_ptr<grpc_event, EventDeleter> ev;

  ev.reset(grpc_completion_queue_pluck(cq_, tag, gpr_inf_past));
  if (!ev) return;
  bool ok = ev->data.op_complete == GRPC_OP_OK;
  void* ignored = tag;
  // the tag must be swallowed if using TryPluck
  GPR_ASSERT(!tag->FinalizeResult(&ignored, &ok));
}

}  // namespace grpc
<commit_msg>Allow nullptr to be passed in if user doesn't care about tag for next,asyncnext<commit_after>/*
 * Copyright 2015, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include <grpc++/completion_queue.h>

#include <memory>

#include <grpc/grpc.h>
#include <grpc/support/log.h>
#include "src/cpp/util/time.h"

namespace grpc {

CompletionQueue::CompletionQueue() { cq_ = grpc_completion_queue_create(); }

CompletionQueue::CompletionQueue(grpc_completion_queue* take) : cq_(take) {}

CompletionQueue::~CompletionQueue() { grpc_completion_queue_destroy(cq_); }

void CompletionQueue::Shutdown() { grpc_completion_queue_shutdown(cq_); }

// Helper class so we can declare a unique_ptr with grpc_event
class EventDeleter {
 public:
  void operator()(grpc_event* ev) {
    if (ev) grpc_event_finish(ev);
  }
};

CompletionQueue::NextStatus CompletionQueue::AsyncNextInternal(
    void** tag, bool* ok, gpr_timespec deadline) {
  std::unique_ptr<grpc_event, EventDeleter> ev;
  void *dummy;

  if (tag == nullptr) // If user doesn't care
    tag = &dummy;     // Need to pass down something

  for (;;) {
    ev.reset(grpc_completion_queue_next(cq_, deadline));
    if (!ev) { // got a NULL back because deadline passed
      return TIMEOUT;
    }
    if (ev->type == GRPC_QUEUE_SHUTDOWN) {
      return SHUTDOWN;
    }
    auto cq_tag = static_cast<CompletionQueueTag*>(ev->tag);
    *ok = ev->data.op_complete == GRPC_OP_OK;
    *tag = cq_tag;
    if (cq_tag->FinalizeResult(tag, ok)) {
      return GOT_EVENT;
    }
  }
}

CompletionQueue::NextStatus CompletionQueue::AsyncNext(
    void** tag, bool* ok, std::chrono::system_clock::time_point deadline) {
  gpr_timespec gpr_deadline;
  Timepoint2Timespec(deadline, &gpr_deadline);
  return AsyncNextInternal(tag, ok, gpr_deadline);
}

bool CompletionQueue::Pluck(CompletionQueueTag* tag) {
  std::unique_ptr<grpc_event, EventDeleter> ev;

  ev.reset(grpc_completion_queue_pluck(cq_, tag, gpr_inf_future));
  bool ok = ev->data.op_complete == GRPC_OP_OK;
  void* ignored = tag;
  GPR_ASSERT(tag->FinalizeResult(&ignored, &ok));
  GPR_ASSERT(ignored == tag);
  return ok;
}

void CompletionQueue::TryPluck(CompletionQueueTag* tag) {
  std::unique_ptr<grpc_event, EventDeleter> ev;

  ev.reset(grpc_completion_queue_pluck(cq_, tag, gpr_inf_past));
  if (!ev) return;
  bool ok = ev->data.op_complete == GRPC_OP_OK;
  void* ignored = tag;
  // the tag must be swallowed if using TryPluck
  GPR_ASSERT(!tag->FinalizeResult(&ignored, &ok));
}

}  // namespace grpc
<|endoftext|>