<commit_before>/*
  Copyright 2008 Larry Gritz and the other authors and contributors.
  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 the software's owners 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.

  (This is the Modified BSD License)
*/

#include <cstdio>
#include <string>
#include <vector>

#include "export.h"
#include "thread.h"
#include "strutil.h"
#include "hash.h"
#include "dassert.h"

#include "ustring.h"

#if 0
// Use reader/writer locks
typedef shared_mutex ustring_mutex_t;
typedef shared_lock ustring_read_lock_t;
typedef unique_lock ustring_write_lock_t;
#elif 0
// Use regular mutex
typedef mutex ustring_mutex_t;
typedef lock_guard ustring_read_lock_t;
typedef lock_guard ustring_write_lock_t;
#elif 1
// Use spin locks
typedef spin_mutex ustring_mutex_t;
typedef spin_lock ustring_read_lock_t;
typedef spin_lock ustring_write_lock_t;
#else
// Use null locks
typedef null_mutex ustring_mutex_t;
typedef null_lock<null_mutex> ustring_read_lock_t;
typedef null_lock<null_mutex> ustring_write_lock_t;
#endif

#ifdef OIIO_HAVE_BOOST_UNORDERED_MAP
typedef boost::unordered_map <const char *, ustring::TableRep *, Strutil::StringHash, Strutil::StringEqual> UstringTable;
#else
typedef hash_map <const char *, ustring::TableRep *, Strutil::StringHash, Strutil::StringEqual> UstringTable;
#endif // OIIO_HAVE_BOOST_UNORDERED_MAP

std::string ustring::empty_std_string ("");

namespace { // anonymous

static long long ustring_stats_memory = 0;
static long long ustring_stats_constructed = 0;
static long long ustring_stats_unique = 0;
static ustring_mutex_t ustring_mutex;

};          // end anonymous namespace

ustring::TableRep::TableRep (const char *s, size_t len)
    : hashed(Strutil::strhash(s))
{
    strcpy ((char *)c_str(), s);
    length = len;
    dummy_capacity = len;
    dummy_refcount = 1;   // so it never frees

#if defined(__GNUC__)
    // We don't want the internal 'string str' to redundantly store the
    // chars, along with our own allocation.  So we use our knowledge of
    // the internal structure of gcc strings to make it point to our chars!
    // Note that we've carefully structured the TableRep fields so they
    // mimic a GCC basic_string::_Rep.
    //
    // It turns out that the first field of a gcc std::string is a
    // pointer to the characters within the basic_string::_Rep.  We
    // merely redirect that pointer, though for std::string to function
    // properly, the chars must be preceeded immediately in memory by
    // the rest of basic_string::_Rep, consisting of length, capacity
    // and refcount fields.  And we have designed our TableRep to do
    // just that!  So now we redirect the std::string's pointer to our
    // own characters and its mocked-up _Rep.
    //
    // See /usr/include/c++/VERSION/bits/basic_string.h for the details
    // of gcc's std::string implementation.

    *(const char **)&str = c_str();
    DASSERT (str.c_str() == c_str());
#else
    // Not gcc -- just assign the internal string.  This will result in
    // double allocation for the chars.  If you care about that, do
    // something special for your platform, much like we did for gcc
    // above.  (Windows users, I'm talking to you.)
    str = s;
#endif
}

ustring::TableRep::~TableRep ()
{
#if defined(__GNUC__)
    // Doctor the string to be empty again before destroying.
    ASSERT (str.c_str() == c_str());
    std::string empty;
    memcpy (&str, &empty, sizeof(std::string));
#endif
}

const ustring::TableRep *
ustring::_make_unique (const char *str)
{
    static UstringTable ustring_table;

    // Eliminate NULLs
    if (! str)
        str = "";

    // Check the ustring table to see if this string already exists.  If so,
    // construct from its canonical representation.
    {
        // Grab a read lock on the table.  Hopefully, the string will
        // already be present, and we can immediately return its rep.
        // Lots of threads may do this simultaneously, as long as they
        // are all in the table.
        ustring_read_lock_t read_lock (ustring_mutex);
        ++ustring_stats_constructed;
        UstringTable::const_iterator found = ustring_table.find (str);
        if (found != ustring_table.end())
           return found->second;
    }

    // This string is not yet in the ustring table.  Create a new entry.
    // Note that we are speculatively releasing the lock and building the
    // string locally.  Then we'll lock again to put it in the table.
    size_t len = strlen(str);
    size_t size = sizeof(ustring::TableRep) + len + 1;
    ustring::TableRep *rep = (ustring::TableRep *) malloc (size);
    new (rep) ustring::TableRep (str, len);

    UstringTable::const_iterator found;
    {
        // Now grab a write lock on the table.  This will prevent other
        // threads from even reading.  Just in case another thread has
        // already entered this thread while we were unlocked and
        // constructing its rep, check the table one more time.  If it's
        // still empty, add it.
        ustring_write_lock_t write_lock (ustring_mutex);
        found = ustring_table.find (str);
        if (found == ustring_table.end()) {
            ustring_table[rep->c_str()] = rep;
            ++ustring_stats_unique;
            ustring_stats_memory += size;
#ifndef __GNUC__
            ustring_stats_memory += len+1;  // non-GNU replicates the chars
#endif
            return rep;
        }
    }
    // Somebody else added this string to the table in that interval
    // when we were unlocked and constructing the rep.  Don't use the
    // new one!  Use the one in the table and disregard the one we
    // speculatively built.  Note that we've already released the lock
    // on the table at this point.
    delete rep;
    return found->second;
}

ustring
ustring::format (const char *fmt, ...)
{
    ustring tok;
    va_list ap;
    va_start (ap, fmt);

    // Allocate a buffer on the stack that's big enough for us almost
    // all the time.  Be prepared to allocate dynamically if it doesn't fit.
    size_t size = 1024;
    char stackbuf[1024];
    std::vector<char> dynamicbuf;
    char *buf = &stackbuf[0];

    while (1) {
        // Try to vsnprintf into our buffer.
        int needed = vsnprintf (buf, size, fmt, ap);
        // NB. C99 (which modern Linux and OS X follow) says vsnprintf
        // failure returns the length it would have needed.  But older
        // glibc and current Windows return -1 for failure, i.e., not
        // telling us how much was needed.

        if (needed <= (int)size && needed >= 0) {
            // It fit fine so we're done.
            return ustring (buf);
        }

        // vsnprintf reported that it wanted to write more characters
        // than we allotted.  So try again using a dynamic buffer.  This
        // doesn't happen very often if we chose our initial size well.
        size = (needed > 0) ? (needed+1) : (size*2);
        dynamicbuf.resize (size);
        buf = &dynamicbuf[0];
    }
}

std::string
ustring::getstats (bool verbose)
{
    ustring_read_lock_t read_lock (ustring_mutex);
    std::ostringstream out;
    if (verbose) {
        out << "ustring statistics:\n";
        out << "  ustring requests: " << ustring_stats_constructed
            << ", unique " << ustring_stats_unique << "\n";
        out << "  ustring memory: " << Strutil::memformat(ustring_stats_memory)
            << "\n";
    } else {
        out << "requests: " << ustring_stats_constructed
            << ", unique " << ustring_stats_unique
            << ", " << Strutil::memformat(ustring_stats_memory);
    }
    return out.str();
}

size_t
ustring::memory ()
{
    ustring_read_lock_t read_lock (ustring_mutex);
    return ustring_stats_memory;
}
<commit_msg>Extra precautions for thread safety of ustring table construction.<commit_after>/*
  Copyright 2008 Larry Gritz and the other authors and contributors.
  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 the software's owners 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.

  (This is the Modified BSD License)
*/

#include <cstdio>
#include <string>
#include <vector>

#include "export.h"
#include "thread.h"
#include "strutil.h"
#include "hash.h"
#include "dassert.h"

#include "ustring.h"

#if 0
// Use reader/writer locks
typedef shared_mutex ustring_mutex_t;
typedef shared_lock ustring_read_lock_t;
typedef unique_lock ustring_write_lock_t;
#elif 0
// Use regular mutex
typedef mutex ustring_mutex_t;
typedef lock_guard ustring_read_lock_t;
typedef lock_guard ustring_write_lock_t;
#elif 1
// Use spin locks
typedef spin_mutex ustring_mutex_t;
typedef spin_lock ustring_read_lock_t;
typedef spin_lock ustring_write_lock_t;
#else
// Use null locks
typedef null_mutex ustring_mutex_t;
typedef null_lock<null_mutex> ustring_read_lock_t;
typedef null_lock<null_mutex> ustring_write_lock_t;
#endif

#ifdef OIIO_HAVE_BOOST_UNORDERED_MAP
typedef boost::unordered_map <const char *, ustring::TableRep *, Strutil::StringHash, Strutil::StringEqual> UstringTable;
#else
typedef hash_map <const char *, ustring::TableRep *, Strutil::StringHash, Strutil::StringEqual> UstringTable;
#endif // OIIO_HAVE_BOOST_UNORDERED_MAP

std::string ustring::empty_std_string ("");

namespace { // anonymous

static long long ustring_stats_memory = 0;
static long long ustring_stats_constructed = 0;
static long long ustring_stats_unique = 0;

// Wrap our static mutex in a function to guarantee it exists when we
// need it, regardless of module initialization order.
static ustring_mutex_t & ustring_mutex ()
{
    static ustring_mutex_t the_real_mutex;
    return the_real_mutex;
}

};          // end anonymous namespace

// Put a ustring in the global scope to force at least one call to
// make_unique to happen before main(), i.e. before threads are launched,
// in order to eliminate any possible thread collosion on construction of
// the ustring_table statically declared within make_unique.
namespace pvt {
static ustring ustring_force_make_unique_call("");
};

ustring::TableRep::TableRep (const char *s, size_t len)
    : hashed(Strutil::strhash(s))
{
    strcpy ((char *)c_str(), s);
    length = len;
    dummy_capacity = len;
    dummy_refcount = 1;   // so it never frees

#if defined(__GNUC__)
    // We don't want the internal 'string str' to redundantly store the
    // chars, along with our own allocation.  So we use our knowledge of
    // the internal structure of gcc strings to make it point to our chars!
    // Note that we've carefully structured the TableRep fields so they
    // mimic a GCC basic_string::_Rep.
    //
    // It turns out that the first field of a gcc std::string is a
    // pointer to the characters within the basic_string::_Rep.  We
    // merely redirect that pointer, though for std::string to function
    // properly, the chars must be preceeded immediately in memory by
    // the rest of basic_string::_Rep, consisting of length, capacity
    // and refcount fields.  And we have designed our TableRep to do
    // just that!  So now we redirect the std::string's pointer to our
    // own characters and its mocked-up _Rep.
    //
    // See /usr/include/c++/VERSION/bits/basic_string.h for the details
    // of gcc's std::string implementation.

    *(const char **)&str = c_str();
    DASSERT (str.c_str() == c_str());
#else
    // Not gcc -- just assign the internal string.  This will result in
    // double allocation for the chars.  If you care about that, do
    // something special for your platform, much like we did for gcc
    // above.  (Windows users, I'm talking to you.)
    str = s;
#endif
}

ustring::TableRep::~TableRep ()
{
#if defined(__GNUC__)
    // Doctor the string to be empty again before destroying.
    ASSERT (str.c_str() == c_str());
    std::string empty;
    memcpy (&str, &empty, sizeof(std::string));
#endif
}

const ustring::TableRep *
ustring::_make_unique (const char *str)
{
    static UstringTable ustring_table;

    // Eliminate NULLs
    if (! str)
        str = "";

    // Check the ustring table to see if this string already exists.  If so,
    // construct from its canonical representation.
    {
        // Grab a read lock on the table.  Hopefully, the string will
        // already be present, and we can immediately return its rep.
        // Lots of threads may do this simultaneously, as long as they
        // are all in the table.
        ustring_read_lock_t read_lock (ustring_mutex());
        ++ustring_stats_constructed;
        UstringTable::const_iterator found = ustring_table.find (str);
        if (found != ustring_table.end())
           return found->second;
    }

    // This string is not yet in the ustring table.  Create a new entry.
    // Note that we are speculatively releasing the lock and building the
    // string locally.  Then we'll lock again to put it in the table.
    size_t len = strlen(str);
    size_t size = sizeof(ustring::TableRep) + len + 1;
    ustring::TableRep *rep = (ustring::TableRep *) malloc (size);
    new (rep) ustring::TableRep (str, len);

    UstringTable::const_iterator found;
    {
        // Now grab a write lock on the table.  This will prevent other
        // threads from even reading.  Just in case another thread has
        // already entered this thread while we were unlocked and
        // constructing its rep, check the table one more time.  If it's
        // still empty, add it.
        ustring_write_lock_t write_lock (ustring_mutex());
        found = ustring_table.find (str);
        if (found == ustring_table.end()) {
            ustring_table[rep->c_str()] = rep;
            ++ustring_stats_unique;
            ustring_stats_memory += size;
#ifndef __GNUC__
            ustring_stats_memory += len+1;  // non-GNU replicates the chars
#endif
            return rep;
        }
    }
    // Somebody else added this string to the table in that interval
    // when we were unlocked and constructing the rep.  Don't use the
    // new one!  Use the one in the table and disregard the one we
    // speculatively built.  Note that we've already released the lock
    // on the table at this point.
    delete rep;
    return found->second;
}

ustring
ustring::format (const char *fmt, ...)
{
    ustring tok;
    va_list ap;
    va_start (ap, fmt);

    // Allocate a buffer on the stack that's big enough for us almost
    // all the time.  Be prepared to allocate dynamically if it doesn't fit.
    size_t size = 1024;
    char stackbuf[1024];
    std::vector<char> dynamicbuf;
    char *buf = &stackbuf[0];

    while (1) {
        // Try to vsnprintf into our buffer.
        int needed = vsnprintf (buf, size, fmt, ap);
        // NB. C99 (which modern Linux and OS X follow) says vsnprintf
        // failure returns the length it would have needed.  But older
        // glibc and current Windows return -1 for failure, i.e., not
        // telling us how much was needed.

        if (needed <= (int)size && needed >= 0) {
            // It fit fine so we're done.
            return ustring (buf);
        }

        // vsnprintf reported that it wanted to write more characters
        // than we allotted.  So try again using a dynamic buffer.  This
        // doesn't happen very often if we chose our initial size well.
        size = (needed > 0) ? (needed+1) : (size*2);
        dynamicbuf.resize (size);
        buf = &dynamicbuf[0];
    }
}

std::string
ustring::getstats (bool verbose)
{
    ustring_read_lock_t read_lock (ustring_mutex());
    std::ostringstream out;
    if (verbose) {
        out << "ustring statistics:\n";
        out << "  ustring requests: " << ustring_stats_constructed
            << ", unique " << ustring_stats_unique << "\n";
        out << "  ustring memory: " << Strutil::memformat(ustring_stats_memory)
            << "\n";
    } else {
        out << "requests: " << ustring_stats_constructed
            << ", unique " << ustring_stats_unique
            << ", " << Strutil::memformat(ustring_stats_memory);
    }
    return out.str();
}

size_t
ustring::memory ()
{
    ustring_read_lock_t read_lock (ustring_mutex());
    return ustring_stats_memory;
}
<|endoftext|>


<commit_before>/* Copyright 2008 Uppsala University
 *
 * 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.
 */

/*
 LuckyMe challenges your luck.
 How many random dataobjects floating around in the Haggle network
 do you attract? You are yourself contributing to the luck of other
 players in the network!

 Besides of being a research application to evalute the search based
 networking paradigm of Haggle, this code can be taken as a skeletton
 for many fun application or game scenarios.
 Think creative, why not use puzzle parts as random objects? Make a
 competition with you buddies to collect as many objects as possible.
 */

#include <libhaggle/haggle.h>

#if defined(OS_UNIX)

#include <signal.h>
#include <unistd.h>
#include <cerrno>

#define ERRNO errno

#elif defined(OS_WINDOWS)

#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <time.h>
#include <winioctl.h>
#include <winerror.h>
#include <iphlpapi.h>
#include <ws2tcpip.h>

#define ERRNO WSAGetLastError()
#define EWOULDBLOCK WSAEWOULDBLOCK
#endif

#include <string.h>
#include <math.h>

#include <cstdlib>
#include <cstdio>
#include <string>
#include <sstream>
#include <iostream>
#include <fstream>
#include <algorithm>

#include "../utils/utils.h"

// --- command line arguments
unsigned int gridSize = 0;
char *fileName = 0;
char *singleSourceName = 0;
unsigned int createDataInterval = 10;

unsigned int attrPoolSize = 100;
unsigned int numDataObjectAttributes = 3;
unsigned int varianceInterestAttributes = 2;

// ---
#define NUM_CONNECTIONS 1
#define HTTP_PORT 8082

#define APP_NAME "LuckyMe"

// ---
using namespace std;
haggle_handle_t haggleHandle;

char hostname[128];
int nodeNumber;

void shutdown(int i);

double fac(int n)
{
    double t=1;
    for (int i=n;i>1;i--)
        t*=i;
    return t;
}

// --- luckyMe functions (create interest and data)

/*
 we are interested in a number of random attributes.
 */
void createInterestBinomial()
{
	unsigned int i = 0;
	char luckAttrValue[32];
	struct attributelist *attrList = haggle_attributelist_new();
	struct attribute *attr = 0;

	const unsigned long luck = random() % attrPoolSize;
	unsigned int weight = 0;

	// use binomial distribution to approximate normal distribution (sum of weights = 100)
	// mean = np = luck, variance = np(1-p) = LUCK_INTERESTS
	double p = 0.5;
	unsigned int n = 4 * sqrt((double)varianceInterestAttributes);
	unsigned int u = n * p;

	printf("createInterest (luck=%ld)\n", luck);
	// printf("binomial distribution  n=%u, p=%f\n", n, p);

	unsigned int interest = 0;
	for (i = 0; i <= n; i++) {
		interest = (luck + i - u + attrPoolSize) % attrPoolSize;
		weight = 100 * fac(n)/(fac(n-i)*fac(i))*pow(p,i)*pow(1-p,n-i);
		if (weight > 0) {
			sprintf(luckAttrValue, "%u", interest);
			attr = haggle_attribute_new_weighted(APP_NAME, luckAttrValue, weight);
			haggle_attributelist_add_attribute(attrList, attr);
			printf("   %s=%s [%d]\n", APP_NAME, luckAttrValue, weight);
		}
	}

	haggle_ipc_add_application_interests(haggleHandle, attrList);
}

/*
 we are interested in two attributes,
 corresponding to row and column(+gridSize) in the grid.
 */
void createInterestGrid()
{
	unsigned int i = 0;
	char luckAttrValue[32];
	struct attributelist *attrList = haggle_attributelist_new();
	struct attribute *attr = 0;
	static const unsigned int numInterests = 2;
	unsigned int interests[numInterests];

	interests[0] = nodeNumber / gridSize;
	interests[1] = (nodeNumber % gridSize) + gridSize;

	printf("createInterest\n");
	for (i=0; i<numInterests; i++) {
		sprintf(luckAttrValue, "%d", interests[i]);
		attr = haggle_attribute_new_weighted(APP_NAME, luckAttrValue, 1);
		haggle_attributelist_add_attribute(attrList, attr);
		printf("   %s=%s [%d]\n", APP_NAME, luckAttrValue, 1);
	}

	haggle_ipc_add_application_interests(haggleHandle, attrList);
}

/*
 we create a dataobject with a number of random attributes.
 at the moment the attributes are uniformly distributed.
 the intention is to build in some correlation to the own
 interests and/or the dataobjects that one received.
 */
void createDataobjectRandom()
{
	static unsigned int cntDataObjectNumber = 1;

	unsigned int i = 0;
	char luckAttrValue[32];
	struct dataobject *dObj = haggle_dataobject_new();

	printf("createDataobject %u\n", cntDataObjectNumber);
	// todo: get unique attributes
	for (i=0; i<numDataObjectAttributes; i++) {
		sprintf(luckAttrValue, "%ld", random() % attrPoolSize);
		haggle_dataobject_add_attribute(dObj, APP_NAME, luckAttrValue);
		printf("   %s=%s\n", APP_NAME, luckAttrValue);
	}

	haggle_ipc_publish_dataobject(haggleHandle, dObj);
	haggle_dataobject_free(dObj);

	cntDataObjectNumber++;
}

/*
 we create a dataobject with a number of attributes.
 all combinations from 1 attribute to 2*gridSize attributes generated.
 */
void createDataobjectGrid()
{
	static unsigned int cntDataObjectNumber = 1;
	unsigned int maxDataObjectNumber = (1 << (2*gridSize));

	int i = 0;
	char luckAttrValue[128];

	if (cntDataObjectNumber > maxDataObjectNumber-1) shutdown(0);

	struct dataobject *dObj = haggle_dataobject_new();

	printf("createDataobject %u\n", cntDataObjectNumber);
	for (i=0; i<6; i++) {
		if ((cntDataObjectNumber>>i) & 0x01) {
			sprintf(luckAttrValue, "%d", i);
			haggle_dataobject_add_attribute(dObj, APP_NAME, luckAttrValue);
			printf("   %s=%s\n", APP_NAME, luckAttrValue);
		}
	}

	haggle_ipc_publish_dataobject(haggleHandle, dObj);
	haggle_dataobject_free(dObj);

	cntDataObjectNumber++;
}

/*
 we get a matching dataobject.
 maybe we should reward the creator or delegate?
 */
void onDataobject(struct dataobject *dObj, void* nix)
{
	haggle_dataobject_free(dObj);
}

void onNeighborUpdate(struct dataobject *dObj, void* nix)
{
	haggle_dataobject_free(dObj);
}

void onShutdown(struct dataobject *dObj, void* nix)
{
	shutdown(0);
}

// ----- USAGE (parse arguments)

#if defined(OS_UNIX)

static void PrintUsage()
{
	fprintf(stderr,
            "Usage: ./%s [-A num] [-d num] [-i num] [-t interval] [-g gridSize] [-s hostname] [-f name]\n",
            APP_NAME);
    fprintf(stderr, "          -A attribute pool (default %u)\n", attrPoolSize);
    fprintf(stderr, "          -d number of attributes per data object (default %u)\n", numDataObjectAttributes);
    fprintf(stderr, "          -i interest variance (default %u)\n", varianceInterestAttributes);
    fprintf(stderr, "          -g use grid topology (gridSize x gridSize, overwrites -Adi, default off)\n");
    fprintf(stderr, "          -t interval to create data objects [s] (default %u)\n", createDataInterval);
    fprintf(stderr, "          -s singe source (create data objects only on node 'name', default off)\n");
	fprintf(stderr, "          -f data file to be sent (default off)\n");
}

static void ParseArguments(int argc, char **argv)
// Parses our command line arguments into the global variables
// listed above.
{
    int ch;

    // Parse command line options using getopt.

    do {
        ch = getopt(argc, argv, "A:d:i:t:g:s:f:");
        if (ch != -1) {
            switch (ch) {
                case 'A':
					attrPoolSize = atoi(optarg);
                    break;
                case 'd':
					numDataObjectAttributes = atoi(optarg);
                    break;
                case 'i':
					varianceInterestAttributes = atoi(optarg);
                    break;
                case 't':
                    createDataInterval = atoi(optarg);
                    break;
                case 'g':
					gridSize = atoi(optarg);
					attrPoolSize = 2*gridSize;
                    break;
                case 's':
                    singleSourceName = optarg;
                    break;
                case 'f':
                    fileName = optarg;
                    break;
                default:
                    PrintUsage();
                    exit(1);
                    break;
            }
        }
    } while (ch != -1);

    // Check for any left over command line arguments.

    if (optind != argc) {
	    PrintUsage();
        fprintf(stderr, "%s: Unexpected argument '%s'\n", argv[0], argv[optind]);
        exit(1);
    }

}

#endif // OS_UNIX

void shutdown(int i) {
	printf("\nshutting down...\n");
	haggle_handle_free(haggleHandle);

	exit(i);
}

void eventLoop() {
	fd_set readfds;
	int result = 0;
	struct timeval timeout;
	static volatile int StopNow = 0;

	while (!StopNow) {
		unsigned int nfds = 0;
		timeout.tv_sec = createDataInterval;
		timeout.tv_usec = 0;

		FD_ZERO(&readfds);

		result = select(nfds, &readfds, NULL, NULL, &timeout);
		if (result < 0) {
			if (ERRNO != EINTR) StopNow = 1;
		} else {
			if (singleSourceName == NULL || strcmp(hostname, singleSourceName) == 0) {
				if (gridSize > 0) {
					createDataobjectGrid();
				} else {
					createDataobjectRandom();
				}
			}
		}
	}
}

#ifdef OS_WINDOWS_MOBILE
int wmain()
{
#else
	int main (int argc, char *argv[])
	{

		signal(SIGINT,  shutdown);      // SIGINT is what you get for a Ctrl-C
#endif
		ParseArguments(argc, argv);

		// get hostname (used to set interest)
		gethostname(hostname, 128);
		nodeNumber = atoi(&(hostname[5]));

		// register with haggle
		if(haggle_handle_get(APP_NAME, &haggleHandle) != HAGGLE_NO_ERROR) {
			printf("could not obtain Haggle handle\n");
			goto done;
		}

		// register callback for new data objects
		// haggle_ipc_register_event_interest(haggleHandle, LIBHAGGLE_EVENT_NEW_DATAOBJECT, onDataobject);
		haggle_ipc_register_event_interest(haggleHandle, LIBHAGGLE_EVENT_HAGGLE_SHUTDOWN, onShutdown);

		// reset random number generator
		struct timeval t;
		gettimeofday(&t, NULL);
		srandom(t.tv_usec);

		// start Haggle eventloop
		haggle_event_loop_run_async(haggleHandle);

		// create interests
		if (gridSize > 0) {
			createInterestGrid();
		} else {
			createInterestBinomial();
		}

		// start luckyMe eventloop
		// (data objects are created in intervals from within this eventloop)
		eventLoop();

	done:
		shutdown(0);

		return 1;
	}

<commit_msg>added support for sending files with luckyMe<commit_after>/* Copyright 2008 Uppsala University
 *
 * 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.
 */

/*
 LuckyMe challenges your luck.
 How many random dataobjects floating around in the Haggle network
 do you attract? You are yourself contributing to the luck of other
 players in the network!

 Besides of being a research application to evalute the search based
 networking paradigm of Haggle, this code can be taken as a skeletton
 for many fun application or game scenarios.
 Think creative, why not use puzzle parts as random objects? Make a
 competition with you buddies to collect as many objects as possible.
 */

#include <libhaggle/haggle.h>

#if defined(OS_UNIX)

#include <signal.h>
#include <unistd.h>
#include <cerrno>

#define ERRNO errno

#elif defined(OS_WINDOWS)

#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <time.h>
#include <winioctl.h>
#include <winerror.h>
#include <iphlpapi.h>
#include <ws2tcpip.h>

#define ERRNO WSAGetLastError()
#define EWOULDBLOCK WSAEWOULDBLOCK
#endif

#include <string.h>
#include <math.h>

#include <cstdlib>
#include <cstdio>
#include <string>
#include <sstream>
#include <iostream>
#include <fstream>
#include <algorithm>

#include "../utils/utils.h"

// --- command line arguments
unsigned int gridSize = 0;
char *fileName = 0;
char *singleSourceName = 0;
unsigned int createDataInterval = 10;

unsigned int attrPoolSize = 100;
unsigned int numDataObjectAttributes = 3;
unsigned int varianceInterestAttributes = 2;

// ---
#define NUM_CONNECTIONS 1
#define HTTP_PORT 8082

#define APP_NAME "LuckyMe"

// ---
using namespace std;
haggle_handle_t haggleHandle;

char hostname[128];
int nodeNumber;

void shutdown(int i);

double fac(int n)
{
    double t=1;
    for (int i=n;i>1;i--)
        t*=i;
    return t;
}

// --- luckyMe functions (create interest and data)

/*
 we are interested in a number of random attributes.
 */
void createInterestBinomial()
{
	unsigned int i = 0;
	char luckAttrValue[32];
	struct attributelist *attrList = haggle_attributelist_new();
	struct attribute *attr = 0;

	const unsigned long luck = random() % attrPoolSize;
	unsigned int weight = 0;

	// use binomial distribution to approximate normal distribution (sum of weights = 100)
	// mean = np = luck, variance = np(1-p) = LUCK_INTERESTS
	double p = 0.5;
	unsigned int n = 4 * sqrt((double)varianceInterestAttributes);
	unsigned int u = n * p;

	printf("createInterest (luck=%ld)\n", luck);
	// printf("binomial distribution  n=%u, p=%f\n", n, p);

	unsigned int interest = 0;
	for (i = 0; i <= n; i++) {
		interest = (luck + i - u + attrPoolSize) % attrPoolSize;
		weight = 100 * fac(n)/(fac(n-i)*fac(i))*pow(p,i)*pow(1-p,n-i);
		if (weight > 0) {
			sprintf(luckAttrValue, "%u", interest);
			attr = haggle_attribute_new_weighted(APP_NAME, luckAttrValue, weight);
			haggle_attributelist_add_attribute(attrList, attr);
			printf("   %s=%s [%d]\n", APP_NAME, luckAttrValue, weight);
		}
	}

	haggle_ipc_add_application_interests(haggleHandle, attrList);
}

/*
 we are interested in two attributes,
 corresponding to row and column(+gridSize) in the grid.
 */
void createInterestGrid()
{
	unsigned int i = 0;
	char luckAttrValue[32];
	struct attributelist *attrList = haggle_attributelist_new();
	struct attribute *attr = 0;
	static const unsigned int numInterests = 2;
	unsigned int interests[numInterests];

	interests[0] = nodeNumber / gridSize;
	interests[1] = (nodeNumber % gridSize) + gridSize;

	printf("createInterest\n");
	for (i=0; i<numInterests; i++) {
		sprintf(luckAttrValue, "%d", interests[i]);
		attr = haggle_attribute_new_weighted(APP_NAME, luckAttrValue, 1);
		haggle_attributelist_add_attribute(attrList, attr);
		printf("   %s=%s [%d]\n", APP_NAME, luckAttrValue, 1);
	}

	haggle_ipc_add_application_interests(haggleHandle, attrList);
}

/*
 we create a dataobject with a number of random attributes.
 at the moment the attributes are uniformly distributed.
 the intention is to build in some correlation to the own
 interests and/or the dataobjects that one received.
 */
void createDataobjectRandom()
{
	static unsigned int cntDataObjectNumber = 1;

	unsigned int i = 0;
	char luckAttrValue[32];

	struct dataobject *dObj = 0;
	if (fileName) {
		dObj = haggle_dataobject_new_from_file(fileName);
	} else {
		dObj = haggle_dataobject_new();
	}

	printf("createDataobject %u\n", cntDataObjectNumber);
	// todo: get unique attributes
	for (i=0; i<numDataObjectAttributes; i++) {
		sprintf(luckAttrValue, "%ld", random() % attrPoolSize);
		haggle_dataobject_add_attribute(dObj, APP_NAME, luckAttrValue);
		printf("   %s=%s\n", APP_NAME, luckAttrValue);
	}

	haggle_ipc_publish_dataobject(haggleHandle, dObj);
	haggle_dataobject_free(dObj);

	cntDataObjectNumber++;
}

/*
 we create a dataobject with a number of attributes.
 all combinations from 1 attribute to 2*gridSize attributes generated.
 */
void createDataobjectGrid()
{
	static unsigned int cntDataObjectNumber = 1;
	unsigned int maxDataObjectNumber = (1 << (2*gridSize));

	int i = 0;
	char luckAttrValue[128];

	if (cntDataObjectNumber > maxDataObjectNumber-1) shutdown(0);

	struct dataobject *dObj = 0;
	if (fileName) {
		dObj = haggle_dataobject_new_from_file(fileName);
	} else {
		dObj = haggle_dataobject_new();
	}

	printf("createDataobject %u\n", cntDataObjectNumber);
	for (i=0; i<6; i++) {
		if ((cntDataObjectNumber>>i) & 0x01) {
			sprintf(luckAttrValue, "%d", i);
			haggle_dataobject_add_attribute(dObj, APP_NAME, luckAttrValue);
			printf("   %s=%s\n", APP_NAME, luckAttrValue);
		}
	}

	haggle_ipc_publish_dataobject(haggleHandle, dObj);
	haggle_dataobject_free(dObj);

	cntDataObjectNumber++;
}

/*
 we get a matching dataobject.
 maybe we should reward the creator or delegate?
 */
void onDataobject(struct dataobject *dObj, void* nix)
{
	haggle_dataobject_free(dObj);
}

void onNeighborUpdate(struct dataobject *dObj, void* nix)
{
	haggle_dataobject_free(dObj);
}

void onShutdown(struct dataobject *dObj, void* nix)
{
	shutdown(0);
}

// ----- USAGE (parse arguments)

#if defined(OS_UNIX)

static void PrintUsage()
{
	fprintf(stderr,
            "Usage: ./%s [-A num] [-d num] [-i num] [-t interval] [-g gridSize] [-s hostname] [-f path]\n",
            APP_NAME);
    fprintf(stderr, "          -A attribute pool (default %u)\n", attrPoolSize);
    fprintf(stderr, "          -d number of attributes per data object (default %u)\n", numDataObjectAttributes);
    fprintf(stderr, "          -i interest variance (default %u)\n", varianceInterestAttributes);
    fprintf(stderr, "          -g use grid topology (gridSize x gridSize, overwrites -Adi, default off)\n");
    fprintf(stderr, "          -t interval to create data objects [s] (default %u)\n", createDataInterval);
    fprintf(stderr, "          -s singe source (create data objects only on node 'name', default off)\n");
	fprintf(stderr, "          -f data file to be sent (default off)\n");
}

static void ParseArguments(int argc, char **argv)
// Parses our command line arguments into the global variables
// listed above.
{
    int ch;

    // Parse command line options using getopt.

    do {
        ch = getopt(argc, argv, "A:d:i:t:g:s:f:");
        if (ch != -1) {
            switch (ch) {
                case 'A':
					attrPoolSize = atoi(optarg);
                    break;
                case 'd':
					numDataObjectAttributes = atoi(optarg);
                    break;
                case 'i':
					varianceInterestAttributes = atoi(optarg);
                    break;
                case 't':
                    createDataInterval = atoi(optarg);
                    break;
                case 'g':
					gridSize = atoi(optarg);
					attrPoolSize = 2*gridSize;
                    break;
                case 's':
                    singleSourceName = optarg;
                    break;
                case 'f':
                    fileName = optarg;
                    break;
                default:
                    PrintUsage();
                    exit(1);
                    break;
            }
        }
    } while (ch != -1);

    // Check for any left over command line arguments.

    if (optind != argc) {
	    PrintUsage();
        fprintf(stderr, "%s: Unexpected argument '%s'\n", argv[0], argv[optind]);
        exit(1);
    }

}

#endif // OS_UNIX

void shutdown(int i) {
	printf("\nshutting down...\n");
	haggle_handle_free(haggleHandle);

	exit(i);
}

void eventLoop() {
	fd_set readfds;
	int result = 0;
	struct timeval timeout;
	static volatile int StopNow = 0;

	while (!StopNow) {
		unsigned int nfds = 0;
		timeout.tv_sec = createDataInterval;
		timeout.tv_usec = 0;

		FD_ZERO(&readfds);

		result = select(nfds, &readfds, NULL, NULL, &timeout);
		if (result < 0) {
			if (ERRNO != EINTR) StopNow = 1;
		} else {
			if (singleSourceName == NULL || strcmp(hostname, singleSourceName) == 0) {
				if (gridSize > 0) {
					createDataobjectGrid();
				} else {
					createDataobjectRandom();
				}
			}
		}
	}
}

#ifdef OS_WINDOWS_MOBILE
int wmain()
{
#else
	int main (int argc, char *argv[])
	{

		signal(SIGINT,  shutdown);      // SIGINT is what you get for a Ctrl-C
#endif
		ParseArguments(argc, argv);

		// get hostname (used to set interest)
		gethostname(hostname, 128);
		nodeNumber = atoi(&(hostname[5]));

		// register with haggle
		if(haggle_handle_get(APP_NAME, &haggleHandle) != HAGGLE_NO_ERROR) {
			printf("could not obtain Haggle handle\n");
			goto done;
		}

		// register callback for new data objects
		// haggle_ipc_register_event_interest(haggleHandle, LIBHAGGLE_EVENT_NEW_DATAOBJECT, onDataobject);
		haggle_ipc_register_event_interest(haggleHandle, LIBHAGGLE_EVENT_HAGGLE_SHUTDOWN, onShutdown);

		// reset random number generator
		struct timeval t;
		gettimeofday(&t, NULL);
		srandom(t.tv_usec);

		// start Haggle eventloop
		haggle_event_loop_run_async(haggleHandle);

		// create interests
		if (gridSize > 0) {
			createInterestGrid();
		} else {
			createInterestBinomial();
		}

		// start luckyMe eventloop
		// (data objects are created in intervals from within this eventloop)
		eventLoop();

	done:
		shutdown(0);

		return 1;
	}

<|endoftext|>


<commit_before>/*
 * Copyright (C) 2017 Tmplt <tmplt@dragons.rocks>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <fmt/format.h>
#include <iostream>
#include <spdlog/spdlog.h>
#include <pybind11/embed.h>

#include "errors.hpp"
#include "components/menu.hpp"
#include "components/logger.hpp"

namespace py = pybind11;

namespace bookwyrm {

menu::~menu()
{
    tb_shutdown();

    std::cout << "selected items:\n";
    for (auto idx : marked_items_)
        std::cout << idx << ' ';

    std::cout << '\n';
}

void menu::display()
{
    menu_mutex_.lock();

    int code = tb_init();
    if (code < 0) {
        string err = "termbox init failed with code: " + code;
        throw component_error(err.data());
    }

    tb_set_cursor(TB_HIDE_CURSOR, TB_HIDE_CURSOR);
    tb_clear();

    menu_mutex_.unlock();

    /*
     * Let the source threads free.
     * This doesn't feel like the best place to have this,
     * but we do need to have a release in scope if we
     * ever want the threads to run.
     * TODO: find out if there is a better place for this.
     */
    py::gil_scoped_release nogil;

    struct tb_event ev;
    bool quit = false;
    while (tb_poll_event(&ev) && !quit) {
        if (ev.type == TB_EVENT_RESIZE) {
            resize();
        } else if (ev.type == TB_EVENT_KEY) {
            switch (ev.key) {
                case TB_KEY_ESC:
                    quit = true;
                    break;
                case TB_KEY_ARROW_DOWN:
                    move(down);
                    break;
                case TB_KEY_ARROW_UP:
                    move(up);
                    break;
                case TB_KEY_SPACE:
                    toggle_select();
                    break;
            }
        }
    }
}

void menu::update()
{
    tb_clear();

    for (size_t i = scroll_offset_; i < item_count(); i++) {
        if (y_ == menu_capacity()) break;
        print_item(items_[i]);
        y_++;
    }

    if (menu_at_bot()) mvprintw(0, tb_height() - 1, "bot");
    if (menu_at_top()) mvprintw(0, tb_height() - 1, "top");

    // print_scrollbar down here
    mvprintw(0, tb_height() - 2, fmt::format("The menu contains {} items.", item_count()));
    mvprintw(5, tb_height() - 1, fmt::format("selected_item_ = {}, scroll_offset_ = {}",
                selected_item_, scroll_offset_));

    y_ = 0;
    tb_present();
}

void menu::print_item(const item &t)
{
    bool on_selected_item = (y_ + scroll_offset_ == selected_item_);

    /*
     * Imitate an Ncurses menu, denote the selected item with a '-'
     * and by reversing fg and bg on the entry.
     * Leave x = 1 to the indicator.
     */
    if (on_selected_item) {
        tb_change_cell(0, y_, '-', 0, 0);
    } else if (is_marked(y_ + scroll_offset_)) {
        tb_change_cell(0, y_, ' ', TB_REVERSE, 0);
    }

    uint16_t attrs = (on_selected_item || is_marked(y_ + scroll_offset_))
        ? TB_REVERSE : 0;
    int x = 1;

    for (uint32_t ch : t.nonexacts.title) {
        tb_change_cell(x++, y_, ch, attrs, 0);
    }
}

void menu::mvprintw(int x, int y, string str)
{
    for (uint32_t ch : str) {
        tb_change_cell(x++, y, ch, 0, 0);
    }
}

void menu::move(move_direction dir)
{
    bool at_first_item = selected_item_ == 0,
         at_last_item  = selected_item_ == static_cast<int>(item_count() - 1);

    switch (dir) {
        case up:
            if (at_first_item) return;
            if (menu_at_top()) scroll_offset_--;
            selected_item_--;
            break;
        case down:
            if (at_last_item) return;
            if (menu_at_bot()) scroll_offset_++;
            selected_item_++;
            break;
    }

    update();
}

void menu::toggle_select()
{
    if (is_marked(selected_item_))
        unmark_item(selected_item_);
    else
        mark_item(selected_item_);

    update();
}

void menu::resize()
{
    /*
     * When the window is resized from the lower left/right
     * corner, the currently selected item may escape the
     * menu, so we lock it here.
     */
    if (menu_at_bot()) selected_item_--;
    update();
}

} /* ns bookwyrm */
<commit_msg>menu: learn some vim bindings<commit_after>/*
 * Copyright (C) 2017 Tmplt <tmplt@dragons.rocks>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <fmt/format.h>
#include <iostream>
#include <spdlog/spdlog.h>
#include <pybind11/embed.h>

#include "errors.hpp"
#include "components/menu.hpp"
#include "components/logger.hpp"

namespace py = pybind11;

namespace bookwyrm {

menu::~menu()
{
    tb_shutdown();

    std::cout << "selected items:\n";
    for (auto idx : marked_items_)
        std::cout << idx << ' ';

    std::cout << '\n';
}

void menu::display()
{
    menu_mutex_.lock();

    int code = tb_init();
    if (code < 0) {
        string err = "termbox init failed with code: " + code;
        throw component_error(err.data());
    }

    tb_set_cursor(TB_HIDE_CURSOR, TB_HIDE_CURSOR);
    tb_clear();

    menu_mutex_.unlock();

    /*
     * Let the source threads free.
     * This doesn't feel like the best place to have this,
     * but we do need to have a release in scope if we
     * ever want the threads to run.
     * TODO: find out if there is a better place for this.
     */
    py::gil_scoped_release nogil;

    /*
     * For some odd reason, we must press ESC twice
     * to quit here.
     * TODO: figure out why.
     */
    bool quit = false;
    struct tb_event ev;
    while (tb_poll_event(&ev) && !quit) {
        if (ev.type == TB_EVENT_RESIZE) {
            resize();
        } else if (ev.type == TB_EVENT_KEY) {
            switch (ev.key) {
                case TB_KEY_ESC:
                    quit = true;
                    break;
                case TB_KEY_ARROW_DOWN:
                    move(down);
                    break;
                case TB_KEY_ARROW_UP:
                    move(up);
                    break;
                case TB_KEY_SPACE:
                    toggle_select();
                    break;
            }

            switch (ev.ch) {
                case 'j':
                    move(down);
                    break;
                case 'k':
                    move(up);
                    break;
            }
        }
    }
}

void menu::update()
{
    tb_clear();

    for (size_t i = scroll_offset_; i < item_count(); i++) {
        if (y_ == menu_capacity()) break;
        print_item(items_[i]);
        y_++;
    }

    if (menu_at_bot()) mvprintw(0, tb_height() - 1, "bot");
    if (menu_at_top()) mvprintw(0, tb_height() - 1, "top");

    // print_scrollbar down here
    mvprintw(0, tb_height() - 2, fmt::format("The menu contains {} items.", item_count()));
    mvprintw(5, tb_height() - 1, fmt::format("selected_item_ = {}, scroll_offset_ = {}",
                selected_item_, scroll_offset_));

    y_ = 0;
    tb_present();
}

void menu::print_item(const item &t)
{
    bool on_selected_item = (y_ + scroll_offset_ == selected_item_);

    /*
     * Imitate an Ncurses menu, denote the selected item with a '-'
     * and by reversing fg and bg on the entry.
     * Leave x = 1 to the indicator.
     */
    if (on_selected_item) {
        tb_change_cell(0, y_, '-', 0, 0);
    } else if (is_marked(y_ + scroll_offset_)) {
        tb_change_cell(0, y_, ' ', TB_REVERSE, 0);
    }

    uint16_t attrs = (on_selected_item || is_marked(y_ + scroll_offset_))
        ? TB_REVERSE : 0;
    int x = 1;

    for (uint32_t ch : t.nonexacts.title) {
        tb_change_cell(x++, y_, ch, attrs, 0);
    }
}

void menu::mvprintw(int x, int y, string str)
{
    for (uint32_t ch : str) {
        tb_change_cell(x++, y, ch, 0, 0);
    }
}

void menu::move(move_direction dir)
{
    bool at_first_item = selected_item_ == 0,
         at_last_item  = selected_item_ == static_cast<int>(item_count() - 1);

    switch (dir) {
        case up:
            if (at_first_item) return;
            if (menu_at_top()) scroll_offset_--;
            selected_item_--;
            break;
        case down:
            if (at_last_item) return;
            if (menu_at_bot()) scroll_offset_++;
            selected_item_++;
            break;
    }

    update();
}

void menu::toggle_select()
{
    if (is_marked(selected_item_))
        unmark_item(selected_item_);
    else
        mark_item(selected_item_);

    update();
}

void menu::resize()
{
    /*
     * When the window is resized from the lower left/right
     * corner, the currently selected item may escape the
     * menu, so we lock it here.
     */
    if (menu_at_bot()) selected_item_--;
    update();
}

} /* ns bookwyrm */
<|endoftext|>


<commit_before>// Filename: mayaToEgg.cxx
// Created by:  drose (15Feb00)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license.  You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://www.panda3d.org/license.txt .
//
// To contact the maintainers of this program write to
// panda3d@yahoogroups.com .
//
////////////////////////////////////////////////////////////////////

#include "mayaToEgg.h"
#include "mayaToEggConverter.h"
#include "config_mayaegg.h"
#include "config_maya.h"  // for maya_cat

////////////////////////////////////////////////////////////////////
//     Function: MayaToEgg::Constructor
//       Access: Public
//  Description:
////////////////////////////////////////////////////////////////////
MayaToEgg::
MayaToEgg() :
  SomethingToEgg("Maya", ".mb")
{
  add_path_replace_options();
  add_path_store_options();
  add_animation_options();
  add_units_options();
  add_normals_options();
  add_transform_options();

  set_program_description
    ("This program converts Maya model files to egg.  Nothing fancy yet.");

  add_option
    ("p", "", 0,
     "Generate polygon output only.  Tesselate all NURBS surfaces to "
     "polygons via the built-in Maya tesselator.  The tesselation will "
     "be based on the tolerance factor given by -ptol.",
     &MayaToEgg::dispatch_none, &_polygon_output);

  add_option
    ("ptol", "tolerance", 0,
     "Specify the fit tolerance for Maya polygon tesselation.  The smaller "
     "the number, the more polygons will be generated.  The default is "
     "0.01.",
     &MayaToEgg::dispatch_double, NULL, &_polygon_tolerance);

  add_option
    ("notrans", "", 0,
     "Don't convert explicit DAG transformations given in the Maya file.  "
     "Instead, convert all vertices to world space and write the file as "
     "one big transform space.  Using this option doesn't change the "
     "position of objects in the scene, just the number of explicit "
     "transforms appearing in the resulting egg file.",
     &MayaToEgg::dispatch_none, &_ignore_transforms);

  add_option
    ("v", "", 0,
     "Increase verbosity.  More v's means more verbose.",
     &MayaToEgg::dispatch_count, NULL, &_verbose);

  _polygon_tolerance = 0.01;
  _verbose = 0;
}

////////////////////////////////////////////////////////////////////
//     Function: MayaToEgg::run
//       Access: Public
//  Description:
////////////////////////////////////////////////////////////////////
void MayaToEgg::
run() {
  // Set the verbose level by using Notify.
  if (_verbose >= 3) {
    maya_cat->set_severity(NS_spam);
    mayaegg_cat->set_severity(NS_spam);
  } else if (_verbose >= 2) {
    maya_cat->set_severity(NS_debug);
    mayaegg_cat->set_severity(NS_debug);
  } else if (_verbose >= 1) {
    maya_cat->set_severity(NS_info);
    mayaegg_cat->set_severity(NS_info);
  }

  // Let's open the output file before we initialize Maya, since Maya
  // now has a nasty habit of changing the current directory.
  get_output();

  nout << "Initializing Maya.\n";
  MayaToEggConverter converter(_program_name);
  if (!converter.open_api()) {
    nout << "Unable to initialize Maya.\n";
    exit(1);
  }

  // Copy in the command-line parameters.
  converter._polygon_output = _polygon_output;
  converter._polygon_tolerance = _polygon_tolerance;
  converter._ignore_transforms = _ignore_transforms;

  // Copy in the path and animation parameters.
  apply_parameters(converter);

  // Set the coordinate system to match Maya's.
  if (!_got_coordinate_system) {
    _coordinate_system = converter._maya->get_coordinate_system();
  }
  _data.set_coordinate_system(_coordinate_system);

  // Use the standard Maya units, if the user didn't specify
  // otherwise.  This always returns centimeters, which is the way all
  // Maya files are stored internally (and is the units returned by
  // all of the API functions called here).
  if (_input_units == DU_invalid) {
    _input_units = converter._maya->get_units();
  }

  converter.set_egg_data(&_data, false);
  apply_parameters(converter);

  if (!converter.convert_file(_input_filename)) {
    nout << "Errors in conversion.\n";
    exit(1);
  }

  write_egg_file();
  nout << "\n";
}

int main(int argc, char *argv[]) {
  MayaToEgg prog;
  prog.parse_command_line(argc, argv);
  prog.run();
  return 0;
}
<commit_msg>don't write egg file until we've finished generating it<commit_after>// Filename: mayaToEgg.cxx
// Created by:  drose (15Feb00)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license.  You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://www.panda3d.org/license.txt .
//
// To contact the maintainers of this program write to
// panda3d@yahoogroups.com .
//
////////////////////////////////////////////////////////////////////

#include "mayaToEgg.h"
#include "mayaToEggConverter.h"
#include "config_mayaegg.h"
#include "config_maya.h"  // for maya_cat

////////////////////////////////////////////////////////////////////
//     Function: MayaToEgg::Constructor
//       Access: Public
//  Description:
////////////////////////////////////////////////////////////////////
MayaToEgg::
MayaToEgg() :
  SomethingToEgg("Maya", ".mb")
{
  add_path_replace_options();
  add_path_store_options();
  add_animation_options();
  add_units_options();
  add_normals_options();
  add_transform_options();

  set_program_description
    ("This program converts Maya model files to egg.  Nothing fancy yet.");

  add_option
    ("p", "", 0,
     "Generate polygon output only.  Tesselate all NURBS surfaces to "
     "polygons via the built-in Maya tesselator.  The tesselation will "
     "be based on the tolerance factor given by -ptol.",
     &MayaToEgg::dispatch_none, &_polygon_output);

  add_option
    ("ptol", "tolerance", 0,
     "Specify the fit tolerance for Maya polygon tesselation.  The smaller "
     "the number, the more polygons will be generated.  The default is "
     "0.01.",
     &MayaToEgg::dispatch_double, NULL, &_polygon_tolerance);

  add_option
    ("notrans", "", 0,
     "Don't convert explicit DAG transformations given in the Maya file.  "
     "Instead, convert all vertices to world space and write the file as "
     "one big transform space.  Using this option doesn't change the "
     "position of objects in the scene, just the number of explicit "
     "transforms appearing in the resulting egg file.",
     &MayaToEgg::dispatch_none, &_ignore_transforms);

  add_option
    ("v", "", 0,
     "Increase verbosity.  More v's means more verbose.",
     &MayaToEgg::dispatch_count, NULL, &_verbose);

  _polygon_tolerance = 0.01;
  _verbose = 0;
}

////////////////////////////////////////////////////////////////////
//     Function: MayaToEgg::run
//       Access: Public
//  Description:
////////////////////////////////////////////////////////////////////
void MayaToEgg::
run() {
  // Set the verbose level by using Notify.
  if (_verbose >= 3) {
    maya_cat->set_severity(NS_spam);
    mayaegg_cat->set_severity(NS_spam);
  } else if (_verbose >= 2) {
    maya_cat->set_severity(NS_debug);
    mayaegg_cat->set_severity(NS_debug);
  } else if (_verbose >= 1) {
    maya_cat->set_severity(NS_info);
    mayaegg_cat->set_severity(NS_info);
  }

  // Let's convert the output file to a full path before we initialize
  // Maya, since Maya now has a nasty habit of changing the current
  // directory.
  if (_got_output_filename) {
    _output_filename.make_absolute();
  }

  nout << "Initializing Maya.\n";
  MayaToEggConverter converter(_program_name);
  if (!converter.open_api()) {
    nout << "Unable to initialize Maya.\n";
    exit(1);
  }

  // Copy in the command-line parameters.
  converter._polygon_output = _polygon_output;
  converter._polygon_tolerance = _polygon_tolerance;
  converter._ignore_transforms = _ignore_transforms;

  // Copy in the path and animation parameters.
  apply_parameters(converter);

  // Set the coordinate system to match Maya's.
  if (!_got_coordinate_system) {
    _coordinate_system = converter._maya->get_coordinate_system();
  }
  _data.set_coordinate_system(_coordinate_system);

  // Use the standard Maya units, if the user didn't specify
  // otherwise.  This always returns centimeters, which is the way all
  // Maya files are stored internally (and is the units returned by
  // all of the API functions called here).
  if (_input_units == DU_invalid) {
    _input_units = converter._maya->get_units();
  }

  converter.set_egg_data(&_data, false);
  apply_parameters(converter);

  if (!converter.convert_file(_input_filename)) {
    nout << "Errors in conversion.\n";
    exit(1);
  }

  write_egg_file();
  nout << "\n";
}

int main(int argc, char *argv[]) {
  MayaToEgg prog;
  prog.parse_command_line(argc, argv);
  prog.run();
  return 0;
}
<|endoftext|>


<commit_before>/**
 * @file sgd_impl.hpp
 * @author Ryan Curtin
 *
 * Implementation of stochastic gradient descent.
 */
#ifndef __MLPACK_CORE_OPTIMIZERS_SGD_SGD_IMPL_HPP
#define __MLPACK_CORE_OPTIMIZERS_SGD_SGD_IMPL_HPP

#include <mlpack/methods/regularized_svd/regularized_svd_function.hpp>
// In case it hasn't been included yet.
#include "sgd.hpp"

namespace mlpack {
namespace optimization {

template<typename DecomposableFunctionType>
SGD<DecomposableFunctionType>::SGD(DecomposableFunctionType& function,
                                   const double stepSize,
                                   const size_t maxIterations,
                                   const double tolerance,
                                   const bool shuffle) :
    function(function),
    stepSize(stepSize),
    maxIterations(maxIterations),
    tolerance(tolerance),
    shuffle(shuffle)
{ /* Nothing to do. */ }

//! Optimize the function (minimize).
template<typename DecomposableFunctionType>
double SGD<DecomposableFunctionType>::Optimize(arma::mat& iterate)
{
  // Find the number of functions to use.
  const size_t numFunctions = function.NumFunctions();

  // This is used only if shuffle is true.
  arma::vec visitationOrder;
  if (shuffle)
    visitationOrder = arma::shuffle(arma::linspace(0, (numFunctions - 1),
        numFunctions));

  // To keep track of where we are and how things are going.
  size_t currentFunction = 0;
  double overallObjective = 0;
  double lastObjective = DBL_MAX;

  // Calculate the first objective function.
  for (size_t i = 0; i < numFunctions; ++i)
    overallObjective += function.Evaluate(iterate, i);

  // Now iterate!
  arma::mat gradient(iterate.n_rows, iterate.n_cols);
  for (size_t i = 1; i != maxIterations; ++i, ++currentFunction)
  {
    // Is this iteration the start of a sequence?
    if ((currentFunction % numFunctions) == 0)
    {
      // Output current objective function.
      Log::Info << "SGD: iteration " << i << ", objective " << overallObjective
          << "." << std::endl;

      if (overallObjective != overallObjective)
      {
        Log::Warn << "SGD: converged to " << overallObjective << "; terminating"
            << " with failure.  Try a smaller step size?" << std::endl;
        return overallObjective;
      }

      if (std::abs(lastObjective - overallObjective) < tolerance)
      {
        Log::Info << "SGD: minimized within tolerance " << tolerance << "; "
            << "terminating optimization." << std::endl;
        return overallObjective;
      }

      // Reset the counter variables.
      lastObjective = overallObjective;
      overallObjective = 0;
      currentFunction = 0;

      if (shuffle) // Determine order of visitation.
        visitationOrder = arma::shuffle(visitationOrder);
    }

    // Evaluate the gradient for this iteration.
    if (shuffle)
      function.Gradient(iterate, visitationOrder[currentFunction], gradient);
    else
      function.Gradient(iterate, currentFunction, gradient);

    // And update the iterate.
    iterate -= stepSize * gradient;

    // Now add that to the overall objective function.
    if (shuffle)
      overallObjective += function.Evaluate(iterate,
          visitationOrder[currentFunction]);
    else
      overallObjective += function.Evaluate(iterate, currentFunction);
  }

  Log::Info << "SGD: maximum iterations (" << maxIterations << ") reached; "
      << "terminating optimization." << std::endl;
  // Calculate final objective.
  overallObjective = 0;
  for (size_t i = 0; i < numFunctions; ++i)
    overallObjective += function.Evaluate(iterate, i);
  return overallObjective;
}

// Convert the object to a string.
template<typename DecomposableFunctionType>
std::string SGD<DecomposableFunctionType>::ToString() const
{
  std::ostringstream convert;
  convert << "SGD [" << this << "]" << std::endl;
  convert << "  Function:" << std::endl;
  convert << util::Indent(function.ToString(), 2);
  convert << "  Step size: " << stepSize << std::endl;
  convert << "  Maximum iterations: " << maxIterations << std::endl;
  convert << "  Tolerance: " << tolerance << std::endl;
  convert << "  Shuffle points: " << (shuffle ? "true" : "false") << std::endl;
  return convert.str();
}

}; // namespace optimization
}; // namespace mlpack

#endif
<commit_msg>Use easier-to-understand isnan().<commit_after>/**
 * @file sgd_impl.hpp
 * @author Ryan Curtin
 *
 * Implementation of stochastic gradient descent.
 */
#ifndef __MLPACK_CORE_OPTIMIZERS_SGD_SGD_IMPL_HPP
#define __MLPACK_CORE_OPTIMIZERS_SGD_SGD_IMPL_HPP

#include <mlpack/methods/regularized_svd/regularized_svd_function.hpp>
// In case it hasn't been included yet.
#include "sgd.hpp"

namespace mlpack {
namespace optimization {

template<typename DecomposableFunctionType>
SGD<DecomposableFunctionType>::SGD(DecomposableFunctionType& function,
                                   const double stepSize,
                                   const size_t maxIterations,
                                   const double tolerance,
                                   const bool shuffle) :
    function(function),
    stepSize(stepSize),
    maxIterations(maxIterations),
    tolerance(tolerance),
    shuffle(shuffle)
{ /* Nothing to do. */ }

//! Optimize the function (minimize).
template<typename DecomposableFunctionType>
double SGD<DecomposableFunctionType>::Optimize(arma::mat& iterate)
{
  // Find the number of functions to use.
  const size_t numFunctions = function.NumFunctions();

  // This is used only if shuffle is true.
  arma::vec visitationOrder;
  if (shuffle)
    visitationOrder = arma::shuffle(arma::linspace(0, (numFunctions - 1),
        numFunctions));

  // To keep track of where we are and how things are going.
  size_t currentFunction = 0;
  double overallObjective = 0;
  double lastObjective = DBL_MAX;

  // Calculate the first objective function.
  for (size_t i = 0; i < numFunctions; ++i)
    overallObjective += function.Evaluate(iterate, i);

  // Now iterate!
  arma::mat gradient(iterate.n_rows, iterate.n_cols);
  for (size_t i = 1; i != maxIterations; ++i, ++currentFunction)
  {
    // Is this iteration the start of a sequence?
    if ((currentFunction % numFunctions) == 0)
    {
      // Output current objective function.
      Log::Info << "SGD: iteration " << i << ", objective " << overallObjective
          << "." << std::endl;

      if (std::isnan(overallObjective))
      {
        Log::Warn << "SGD: converged to " << overallObjective << "; terminating"
            << " with failure.  Try a smaller step size?" << std::endl;
        return overallObjective;
      }

      if (std::abs(lastObjective - overallObjective) < tolerance)
      {
        Log::Info << "SGD: minimized within tolerance " << tolerance << "; "
            << "terminating optimization." << std::endl;
        return overallObjective;
      }

      // Reset the counter variables.
      lastObjective = overallObjective;
      overallObjective = 0;
      currentFunction = 0;

      if (shuffle) // Determine order of visitation.
        visitationOrder = arma::shuffle(visitationOrder);
    }

    // Evaluate the gradient for this iteration.
    if (shuffle)
      function.Gradient(iterate, visitationOrder[currentFunction], gradient);
    else
      function.Gradient(iterate, currentFunction, gradient);

    // And update the iterate.
    iterate -= stepSize * gradient;

    // Now add that to the overall objective function.
    if (shuffle)
      overallObjective += function.Evaluate(iterate,
          visitationOrder[currentFunction]);
    else
      overallObjective += function.Evaluate(iterate, currentFunction);
  }

  Log::Info << "SGD: maximum iterations (" << maxIterations << ") reached; "
      << "terminating optimization." << std::endl;
  // Calculate final objective.
  overallObjective = 0;
  for (size_t i = 0; i < numFunctions; ++i)
    overallObjective += function.Evaluate(iterate, i);
  return overallObjective;
}

// Convert the object to a string.
template<typename DecomposableFunctionType>
std::string SGD<DecomposableFunctionType>::ToString() const
{
  std::ostringstream convert;
  convert << "SGD [" << this << "]" << std::endl;
  convert << "  Function:" << std::endl;
  convert << util::Indent(function.ToString(), 2);
  convert << "  Step size: " << stepSize << std::endl;
  convert << "  Maximum iterations: " << maxIterations << std::endl;
  convert << "  Tolerance: " << tolerance << std::endl;
  convert << "  Shuffle points: " << (shuffle ? "true" : "false") << std::endl;
  return convert.str();
}

}; // namespace optimization
}; // namespace mlpack

#endif
<|endoftext|>


<commit_before>/**
 * @file	src/core/Dispatcher.cpp
 * @date	Feb. 2016
 * @author	PhRG - opticalp.fr
 */

/*
 Copyright (c) 2016 Ph. Renaud-Goud / Opticalp

 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.
 */

#include "Dispatcher.h"

#include "ModuleManager.h"
#include "ThreadManager.h"
#include "Module.h"
#include "ModuleTask.h"
#include "DataLogger.h"
#include "DataProxy.h"
#include "InPort.h"
#include "InDataPort.h"
#include "OutPort.h"
#include "DataSource.h"

#include "ModuleCanceller.h"

#include "Poco/NumberFormatter.h"

Dispatcher::Dispatcher():
    VerboseEntity(name()),
    initialized(false),
    emptyOutPort(),
    emptyInPort(),
    emptyTrigPort()
{
    // nothing else to do
}

Dispatcher::~Dispatcher()
{
    if (initialized)
        uninitialize();
}

void Dispatcher::initialize(Poco::Util::Application& app)
{
    if (initialized)
    {
        poco_error(logger(),"The dispatcher is already initialized, "
                "please uninitialize first");
    }

    setLogger(name());

    std::vector< SharedPtr<Module*> > modules;
    modules = Poco::Util::Application::instance().getSubsystem<ModuleManager>().getModules();

    initialized = true;

    for (std::vector< SharedPtr<Module*> >::iterator it=modules.begin(), ite=modules.end();
            it!=ite; it++)
        addModule(*it);

}

void Dispatcher::uninitialize()
{
    initialized = false;

    poco_information(logger(), "Dispatcher uninit...");

    // TODO: data man un-init

    ThreadManager& threadMan = Poco::Util::Application::instance()
                                        .getSubsystem<ThreadManager>();
    size_t taskCnt = threadMan.count();
    if (taskCnt)
        poco_warning(logger(), "Dispatcher uninit: "
            + Poco::NumberFormatter::format(taskCnt)
            + " task(s) to stop");

    threadMan.cancelAll();
    threadMan.waitAll();

    inPortsLock.writeLock();
    outPortsLock.writeLock();

//    poco_information(logger(), "remove dispatcher-stored input ports");

    while( allInPorts.size() )
        removeInPort(*allInPorts.back());

//    poco_information(logger(), "remove dispatcher-stored output ports");

    while( allOutPorts.size() )
        removeOutPort(*allOutPorts.back());

    inPortsLock.unlock();
    outPortsLock.unlock();

    poco_information(logger(), "Dispatcher uninit OK. Locks released");
}

void Dispatcher::resetWorkflow()
{
    if (!initialized)
        throw Poco::RuntimeException("resetWorkflow",
                "The dispatcher is not initialized");

    {
        Poco::ScopedReadRWLock lockIn(inPortsLock);
        for (std::vector< SharedPtr<InPort*> >::iterator it=allInPorts.begin(), ite=allInPorts.end();
                it!=ite; it++)
        {
            unbind(**it);
            seqUnbind(**it);
        }
    }

    {
        Poco::ScopedReadRWLock lockOut(outPortsLock);
        for (std::vector< SharedPtr<OutPort*> >::iterator it=allOutPorts.begin(), ite=allOutPorts.end();
                it!=ite; it++)
        {
            unbind(**it);
            seqUnbind(**it);
        }
    }
}

void Dispatcher::addModule(SharedPtr<Module*> module)
{
    if (!initialized)
    {
        // poco_information(logger(),
        //         "module insertion in the dispatcher: not initialized");
        return;
    }

    // n.b. the module is "locked" in the way that it cannot change
    // its connections, as long as the connection changes come from the
    // dispatcher, and the dispatcher has a write lock on the inPorts
    // and the outPorts.

    inPortsLock.writeLock();
    outPortsLock.writeLock();

    std::vector<InPort*> inPorts((*module)->getInPorts());
    std::vector<OutPort*> outPorts((*module)->getOutPorts());

    for (std::vector<InPort*>::iterator it = inPorts.begin(),
            ite = inPorts.end() ; it != ite ; it++)
        addInPort(*it);
    for (std::vector<OutPort*>::iterator it = outPorts.begin(),
            ite = outPorts.end() ; it != ite ; it++)
        addOutPort(*it);

    inPortsLock.unlock();
    outPortsLock.unlock();

    // poco_information(logger(),"module " + (*module)->name() + " added in the dispatcher");
}

void Dispatcher::removeModule(SharedPtr<Module*> module)
{
    poco_information(logger(),"removing module " + (*module)->name());

    if (!initialized)
    {
        // poco_information(logger(),
        //         "module removal from the dispatcher: not initialized");
        return;
    }

    inPortsLock.writeLock();
    outPortsLock.writeLock();

    std::vector<InPort*> inPorts((*module)->getInPorts());
    std::vector<OutPort*> outPorts((*module)->getOutPorts());

    // remove the input ports first in order to break the connections
    for (std::vector<InPort*>::reverse_iterator it = inPorts.rbegin(),
            ite = inPorts.rend() ; it != ite ; it++)
        removeInPort(*it);
    for (std::vector<OutPort*>::reverse_iterator it = outPorts.rbegin(),
            ite = outPorts.rend() ; it != ite ; it++)
        removeOutPort(*it);

    inPortsLock.unlock();
    outPortsLock.unlock();
}

SharedPtr<InPort*> Dispatcher::getInPort(InPort* port)
{
    if (!initialized)
        throw Poco::RuntimeException("getInPort",
                "The dispatcher is not initialized");

    inPortsLock.readLock();
    for (std::vector< SharedPtr<InPort*> >::iterator it=allInPorts.begin(), ite=allInPorts.end();
            it!=ite; it++)
    {
        if (port==**it)
        {
            inPortsLock.unlock();
            return *it;
        }
    }

    inPortsLock.unlock();
    throw Poco::NotFoundException("getInPort", "port not found: "
            "Should have been deleted during the query");
}

SharedPtr<OutPort*> Dispatcher::getOutPort(OutPort* port)
{
    if (!initialized)
        throw Poco::RuntimeException("getInPort",
                "The dispatcher is not initialized");

    outPortsLock.readLock();
    for (std::vector< SharedPtr<OutPort*> >::iterator it=allOutPorts.begin(), ite=allOutPorts.end();
            it!=ite; it++)
    {
        if (port==**it)
        {
            outPortsLock.unlock();
            return *it;
        }
    }

    outPortsLock.unlock();
    throw Poco::NotFoundException("getOutPort", "port not found: "
            "Should have been deleted during the query");
}

void Dispatcher::removeInPort(InPort* port)
{
    // using a reverse iterator to improve performance when called
    // from the uninitializer
    for (std::vector< SharedPtr<InPort*> >::reverse_iterator it = allInPorts.rbegin(),
            ite = allInPorts.rend(); it!=ite; it++)
    {
        if (port == **it)
        {
            unbind(port);
            seqUnbind(port);

            // replace the pointed port by something throwing exceptions
            if ((**it)->isTrig())
                **it = &emptyTrigPort;
            else
                **it = &emptyInPort;

            allInPorts.erase((it+1).base());
            // poco_information(logger(),
            //         port->name() + " input port from module "
            //         + port->parent()->name() + " is erased "
            //                 "from Dispatcher::allInPorts. ");
            return;
        }
    }

    poco_error(logger(), "removeInPort(): "
            "the port was not found");
}

void Dispatcher::addInPort(InPort* port)
{
    // poco_information(logger(),"adding port " + port->name()
    //         + " from module " + port->parent()->name());
    allInPorts.push_back(SharedPtr<InPort*>(new (InPort*)(port)));
}

void Dispatcher::removeOutPort(OutPort* port)
{
    // using a reverse iterator to improve performance when called
    // from the uninitializer
    for (std::vector< SharedPtr<OutPort*> >::reverse_iterator it = allOutPorts.rbegin(),
            ite = allOutPorts.rend(); it!=ite; it++)
    {
        if (port == **it)
        {
            unbind(port);
            seqUnbind(port);

            **it = &emptyOutPort; // replace the pointed factory by something throwing exceptions
            allOutPorts.erase((it+1).base());
//            poco_information(logger(),
//                     port->name() + " output port from module "
//                     + port->parent()->name() + " is erased "
//                             "from Dispatcher::allInPorts. ");
            return;
        }
    }

    poco_error(logger(), "removeOutPort(): "
            "the port was not found");
}

void Dispatcher::addOutPort(OutPort* port)
{
    // poco_information(logger(),"adding port " + port->name()
    //         + " from module " + port->parent()->name());
    allOutPorts.push_back(SharedPtr<OutPort*>(new (OutPort*)(port)));
}

void Dispatcher::bind(DataSource* source, DataTarget* target)
{
//	poco_information(logger(), "bind source " + source->name()
//			+ " to target " + target->name());
	target->setDataSource(source);
}

void Dispatcher::unbind(DataTarget* target)
{
//	poco_information(logger(), "unbinding target: " + target->name());

    try
    {
        DataProxy* proxy = dynamic_cast<DataProxy*>(target->getDataSource());
        if (proxy)
            unbind(static_cast<DataTarget*>(proxy));

        target->detachDataSource();
    }
    catch (Poco::NullPointerException&)
    {
//        poco_information(logger(), "unbinding target: " + target->name()
//                + " has no source");
    }
}

void Dispatcher::unbind(DataSource* source)
{
	std::set<DataTarget*> targets = source->getDataTargets();

//	poco_information(logger(), "unbinding source: " + source->name());

	while (targets.size())
	{
		std::set<DataTarget*>::iterator it = targets.begin();
		(*it)->incUser();
        unbind(*it);

        DataProxy* proxy = dynamic_cast<DataProxy*>(*it);
		if (proxy)
		    unbind(static_cast<DataSource*>(proxy));

        (*it)->decUser();
		targets.erase(it);
	}
}

void Dispatcher::seqBind(SeqSource* source, SeqTarget* target)
{
    target->setSeqSource(source);
}

void Dispatcher::seqUnbind(SeqTarget* target)
{
	target->detachSeqSource();
}

void Dispatcher::seqUnbind(SeqSource* source)
{
	std::set<SeqTarget*> targets = source->getSeqTargets();

	while (targets.size())
	{
		std::set<SeqTarget*>::iterator it = targets.begin();
		seqUnbind(*it);
		targets.erase(it);
	}
}

void Dispatcher::setOutputDataReady(DataSource* source)
{
//    poco_information(logger(), port->name() + " data ready");

	OutPort* tmpOut = NULL;
	if (logger().information())
		tmpOut = dynamic_cast<OutPort*>(source);

	std::set<DataTarget*> targets = source->getDataTargets();
    for ( std::set<DataTarget*>::iterator it = targets.begin(),
            ite = targets.end(); it != ite; it++ )
    {
    	// append to source -> pendingDataTargets
    	try
    	{
    		source->registerPendingTarget(*it);
    	}
    	catch (ExecutionAbortedException& exc)
    	{
    		poco_error(logger(), (*it)->name()
    				+ " can not be started: "
					+ exc.displayText());
    		throw;
    	}

        if (logger().information())
        {
			// modules
			InPort* tmpPort = dynamic_cast<InPort*>(*it);
			if (tmpPort)
			{
				if (tmpOut)
					logger().information(tmpOut->parent()->name()
						+ " port " + source->name()
						+ " STARTS " + tmpPort->parent()->name() );
				else
					logger().information(source->name()
						+ " STARTS " + tmpPort->parent()->name() );
			}
        }

        // nb: tryRunTarget handles the source release in case of
        // false return
        if (!(*it)->tryRunTarget())
            poco_warning(logger(), (*it)->name()
                    + " can not be started by "
                    + source->name());
    }
}

void Dispatcher::dispatchTargetCancel(DataSource* source)
{
	std::set<DataTarget*> targets = source->getDataTargets();
    for ( std::set<DataTarget*>::iterator it = targets.begin(),
            ite = targets.end(); it != ite; it++ )
    	(*it)->cancelFromSource(source);
}

void Dispatcher::dispatchTargetWaitCancelled(DataSource* source)
{
	std::set<DataTarget*> targets = source->getDataTargets();
    for ( std::set<DataTarget*>::iterator it = targets.begin(),
            ite = targets.end(); it != ite; it++ )
    	(*it)->waitCancelledFromSource(source);
}

void Dispatcher::dispatchTargetReset(DataSource* source)
{
	std::set<DataTarget*> targets = source->getDataTargets();
    for ( std::set<DataTarget*>::iterator it = targets.begin(),
            ite = targets.end(); it != ite; it++ )
    	(*it)->resetFromSource(source);
}

void Dispatcher::cancel(Module* module)
{
	if ( (module == NULL)
	       || (module == Poco::Util::Application::instance()
							.getSubsystem<ModuleManager>()
							.getEmptyModule() )  )
		return;

	// Called by ModuleTask::moduleCancel
	//  * Immediate cancel the given module
	//  * Handle the waitCancelled and reseting process

	if (module->immediateCancel())
	{
        // async waitCancelled and reset
        ModuleCanceller* listener = new ModuleCanceller(module);

        poco_information(logger(), module->name() + " async wait cancelled");
        Poco::Util::Application::instance()
                .getSubsystem<ThreadManager>()
                .startRunnable(*listener);
	}
}
<commit_msg>resetWorkflow: unbind connexions from parameter workers too<commit_after>/**
 * @file	src/core/Dispatcher.cpp
 * @date	Feb. 2016
 * @author	PhRG - opticalp.fr
 */

/*
 Copyright (c) 2016 Ph. Renaud-Goud / Opticalp

 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.
 */

#include "Dispatcher.h"

#include "ModuleManager.h"
#include "ThreadManager.h"
#include "Module.h"
#include "ModuleTask.h"
#include "DataLogger.h"
#include "DataProxy.h"
#include "InPort.h"
#include "InDataPort.h"
#include "OutPort.h"
#include "DataSource.h"

#include "ModuleCanceller.h"

#include "Poco/NumberFormatter.h"

Dispatcher::Dispatcher():
    VerboseEntity(name()),
    initialized(false),
    emptyOutPort(),
    emptyInPort(),
    emptyTrigPort()
{
    // nothing else to do
}

Dispatcher::~Dispatcher()
{
    if (initialized)
        uninitialize();
}

void Dispatcher::initialize(Poco::Util::Application& app)
{
    if (initialized)
    {
        poco_error(logger(),"The dispatcher is already initialized, "
                "please uninitialize first");
    }

    setLogger(name());

    std::vector< SharedPtr<Module*> > modules;
    modules = Poco::Util::Application::instance().getSubsystem<ModuleManager>().getModules();

    initialized = true;

    for (std::vector< SharedPtr<Module*> >::iterator it=modules.begin(), ite=modules.end();
            it!=ite; it++)
        addModule(*it);

}

void Dispatcher::uninitialize()
{
    initialized = false;

    poco_information(logger(), "Dispatcher uninit...");

    // TODO: data man un-init

    ThreadManager& threadMan = Poco::Util::Application::instance()
                                        .getSubsystem<ThreadManager>();
    size_t taskCnt = threadMan.count();
    if (taskCnt)
        poco_warning(logger(), "Dispatcher uninit: "
            + Poco::NumberFormatter::format(taskCnt)
            + " task(s) to stop");

    threadMan.cancelAll();
    threadMan.waitAll();

    inPortsLock.writeLock();
    outPortsLock.writeLock();

//    poco_information(logger(), "remove dispatcher-stored input ports");

    while( allInPorts.size() )
        removeInPort(*allInPorts.back());

//    poco_information(logger(), "remove dispatcher-stored output ports");

    while( allOutPorts.size() )
        removeOutPort(*allOutPorts.back());

    inPortsLock.unlock();
    outPortsLock.unlock();

    poco_information(logger(), "Dispatcher uninit OK. Locks released");
}

void Dispatcher::resetWorkflow()
{
    if (!initialized)
        throw Poco::RuntimeException("resetWorkflow",
                "The dispatcher is not initialized");

    {
        Poco::ScopedReadRWLock lockIn(inPortsLock);
        for (std::vector< SharedPtr<InPort*> >::iterator it=allInPorts.begin(), ite=allInPorts.end();
                it!=ite; it++)
        {
            unbind(**it);
            seqUnbind(**it);
        }
    }

    {
        Poco::ScopedReadRWLock lockOut(outPortsLock);
        for (std::vector< SharedPtr<OutPort*> >::iterator it=allOutPorts.begin(), ite=allOutPorts.end();
                it!=ite; it++)
        {
            unbind(**it);
            seqUnbind(**it);
        }
    }

    // module param setters and getters
    std::vector< SharedPtr<Module*> > modules;
    modules = Poco::Util::Application::instance().getSubsystem<ModuleManager>().getModules();

    for (std::vector< SharedPtr<Module*> >::iterator it=modules.begin(), ite=modules.end();
            it!=ite; it++)
    {
        // check for parameter workers
        std::set< Poco::AutoPtr<ParameterGetter> > paramGetters = (**it)->getParameterGetters();
        for (std::set< Poco::AutoPtr<ParameterGetter> >::iterator git = paramGetters.begin(),
                gite = paramGetters.end(); git != gite; git++)
        {
            unbind(static_cast<DataSource*>(const_cast<ParameterGetter*>(git->get())));
            unbind(static_cast<DataTarget*>(const_cast<ParameterGetter*>(git->get())));
        }

        std::set< Poco::AutoPtr<ParameterSetter> > paramSetters = (**it)->getParameterSetters();
        for (std::set< Poco::AutoPtr<ParameterSetter> >::iterator sit = paramSetters.begin(),
                site = paramSetters.end(); sit != site; sit++)
        {
            unbind(static_cast<DataTarget*>(const_cast<ParameterSetter*>(sit->get())));
        }
    }
}

void Dispatcher::addModule(SharedPtr<Module*> module)
{
    if (!initialized)
    {
        // poco_information(logger(),
        //         "module insertion in the dispatcher: not initialized");
        return;
    }

    // n.b. the module is "locked" in the way that it cannot change
    // its connections, as long as the connection changes come from the
    // dispatcher, and the dispatcher has a write lock on the inPorts
    // and the outPorts.

    inPortsLock.writeLock();
    outPortsLock.writeLock();

    std::vector<InPort*> inPorts((*module)->getInPorts());
    std::vector<OutPort*> outPorts((*module)->getOutPorts());

    for (std::vector<InPort*>::iterator it = inPorts.begin(),
            ite = inPorts.end() ; it != ite ; it++)
        addInPort(*it);
    for (std::vector<OutPort*>::iterator it = outPorts.begin(),
            ite = outPorts.end() ; it != ite ; it++)
        addOutPort(*it);

    inPortsLock.unlock();
    outPortsLock.unlock();

    // poco_information(logger(),"module " + (*module)->name() + " added in the dispatcher");
}

void Dispatcher::removeModule(SharedPtr<Module*> module)
{
    poco_information(logger(),"removing module " + (*module)->name());

    if (!initialized)
    {
        // poco_information(logger(),
        //         "module removal from the dispatcher: not initialized");
        return;
    }

    inPortsLock.writeLock();
    outPortsLock.writeLock();

    std::vector<InPort*> inPorts((*module)->getInPorts());
    std::vector<OutPort*> outPorts((*module)->getOutPorts());

    // remove the input ports first in order to break the connections
    for (std::vector<InPort*>::reverse_iterator it = inPorts.rbegin(),
            ite = inPorts.rend() ; it != ite ; it++)
        removeInPort(*it);
    for (std::vector<OutPort*>::reverse_iterator it = outPorts.rbegin(),
            ite = outPorts.rend() ; it != ite ; it++)
        removeOutPort(*it);

    inPortsLock.unlock();
    outPortsLock.unlock();
}

SharedPtr<InPort*> Dispatcher::getInPort(InPort* port)
{
    if (!initialized)
        throw Poco::RuntimeException("getInPort",
                "The dispatcher is not initialized");

    inPortsLock.readLock();
    for (std::vector< SharedPtr<InPort*> >::iterator it=allInPorts.begin(), ite=allInPorts.end();
            it!=ite; it++)
    {
        if (port==**it)
        {
            inPortsLock.unlock();
            return *it;
        }
    }

    inPortsLock.unlock();
    throw Poco::NotFoundException("getInPort", "port not found: "
            "Should have been deleted during the query");
}

SharedPtr<OutPort*> Dispatcher::getOutPort(OutPort* port)
{
    if (!initialized)
        throw Poco::RuntimeException("getInPort",
                "The dispatcher is not initialized");

    outPortsLock.readLock();
    for (std::vector< SharedPtr<OutPort*> >::iterator it=allOutPorts.begin(), ite=allOutPorts.end();
            it!=ite; it++)
    {
        if (port==**it)
        {
            outPortsLock.unlock();
            return *it;
        }
    }

    outPortsLock.unlock();
    throw Poco::NotFoundException("getOutPort", "port not found: "
            "Should have been deleted during the query");
}

void Dispatcher::removeInPort(InPort* port)
{
    // using a reverse iterator to improve performance when called
    // from the uninitializer
    for (std::vector< SharedPtr<InPort*> >::reverse_iterator it = allInPorts.rbegin(),
            ite = allInPorts.rend(); it!=ite; it++)
    {
        if (port == **it)
        {
            unbind(port);
            seqUnbind(port);

            // replace the pointed port by something throwing exceptions
            if ((**it)->isTrig())
                **it = &emptyTrigPort;
            else
                **it = &emptyInPort;

            allInPorts.erase((it+1).base());
            // poco_information(logger(),
            //         port->name() + " input port from module "
            //         + port->parent()->name() + " is erased "
            //                 "from Dispatcher::allInPorts. ");
            return;
        }
    }

    poco_error(logger(), "removeInPort(): "
            "the port was not found");
}

void Dispatcher::addInPort(InPort* port)
{
    // poco_information(logger(),"adding port " + port->name()
    //         + " from module " + port->parent()->name());
    allInPorts.push_back(SharedPtr<InPort*>(new (InPort*)(port)));
}

void Dispatcher::removeOutPort(OutPort* port)
{
    // using a reverse iterator to improve performance when called
    // from the uninitializer
    for (std::vector< SharedPtr<OutPort*> >::reverse_iterator it = allOutPorts.rbegin(),
            ite = allOutPorts.rend(); it!=ite; it++)
    {
        if (port == **it)
        {
            unbind(port);
            seqUnbind(port);

            **it = &emptyOutPort; // replace the pointed factory by something throwing exceptions
            allOutPorts.erase((it+1).base());
//            poco_information(logger(),
//                     port->name() + " output port from module "
//                     + port->parent()->name() + " is erased "
//                             "from Dispatcher::allInPorts. ");
            return;
        }
    }

    poco_error(logger(), "removeOutPort(): "
            "the port was not found");
}

void Dispatcher::addOutPort(OutPort* port)
{
    // poco_information(logger(),"adding port " + port->name()
    //         + " from module " + port->parent()->name());
    allOutPorts.push_back(SharedPtr<OutPort*>(new (OutPort*)(port)));
}

void Dispatcher::bind(DataSource* source, DataTarget* target)
{
//	poco_information(logger(), "bind source " + source->name()
//			+ " to target " + target->name());
	target->setDataSource(source);
}

void Dispatcher::unbind(DataTarget* target)
{
//	poco_information(logger(), "unbinding target: " + target->name());

    try
    {
        DataProxy* proxy = dynamic_cast<DataProxy*>(target->getDataSource());
        if (proxy)
            unbind(static_cast<DataTarget*>(proxy));

        target->detachDataSource();
    }
    catch (Poco::NullPointerException&)
    {
//        poco_information(logger(), "unbinding target: " + target->name()
//                + " has no source");
    }
}

void Dispatcher::unbind(DataSource* source)
{
	std::set<DataTarget*> targets = source->getDataTargets();

//	poco_information(logger(), "unbinding source: " + source->name());

	while (targets.size())
	{
		std::set<DataTarget*>::iterator it = targets.begin();
		(*it)->incUser();
        unbind(*it);

        DataProxy* proxy = dynamic_cast<DataProxy*>(*it);
		if (proxy)
		    unbind(static_cast<DataSource*>(proxy));

        (*it)->decUser();
		targets.erase(it);
	}
}

void Dispatcher::seqBind(SeqSource* source, SeqTarget* target)
{
    target->setSeqSource(source);
}

void Dispatcher::seqUnbind(SeqTarget* target)
{
	target->detachSeqSource();
}

void Dispatcher::seqUnbind(SeqSource* source)
{
	std::set<SeqTarget*> targets = source->getSeqTargets();

	while (targets.size())
	{
		std::set<SeqTarget*>::iterator it = targets.begin();
		seqUnbind(*it);
		targets.erase(it);
	}
}

void Dispatcher::setOutputDataReady(DataSource* source)
{
//    poco_information(logger(), port->name() + " data ready");

	OutPort* tmpOut = NULL;
	if (logger().information())
		tmpOut = dynamic_cast<OutPort*>(source);

	std::set<DataTarget*> targets = source->getDataTargets();
    for ( std::set<DataTarget*>::iterator it = targets.begin(),
            ite = targets.end(); it != ite; it++ )
    {
    	// append to source -> pendingDataTargets
    	try
    	{
    		source->registerPendingTarget(*it);
    	}
    	catch (ExecutionAbortedException& exc)
    	{
    		poco_error(logger(), (*it)->name()
    				+ " can not be started: "
					+ exc.displayText());
    		throw;
    	}

        if (logger().information())
        {
			// modules
			InPort* tmpPort = dynamic_cast<InPort*>(*it);
			if (tmpPort)
			{
				if (tmpOut)
					logger().information(tmpOut->parent()->name()
						+ " port " + source->name()
						+ " STARTS " + tmpPort->parent()->name() );
				else
					logger().information(source->name()
						+ " STARTS " + tmpPort->parent()->name() );
			}
        }

        // nb: tryRunTarget handles the source release in case of
        // false return
        if (!(*it)->tryRunTarget())
            poco_warning(logger(), (*it)->name()
                    + " can not be started by "
                    + source->name());
    }
}

void Dispatcher::dispatchTargetCancel(DataSource* source)
{
	std::set<DataTarget*> targets = source->getDataTargets();
    for ( std::set<DataTarget*>::iterator it = targets.begin(),
            ite = targets.end(); it != ite; it++ )
    	(*it)->cancelFromSource(source);
}

void Dispatcher::dispatchTargetWaitCancelled(DataSource* source)
{
	std::set<DataTarget*> targets = source->getDataTargets();
    for ( std::set<DataTarget*>::iterator it = targets.begin(),
            ite = targets.end(); it != ite; it++ )
    	(*it)->waitCancelledFromSource(source);
}

void Dispatcher::dispatchTargetReset(DataSource* source)
{
	std::set<DataTarget*> targets = source->getDataTargets();
    for ( std::set<DataTarget*>::iterator it = targets.begin(),
            ite = targets.end(); it != ite; it++ )
    	(*it)->resetFromSource(source);
}

void Dispatcher::cancel(Module* module)
{
	if ( (module == NULL)
	       || (module == Poco::Util::Application::instance()
							.getSubsystem<ModuleManager>()
							.getEmptyModule() )  )
		return;

	// Called by ModuleTask::moduleCancel
	//  * Immediate cancel the given module
	//  * Handle the waitCancelled and reseting process

	if (module->immediateCancel())
	{
        // async waitCancelled and reset
        ModuleCanceller* listener = new ModuleCanceller(module);

        poco_information(logger(), module->name() + " async wait cancelled");
        Poco::Util::Application::instance()
                .getSubsystem<ThreadManager>()
                .startRunnable(*listener);
	}
}
<|endoftext|>