<commit_before>/*************************************************************************
This file is part of libresourceqt

Copyright (C) 2010 Nokia Corporation.

This library is free software; you can redistribute
it and/or modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation
version 2.1 of the License.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA.
*************************************************************************/

#include <QtCore/QCoreApplication>
#include <QtCore/QFile>
#include <QTextStream>

#include <sys/time.h>
#include <sys/types.h>
#include <sys/select.h>

#include "client.h"

#define outputln output << "\n"

using namespace ResourcePolicy;

QMap<QString, CommandListArgs> Client::commandList;

CommandListArgs::CommandListArgs()
        : args(), help()
{
}

CommandListArgs::CommandListArgs(const QString &arguments, const QString &helpText)
        : args(arguments), help(helpText)
{
}

CommandListArgs::~CommandListArgs()
{
}

Client::Client()
        : QObject(), standardInput(stdin, QIODevice::ReadOnly), stdInNotifier(0, QSocketNotifier::Read), pendingAddAudio(false), applicationClass(),
        resourceSet(NULL), output(stdout)
{
    commandList["help"] = CommandListArgs("", "print this help message");
    commandList["quit"] = CommandListArgs("", "exit application");
    commandList["free"] = CommandListArgs("", "destroy and free the resources");
    commandList["acquire"] = CommandListArgs("", "acquire required resources");
    commandList["release"] = CommandListArgs("", "release resources");
    commandList["update"] = CommandListArgs("", "update modified resource set after add or remove command");
    commandList["add"] = CommandListArgs("reslist [-o]", "add resource list, if -o provided, set as optional");
    commandList["remove"] = CommandListArgs("reslist [-o]", "remove resource list, if -o provided, removed only optional flag");
    commandList["audio"] = CommandListArgs("pid <pid> | group <audio group> | tag <name> <value>", "set audio properties");
    commandList["addaudio"] = CommandListArgs("<audio group> <pid> <tag name> <tag value>", "Add an audio resource and set the properties");
    commandList["show"] = CommandListArgs("", "show resources");

}

Client::~Client()
{
    delete resourceSet;
}

void Client::showPrompt()
{
    output << "resource-Qt> " << flush;
}

bool Client::initialize(const CommandLineParser &parser)
{
    QSet<ResourcePolicy::ResourceType> allResources;
    QSet<ResourcePolicy::ResourceType> optionalResources;

    if (parser.shouldAlwaysReply()) {
        output << "client: AlwaysReply" << endl;
    }

    if (parser.shouldAutoRelease()) {
        output << "client: AutoRelease" << endl;
    }

    resourceSet = new ResourceSet(parser.resourceApplicationClass(), this,
                                  parser.shouldAlwaysReply(),
                                  parser.shouldAutoRelease());
    if (resourceSet == NULL) {
        return false;
    }

    allResources.unite(parser.resources());
    optionalResources.unite(parser.optionalResources());
    foreach(ResourcePolicy::ResourceType resource, allResources) {
        resourceSet->addResource(resource);
        if (optionalResources.contains(resource)) {
            resourceSet->resource(resource)->setOptional();
        }
    }

    if (!connect(resourceSet, SIGNAL(resourcesGranted(QList<ResourcePolicy::ResourceType>)),
                 this, SLOT(resourceAcquiredHandler(QList<ResourcePolicy::ResourceType>)))) {
        return false;
    }

    if (!connect(resourceSet, SIGNAL(resourcesDenied()), this, SLOT(resourceDeniedHandler()))) {
        return false;
    }

    if (!connect(resourceSet, SIGNAL(lostResources()), this, SLOT(resourceLostHandler()))) {
        return false;
    }
    if (!connect(resourceSet, SIGNAL(resourcesReleased()), this, SLOT(resourceReleasedHandler()))) {
        return false;
    }
    if (!connect(resourceSet, SIGNAL(resourcesBecameAvailable(const QList<ResourcePolicy::ResourceType> &)),
                 this, SLOT(resourcesBecameAvailableHandler(const QList<ResourcePolicy::ResourceType> &)))) {
        return false;
    }
    if (!connect(&stdInNotifier, SIGNAL(activated(int)), this, SLOT(readLine(int)))) {
        return false;
    }
    if (!connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()),
                 this, SLOT(doExit()))) {
        return false;
    }
    output << "accepting input" << endl;
    showPrompt();

    return true;
}

void Client::doExit()
{
    if (resourceSet != NULL)
        resourceSet->release();
}

const char * resourceTypeToString(ResourceType type)
{
    switch (type) {
    case AudioPlaybackType:
        return "AudioPlayback";
    case AudioRecorderType:
        return "AudioRecording";
    case VideoPlaybackType:
        return "VideoPlayback";
    case VideoRecorderType:
        return "VideoRegording";
    case VibraType:
        return "Vibra";
    case LedsType:
        return "Leds";
    case BacklightType:
        return "Backlight";
    case SystemButtonType:
        return "SystemButton";
    case LockButtonType:
        return "LockButton";
    case ScaleButtonType:
        return "ScaleButton";
    case SnapButtonType:
        return "SnapButton";
    case LensCoverType:
        return "LensCover";
    case HeadsetButtonsType:
        return "HeadsetButtons";
    default:
        return "Unknown/Invalid Resource";
    }
}

void Client::showResources(const QList<ResourceType> &resList)
{
    outputln << "Resource Set:\n";
    foreach(ResourceType resource, resList) {
        output << "\t" << resourceTypeToString(resource) << endl;
    }
}

void Client::showResources(const QList<Resource*> &resList)
{
    outputln << "Resource Set:\n";
    foreach(Resource* resource, resList) {
        output << "\t" << resourceTypeToString(resource->type());
        if (resource->isOptional())
            output << " (optional)";
        if (resource->isGranted())
            output << " (granted)";
        output << endl;
    }
}

void Client::resourceAcquiredHandler(const QList<ResourceType>&)
{
    long int ms = stop_timer();
    if (ms > 0) {
        outputln << "Operation took " << ms << "ms" << endl;
    }

    QList<Resource*> list = resourceSet->resources();
    if (!list.count()) {
        qFatal("Resource set is empty, but we received a grant. Possible bug?");
    }
    else {
        QList<ResourceType> grantedResources;
        foreach(ResourcePolicy::Resource *resource, list) {
            if (resource->isGranted()) {
                grantedResources << resource->type();
            }
        }
        output << "granted:" << grantedResources << endl;
    }
    showPrompt();
}

void Client::resourceDeniedHandler()
{
    long int ms = stop_timer();
    if (ms > 0) {
        outputln << "Operation took " << ms << "ms" << endl;
    }
    QList<Resource*> allResources = resourceSet->resources();
    output << "denied:" << allResources << endl;
    showPrompt();
}

void Client::resourceLostHandler()
{
    long int ms = stop_timer();
    if (ms > 0) {
        outputln << "Operation took " << ms << "ms" << endl;
    }

    QList<Resource*> allResources = resourceSet->resources();
    outputln << "lost:" << allResources << endl;
    showPrompt();
}

void Client::resourceReleasedHandler()
{
    long int ms = stop_timer();
    if (ms > 0) {
        outputln << "Operation took " << ms << "ms" << endl;
    }

    QList<Resource*> allResources = resourceSet->resources();
    outputln << "released:"<< allResources << endl;
    showPrompt();
}

void Client::resourcesBecameAvailableHandler(const QList<ResourcePolicy::ResourceType> &availableResources)
{
    if (pendingAddAudio) {
        pendingAddAudio = false;
        long int ms = stop_timer();
        if (ms > 0) {
            outputln << "Operation took " << ms << "ms" << endl;
        }
    }
    outputln << "advice:" << availableResources << endl;
    showPrompt();
}

void Client::readLine(int)
{
    QString line = standardInput.readLine();
    if (line.isNull() || line.isEmpty()) {
        showPrompt();
        return;
    }
    QTextStream input(&line, QIODevice::ReadOnly);
    QString command;
    input >> command;
    if (command.isNull() || command.isEmpty()) {
        qDebug("Unable to read a command");
        return;
    }

    if ((command == "quit") || (command == "exit")) {
        QCoreApplication::quit();
        return;
    }
    else if (command == "help") {
        output << "Available commands:\n";
        QMap<QString, CommandListArgs>::const_iterator i =
            commandList.constBegin();
        while (i != commandList.constEnd()) {
            output << qSetFieldWidth(10) << right << i.key()
            << qSetFieldWidth(1) << " "
            << qSetFieldWidth(55) << left << i.value().args
            << qSetFieldWidth(0) << i.value().help << endl;
            ++i;
        }
    }
    else if (command == "show") {
        if (!resourceSet) {
            qCritical("%s failed!", qPrintable(command));
        }
        else {
            QList<Resource*> list = resourceSet->resources();
            if (!list.count()) {
                output << "Resource set is empty, use add command to add some."
                << endl;
            }
            else {
                showResources(list);
            }
        }
    }
    else if (command == "acquire") {
        start_timer();
        if (!resourceSet || !resourceSet->acquire()) {
            stop_timer();
            qCritical("%s failed!", qPrintable(command));
        }
    }
    else if (command == "release") {
        start_timer();
        if (!resourceSet || !resourceSet->release()) {
            stop_timer();
            qCritical("%s failed!", qPrintable(command));
        }
    }
    else if (command == "add") {
        QString resourceList, flag;
        input >> resourceList >> flag;
        if (!resourceSet) {
            qCritical("%s failed!", qPrintable(command));
        }
        else if (resourceList.isEmpty() || resourceList.isNull()) {
            output << "List of desired resources is missing. Use help.";
        }
        else {
            bool optional = false;
            QSet<ResourcePolicy::ResourceType> resToAdd;
            CommandLineParser::parseResourceList(resourceList, resToAdd);
            if (flag == "-o") {
                optional = true;
            }
            foreach(ResourcePolicy::ResourceType resource, resToAdd) {
                if (!resourceSet->contains(resource)) {
                    resourceSet->addResource(resource);
                }
                if (optional) {
                    resourceSet->resource(resource)->setOptional();
                }
            }
        }
    }
    else if (command == "remove") {
        QString resourceList, flag;
        input >> resourceList >> flag;
        if (!resourceSet) {
            qCritical("%s failed!", qPrintable(command));
        }
        else if (resourceList.isEmpty() || resourceList.isNull()) {
            output << "List of desired resources is missing. Use help.";
        }
        else {
            QSet<ResourcePolicy::ResourceType> resToRemove;
            CommandLineParser::parseResourceList(resourceList, resToRemove);
            if (flag == "-o") {
                foreach(ResourcePolicy::ResourceType resource, resToRemove) {
                    resourceSet->resource(resource)->setOptional(false);
                }
            }
            else {
                foreach(ResourcePolicy::ResourceType resource, resToRemove) {
                    resourceSet->deleteResource(resource);
                }
            }
        }
    }
    else if (command == "update") {
        if (!resourceSet || !resourceSet->update()) {
            qCritical("%s failed!", qPrintable(command));
        }
    }
    else if (command == "audio") {
        QString what, group, tagName, tagValue;
        quint32 pid = 0;
        input >> what;

        if (what.isEmpty() || what.isNull()) {
            output << "Not enough parameters! See help" << endl;
        }
        else {
            Resource *resource = resourceSet->resource(AudioPlaybackType);
            AudioResource *audioResource = static_cast<AudioResource*>(resource);
            qDebug("resource = %p audioResource = %p", resource, audioResource);
            if (audioResource == NULL) {
                output << "No AudioResource available in set!" << endl;
            }
            else {
                if (what == "group") {
                    input >> group;
                    audioResource->setAudioGroup(group);
                }
                else if (what == "pid") {
                    input >> pid;
                    if (pid != 0) {
                        qDebug("Setting audio PID to %u", pid);
                        audioResource->setProcessID(pid);
                    }
                    else {
                        output << "Bad pid parameter!" << endl;
                    }
                }
                else if (what == "tag") {
                    input >> tagName >> tagValue;
                    if (tagName.isEmpty() || tagName.isNull() ||
                            tagValue.isEmpty() || tagValue.isNull()) {
                        output << "tag requires 2 parameters name and value. See help"
                        << endl;
                    }
                    else {
                        audioResource->setStreamTag(tagValue, tagName);
                    }
                }
                else {
                    output << "Unknown audio command!";
                }
            }
        }
    }
    else if (command == "addaudio") {
        QString group, tagName, tagValue;
        quint32 pid = 0;
        input >> group >> pid >> tagName >> tagValue;

        if (group.isEmpty() || (pid == 0) || tagName.isEmpty() || tagValue.isEmpty()) {
            output << "Invalid parameters! See help!" << endl;
        }
        else {
            AudioResource *audioResource = new AudioResource(group);
            if (audioResource == NULL) {
                output << "Failed to create an AudioResource object!" << endl;
            }
            else {
                audioResource->setProcessID(pid);
                audioResource->setStreamTag(tagName, tagValue);
                pendingAddAudio = true;
                start_timer();
                resourceSet->addResourceObject(audioResource);
            }
        }
    }
    else if (command == "free") {
        delete resourceSet;
        resourceSet = new ResourceSet(applicationClass);
    }
    else {
        output << "unknown command '" << command << "'" << endl;
    }

    showPrompt();
}

QTextStream & operator<<(QTextStream &output,
                         const QList<ResourcePolicy::Resource *>resources)
{
    char separator = ' ';
    foreach(Resource* resource, resources) {
        output << separator << resourceTypeToString(resource->type());
        separator = ',';
    }
    return output;
}

QTextStream & operator<< (QTextStream &output,
                          const QList<ResourcePolicy::ResourceType>resources)
{
    char separator = ' ';
    foreach(ResourceType resource, resources) {
        output << separator << resourceTypeToString(resource);
        separator = ',';
    }
    return output;
}

<commit_msg>Fixed a typo in client.cpp, VideoRegording->VideoRecording<commit_after>/*************************************************************************
This file is part of libresourceqt

Copyright (C) 2010 Nokia Corporation.

This library is free software; you can redistribute
it and/or modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation
version 2.1 of the License.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA.
*************************************************************************/

#include <QtCore/QCoreApplication>
#include <QtCore/QFile>
#include <QTextStream>

#include <sys/time.h>
#include <sys/types.h>
#include <sys/select.h>

#include "client.h"

#define outputln output << "\n"

using namespace ResourcePolicy;

QMap<QString, CommandListArgs> Client::commandList;

CommandListArgs::CommandListArgs()
        : args(), help()
{
}

CommandListArgs::CommandListArgs(const QString &arguments, const QString &helpText)
        : args(arguments), help(helpText)
{
}

CommandListArgs::~CommandListArgs()
{
}

Client::Client()
        : QObject(), standardInput(stdin, QIODevice::ReadOnly), stdInNotifier(0, QSocketNotifier::Read), pendingAddAudio(false), applicationClass(),
        resourceSet(NULL), output(stdout)
{
    commandList["help"] = CommandListArgs("", "print this help message");
    commandList["quit"] = CommandListArgs("", "exit application");
    commandList["free"] = CommandListArgs("", "destroy and free the resources");
    commandList["acquire"] = CommandListArgs("", "acquire required resources");
    commandList["release"] = CommandListArgs("", "release resources");
    commandList["update"] = CommandListArgs("", "update modified resource set after add or remove command");
    commandList["add"] = CommandListArgs("reslist [-o]", "add resource list, if -o provided, set as optional");
    commandList["remove"] = CommandListArgs("reslist [-o]", "remove resource list, if -o provided, removed only optional flag");
    commandList["audio"] = CommandListArgs("pid <pid> | group <audio group> | tag <name> <value>", "set audio properties");
    commandList["addaudio"] = CommandListArgs("<audio group> <pid> <tag name> <tag value>", "Add an audio resource and set the properties");
    commandList["show"] = CommandListArgs("", "show resources");

}

Client::~Client()
{
    delete resourceSet;
}

void Client::showPrompt()
{
    output << "resource-Qt> " << flush;
}

bool Client::initialize(const CommandLineParser &parser)
{
    QSet<ResourcePolicy::ResourceType> allResources;
    QSet<ResourcePolicy::ResourceType> optionalResources;

    if (parser.shouldAlwaysReply()) {
        output << "client: AlwaysReply" << endl;
    }

    if (parser.shouldAutoRelease()) {
        output << "client: AutoRelease" << endl;
    }

    resourceSet = new ResourceSet(parser.resourceApplicationClass(), this,
                                  parser.shouldAlwaysReply(),
                                  parser.shouldAutoRelease());
    if (resourceSet == NULL) {
        return false;
    }

    allResources.unite(parser.resources());
    optionalResources.unite(parser.optionalResources());
    foreach(ResourcePolicy::ResourceType resource, allResources) {
        resourceSet->addResource(resource);
        if (optionalResources.contains(resource)) {
            resourceSet->resource(resource)->setOptional();
        }
    }

    if (!connect(resourceSet, SIGNAL(resourcesGranted(QList<ResourcePolicy::ResourceType>)),
                 this, SLOT(resourceAcquiredHandler(QList<ResourcePolicy::ResourceType>)))) {
        return false;
    }

    if (!connect(resourceSet, SIGNAL(resourcesDenied()), this, SLOT(resourceDeniedHandler()))) {
        return false;
    }

    if (!connect(resourceSet, SIGNAL(lostResources()), this, SLOT(resourceLostHandler()))) {
        return false;
    }
    if (!connect(resourceSet, SIGNAL(resourcesReleased()), this, SLOT(resourceReleasedHandler()))) {
        return false;
    }
    if (!connect(resourceSet, SIGNAL(resourcesBecameAvailable(const QList<ResourcePolicy::ResourceType> &)),
                 this, SLOT(resourcesBecameAvailableHandler(const QList<ResourcePolicy::ResourceType> &)))) {
        return false;
    }
    if (!connect(&stdInNotifier, SIGNAL(activated(int)), this, SLOT(readLine(int)))) {
        return false;
    }
    if (!connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()),
                 this, SLOT(doExit()))) {
        return false;
    }
    output << "accepting input" << endl;
    showPrompt();

    return true;
}

void Client::doExit()
{
    if (resourceSet != NULL)
        resourceSet->release();
}

const char * resourceTypeToString(ResourceType type)
{
    switch (type) {
    case AudioPlaybackType:
        return "AudioPlayback";
    case AudioRecorderType:
        return "AudioRecording";
    case VideoPlaybackType:
        return "VideoPlayback";
    case VideoRecorderType:
        return "VideoRecording";
    case VibraType:
        return "Vibra";
    case LedsType:
        return "Leds";
    case BacklightType:
        return "Backlight";
    case SystemButtonType:
        return "SystemButton";
    case LockButtonType:
        return "LockButton";
    case ScaleButtonType:
        return "ScaleButton";
    case SnapButtonType:
        return "SnapButton";
    case LensCoverType:
        return "LensCover";
    case HeadsetButtonsType:
        return "HeadsetButtons";
    default:
        return "Unknown/Invalid Resource";
    }
}

void Client::showResources(const QList<ResourceType> &resList)
{
    outputln << "Resource Set:\n";
    foreach(ResourceType resource, resList) {
        output << "\t" << resourceTypeToString(resource) << endl;
    }
}

void Client::showResources(const QList<Resource*> &resList)
{
    outputln << "Resource Set:\n";
    foreach(Resource* resource, resList) {
        output << "\t" << resourceTypeToString(resource->type());
        if (resource->isOptional())
            output << " (optional)";
        if (resource->isGranted())
            output << " (granted)";
        output << endl;
    }
}

void Client::resourceAcquiredHandler(const QList<ResourceType>&)
{
    long int ms = stop_timer();
    if (ms > 0) {
        outputln << "Operation took " << ms << "ms" << endl;
    }

    QList<Resource*> list = resourceSet->resources();
    if (!list.count()) {
        qFatal("Resource set is empty, but we received a grant. Possible bug?");
    }
    else {
        QList<ResourceType> grantedResources;
        foreach(ResourcePolicy::Resource *resource, list) {
            if (resource->isGranted()) {
                grantedResources << resource->type();
            }
        }
        output << "granted:" << grantedResources << endl;
    }
    showPrompt();
}

void Client::resourceDeniedHandler()
{
    long int ms = stop_timer();
    if (ms > 0) {
        outputln << "Operation took " << ms << "ms" << endl;
    }
    QList<Resource*> allResources = resourceSet->resources();
    output << "denied:" << allResources << endl;
    showPrompt();
}

void Client::resourceLostHandler()
{
    long int ms = stop_timer();
    if (ms > 0) {
        outputln << "Operation took " << ms << "ms" << endl;
    }

    QList<Resource*> allResources = resourceSet->resources();
    outputln << "lost:" << allResources << endl;
    showPrompt();
}

void Client::resourceReleasedHandler()
{
    long int ms = stop_timer();
    if (ms > 0) {
        outputln << "Operation took " << ms << "ms" << endl;
    }

    QList<Resource*> allResources = resourceSet->resources();
    outputln << "released:"<< allResources << endl;
    showPrompt();
}

void Client::resourcesBecameAvailableHandler(const QList<ResourcePolicy::ResourceType> &availableResources)
{
    if (pendingAddAudio) {
        pendingAddAudio = false;
        long int ms = stop_timer();
        if (ms > 0) {
            outputln << "Operation took " << ms << "ms" << endl;
        }
    }
    outputln << "advice:" << availableResources << endl;
    showPrompt();
}

void Client::readLine(int)
{
    QString line = standardInput.readLine();
    if (line.isNull() || line.isEmpty()) {
        showPrompt();
        return;
    }
    QTextStream input(&line, QIODevice::ReadOnly);
    QString command;
    input >> command;
    if (command.isNull() || command.isEmpty()) {
        qDebug("Unable to read a command");
        return;
    }

    if ((command == "quit") || (command == "exit")) {
        QCoreApplication::quit();
        return;
    }
    else if (command == "help") {
        output << "Available commands:\n";
        QMap<QString, CommandListArgs>::const_iterator i =
            commandList.constBegin();
        while (i != commandList.constEnd()) {
            output << qSetFieldWidth(10) << right << i.key()
            << qSetFieldWidth(1) << " "
            << qSetFieldWidth(55) << left << i.value().args
            << qSetFieldWidth(0) << i.value().help << endl;
            ++i;
        }
    }
    else if (command == "show") {
        if (!resourceSet) {
            qCritical("%s failed!", qPrintable(command));
        }
        else {
            QList<Resource*> list = resourceSet->resources();
            if (!list.count()) {
                output << "Resource set is empty, use add command to add some."
                << endl;
            }
            else {
                showResources(list);
            }
        }
    }
    else if (command == "acquire") {
        start_timer();
        if (!resourceSet || !resourceSet->acquire()) {
            stop_timer();
            qCritical("%s failed!", qPrintable(command));
        }
    }
    else if (command == "release") {
        start_timer();
        if (!resourceSet || !resourceSet->release()) {
            stop_timer();
            qCritical("%s failed!", qPrintable(command));
        }
    }
    else if (command == "add") {
        QString resourceList, flag;
        input >> resourceList >> flag;
        if (!resourceSet) {
            qCritical("%s failed!", qPrintable(command));
        }
        else if (resourceList.isEmpty() || resourceList.isNull()) {
            output << "List of desired resources is missing. Use help.";
        }
        else {
            bool optional = false;
            QSet<ResourcePolicy::ResourceType> resToAdd;
            CommandLineParser::parseResourceList(resourceList, resToAdd);
            if (flag == "-o") {
                optional = true;
            }
            foreach(ResourcePolicy::ResourceType resource, resToAdd) {
                if (!resourceSet->contains(resource)) {
                    resourceSet->addResource(resource);
                }
                if (optional) {
                    resourceSet->resource(resource)->setOptional();
                }
            }
        }
    }
    else if (command == "remove") {
        QString resourceList, flag;
        input >> resourceList >> flag;
        if (!resourceSet) {
            qCritical("%s failed!", qPrintable(command));
        }
        else if (resourceList.isEmpty() || resourceList.isNull()) {
            output << "List of desired resources is missing. Use help.";
        }
        else {
            QSet<ResourcePolicy::ResourceType> resToRemove;
            CommandLineParser::parseResourceList(resourceList, resToRemove);
            if (flag == "-o") {
                foreach(ResourcePolicy::ResourceType resource, resToRemove) {
                    resourceSet->resource(resource)->setOptional(false);
                }
            }
            else {
                foreach(ResourcePolicy::ResourceType resource, resToRemove) {
                    resourceSet->deleteResource(resource);
                }
            }
        }
    }
    else if (command == "update") {
        if (!resourceSet || !resourceSet->update()) {
            qCritical("%s failed!", qPrintable(command));
        }
    }
    else if (command == "audio") {
        QString what, group, tagName, tagValue;
        quint32 pid = 0;
        input >> what;

        if (what.isEmpty() || what.isNull()) {
            output << "Not enough parameters! See help" << endl;
        }
        else {
            Resource *resource = resourceSet->resource(AudioPlaybackType);
            AudioResource *audioResource = static_cast<AudioResource*>(resource);
            qDebug("resource = %p audioResource = %p", resource, audioResource);
            if (audioResource == NULL) {
                output << "No AudioResource available in set!" << endl;
            }
            else {
                if (what == "group") {
                    input >> group;
                    audioResource->setAudioGroup(group);
                }
                else if (what == "pid") {
                    input >> pid;
                    if (pid != 0) {
                        qDebug("Setting audio PID to %u", pid);
                        audioResource->setProcessID(pid);
                    }
                    else {
                        output << "Bad pid parameter!" << endl;
                    }
                }
                else if (what == "tag") {
                    input >> tagName >> tagValue;
                    if (tagName.isEmpty() || tagName.isNull() ||
                            tagValue.isEmpty() || tagValue.isNull()) {
                        output << "tag requires 2 parameters name and value. See help"
                        << endl;
                    }
                    else {
                        audioResource->setStreamTag(tagValue, tagName);
                    }
                }
                else {
                    output << "Unknown audio command!";
                }
            }
        }
    }
    else if (command == "addaudio") {
        QString group, tagName, tagValue;
        quint32 pid = 0;
        input >> group >> pid >> tagName >> tagValue;

        if (group.isEmpty() || (pid == 0) || tagName.isEmpty() || tagValue.isEmpty()) {
            output << "Invalid parameters! See help!" << endl;
        }
        else {
            AudioResource *audioResource = new AudioResource(group);
            if (audioResource == NULL) {
                output << "Failed to create an AudioResource object!" << endl;
            }
            else {
                audioResource->setProcessID(pid);
                audioResource->setStreamTag(tagName, tagValue);
                pendingAddAudio = true;
                start_timer();
                resourceSet->addResourceObject(audioResource);
            }
        }
    }
    else if (command == "free") {
        delete resourceSet;
        resourceSet = new ResourceSet(applicationClass);
    }
    else {
        output << "unknown command '" << command << "'" << endl;
    }

    showPrompt();
}

QTextStream & operator<<(QTextStream &output,
                         const QList<ResourcePolicy::Resource *>resources)
{
    char separator = ' ';
    foreach(Resource* resource, resources) {
        output << separator << resourceTypeToString(resource->type());
        separator = ',';
    }
    return output;
}

QTextStream & operator<< (QTextStream &output,
                          const QList<ResourcePolicy::ResourceType>resources)
{
    char separator = ' ';
    foreach(ResourceType resource, resources) {
        output << separator << resourceTypeToString(resource);
        separator = ',';
    }
    return output;
}

<|endoftext|>


<commit_before>#pragma once

#include <iosfwd>
#include <limits>
#include <stdexcept>
#include <string>

namespace blackhole {
namespace cpp17 {

template<typename Char, typename Traits = std::char_traits<Char>>
class basic_string_view {
public:
    static constexpr std::size_t npos = std::numeric_limits<std::size_t>::max();

    typedef Char value_type;
    typedef Traits traits_type;

private:
    const char* data_;
    /// Size without \0.
    std::size_t size_;

public:
    /// Constructs an empty `string_view`.
    constexpr
    basic_string_view() noexcept:
        data_(nullptr),
        size_(0)
    {}

    template<std::size_t N>
    constexpr
    basic_string_view(const char(&literal)[N]) noexcept:
        data_(literal),
        size_(N - 1)
    {}

    constexpr
    basic_string_view(const char* literal, std::size_t size) noexcept:
        data_(literal),
        size_(size)
    {}

    basic_string_view(const std::string& message) noexcept:
        data_(message.data()),
        size_(message.size())
    {}

    constexpr
    auto data() const noexcept -> const char* {
        return data_;
    }

    constexpr
    auto size() const noexcept -> std::size_t {
        return size_;
    }

    constexpr
    auto operator[](std::size_t id) const -> char {
        return id < size() ?
            data_[id] :
            throw std::out_of_range("out of range");
    }

    /// Operations.

    /// Creates a `std::string` with a copy of the content of the current view.
    template<class Allocator = std::allocator<Char>>
    auto to_string(const Allocator& alloc = Allocator()) const -> std::basic_string<Char, Traits, Allocator> {
        return {data(), size(), alloc};
    }

    /// Returns a view of the substring [pos, pos + rcount), where rcount is the smaller of count
    /// and `size() - pos`.
    constexpr
    auto substr(std::size_t pos = 0, std::size_t count = npos) const -> basic_string_view {
        return pos <= size() ?
            basic_string_view(data() + pos, std::min(count, size() - pos)) :
            throw std::out_of_range("out of range");
    }

    constexpr
    auto operator==(const basic_string_view& other) const -> bool {
        return size() == other.size() && to_string() == other.to_string();
    }
};

template<class Char, class Traits>
std::basic_ostream<Char, Traits>&
operator<<(std::basic_ostream<Char, Traits>& stream, const basic_string_view<Char, Traits>& value) {
    return stream << value.to_string();
}

typedef basic_string_view<char> string_view;

}  // namespace cpp17

using cpp17::string_view;

}  // namespace blackhole
<commit_msg>refactor(string view): add a lot of documentation<commit_after>#pragma once

#include <iosfwd>
#include <limits>
#include <stdexcept>
#include <string>

namespace blackhole {
namespace cpp17 {

/// The class template `basic_string_view` describes an object that can refer to a constant
/// contiguous sequence of char-like objects with the first element of the sequence at position
/// zero.
///
/// A typical implementation holds only two members: a pointer to constant Char and a size.
///
/// \tparam Char - character type.
/// \tparam Traits - traits class specifying the operations on the character type.
template<typename Char, typename Traits = std::char_traits<Char>>
class basic_string_view {
public:
    /// This is a special value equal to the maximum value representable by the type size_type.
    ///
    /// The exact meaning depends on context, but it is generally used either as end of view
    /// indicator by the functions that expect a view index or as the error indicator by the
    /// functions that return a view index.
    static constexpr std::size_t npos = std::numeric_limits<std::size_t>::max();

    typedef Char value_type;
    typedef Traits traits_type;
    typedef Char* pointer;
    typedef const Char* const_pointer;
    typedef Char& reference;
    typedef const Char& const_reference;

    typedef std::size_t size_type;

private:
    const_pointer data_;

    /// Size without \0.
    size_type size_;

public:
    /// Constructs an empty string view.
    constexpr basic_string_view() noexcept:
        data_(nullptr),
        size_(0)
    {}

    /// Constructs a view of the null-terminated character string pointed to by `literal`, not
    /// including the terminating null character.
    template<size_type N>
    constexpr basic_string_view(const Char(&literal)[N]) noexcept :
        data_(literal),
        size_(N - 1)
    {}

    /// Constructs a view of the first `size` characters of the character array starting with the
    /// element pointed by `literal`.
    ///
    /// The `literal` can contain null characters.
    /// The behavior is undefined if [literal, literal + size) is not a valid range (even though the
    /// constructor may not access any of the elements of this range).
    constexpr basic_string_view(const Char* literal, std::size_t size) noexcept :
        data_(literal),
        size_(size)
    {}

    /// Constructs a view of the first `string.size()` characters of the character array starting
    /// with the element pointed by `string.data()`.
    template<typename Allocator>
    basic_string_view(const std::basic_string<Char, Traits, Allocator>& string) noexcept :
        data_(string.data()),
        size_(string.size())
    {}

    /// Constructs a view of the same content as other.
    constexpr basic_string_view(const basic_string_view& other) = default;

    /// Replaces the view with the given other view.
    auto operator=(const basic_string_view& other) -> basic_string_view& = default;

    /// Returns a pointer to the underlying character array.
    ///
    /// The pointer is such that the range [data(); data() + size()) is valid and the values in it
    /// correspond to the values of the view.
    ///
    /// \note unlike `basic_string::data()` and string literals, `data()` may return a pointer to a
    /// buffer that is not null-terminated. Therefore it is typically a mistake to pass `data()` to
    /// a routine that takes just a const Char* and expects a null-terminated string.
    constexpr auto data() const noexcept -> const_pointer {
        return data_;
    }

    /// Returns the number of Char elements in the view.
    constexpr auto size() const noexcept -> size_type {
        return size_;
    }

    /// Returns a const reference to the character at specified location `id`.
    constexpr auto operator[](std::size_t id) const -> char {
        return id < size() ?
            data_[id] :
            throw std::out_of_range("out of range");
    }

    /// Operations.

    /// Creates a `basic_string` with a copy of the content of the current view.
    template<class Allocator = std::allocator<Char>>
    auto to_string(const Allocator& alloc = Allocator()) const ->
        std::basic_string<Char, Traits, Allocator>
    {
        return {data(), size(), alloc};
    }

    /// Returns a view of the substring [pos, pos + rcount), where rcount is the smaller of count
    /// and `size() - pos`.
    constexpr
    auto substr(std::size_t pos = 0, std::size_t count = npos) const -> basic_string_view {
        return pos <= size() ?
            basic_string_view(data() + pos, std::min(count, size() - pos)) :
            throw std::out_of_range("out of range");
    }

    constexpr
    auto operator==(const basic_string_view& other) const -> bool {
        return size() == other.size() && to_string() == other.to_string();
    }
};

template<class Char, class Traits>
auto operator<<(std::basic_ostream<Char, Traits>& stream, const basic_string_view<Char, Traits>& value) ->
    std::basic_ostream<Char, Traits>&
{
    return stream << value.to_string();
}

/// Several typedefs for common character types are provided:
typedef basic_string_view<char> string_view;

}  // namespace cpp17

using cpp17::string_view;

}  // namespace blackhole
<|endoftext|>


<commit_before>/**
 * \file
 * \brief ConditionVariable class header
 *
 * \author Copyright (C) 2014-2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
 *
 * \par License
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
 * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * \date 2015-02-02
 */

#ifndef INCLUDE_DISTORTOS_CONDITIONVARIABLE_HPP_
#define INCLUDE_DISTORTOS_CONDITIONVARIABLE_HPP_

#include "distortos/scheduler/ThreadControlBlockList.hpp"

namespace distortos
{

class Mutex;

/**
 * \brief ConditionVariable is an advanced synchronization primitive
 *
 * Similar to std::condition_variable - http://en.cppreference.com/w/cpp/thread/condition_variable
 * Similar to POSIX pthread_cond_t
 */

class ConditionVariable
{
public:

	/**
	 * \brief ConditionVariable constructor
	 *
	 * Similar to std::condition_variable::condition_variable() -
	 * http://en.cppreference.com/w/cpp/thread/condition_variable/condition_variable
	 * Similar to pthread_cond_init() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_init.html
	 */

	ConditionVariable();

	/**
	 * \brief Notifies all waiting threads.
	 *
	 * Similar to std::condition_variable::notify_all() -
	 * http://en.cppreference.com/w/cpp/thread/condition_variable/notify_all
	 * Similar to pthread_cond_broadcast() -
	 * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_signal.html
	 *
	 * Unblocks all threads waiting on this condition variable. The notifying thread does not need to hold the same
	 * mutex as the one held by the waiting thread(s).
	 */

	void notifyAll();

	/**
	 * \brief Notifies one waiting thread.
	 *
	 * Similar to std::condition_variable::notify_one() -
	 * http://en.cppreference.com/w/cpp/thread/condition_variable/notify_one
	 * Similar to pthread_cond_signal() -
	 * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_signal.html
	 *
	 * Unblocks one thread waiting on this condition variable. The notifying thread does not need to hold the same
	 * mutex as the one held by the waiting thread(s).
	 */

	void notifyOne();

	/**
	 * \brief Waits for notification.
	 *
	 * Similar to std::condition_variable::wait() - http://en.cppreference.com/w/cpp/thread/condition_variable/wait
	 * Similar to pthread_cond_wait() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_wait.html
	 *
	 * Atomically releases supplied mutex and blocks current thread until the condition variable is notified. The thread
	 * will be unblocked when notifyAll() or notifyOne() is executed. It may also be unblocked spuriously. When
	 * unblocked, regardless of the reason, lock is reacquired and wait exits.
	 *
	 * \param [in] mutex is a reference to mutex which must be owned by calling thread
	 *
	 * \return zero if the wait was completed successfully, error code otherwise:
	 * - EPERM - the mutex type is ErrorChecking or Recursive, and the current thread does not own the mutex;
	 */

	int wait(Mutex& mutex);

	/**
	 * \brief Waits for predicate to become true.
	 *
	 * Similar to std::condition_variable::wait() - http://en.cppreference.com/w/cpp/thread/condition_variable/wait
	 * Similar to pthread_cond_wait() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_wait.html
	 *
	 * Overload for wait() which also checks the predicate. This function will return only if the predicate is true.
	 *
	 * \param Predicate is a type of functor to check the predicate
	 *
	 * \param [in] mutex is a reference to mutex which must be owned by calling thread
	 * \param [in] predicate is the predicate that will be checked
	 *
	 * \return zero if the wait was completed successfully, error code otherwise:
	 * - EPERM - the mutex type is ErrorChecking or Recursive, and the current thread does not own the mutex;
	 */

	template<typename Predicate>
	int wait(Mutex& mutex, Predicate predicate)
	{
		while (predicate() == false)
		{
			const auto ret = wait(mutex);
			if (ret != 0)
				return ret;
		}

		return 0;
	}

	/**
	 * \brief Waits for notification for given duration of time.
	 *
	 * Similar to std::condition_variable::wait_for() -
	 * http://en.cppreference.com/w/cpp/thread/condition_variable/wait_for
	 * Similar to pthread_cond_timedwait() -
	 * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_timedwait.html#
	 *
	 * Atomically releases supplied mutex and blocks current thread until the condition variable is notified. The thread
	 * will be unblocked when notifyAll() or notifyOne() is executed or when given duration of time expires. It may also
	 * be unblocked spuriously. When unblocked, regardless of the reason, lock is reacquired and wait exits.
	 *
	 * \param [in] mutex is a reference to mutex which must be owned by calling thread
	 * \param [in] duration is the duration after which the wait for notification will be terminated
	 *
	 * \return zero if the wait was completed successfully, error code otherwise:
	 * - EPERM - the mutex type is ErrorChecking or Recursive, and the current thread does not own the mutex;
	 * - ETIMEDOUT - no notification was received before the specified timeout expired;
	 */

	int waitFor(Mutex& mutex, TickClock::duration duration);

	/**
	 * \brief Waits for notification for given duration of time.
	 *
	 * Similar to std::condition_variable::wait_for() -
	 * http://en.cppreference.com/w/cpp/thread/condition_variable/wait_for
	 * Similar to pthread_cond_timedwait() -
	 * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_timedwait.html#
	 *
	 * Template variant of waitFor(Mutex& mutex, TickClock::duration duration).
	 *
	 * \param Rep is type of tick counter
	 * \param Period is std::ratio type representing the tick period of the clock, in seconds
	 *
	 * \param [in] mutex is a reference to mutex which must be owned by calling thread
	 * \param [in] duration is the duration after which the wait for notification will be terminated
	 *
	 * \return zero if the wait was completed successfully, error code otherwise:
	 * - EPERM - the mutex type is ErrorChecking or Recursive, and the current thread does not own the mutex;
	 * - ETIMEDOUT - no notification was received before the specified timeout expired;
	 */

	template<typename Rep, typename Period>
	int waitFor(Mutex& mutex, const std::chrono::duration<Rep, Period> duration)
	{
		return waitFor(mutex, std::chrono::duration_cast<TickClock::duration>(duration));
	}

	/**
	 * \brief Waits for predicate to become true for given duration of time.
	 *
	 * Similar to std::condition_variable::wait_for() -
	 * http://en.cppreference.com/w/cpp/thread/condition_variable/wait_for
	 * Similar to pthread_cond_timedwait() -
	 * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_timedwait.html#
	 *
	 * Overload for waitFor() which also checks the predicate. This function will return only if the predicate is true
	 * or when given duration of time expires.
	 *
	 * \param Rep is type of tick counter
	 * \param Period is std::ratio type representing the tick period of the clock, in seconds
	 * \param Predicate is a type of functor to check the predicate
	 *
	 * \param [in] mutex is a reference to mutex which must be owned by calling thread
	 * \param [in] duration is the duration after which the wait for notification will be terminated
	 * \param [in] predicate is the predicate that will be checked
	 *
	 * \return zero if the wait was completed successfully, error code otherwise:
	 * - EPERM - the mutex type is ErrorChecking or Recursive, and the current thread does not own the mutex;
	 * - ETIMEDOUT - no notification was received before the specified timeout expired;
	 */

	template<typename Rep, typename Period, typename Predicate>
	int waitFor(Mutex& mutex, const std::chrono::duration<Rep, Period> duration, Predicate predicate)
	{
		return waitUntil(mutex, TickClock::now() + duration + TickClock::duration{1}, std::move(predicate));
	}

	/**
	 * \brief Waits for notification until given time point.
	 *
	 * Similar to std::condition_variable::wait_until() -
	 * http://en.cppreference.com/w/cpp/thread/condition_variable/wait_until
	 * Similar to pthread_cond_timedwait() -
	 * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_timedwait.html#
	 *
	 * Atomically releases supplied mutex and blocks current thread until the condition variable is notified. The thread
	 * will be unblocked when notifyAll() or notifyOne() is executed or when given time point is reached. It may also be
	 * unblocked spuriously. When unblocked, regardless of the reason, lock is reacquired and wait exits.
	 *
	 * \param [in] mutex is a reference to mutex which must be owned by calling thread
	 * \param [in] timePoint is the time point at which the wait for notification will be terminated
	 *
	 * \return zero if the wait was completed successfully, error code otherwise:
	 * - EPERM - the mutex type is ErrorChecking or Recursive, and the current thread does not own the mutex;
	 * - ETIMEDOUT - no notification was received before the specified timeout expired;
	 */

	int waitUntil(Mutex& mutex, TickClock::time_point timePoint);

	/**
	 * \brief Waits for notification until given time point.
	 *
	 * Similar to std::condition_variable::wait_until() -
	 * http://en.cppreference.com/w/cpp/thread/condition_variable/wait_until
	 * Similar to pthread_cond_timedwait() -
	 * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_timedwait.html#
	 *
	 * Template variant of waitUntil(Mutex& mutex, TickClock::time_point timePoint).
	 *
	 * \param Duration is a std::chrono::duration type used to measure duration
	 *
	 * \param [in] mutex is a reference to mutex which must be owned by calling thread
	 * \param [in] timePoint is the time point at which the wait for notification will be terminated
	 *
	 * \return zero if the wait was completed successfully, error code otherwise:
	 * - EPERM - the mutex type is ErrorChecking or Recursive, and the current thread does not own the mutex;
	 * - ETIMEDOUT - no notification was received before the specified timeout expired;
	 */

	template<typename Duration>
	int waitUntil(Mutex& mutex, const std::chrono::time_point<TickClock, Duration> timePoint)
	{
		return waitUntil(mutex, std::chrono::time_point_cast<TickClock::duration>(timePoint));
	}

	/**
	 * \brief Waits for predicate to become true until given time point.
	 *
	 * Similar to std::condition_variable::wait_until() -
	 * http://en.cppreference.com/w/cpp/thread/condition_variable/wait_until
	 * Similar to pthread_cond_timedwait() -
	 * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_timedwait.html#
	 *
	 * Overload for waitUntil() which also checks the predicate. This function will return only if the predicate is true
	 * or when given time point is reached.
	 *
	 * \param Duration is a std::chrono::duration type used to measure duration
	 * \param Predicate is a type of functor to check the predicate
	 *
	 * \param [in] mutex is a reference to mutex which must be owned by calling thread
	 * \param [in] timePoint is the time point at which the wait for notification will be terminated
	 * \param [in] predicate is the predicate that will be checked
	 *
	 * \return zero if the wait was completed successfully, error code otherwise:
	 * - EPERM - the mutex type is ErrorChecking or Recursive, and the current thread does not own the mutex;
	 * - ETIMEDOUT - no notification was received before the specified timeout expired;
	 */

	template<typename Duration, typename Predicate>
	int waitUntil(Mutex& mutex, const std::chrono::time_point<TickClock, Duration> timePoint, Predicate predicate)
	{
		while (predicate() == false)
		{
			const auto ret = waitUntil(mutex, timePoint);
			if (ret != 0)
				return ret;
		}

		return 0;
	}

private:

	/// ThreadControlBlock objects blocked on this condition variable
	scheduler::ThreadControlBlockList blockedList_;
};

}	// namespace distortos

#endif	// INCLUDE_DISTORTOS_CONDITIONVARIABLE_HPP_
<commit_msg>ConditionVariable: make some member functions non-inline<commit_after>/**
 * \file
 * \brief ConditionVariable class header
 *
 * \author Copyright (C) 2014-2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
 *
 * \par License
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
 * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * \date 2015-10-18
 */

#ifndef INCLUDE_DISTORTOS_CONDITIONVARIABLE_HPP_
#define INCLUDE_DISTORTOS_CONDITIONVARIABLE_HPP_

#include "distortos/scheduler/ThreadControlBlockList.hpp"

namespace distortos
{

class Mutex;

/**
 * \brief ConditionVariable is an advanced synchronization primitive
 *
 * Similar to std::condition_variable - http://en.cppreference.com/w/cpp/thread/condition_variable
 * Similar to POSIX pthread_cond_t
 */

class ConditionVariable
{
public:

	/**
	 * \brief ConditionVariable constructor
	 *
	 * Similar to std::condition_variable::condition_variable() -
	 * http://en.cppreference.com/w/cpp/thread/condition_variable/condition_variable
	 * Similar to pthread_cond_init() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_init.html
	 */

	ConditionVariable();

	/**
	 * \brief Notifies all waiting threads.
	 *
	 * Similar to std::condition_variable::notify_all() -
	 * http://en.cppreference.com/w/cpp/thread/condition_variable/notify_all
	 * Similar to pthread_cond_broadcast() -
	 * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_signal.html
	 *
	 * Unblocks all threads waiting on this condition variable. The notifying thread does not need to hold the same
	 * mutex as the one held by the waiting thread(s).
	 */

	void notifyAll();

	/**
	 * \brief Notifies one waiting thread.
	 *
	 * Similar to std::condition_variable::notify_one() -
	 * http://en.cppreference.com/w/cpp/thread/condition_variable/notify_one
	 * Similar to pthread_cond_signal() -
	 * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_signal.html
	 *
	 * Unblocks one thread waiting on this condition variable. The notifying thread does not need to hold the same
	 * mutex as the one held by the waiting thread(s).
	 */

	void notifyOne();

	/**
	 * \brief Waits for notification.
	 *
	 * Similar to std::condition_variable::wait() - http://en.cppreference.com/w/cpp/thread/condition_variable/wait
	 * Similar to pthread_cond_wait() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_wait.html
	 *
	 * Atomically releases supplied mutex and blocks current thread until the condition variable is notified. The thread
	 * will be unblocked when notifyAll() or notifyOne() is executed. It may also be unblocked spuriously. When
	 * unblocked, regardless of the reason, lock is reacquired and wait exits.
	 *
	 * \param [in] mutex is a reference to mutex which must be owned by calling thread
	 *
	 * \return zero if the wait was completed successfully, error code otherwise:
	 * - EPERM - the mutex type is ErrorChecking or Recursive, and the current thread does not own the mutex;
	 */

	int wait(Mutex& mutex);

	/**
	 * \brief Waits for predicate to become true.
	 *
	 * Similar to std::condition_variable::wait() - http://en.cppreference.com/w/cpp/thread/condition_variable/wait
	 * Similar to pthread_cond_wait() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_wait.html
	 *
	 * Overload for wait() which also checks the predicate. This function will return only if the predicate is true.
	 *
	 * \param Predicate is a type of functor to check the predicate
	 *
	 * \param [in] mutex is a reference to mutex which must be owned by calling thread
	 * \param [in] predicate is the predicate that will be checked
	 *
	 * \return zero if the wait was completed successfully, error code otherwise:
	 * - EPERM - the mutex type is ErrorChecking or Recursive, and the current thread does not own the mutex;
	 */

	template<typename Predicate>
	int wait(Mutex& mutex, Predicate predicate);

	/**
	 * \brief Waits for notification for given duration of time.
	 *
	 * Similar to std::condition_variable::wait_for() -
	 * http://en.cppreference.com/w/cpp/thread/condition_variable/wait_for
	 * Similar to pthread_cond_timedwait() -
	 * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_timedwait.html#
	 *
	 * Atomically releases supplied mutex and blocks current thread until the condition variable is notified. The thread
	 * will be unblocked when notifyAll() or notifyOne() is executed or when given duration of time expires. It may also
	 * be unblocked spuriously. When unblocked, regardless of the reason, lock is reacquired and wait exits.
	 *
	 * \param [in] mutex is a reference to mutex which must be owned by calling thread
	 * \param [in] duration is the duration after which the wait for notification will be terminated
	 *
	 * \return zero if the wait was completed successfully, error code otherwise:
	 * - EPERM - the mutex type is ErrorChecking or Recursive, and the current thread does not own the mutex;
	 * - ETIMEDOUT - no notification was received before the specified timeout expired;
	 */

	int waitFor(Mutex& mutex, TickClock::duration duration);

	/**
	 * \brief Waits for notification for given duration of time.
	 *
	 * Similar to std::condition_variable::wait_for() -
	 * http://en.cppreference.com/w/cpp/thread/condition_variable/wait_for
	 * Similar to pthread_cond_timedwait() -
	 * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_timedwait.html#
	 *
	 * Template variant of waitFor(Mutex& mutex, TickClock::duration duration).
	 *
	 * \param Rep is type of tick counter
	 * \param Period is std::ratio type representing the tick period of the clock, in seconds
	 *
	 * \param [in] mutex is a reference to mutex which must be owned by calling thread
	 * \param [in] duration is the duration after which the wait for notification will be terminated
	 *
	 * \return zero if the wait was completed successfully, error code otherwise:
	 * - EPERM - the mutex type is ErrorChecking or Recursive, and the current thread does not own the mutex;
	 * - ETIMEDOUT - no notification was received before the specified timeout expired;
	 */

	template<typename Rep, typename Period>
	int waitFor(Mutex& mutex, const std::chrono::duration<Rep, Period> duration)
	{
		return waitFor(mutex, std::chrono::duration_cast<TickClock::duration>(duration));
	}

	/**
	 * \brief Waits for predicate to become true for given duration of time.
	 *
	 * Similar to std::condition_variable::wait_for() -
	 * http://en.cppreference.com/w/cpp/thread/condition_variable/wait_for
	 * Similar to pthread_cond_timedwait() -
	 * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_timedwait.html#
	 *
	 * Overload for waitFor() which also checks the predicate. This function will return only if the predicate is true
	 * or when given duration of time expires.
	 *
	 * \param Rep is type of tick counter
	 * \param Period is std::ratio type representing the tick period of the clock, in seconds
	 * \param Predicate is a type of functor to check the predicate
	 *
	 * \param [in] mutex is a reference to mutex which must be owned by calling thread
	 * \param [in] duration is the duration after which the wait for notification will be terminated
	 * \param [in] predicate is the predicate that will be checked
	 *
	 * \return zero if the wait was completed successfully, error code otherwise:
	 * - EPERM - the mutex type is ErrorChecking or Recursive, and the current thread does not own the mutex;
	 * - ETIMEDOUT - no notification was received before the specified timeout expired;
	 */

	template<typename Rep, typename Period, typename Predicate>
	int waitFor(Mutex& mutex, const std::chrono::duration<Rep, Period> duration, Predicate predicate)
	{
		return waitUntil(mutex, TickClock::now() + duration + TickClock::duration{1}, std::move(predicate));
	}

	/**
	 * \brief Waits for notification until given time point.
	 *
	 * Similar to std::condition_variable::wait_until() -
	 * http://en.cppreference.com/w/cpp/thread/condition_variable/wait_until
	 * Similar to pthread_cond_timedwait() -
	 * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_timedwait.html#
	 *
	 * Atomically releases supplied mutex and blocks current thread until the condition variable is notified. The thread
	 * will be unblocked when notifyAll() or notifyOne() is executed or when given time point is reached. It may also be
	 * unblocked spuriously. When unblocked, regardless of the reason, lock is reacquired and wait exits.
	 *
	 * \param [in] mutex is a reference to mutex which must be owned by calling thread
	 * \param [in] timePoint is the time point at which the wait for notification will be terminated
	 *
	 * \return zero if the wait was completed successfully, error code otherwise:
	 * - EPERM - the mutex type is ErrorChecking or Recursive, and the current thread does not own the mutex;
	 * - ETIMEDOUT - no notification was received before the specified timeout expired;
	 */

	int waitUntil(Mutex& mutex, TickClock::time_point timePoint);

	/**
	 * \brief Waits for notification until given time point.
	 *
	 * Similar to std::condition_variable::wait_until() -
	 * http://en.cppreference.com/w/cpp/thread/condition_variable/wait_until
	 * Similar to pthread_cond_timedwait() -
	 * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_timedwait.html#
	 *
	 * Template variant of waitUntil(Mutex& mutex, TickClock::time_point timePoint).
	 *
	 * \param Duration is a std::chrono::duration type used to measure duration
	 *
	 * \param [in] mutex is a reference to mutex which must be owned by calling thread
	 * \param [in] timePoint is the time point at which the wait for notification will be terminated
	 *
	 * \return zero if the wait was completed successfully, error code otherwise:
	 * - EPERM - the mutex type is ErrorChecking or Recursive, and the current thread does not own the mutex;
	 * - ETIMEDOUT - no notification was received before the specified timeout expired;
	 */

	template<typename Duration>
	int waitUntil(Mutex& mutex, const std::chrono::time_point<TickClock, Duration> timePoint)
	{
		return waitUntil(mutex, std::chrono::time_point_cast<TickClock::duration>(timePoint));
	}

	/**
	 * \brief Waits for predicate to become true until given time point.
	 *
	 * Similar to std::condition_variable::wait_until() -
	 * http://en.cppreference.com/w/cpp/thread/condition_variable/wait_until
	 * Similar to pthread_cond_timedwait() -
	 * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_timedwait.html#
	 *
	 * Overload for waitUntil() which also checks the predicate. This function will return only if the predicate is true
	 * or when given time point is reached.
	 *
	 * \param Duration is a std::chrono::duration type used to measure duration
	 * \param Predicate is a type of functor to check the predicate
	 *
	 * \param [in] mutex is a reference to mutex which must be owned by calling thread
	 * \param [in] timePoint is the time point at which the wait for notification will be terminated
	 * \param [in] predicate is the predicate that will be checked
	 *
	 * \return zero if the wait was completed successfully, error code otherwise:
	 * - EPERM - the mutex type is ErrorChecking or Recursive, and the current thread does not own the mutex;
	 * - ETIMEDOUT - no notification was received before the specified timeout expired;
	 */

	template<typename Duration, typename Predicate>
	int waitUntil(Mutex& mutex, std::chrono::time_point<TickClock, Duration> timePoint, Predicate predicate);

private:

	/// ThreadControlBlock objects blocked on this condition variable
	scheduler::ThreadControlBlockList blockedList_;
};

template<typename Predicate>
int ConditionVariable::wait(Mutex& mutex, Predicate predicate)
{
	while (predicate() == false)
	{
		const auto ret = wait(mutex);
		if (ret != 0)
			return ret;
	}

	return 0;
}

template<typename Duration, typename Predicate>
int ConditionVariable::waitUntil(Mutex& mutex, const std::chrono::time_point<TickClock, Duration> timePoint, Predicate predicate)
{
	while (predicate() == false)
	{
		const auto ret = waitUntil(mutex, timePoint);
		if (ret != 0)
			return ret;
	}

	return 0;
}

}	// namespace distortos

#endif	// INCLUDE_DISTORTOS_CONDITIONVARIABLE_HPP_
<|endoftext|>


<commit_before>/*
 * Copyright 2017 deepstreamHub GmbH
 *
 * 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 <cstdio>

#include <algorithm>
#include <stdexcept>
#include <iterator>

#include <buffer.hpp>
#include <event.hpp>
#include <message_builder.hpp>

#include <cassert>

namespace deepstream
{

Event::Event(const SendFn& send) :
	send_(send)
{
	assert( send_ );
}

Event::SubscribeFnPtr Event::subscribe(const Name& name, const SubscribeFn& f)
{
	SubscribeFnPtr p_f( new SubscribeFn(f) );
	subscribe(name, p_f);

	return p_f;
}

void Event::subscribe(const Name& name, const SubscribeFnPtr& p_f)
{
	assert( p_f );

	if( name.empty() )
		throw std::invalid_argument( "Empty event subscription pattern" );

	auto ret = subscriber_map_.equal_range(name);
	SubscriberMap::iterator first = ret.first;
	SubscriberMap::iterator last = ret.second;

	if( first != last )
	{
		assert( first != subscriber_map_.end() );
		assert( first->first == name );
		assert( std::distance(first, last) == 1 );

		// avoid inserting duplicates
		SubscriberList& subscribers = first->second;
		assert( !subscribers.empty() );

		SubscriberList::const_iterator it =
			std::find( subscribers.cbegin(), subscribers.cend(), p_f );

		if( it == subscribers.cend() )
			subscribers.push_back(p_f);

		return;
	}

	subscriber_map_[name].push_back(p_f);

	MessageBuilder message(Topic::EVENT, Action::SUBSCRIBE);
	message.add_argument( name );
	send_(message);
}

void Event::unsubscribe(const Name& name)
{
	SubscriberMap::iterator it = subscriber_map_.find(name);

	if( it == subscriber_map_.end() )
		return;

	subscriber_map_.erase(it);

	MessageBuilder message(Topic::EVENT, Action::UNSUBSCRIBE);
	message.add_argument(name);
	send_(message);
}

void Event::unsubscribe(const Name& name, const SubscribeFnPtr& p_f)
{
	SubscriberMap::iterator it = subscriber_map_.find(name);

	if( it == subscriber_map_.end() )
		return;

	SubscriberList& subscribers = it->second;
	SubscriberList::iterator ju =
		std::find( subscribers.begin(), subscribers.end(), p_f );

	if( ju == subscribers.end() )
		return;

	subscribers.erase(ju);

	if( subscribers.empty() )
		unsubscribe(name);
}

void Event::listen(const std::string& pattern, const ListenFnPtr& p_f)
{
	ListenerMap::iterator it = listener_map_.find(pattern);

	if( it != listener_map_.end() )
		return;

	listener_map_[pattern] = p_f;

	MessageBuilder message(Topic::EVENT, Action::LISTEN);
	message.add_argument(pattern);
	send_(message);
}

void Event::unlisten(const std::string& pattern)
{
	ListenerMap::iterator it = listener_map_.find(pattern);

	if( it == listener_map_.end() )
		return;

	listener_map_.erase(it);

	MessageBuilder message(Topic::EVENT, Action::UNLISTEN);
	message.add_argument(pattern);
	send_(message);
}

void Event::notify_(const Message& message)
{
	assert( message.topic() == Topic::EVENT );

	switch( message.action() )
	{
		case Action::EVENT:
			notify_subscribers_(message);
			break;

		case Action::SUBSCRIPTION_FOR_PATTERN_FOUND:
		case Action::SUBSCRIPTION_FOR_PATTERN_REMOVED:
			assert( !message.is_ack() );
			notify_listeners_(message);
			break;

		case Action::LISTEN:
		case Action::SUBSCRIBE:
			assert( message.is_ack() );
			break;

		case Action::UNLISTEN:
		case Action::UNSUBSCRIBE:
			assert( message.is_ack() );
			break;

		default:
			assert(0);
	}
}

void Event::notify_subscribers_(const Message& message)
{
	assert( message.action() == Action::EVENT );
	assert( message.num_arguments() == (message.is_ack() ? 1 : 2) );

	if( message.is_ack() )
		return;

	const Buffer& arg0 = message[0];
	Name name( arg0.cbegin(), arg0.cend() );
	const Buffer& data = message[1];

	SubscriberMap::iterator it = subscriber_map_.find(name);

	if( it == subscriber_map_.end() )
	{
		std::fprintf(
			stderr, "E|EVT: no subscriber named '%s'\n", name.c_str()
		);
		return;
	}

	assert( it->first == name );

	// Copying the list of subscribers is a necessity here because the callbacks
	// may unsubscribe during their execution and modifications of a subscriber
	// list may invalidate iterations and ranges. Also, copying the smart
	// pointer pointer here is a necessity because the referenced function may
	// decide to unsubscribe and in this case, the std::function object must not
	// be destructed. Copying ensures a non-zero reference count until `*p_f`
	// returns.
	// Finally, the iterator `it` may be invalid after executing a callback
	// because the list of subscribers for this `name` may have been erased.
	SubscriberList subscribers = it->second;
	it = subscriber_map_.end();

	for(const SubscribeFnPtr& p_f : subscribers)
	{
		auto f = *p_f;
		f(data);
	}
}

void Event::notify_listeners_(const Message& message)
{
	assert( message.action() == Action::SUBSCRIPTION_FOR_PATTERN_FOUND ||
			message.action() == Action::SUBSCRIPTION_FOR_PATTERN_REMOVED );
	assert( !message.is_ack() );
	assert( message.num_arguments() == 2 );

	const Buffer& arg0 = message[0];
	Name pattern( arg0.cbegin(), arg0.cend() );

	const Buffer& arg1 = message[1];
	Name match( arg1.cbegin(), arg1.cend() );

	bool is_subscribed =
		message.action() == Action::SUBSCRIPTION_FOR_PATTERN_FOUND;

	ListenerMap::iterator it = listener_map_.find(pattern);

	if( it == listener_map_.end() )
	{
		std::fprintf(
			stderr,
			"%s: no listener for pattern '%s'\n",
			message.header().to_string(), pattern.c_str()
		);

		return;
	}

	assert( it->first == pattern );

	// copy the smart pointer (increase the reference count) because the
	// listener may decide to unlisten.
	ListenFnPtr p_f = it->second;
	auto f = *p_f;
	f(pattern, is_subscribed, match);
}

}
<commit_msg>Events: always check user-provided arguments<commit_after>/*
 * Copyright 2017 deepstreamHub GmbH
 *
 * 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 <cstdio>

#include <algorithm>
#include <stdexcept>
#include <iterator>

#include <buffer.hpp>
#include <event.hpp>
#include <message_builder.hpp>

#include <cassert>

namespace deepstream
{

Event::Event(const SendFn& send) :
	send_(send)
{
	assert( send_ );
}

Event::SubscribeFnPtr Event::subscribe(const Name& name, const SubscribeFn& f)
{
	SubscribeFnPtr p_f( new SubscribeFn(f) );
	subscribe(name, p_f);

	return p_f;
}

void Event::subscribe(const Name& name, const SubscribeFnPtr& p_f)
{
	if( name.empty() )
		throw std::invalid_argument( "Empty event subscription pattern" );
	if( !p_f )
		throw std::invalid_argument( "Subscribe function pointer is NULL" );

	auto ret = subscriber_map_.equal_range(name);
	SubscriberMap::iterator first = ret.first;
	SubscriberMap::iterator last = ret.second;

	if( first != last )
	{
		assert( first != subscriber_map_.end() );
		assert( first->first == name );
		assert( std::distance(first, last) == 1 );

		// avoid inserting duplicates
		SubscriberList& subscribers = first->second;
		assert( !subscribers.empty() );

		SubscriberList::const_iterator it =
			std::find( subscribers.cbegin(), subscribers.cend(), p_f );

		if( it == subscribers.cend() )
			subscribers.push_back(p_f);

		return;
	}

	subscriber_map_[name].push_back(p_f);

	MessageBuilder message(Topic::EVENT, Action::SUBSCRIBE);
	message.add_argument( name );
	send_(message);
}

void Event::unsubscribe(const Name& name)
{
	SubscriberMap::iterator it = subscriber_map_.find(name);

	if( it == subscriber_map_.end() )
		return;

	subscriber_map_.erase(it);

	MessageBuilder message(Topic::EVENT, Action::UNSUBSCRIBE);
	message.add_argument(name);
	send_(message);
}

void Event::unsubscribe(const Name& name, const SubscribeFnPtr& p_f)
{
	SubscriberMap::iterator it = subscriber_map_.find(name);

	if( it == subscriber_map_.end() )
		return;

	SubscriberList& subscribers = it->second;
	SubscriberList::iterator ju =
		std::find( subscribers.begin(), subscribers.end(), p_f );

	if( ju == subscribers.end() )
		return;

	subscribers.erase(ju);

	if( subscribers.empty() )
		unsubscribe(name);
}

void Event::listen(const std::string& pattern, const ListenFnPtr& p_f)
{
	if( pattern.empty() )
		throw std::invalid_argument( "Cannot listen for empty patterns" );
	if( !p_f )
		throw std::invalid_argument( "Listen function pointer is NULL" );

	ListenerMap::iterator it = listener_map_.find(pattern);

	if( it != listener_map_.end() )
		return;

	listener_map_[pattern] = p_f;

	MessageBuilder message(Topic::EVENT, Action::LISTEN);
	message.add_argument(pattern);
	send_(message);
}

void Event::unlisten(const std::string& pattern)
{
	ListenerMap::iterator it = listener_map_.find(pattern);

	if( it == listener_map_.end() )
		return;

	listener_map_.erase(it);

	MessageBuilder message(Topic::EVENT, Action::UNLISTEN);
	message.add_argument(pattern);
	send_(message);
}

void Event::notify_(const Message& message)
{
	assert( message.topic() == Topic::EVENT );

	switch( message.action() )
	{
		case Action::EVENT:
			notify_subscribers_(message);
			break;

		case Action::SUBSCRIPTION_FOR_PATTERN_FOUND:
		case Action::SUBSCRIPTION_FOR_PATTERN_REMOVED:
			assert( !message.is_ack() );
			notify_listeners_(message);
			break;

		case Action::LISTEN:
		case Action::SUBSCRIBE:
			assert( message.is_ack() );
			break;

		case Action::UNLISTEN:
		case Action::UNSUBSCRIBE:
			assert( message.is_ack() );
			break;

		default:
			assert(0);
	}
}

void Event::notify_subscribers_(const Message& message)
{
	assert( message.action() == Action::EVENT );
	assert( message.num_arguments() == (message.is_ack() ? 1 : 2) );

	if( message.is_ack() )
		return;

	const Buffer& arg0 = message[0];
	Name name( arg0.cbegin(), arg0.cend() );
	const Buffer& data = message[1];

	SubscriberMap::iterator it = subscriber_map_.find(name);

	if( it == subscriber_map_.end() )
	{
		std::fprintf(
			stderr, "E|EVT: no subscriber named '%s'\n", name.c_str()
		);
		return;
	}

	assert( it->first == name );

	// Copying the list of subscribers is a necessity here because the callbacks
	// may unsubscribe during their execution and modifications of a subscriber
	// list may invalidate iterations and ranges. Also, copying the smart
	// pointer pointer here is a necessity because the referenced function may
	// decide to unsubscribe and in this case, the std::function object must not
	// be destructed. Copying ensures a non-zero reference count until `*p_f`
	// returns.
	// Finally, the iterator `it` may be invalid after executing a callback
	// because the list of subscribers for this `name` may have been erased.
	SubscriberList subscribers = it->second;
	it = subscriber_map_.end();

	for(const SubscribeFnPtr& p_f : subscribers)
	{
		auto f = *p_f;
		f(data);
	}
}

void Event::notify_listeners_(const Message& message)
{
	assert( message.action() == Action::SUBSCRIPTION_FOR_PATTERN_FOUND ||
			message.action() == Action::SUBSCRIPTION_FOR_PATTERN_REMOVED );
	assert( !message.is_ack() );
	assert( message.num_arguments() == 2 );

	const Buffer& arg0 = message[0];
	Name pattern( arg0.cbegin(), arg0.cend() );

	const Buffer& arg1 = message[1];
	Name match( arg1.cbegin(), arg1.cend() );

	bool is_subscribed =
		message.action() == Action::SUBSCRIPTION_FOR_PATTERN_FOUND;

	ListenerMap::iterator it = listener_map_.find(pattern);

	if( it == listener_map_.end() )
	{
		std::fprintf(
			stderr,
			"%s: no listener for pattern '%s'\n",
			message.header().to_string(), pattern.c_str()
		);

		return;
	}

	assert( it->first == pattern );

	// copy the smart pointer (increase the reference count) because the
	// listener may decide to unlisten.
	ListenFnPtr p_f = it->second;
	auto f = *p_f;
	f(pattern, is_subscribed, match);
}

}
<|endoftext|>