<commit_before>/*
 * libjingle
 * Copyright 2004--2011, Google Inc.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  1. Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *  2. 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.
 *  3. The name of the author may not be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <signal.h>
#include <stdarg.h>

#include "talk/base/gunit.h"
#include "talk/base/logging.h"
#include "talk/base/physicalsocketserver.h"
#include "talk/base/scoped_ptr.h"
#include "talk/base/socket_unittest.h"
#include "talk/base/testutils.h"
#include "talk/base/thread.h"

namespace talk_base {

class PhysicalSocketTest : public SocketTest {
};

TEST_F(PhysicalSocketTest, TestConnectIPv4) {
  SocketTest::TestConnectIPv4();
}

TEST_F(PhysicalSocketTest, TestConnectIPv6) {
  SocketTest::TestConnectIPv6();
}

TEST_F(PhysicalSocketTest, TestConnectWithDnsLookupIPv4) {
  SocketTest::TestConnectWithDnsLookupIPv4();
}

TEST_F(PhysicalSocketTest, TestConnectWithDnsLookupIPv6) {
  SocketTest::TestConnectWithDnsLookupIPv6();
}

TEST_F(PhysicalSocketTest, TestConnectFailIPv4) {
  SocketTest::TestConnectFailIPv4();
}

TEST_F(PhysicalSocketTest, TestConnectFailIPv6) {
  SocketTest::TestConnectFailIPv6();
}

TEST_F(PhysicalSocketTest, TestConnectWithDnsLookupFailIPv4) {
  SocketTest::TestConnectWithDnsLookupFailIPv4();
}

TEST_F(PhysicalSocketTest, TestConnectWithDnsLookupFailIPv6) {
  SocketTest::TestConnectWithDnsLookupFailIPv6();
}

TEST_F(PhysicalSocketTest, TestConnectWithClosedSocketIPv4) {
  SocketTest::TestConnectWithClosedSocketIPv4();
}

TEST_F(PhysicalSocketTest, TestConnectWithClosedSocketIPv6) {
  SocketTest::TestConnectWithClosedSocketIPv6();
}

TEST_F(PhysicalSocketTest, TestConnectWhileNotClosedIPv4) {
  SocketTest::TestConnectWhileNotClosedIPv4();
}

TEST_F(PhysicalSocketTest, TestConnectWhileNotClosedIPv6) {
  SocketTest::TestConnectWhileNotClosedIPv6();
}

TEST_F(PhysicalSocketTest, TestServerCloseDuringConnectIPv4) {
  SocketTest::TestServerCloseDuringConnectIPv4();
}

TEST_F(PhysicalSocketTest, TestServerCloseDuringConnectIPv6) {
  SocketTest::TestServerCloseDuringConnectIPv6();
}

TEST_F(PhysicalSocketTest, TestClientCloseDuringConnectIPv4) {
  SocketTest::TestClientCloseDuringConnectIPv4();
}

TEST_F(PhysicalSocketTest, TestClientCloseDuringConnectIPv6) {
  SocketTest::TestClientCloseDuringConnectIPv6();
}

TEST_F(PhysicalSocketTest, TestServerCloseIPv4) {
  SocketTest::TestServerCloseIPv4();
}

TEST_F(PhysicalSocketTest, TestServerCloseIPv6) {
  SocketTest::TestServerCloseIPv6();
}

TEST_F(PhysicalSocketTest, TestCloseInClosedCallbackIPv4) {
  SocketTest::TestCloseInClosedCallbackIPv4();
}

TEST_F(PhysicalSocketTest, TestCloseInClosedCallbackIPv6) {
  SocketTest::TestCloseInClosedCallbackIPv6();
}

TEST_F(PhysicalSocketTest, TestSocketServerWaitIPv4) {
  SocketTest::TestSocketServerWaitIPv4();
}

TEST_F(PhysicalSocketTest, TestSocketServerWaitIPv6) {
  SocketTest::TestSocketServerWaitIPv6();
}

TEST_F(PhysicalSocketTest, TestTcpIPv4) {
  SocketTest::TestTcpIPv4();
}

TEST_F(PhysicalSocketTest, TestTcpIPv6) {
  SocketTest::TestTcpIPv6();
}

TEST_F(PhysicalSocketTest, TestUdpIPv4) {
  SocketTest::TestUdpIPv4();
}

TEST_F(PhysicalSocketTest, TestUdpIPv6) {
  SocketTest::TestUdpIPv6();
}

TEST_F(PhysicalSocketTest, TestUdpReadyToSendIPv4) {
  SocketTest::TestUdpReadyToSendIPv4();
}

TEST_F(PhysicalSocketTest, TestUdpReadyToSendIPv6) {
  SocketTest::TestUdpReadyToSendIPv6();
}

TEST_F(PhysicalSocketTest, TestGetSetOptionsIPv4) {
  SocketTest::TestGetSetOptionsIPv4();
}

TEST_F(PhysicalSocketTest, TestGetSetOptionsIPv6) {
  SocketTest::TestGetSetOptionsIPv6();
}

#ifdef POSIX

class PosixSignalDeliveryTest : public testing::Test {
 public:
  static void RecordSignal(int signum) {
    signals_received_.push_back(signum);
    signaled_thread_ = Thread::Current();
  }

 protected:
  void SetUp() {
    ss_.reset(new PhysicalSocketServer());
  }

  void TearDown() {
    ss_.reset(NULL);
    signals_received_.clear();
    signaled_thread_ = NULL;
  }

  bool ExpectSignal(int signum) {
    if (signals_received_.empty()) {
      LOG(LS_ERROR) << "ExpectSignal(): No signal received";
      return false;
    }
    if (signals_received_[0] != signum) {
      LOG(LS_ERROR) << "ExpectSignal(): Received signal " <<
          signals_received_[0] << ", expected " << signum;
      return false;
    }
    signals_received_.erase(signals_received_.begin());
    return true;
  }

  bool ExpectNone() {
    bool ret = signals_received_.empty();
    if (!ret) {
      LOG(LS_ERROR) << "ExpectNone(): Received signal " << signals_received_[0]
          << ", expected none";
    }
    return ret;
  }

  static std::vector<int> signals_received_;
  static Thread *signaled_thread_;

  scoped_ptr<PhysicalSocketServer> ss_;
};

std::vector<int> PosixSignalDeliveryTest::signals_received_;
Thread *PosixSignalDeliveryTest::signaled_thread_ = NULL;

// Test receiving a synchronous signal while not in Wait() and then entering
// Wait() afterwards.
TEST_F(PosixSignalDeliveryTest, RaiseThenWait) {
  ASSERT_TRUE(ss_->SetPosixSignalHandler(SIGTERM, &RecordSignal));
  raise(SIGTERM);
  EXPECT_TRUE(ss_->Wait(0, true));
  EXPECT_TRUE(ExpectSignal(SIGTERM));
  EXPECT_TRUE(ExpectNone());
}

// Test that we can handle getting tons of repeated signals and that we see all
// the different ones.
TEST_F(PosixSignalDeliveryTest, InsanelyManySignals) {
  ss_->SetPosixSignalHandler(SIGTERM, &RecordSignal);
  ss_->SetPosixSignalHandler(SIGINT, &RecordSignal);
  for (int i = 0; i < 10000; ++i) {
    raise(SIGTERM);
  }
  raise(SIGINT);
  EXPECT_TRUE(ss_->Wait(0, true));
  // Order will be lowest signal numbers first.
  EXPECT_TRUE(ExpectSignal(SIGINT));
  EXPECT_TRUE(ExpectSignal(SIGTERM));
  EXPECT_TRUE(ExpectNone());
}

// Test that a signal during a Wait() call is detected.
TEST_F(PosixSignalDeliveryTest, SignalDuringWait) {
  ss_->SetPosixSignalHandler(SIGALRM, &RecordSignal);
  alarm(1);
  EXPECT_TRUE(ss_->Wait(1500, true));
  EXPECT_TRUE(ExpectSignal(SIGALRM));
  EXPECT_TRUE(ExpectNone());
}

class RaiseSigTermRunnable : public Runnable {
  void Run(Thread *thread) {
    thread->socketserver()->Wait(1000, false);

    // Allow SIGTERM. This will be the only thread with it not masked so it will
    // be delivered to us.
    sigset_t mask;
    sigemptyset(&mask);
    pthread_sigmask(SIG_SETMASK, &mask, NULL);

    // Raise it.
    raise(SIGTERM);
  }
};

// Test that it works no matter what thread the kernel chooses to give the
// signal to (since it's not guaranteed to be the one that Wait() runs on).
TEST_F(PosixSignalDeliveryTest, SignalOnDifferentThread) {
  ss_->SetPosixSignalHandler(SIGTERM, &RecordSignal);
  // Mask out SIGTERM so that it can't be delivered to this thread.
  sigset_t mask;
  sigemptyset(&mask);
  sigaddset(&mask, SIGTERM);
  EXPECT_EQ(0, pthread_sigmask(SIG_SETMASK, &mask, NULL));
  // Start a new thread that raises it. It will have to be delivered to that
  // thread. Our implementation should safely handle it and dispatch
  // RecordSignal() on this thread.
  scoped_ptr<Thread> thread(new Thread());
  scoped_ptr<RaiseSigTermRunnable> runnable(new RaiseSigTermRunnable());
  thread->Start(runnable.get());
  EXPECT_TRUE(ss_->Wait(1500, true));
  EXPECT_TRUE(ExpectSignal(SIGTERM));
  EXPECT_EQ(Thread::Current(), signaled_thread_);
  EXPECT_TRUE(ExpectNone());
}

#endif

}  // namespace talk_base
<commit_msg>Disable PhysicalSocketTest.TestUdpReadyToSendIPv4 for TSAN2<commit_after>/*
 * libjingle
 * Copyright 2004--2011, Google Inc.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  1. Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *  2. 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.
 *  3. The name of the author may not be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <signal.h>
#include <stdarg.h>

#include "talk/base/gunit.h"
#include "talk/base/logging.h"
#include "talk/base/physicalsocketserver.h"
#include "talk/base/scoped_ptr.h"
#include "talk/base/socket_unittest.h"
#include "talk/base/testutils.h"
#include "talk/base/thread.h"

namespace talk_base {

class PhysicalSocketTest : public SocketTest {
};

TEST_F(PhysicalSocketTest, TestConnectIPv4) {
  SocketTest::TestConnectIPv4();
}

TEST_F(PhysicalSocketTest, TestConnectIPv6) {
  SocketTest::TestConnectIPv6();
}

TEST_F(PhysicalSocketTest, TestConnectWithDnsLookupIPv4) {
  SocketTest::TestConnectWithDnsLookupIPv4();
}

TEST_F(PhysicalSocketTest, TestConnectWithDnsLookupIPv6) {
  SocketTest::TestConnectWithDnsLookupIPv6();
}

TEST_F(PhysicalSocketTest, TestConnectFailIPv4) {
  SocketTest::TestConnectFailIPv4();
}

TEST_F(PhysicalSocketTest, TestConnectFailIPv6) {
  SocketTest::TestConnectFailIPv6();
}

TEST_F(PhysicalSocketTest, TestConnectWithDnsLookupFailIPv4) {
  SocketTest::TestConnectWithDnsLookupFailIPv4();
}

TEST_F(PhysicalSocketTest, TestConnectWithDnsLookupFailIPv6) {
  SocketTest::TestConnectWithDnsLookupFailIPv6();
}

TEST_F(PhysicalSocketTest, TestConnectWithClosedSocketIPv4) {
  SocketTest::TestConnectWithClosedSocketIPv4();
}

TEST_F(PhysicalSocketTest, TestConnectWithClosedSocketIPv6) {
  SocketTest::TestConnectWithClosedSocketIPv6();
}

TEST_F(PhysicalSocketTest, TestConnectWhileNotClosedIPv4) {
  SocketTest::TestConnectWhileNotClosedIPv4();
}

TEST_F(PhysicalSocketTest, TestConnectWhileNotClosedIPv6) {
  SocketTest::TestConnectWhileNotClosedIPv6();
}

TEST_F(PhysicalSocketTest, TestServerCloseDuringConnectIPv4) {
  SocketTest::TestServerCloseDuringConnectIPv4();
}

TEST_F(PhysicalSocketTest, TestServerCloseDuringConnectIPv6) {
  SocketTest::TestServerCloseDuringConnectIPv6();
}

TEST_F(PhysicalSocketTest, TestClientCloseDuringConnectIPv4) {
  SocketTest::TestClientCloseDuringConnectIPv4();
}

TEST_F(PhysicalSocketTest, TestClientCloseDuringConnectIPv6) {
  SocketTest::TestClientCloseDuringConnectIPv6();
}

TEST_F(PhysicalSocketTest, TestServerCloseIPv4) {
  SocketTest::TestServerCloseIPv4();
}

TEST_F(PhysicalSocketTest, TestServerCloseIPv6) {
  SocketTest::TestServerCloseIPv6();
}

TEST_F(PhysicalSocketTest, TestCloseInClosedCallbackIPv4) {
  SocketTest::TestCloseInClosedCallbackIPv4();
}

TEST_F(PhysicalSocketTest, TestCloseInClosedCallbackIPv6) {
  SocketTest::TestCloseInClosedCallbackIPv6();
}

TEST_F(PhysicalSocketTest, TestSocketServerWaitIPv4) {
  SocketTest::TestSocketServerWaitIPv4();
}

TEST_F(PhysicalSocketTest, TestSocketServerWaitIPv6) {
  SocketTest::TestSocketServerWaitIPv6();
}

TEST_F(PhysicalSocketTest, TestTcpIPv4) {
  SocketTest::TestTcpIPv4();
}

TEST_F(PhysicalSocketTest, TestTcpIPv6) {
  SocketTest::TestTcpIPv6();
}

TEST_F(PhysicalSocketTest, TestUdpIPv4) {
  SocketTest::TestUdpIPv4();
}

TEST_F(PhysicalSocketTest, TestUdpIPv6) {
  SocketTest::TestUdpIPv6();
}

// Disable for TSan v2, see
// https://code.google.com/p/webrtc/issues/detail?id=3498 for details.
#if !defined(THREAD_SANITIZER)

TEST_F(PhysicalSocketTest, TestUdpReadyToSendIPv4) {
  SocketTest::TestUdpReadyToSendIPv4();
}

#endif // if !defined(THREAD_SANITIZER)

TEST_F(PhysicalSocketTest, TestUdpReadyToSendIPv6) {
  SocketTest::TestUdpReadyToSendIPv6();
}

TEST_F(PhysicalSocketTest, TestGetSetOptionsIPv4) {
  SocketTest::TestGetSetOptionsIPv4();
}

TEST_F(PhysicalSocketTest, TestGetSetOptionsIPv6) {
  SocketTest::TestGetSetOptionsIPv6();
}

#ifdef POSIX

class PosixSignalDeliveryTest : public testing::Test {
 public:
  static void RecordSignal(int signum) {
    signals_received_.push_back(signum);
    signaled_thread_ = Thread::Current();
  }

 protected:
  void SetUp() {
    ss_.reset(new PhysicalSocketServer());
  }

  void TearDown() {
    ss_.reset(NULL);
    signals_received_.clear();
    signaled_thread_ = NULL;
  }

  bool ExpectSignal(int signum) {
    if (signals_received_.empty()) {
      LOG(LS_ERROR) << "ExpectSignal(): No signal received";
      return false;
    }
    if (signals_received_[0] != signum) {
      LOG(LS_ERROR) << "ExpectSignal(): Received signal " <<
          signals_received_[0] << ", expected " << signum;
      return false;
    }
    signals_received_.erase(signals_received_.begin());
    return true;
  }

  bool ExpectNone() {
    bool ret = signals_received_.empty();
    if (!ret) {
      LOG(LS_ERROR) << "ExpectNone(): Received signal " << signals_received_[0]
          << ", expected none";
    }
    return ret;
  }

  static std::vector<int> signals_received_;
  static Thread *signaled_thread_;

  scoped_ptr<PhysicalSocketServer> ss_;
};

std::vector<int> PosixSignalDeliveryTest::signals_received_;
Thread *PosixSignalDeliveryTest::signaled_thread_ = NULL;

// Test receiving a synchronous signal while not in Wait() and then entering
// Wait() afterwards.
TEST_F(PosixSignalDeliveryTest, RaiseThenWait) {
  ASSERT_TRUE(ss_->SetPosixSignalHandler(SIGTERM, &RecordSignal));
  raise(SIGTERM);
  EXPECT_TRUE(ss_->Wait(0, true));
  EXPECT_TRUE(ExpectSignal(SIGTERM));
  EXPECT_TRUE(ExpectNone());
}

// Test that we can handle getting tons of repeated signals and that we see all
// the different ones.
TEST_F(PosixSignalDeliveryTest, InsanelyManySignals) {
  ss_->SetPosixSignalHandler(SIGTERM, &RecordSignal);
  ss_->SetPosixSignalHandler(SIGINT, &RecordSignal);
  for (int i = 0; i < 10000; ++i) {
    raise(SIGTERM);
  }
  raise(SIGINT);
  EXPECT_TRUE(ss_->Wait(0, true));
  // Order will be lowest signal numbers first.
  EXPECT_TRUE(ExpectSignal(SIGINT));
  EXPECT_TRUE(ExpectSignal(SIGTERM));
  EXPECT_TRUE(ExpectNone());
}

// Test that a signal during a Wait() call is detected.
TEST_F(PosixSignalDeliveryTest, SignalDuringWait) {
  ss_->SetPosixSignalHandler(SIGALRM, &RecordSignal);
  alarm(1);
  EXPECT_TRUE(ss_->Wait(1500, true));
  EXPECT_TRUE(ExpectSignal(SIGALRM));
  EXPECT_TRUE(ExpectNone());
}

class RaiseSigTermRunnable : public Runnable {
  void Run(Thread *thread) {
    thread->socketserver()->Wait(1000, false);

    // Allow SIGTERM. This will be the only thread with it not masked so it will
    // be delivered to us.
    sigset_t mask;
    sigemptyset(&mask);
    pthread_sigmask(SIG_SETMASK, &mask, NULL);

    // Raise it.
    raise(SIGTERM);
  }
};

// Test that it works no matter what thread the kernel chooses to give the
// signal to (since it's not guaranteed to be the one that Wait() runs on).
TEST_F(PosixSignalDeliveryTest, SignalOnDifferentThread) {
  ss_->SetPosixSignalHandler(SIGTERM, &RecordSignal);
  // Mask out SIGTERM so that it can't be delivered to this thread.
  sigset_t mask;
  sigemptyset(&mask);
  sigaddset(&mask, SIGTERM);
  EXPECT_EQ(0, pthread_sigmask(SIG_SETMASK, &mask, NULL));
  // Start a new thread that raises it. It will have to be delivered to that
  // thread. Our implementation should safely handle it and dispatch
  // RecordSignal() on this thread.
  scoped_ptr<Thread> thread(new Thread());
  scoped_ptr<RaiseSigTermRunnable> runnable(new RaiseSigTermRunnable());
  thread->Start(runnable.get());
  EXPECT_TRUE(ss_->Wait(1500, true));
  EXPECT_TRUE(ExpectSignal(SIGTERM));
  EXPECT_EQ(Thread::Current(), signaled_thread_);
  EXPECT_TRUE(ExpectNone());
}

#endif

}  // namespace talk_base
<|endoftext|>


<commit_before>// Copyright (c) 2012 Steinwurf ApS
// All Rights Reserved
//
// Distributed under the "BSD License". See the accompanying LICENSE.rst file.

#pragma once

#include <functional>
#include <memory>
#include <list>

namespace sak
{
    /// @brief The resource pool stores value objects and recycles them
    template<class Value>
    class resource_pool
    {
    public:

        /// The type managed
        using value_type = Value;

        /// The pointer to the resource
        using value_ptr = std::shared_ptr<value_type>;

        /// The allocator function
        using allocator_function = std::function<value_ptr()>;

        /// The recycle function
        using recycle_function = std::function<void(value_ptr)>;

    public:

        /// Default constructor
        resource_pool() :
            m_pool(std::make_shared<impl>())
        { }

        void set_allocator(const allocator_function& allocator)
        {
            assert(m_pool);
            m_pool->set_allocator(allocator);
        }

        void set_recycle_callback(const recycle_function& allocator)
        {
            assert(m_pool);
            assert(0);
            // m_pool->set_allocator(allocator);
        }

        /// @returns the number of resource in use
        uint32_t total_resources() const
        {
            assert(m_pool);
            return m_pool->size();
        }

        /// @returns the number of resource in use
        uint32_t used_resources() const
        {
            assert(m_pool);
            return m_pool->size();
        }

        /// @returns the number of unused resources
        uint32_t unused_resources() const
        {
            assert(m_pool);
            return m_pool->free();
        }

        /// @returns a resource from the pool
        value_ptr allocate()
        {
            assert(m_pool);
            return m_pool->allocate();
        }

    private:

        // The
        struct impl : public std::enable_shared_from_this<impl>
        {

            impl()
                : m_allocator(std::make_shared<value_type>),
                  m_pool_size(0)
            {
                assert(m_allocator);
            }

            void set_allocator(const allocator_function& allocator)
            {
                assert(allocator);
                m_allocator = allocator;
            }

            value_ptr allocate()
            {
                value_ptr resource;

                if (m_free_list.size() > 0)
                {
                    resource = m_free_list.back();
                    m_free_list.pop_back();
                }
                else
                {
                    assert(m_allocator);
                    resource = m_allocator();
                    ++m_pool_size;
                }

                auto pool = impl::shared_from_this();

                return value_ptr(resource.get(), deleter(pool, resource));
            }

            uint32_t size() const
            {
                return m_pool_size;
            }

            uint32_t free() const
            {
                return static_cast<uint32_t>(m_free_list.size());
            }

            void recycle(const value_ptr& resource)
            {
                m_free_list.push_back(resource);
            }

        private:

            // Stores all the free resources
            std::list<value_ptr> m_free_list;

            // The allocator to use
            allocator_function m_allocator;

            // The total number of resource allocated
            uint32_t m_pool_size;
        };

        typedef std::shared_ptr<impl> pool_ptr;
        typedef std::weak_ptr<impl> pool_weak_ptr;

        struct deleter
        {
            deleter(const std::weak_ptr<impl>& pool,
                    const value_ptr& resource)
                : m_pool(pool),
                  m_resource(resource)
            {
                assert(!m_pool.expired());
                assert(m_resource);
            }

            void operator()(value_type*)
            {
                // Place the resource in the free list
                auto pool = m_pool.lock();

                if (pool)
                {
                    pool->recycle(m_resource);
                }
            }

            // Poiner to the pool needed for recycling
            std::weak_ptr<impl> m_pool;

            // The resource object
            value_ptr m_resource;
        };

    private:

        // The pool impl
        std::shared_ptr<impl> m_pool;

    };
}
<commit_msg>Working on the API<commit_after>// Copyright (c) 2012 Steinwurf ApS
// All Rights Reserved
//
// Distributed under the "BSD License". See the accompanying LICENSE.rst file.

#pragma once

#include <functional>
#include <memory>
#include <list>

namespace sak
{
    /// @brief The resource pool stores value objects and recycles them
    template<class Value>
    class resource_pool
    {
    public:

        /// The type managed
        using value_type = Value;

        /// The pointer to the resource
        using value_ptr = std::shared_ptr<value_type>;

        /// The allocate function
        using allocate_function = std::function<value_ptr()>;

        /// The recycle function
        using recycle_function = std::function<void(value_ptr)>;

    public:

        /// Default constructor
        resource_pool() :
            m_pool(std::make_shared<impl>())
        { }

        template<class Allocate>
        resource_pool(const Allocate& allocate) :
            m_pool(std::make_shared<impl>())
        { }

        template<class Allocate, class Recycle>
        resource_pool(const Allocate& allocate, const Recycle& recycle) :
            m_pool(std::make_shared<impl>())
        { }

        /// @returns the number of resource in use
        uint32_t total_resources() const
        {
            assert(m_pool);
            return m_pool->size();
        }

        /// @returns the number of resource in use
        uint32_t used_resources() const
        {
            assert(m_pool);
            return m_pool->size();
        }

        /// @returns the number of unused resources
        uint32_t unused_resources() const
        {
            assert(m_pool);
            return m_pool->free();
        }

        /// @returns a resource from the pool
        value_ptr allocate()
        {
            assert(m_pool);
            return m_pool->allocate();
        }

    private:

        // The
        struct impl : public std::enable_shared_from_this<impl>
        {

            impl()
                : m_allocator(
                  m_pool_size(0)
            { }

            template<class Allocate>
            impl(const Allocate& allocate) :
                m_pool(std::make_shared<impl>())
            { }

            template<class Allocate, class Recycle>
            impl(const Allocate& allocate, const Recycle& recycle) :
                m_pool(std::make_shared<impl>())
            { }

            value_ptr allocate()
            {
                value_ptr resource;

                if (m_free_list.size() > 0)
                {
                    resource = m_free_list.back();
                    m_free_list.pop_back();
                }
                else
                {
                    assert(m_allocator);
                    resource = m_allocator();
                    ++m_pool_size;
                }

                auto pool = impl::shared_from_this();

                return value_ptr(resource.get(), deleter(pool, resource));
            }

            uint32_t size() const
            {
                return m_pool_size;
            }

            uint32_t free() const
            {
                return static_cast<uint32_t>(m_free_list.size());
            }

            void recycle(const value_ptr& resource)
            {
                m_free_list.push_back(resource);
            }

        private:

            // Stores all the free resources
            std::list<value_ptr> m_free_list;

            // The allocator to use
            allocate_function m_allocate;

            // The allocator to use
            recycle_function m_recycle;

            // The total number of resource allocated
            uint32_t m_pool_size;
        };

        typedef std::shared_ptr<impl> pool_ptr;
        typedef std::weak_ptr<impl> pool_weak_ptr;

        struct deleter
        {
            deleter(const std::weak_ptr<impl>& pool,
                    const value_ptr& resource)
                : m_pool(pool),
                  m_resource(resource)
            {
                assert(!m_pool.expired());
                assert(m_resource);
            }

            void operator()(value_type*)
            {
                // Place the resource in the free list
                auto pool = m_pool.lock();

                if (pool)
                {
                    pool->recycle(m_resource);
                }
            }

            // Poiner to the pool needed for recycling
            std::weak_ptr<impl> m_pool;

            // The resource object
            value_ptr m_resource;
        };

    private:

        // The pool impl
        std::shared_ptr<impl> m_pool;

    };
}
<|endoftext|>


<commit_before>// Copyright 2019 Google LLC
//
// 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
//
//      https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/TableGen/Record.h"
#include "llvm/TableGen/TableGenBackend.h"
#include "mlir/TableGen/Attribute.h"
#include "mlir/TableGen/GenInfo.h"
#include "mlir/TableGen/Operator.h"

namespace mlir {
namespace iree_compiler {
namespace {

using ::llvm::formatv;
using ::llvm::Record;
using ::mlir::tblgen::Operator;

bool emitEncodeFnDefs(const llvm::RecordKeeper &recordKeeper, raw_ostream &os) {
  llvm::emitSourceFileHeader("IREE VM Operation Encoder Definitions", os);

  auto defs = recordKeeper.getAllDerivedDefinitions("VM_Op");
  for (const auto *def : defs) {
    if (def->isValueUnset("encoding")) continue;
    auto encodingExprs = def->getValueAsListOfDefs("encoding");
    if (encodingExprs.empty()) continue;

    Operator op(def);
    os << formatv(
        "LogicalResult {0}::encode(SymbolTable &syms, VMFuncEncoder &e) {{\n",
        op.getQualCppClassName());

    os << "  if (";
    interleave(
        encodingExprs, os,
        [&](Record *encodingExpr) {
          os << formatv("failed({0})", encodingExpr->getValueAsString("expr"));
        },
        " ||\n      ");
    os << ") {\n";
    os << "    return emitOpError() << \"failed to encode (internal)\";\n";
    os << "  }\n";

    os << "  return success();\n";
    os << "}\n\n";
  }

  return false;
}

static GenRegistration genVMOpEncoderDefs(
    "gen-iree-vm-op-encoder-defs",
    "Generates IREE VM operation encoder definitions (.cpp)",
    [](const llvm::RecordKeeper &records, raw_ostream &os) {
      return emitEncodeFnDefs(records, os);
    });

}  // namespace
}  // namespace iree_compiler
}  // namespace mlir
<commit_msg>Adding iree-tblgen support for opcode prefixes.<commit_after>// Copyright 2019 Google LLC
//
// 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
//
//      https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/TableGen/Record.h"
#include "llvm/TableGen/TableGenBackend.h"
#include "mlir/TableGen/Attribute.h"
#include "mlir/TableGen/GenInfo.h"
#include "mlir/TableGen/Operator.h"

namespace mlir {
namespace iree_compiler {
namespace {

using ::llvm::formatv;
using ::llvm::Record;
using ::mlir::tblgen::Operator;

bool emitEncodeFnDefs(const llvm::RecordKeeper &recordKeeper, raw_ostream &os) {
  llvm::emitSourceFileHeader("IREE VM Operation Encoder Definitions", os);

  // Gather prefix opcodes:
  DenseMap<StringRef, int> prefixOpcodes;
  auto opcodes = recordKeeper.getAllDerivedDefinitions("VM_OPC");
  for (const auto *opcode : opcodes) {
    auto symbol = opcode->getValueAsString("symbol");
    if (symbol.startswith("Prefix")) {
      prefixOpcodes[symbol] = opcode->getValueAsInt("value");
    }
  }

  auto defs = recordKeeper.getAllDerivedDefinitions("VM_Op");
  for (const auto *def : defs) {
    if (def->isValueUnset("encoding")) continue;
    auto encodingExprs = def->getValueAsListOfDefs("encoding");
    if (encodingExprs.empty()) continue;

    Operator op(def);
    os << formatv(
        "LogicalResult {0}::encode(SymbolTable &syms, VMFuncEncoder &e) {{\n",
        op.getQualCppClassName());

    for (auto &pair : prefixOpcodes) {
      std::string traitName = (StringRef("OpTrait::IREE::VM::") +
                               pair.first.substr(strlen("Prefix")))
                                  .str();
      if (op.getTrait(traitName)) {
        os << formatv(
            "  if (failed(e.encodeOpcode(\"{0}\", {1}))) return emitOpError() "
            "<< "
            "\"failed to encode op prefix\";\n",
            pair.first, pair.second);
      }
    }

    os << "  if (";
    interleave(
        encodingExprs, os,
        [&](Record *encodingExpr) {
          os << formatv("failed({0})", encodingExpr->getValueAsString("expr"));
        },
        " ||\n      ");
    os << ") {\n";
    os << "    return emitOpError() << \"failed to encode (internal)\";\n";
    os << "  }\n";

    os << "  return success();\n";
    os << "}\n\n";
  }

  return false;
}

static GenRegistration genVMOpEncoderDefs(
    "gen-iree-vm-op-encoder-defs",
    "Generates IREE VM operation encoder definitions (.cpp)",
    [](const llvm::RecordKeeper &records, raw_ostream &os) {
      return emitEncodeFnDefs(records, os);
    });

}  // namespace
}  // namespace iree_compiler
}  // namespace mlir
<|endoftext|>


<commit_before>#include "protagonist.h"
#include "SerializeResult.h"
#include "v8_wrapper.h"
#include "snowcrash.h"

using namespace v8;
using namespace protagonist;

Result::Result()
{
}

Result::~Result()
{
}

Persistent<Function> Result::constructor;

void Result::Init(Handle<Object> exports)
{
    NanScope();

    Local<FunctionTemplate> t = NanNew<FunctionTemplate>(New);
    t->SetClassName(NanNew<String>("Result"));
    t->InstanceTemplate()->SetInternalFieldCount(1);

    NanAssignPersistent<Function>(constructor, t->GetFunction());
    exports->Set(NanNew<String>("Result"), t->GetFunction());
}

NAN_METHOD(Result::New)
{
    NanScope();

    Result* result = ::new Result();
    result->Wrap(args.This());

    NanReturnValue(args.This());
}

v8::Local<v8::Object> Result::WrapResult(snowcrash::ParseResult<snowcrash::Blueprint>& parseResult,
                                         const snowcrash::BlueprintParserOptions& options,
                                         const drafter::ASTType& astType)
{
    static const char* AstKey = "ast";
    static const char* ErrorKey = "error";
    static const char* SourcemapKey = "sourcemap";

    sos::Object result;

    try {
        result = drafter::WrapResult(parseResult, options, astType);
    }
    catch (snowcrash::Error& error) {
        parseResult.report.error = error;
    }

    if (parseResult.report.error.code != snowcrash::Error::OK) {
        result.set(AstKey, sos::Null());

        if ((options & snowcrash::ExportSourcemapOption) != 0) {
            result.set(SourcemapKey, sos::Null());
        }
    }
    else {
        result.unset(ErrorKey);
    }

    return v8_wrap(result)->ToObject();
}
<commit_msg>Minor fixes<commit_after>#include "protagonist.h"
#include "SerializeResult.h"
#include "v8_wrapper.h"
#include "snowcrash.h"

using namespace v8;
using namespace protagonist;

Result::Result()
{
}

Result::~Result()
{
}

Persistent<Function> Result::constructor;

void Result::Init(Handle<Object> exports)
{
    NanScope();

    Local<FunctionTemplate> t = NanNew<FunctionTemplate>(New);
    t->SetClassName(NanNew<String>("Result"));
    t->InstanceTemplate()->SetInternalFieldCount(1);

    NanAssignPersistent<Function>(constructor, t->GetFunction());
    exports->Set(NanNew<String>("Result"), t->GetFunction());
}

NAN_METHOD(Result::New)
{
    NanScope();

    Result* result = ::new Result();
    result->Wrap(args.This());

    NanReturnValue(args.This());
}

v8::Local<v8::Object> Result::WrapResult(snowcrash::ParseResult<snowcrash::Blueprint>& parseResult,
                                         const snowcrash::BlueprintParserOptions& options,
                                         const drafter::ASTType& astType)
{
    static const char* AstKey = "ast";
    static const char* ErrorKey = "error";
    static const char* SourcemapKey = "sourcemap";

    sos::Object result;

    try {
        result = drafter::WrapResult(parseResult, options, astType);
    }
    catch (snowcrash::Error& error) {
        parseResult.report.error = error;
    }

    if (astType == drafter::NormalASTType && parseResult.report.error.code != snowcrash::Error::OK) {
        result.set(AstKey, sos::Null());

        if ((options & snowcrash::ExportSourcemapOption) != 0) {
            result.set(SourcemapKey, sos::Null());
        }
    }

    result.unset(ErrorKey);

    return v8_wrap(result)->ToObject();
}
<|endoftext|>


<commit_before>/** \file    solr_query_test.cc
 *  \brief   A test harness for the Solr::Query function.
 *  \author  Dr. Johannes Ruscheinski
 */

/*
    Copyright (C) 2016-2018, Library of the University of Tübingen

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero 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 Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#include <iostream>
#include <stdexcept>
#include <vector>
#include <cstdlib>
#include <cstring>
#include "Solr.h"
#include "util.h"
#include "StringUtil.h"

[[noreturn]] void Usage() {
    std::cerr << "usage: " << ::progname << " query fields host_and_port timeout query_result_format [max_no_of_rows]\n";
    std::cerr << "       Where \"query_result_format\" must be either \"XML\" or \"JSON\".\n\n";
    std::exit(EXIT_FAILURE);
}

int main(int argc, char *argv[]) {
    ::progname = argv[0];

    if (argc != 6 and argc != 7)
        Usage();

    const std::string query(argv[1]);
    const std::string fields(argv[2]);
    const std::string host_and_port(argv[3]);

    unsigned timeout;
    if (not StringUtil::ToUnsigned(argv[4], &timeout) or timeout < 1)
        logger->error("can't convert \"" + std::string(argv[4]) + " \" to a postive integer!");

    const std::string result_format_candidate(argv[5]);
    Solr::QueryResultFormat result_format;
    if (result_format_candidate == "xml")
        result_format = Solr::QueryResultFormat::XML;
    else if (result_format_candidate == "json")
        result_format = Solr::QueryResultFormat::JSON;
    else
        ERROR("unknown query result format \"" + result_format_candidate + "\"!");

    unsigned max_no_of_rows(0);
    if (argc == 7) {
        if (not StringUtil::ToUnsigned(argv[6], &max_no_of_rows))
            ERROR("can't convert \"" + std::string(argv[6]) + "\" to an unsigned integer!");
    }

    try {
        std::string xml_or_json_result, err_msg;
        if (not Solr::Query(query, fields, &xml_or_json_result, &err_msg, host_and_port, timeout, result_format,
                            max_no_of_rows))
        {
            std::cerr << xml_or_json_result << '\n';
            ERROR("Query failed! (" + err_msg + ")");
        }

        std::cout << xml_or_json_result;
    } catch (const std::exception &x) {
        ERROR("caught exception: " + std::string(x.what()));
    }
}
<commit_msg>Fixed a couple of problems.<commit_after>/** \file    solr_query_test.cc
 *  \brief   A test harness for the Solr::Query function.
 *  \author  Dr. Johannes Ruscheinski
 */

/*
    Copyright (C) 2016-2018, Library of the University of Tübingen

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero 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 Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#include <iostream>
#include <stdexcept>
#include <vector>
#include <cstdlib>
#include <cstring>
#include "Solr.h"
#include "util.h"
#include "StringUtil.h"

[[noreturn]] void Usage() {
    std::cerr << "usage: " << ::progname << " query fields host_and_port timeout query_result_format [max_no_of_rows]\n";
    std::cerr << "       Where \"query_result_format\" must be either \"XML\" or \"JSON\".\n\n";
    std::exit(EXIT_FAILURE);
}

int main(int argc, char *argv[]) {
    ::progname = argv[0];

    if (argc != 6 and argc != 7)
        Usage();

    const std::string query(argv[1]);
    const std::string fields(argv[2]);
    const std::string host_and_port(argv[3]);

    unsigned timeout;
    if (not StringUtil::ToUnsigned(argv[4], &timeout) or timeout < 1)
        logger->error("can't convert \"" + std::string(argv[4]) + " \" to a postive integer!");

    const std::string result_format_candidate(argv[5]);
    Solr::QueryResultFormat result_format;
    if (result_format_candidate == "XML")
        result_format = Solr::QueryResultFormat::XML;
    else if (result_format_candidate == "JSON")
        result_format = Solr::QueryResultFormat::JSON;
    else
        ERROR("unknown query result format \"" + result_format_candidate + "\"!");

    unsigned max_no_of_rows(Solr::JAVA_INT_MAX);
    if (argc == 7) {
        if (not StringUtil::ToUnsigned(argv[6], &max_no_of_rows))
            ERROR("can't convert \"" + std::string(argv[6]) + "\" to an unsigned integer!");
    }

    try {
        std::string xml_or_json_result, err_msg;
        if (not Solr::Query(query, fields, &xml_or_json_result, &err_msg, host_and_port, timeout, result_format, max_no_of_rows))
        {
            std::cerr << xml_or_json_result << '\n';
            ERROR("Query failed! (" + err_msg + ")");
        }

        std::cout << xml_or_json_result;
    } catch (const std::exception &x) {
        ERROR("caught exception: " + std::string(x.what()));
    }
}
<|endoftext|>


<commit_before>/*****************************************************************************************
 *                                                                                *
 * OpenSpace                                                                             *
 *                                                                                *
 * Copyright (c) 2014-2017                                                               *
 *                                                                                *
 * 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 <openspace/engine/openspaceengine.h>
#include <openspace/scene/sceneloader.h>
#include <openspace/scene/scene.h>
#include <openspace/scene/scenegraphnode.h>
#include <openspace/documentation/verifier.h>

#include <ghoul/filesystem/filesystem.h>
#include <ghoul/filesystem/file.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/misc/onscopeexit.h>

#include <unordered_set>

namespace {
    const std::string _loggerCat = "SceneLoader";
    const std::string KeyPathScene = "ScenePath";
    const std::string KeyModules = "Modules";
    const std::string ModuleExtension = ".mod";
    const std::string KeyPathModule = "ModulePath";

    const std::string RootNodeName = "Root";
    const std::string KeyName = "Name";
    const std::string KeyParentName = "Parent";
    const std::string KeyDependencies = "Dependencies";
    const std::string KeyCamera = "Camera";
    const std::string KeyCameraFocus = "Focus";
    const std::string KeyCameraPosition = "Position";
    const std::string KeyCameraRotation = "Rotation";
}

struct ModuleInformation {
    ghoul::Dictionary dictionary;
    std::string moduleFile;
    std::string modulePath;
    std::string moduleName;
};

namespace openspace {

std::unique_ptr<Scene> SceneLoader::loadScene(const std::string& path) {
    // Set up lua state.
    lua_State* state = ghoul::lua::createNewLuaState();
    OnExit(
        // Delete the Lua state at the end of the scope, no matter what.
        [state]() {ghoul::lua::destroyLuaState(state); }
    );
    OsEng.scriptEngine().initializeLuaState(state);

    std::string absScenePath = absPath(path);
    ghoul::filesystem::File sceneFile(absScenePath);
    std::string sceneDirectory = sceneFile.directoryName();

    ghoul::Dictionary sceneDictionary;
    if (!FileSys.fileExists(absScenePath)) {
        throw ghoul::FileNotFoundError(absScenePath);
    }
    ghoul::lua::loadDictionaryFromFile(absScenePath, sceneDictionary, state);

    documentation::testSpecificationAndThrow(Scene::Documentation(), sceneDictionary, "Scene");

    std::string relativeSceneDirectory = ".";
    sceneDictionary.getValue<std::string>(KeyPathScene, relativeSceneDirectory);
    std::string modulesPath = FileSys.absPath(sceneDirectory + FileSys.PathSeparator + relativeSceneDirectory);

    ghoul::Dictionary moduleDictionary;
    sceneDictionary.getValue(KeyModules, moduleDictionary);

    // Above we generated a ghoul::Dictionary from the scene file; now we run the scene
    // file again to load any variables defined inside into the state that is passed to
    // the modules. This allows us to specify global variables that can then be used
    // inside the modules to toggle settings.
    ghoul::lua::runScriptFile(state, absScenePath);
    std::vector<std::string> keys = moduleDictionary.keys();
    ghoul::filesystem::Directory oldDirectory = FileSys.currentDirectory();

    std::vector<SceneLoader::LoadedNode> allNodes;

    for (const std::string& key : keys) {
        std::string fullModuleName = moduleDictionary.value<std::string>(key);
        std::replace(fullModuleName.begin(), fullModuleName.end(), '/', FileSys.PathSeparator);
        std::string modulePath = FileSys.pathByAppendingComponent(modulesPath, fullModuleName);

        std::vector<SceneLoader::LoadedNode> nodes = loadDirectory(modulePath, state);
        std::move(nodes.begin(), nodes.end(), std::back_inserter(allNodes));
    }

    FileSys.setCurrentDirectory(oldDirectory);

    std::unique_ptr<Scene> scene = std::make_unique<Scene>();

    std::unique_ptr<SceneGraphNode> rootNode = std::make_unique<SceneGraphNode>();
    rootNode->setName(SceneGraphNode::RootNodeName);
    scene->setRoot(std::move(rootNode));

    addLoadedNodes(*scene, std::move(allNodes));

    ghoul::Dictionary cameraDictionary;
    sceneDictionary.getValue(KeyCamera, cameraDictionary);
    LoadedCamera loadedCamera = loadCamera(cameraDictionary);

    auto& nodeMap = scene->nodesByName();
    auto it = nodeMap.find(loadedCamera.parent);
    if (it != nodeMap.end()) {
        loadedCamera.camera->setParent(it->second);
    } else {
        LWARNING(
            "Could not find the camera parent '" + loadedCamera.parent +
            "'. Attaching camera to root node.");
        loadedCamera.camera->setParent(scene->root());
    }

    scene->setCamera(std::move(loadedCamera.camera));

    return scene;
}

std::vector<SceneGraphNode*> SceneLoader::importDirectory(Scene& scene, const std::string& path) {
    lua_State* state = ghoul::lua::createNewLuaState();
    OnExit(
        // Delete the Lua state at the end of the scope, no matter what.
        [state]() {ghoul::lua::destroyLuaState(state); }
    );
    OsEng.scriptEngine().initializeLuaState(state);

    std::string absDirectoryPath = absPath(path);

    ghoul::filesystem::Directory oldDirectory = FileSys.currentDirectory();
    std::vector<SceneLoader::LoadedNode> nodes = loadDirectory(path, state);
    FileSys.setCurrentDirectory(oldDirectory);
    return addLoadedNodes(scene, std::move(nodes));
}

SceneGraphNode* SceneLoader::importNodeDictionary(Scene& scene, const ghoul::Dictionary& dict) {
    std::vector<SceneLoader::LoadedNode> loadedNodes;
    loadedNodes.push_back(loadNode(dict));
    std::vector<SceneGraphNode*> nodes = addLoadedNodes(scene, std::move(loadedNodes));
    if (nodes.size() == 1) {
        return nodes[0];
    }
    return nullptr;
}

SceneLoader::LoadedCamera SceneLoader::loadCamera(const ghoul::Dictionary& cameraDict) {
    std::string focus;
    glm::vec3 cameraPosition;
    glm::vec4 cameraRotation;

    bool readSuccessful = true;
    readSuccessful &= cameraDict.getValue(KeyCameraFocus, focus);
    readSuccessful &= cameraDict.getValue(KeyCameraPosition, cameraPosition);
    readSuccessful &= cameraDict.getValue(KeyCameraRotation, cameraRotation);

    std::unique_ptr<Camera> camera = std::make_unique<Camera>();

    camera->setPositionVec3(cameraPosition);
    camera->setRotation(glm::dquat(
        cameraRotation.x, cameraRotation.y, cameraRotation.z, cameraRotation.w));

    LoadedCamera loadedCamera(focus, std::move(camera));

    if (!readSuccessful) {
        throw Scene::InvalidSceneError(
            "Position, Rotation and Focus need to be defined for camera dictionary.");
    }

    return loadedCamera;
}

std::vector<SceneLoader::LoadedNode> SceneLoader::loadDirectory(
    const std::string& path,
    lua_State* luaState)
{
    std::string::size_type pos = path.find_last_of(FileSys.PathSeparator);
    if (pos == std::string::npos) {
        LERROR("Error parsing directory name '" << path << "'");
        return std::vector<SceneLoader::LoadedNode>();
    }
    std::string moduleName = path.substr(pos + 1);
    std::string moduleFile = FileSys.pathByAppendingComponent(path, moduleName) + ModuleExtension;

    if (FileSys.fileExists(moduleFile)) {
        // TODO: Get rid of changing the working directory (global state is bad) -- emiax
        // This requires refactoring all renderables to not use relative paths in constructors.
        FileSys.setCurrentDirectory(ghoul::filesystem::Directory(path));

        // We have a module file, so it is a direct include.
        return loadModule(moduleFile, luaState);
    } else {
        std::vector<SceneLoader::LoadedNode> allLoadedNodes;
        // If we do not have a module file, we have to include all subdirectories.
        using ghoul::filesystem::Directory;
        using std::string;
        std::vector<string> directories = Directory(path).readDirectories();

        for (const string& s : directories) {
            //std::string submodulePath = FileSys.pathByAppendingComponent(path, s);
            std::vector<SceneLoader::LoadedNode> loadedNodes = loadDirectory(s, luaState);
            std::move(loadedNodes.begin(), loadedNodes.end(), std::back_inserter(allLoadedNodes));
        }
        return allLoadedNodes;
    }
}

SceneLoader::LoadedNode SceneLoader::loadNode(const ghoul::Dictionary& dictionary) {
    std::vector<std::string> dependencies;

    std::string nodeName = dictionary.value<std::string>(KeyName);
    std::string parentName = dictionary.value<std::string>(KeyParentName);
    std::unique_ptr<SceneGraphNode> node = SceneGraphNode::createFromDictionary(dictionary);

    if (dictionary.hasKey(SceneGraphNode::KeyDependencies)) {
        if (!dictionary.hasValue<ghoul::Dictionary>(SceneGraphNode::KeyDependencies)) {
            LERROR("Dependencies did not have the corrent type");
        }
        ghoul::Dictionary nodeDependencies;
        dictionary.getValue(SceneGraphNode::KeyDependencies, nodeDependencies);

        std::vector<std::string> keys = nodeDependencies.keys();
        for (const std::string& key : keys) {
            std::string value = nodeDependencies.value<std::string>(key);
            dependencies.push_back(value);
        }
    }
    return SceneLoader::LoadedNode(nodeName, parentName, dependencies, std::move(node));
}

std::vector<SceneLoader::LoadedNode> SceneLoader::loadModule(const std::string& path, lua_State* luaState) {
    ghoul::Dictionary moduleDictionary;
    try {
        ghoul::lua::loadDictionaryFromFile(path, moduleDictionary, luaState);
    } catch (const ghoul::lua::LuaRuntimeException& e) {
        LERRORC(e.component, e.message);
        return std::vector<SceneLoader::LoadedNode>();
    }

    std::vector<SceneLoader::LoadedNode> loadedNodes;
    std::vector<std::string> keys = moduleDictionary.keys();
    for (const std::string& key : keys) {
        ghoul::Dictionary nodeDictionary;
        if (!moduleDictionary.getValue(key, nodeDictionary)) {
            LERROR("Node dictionary did not have the corrent type");
            continue;
        }
        try {
            loadedNodes.push_back(loadNode(nodeDictionary));
        } catch (ghoul::RuntimeError& e) {
            LERROR("Failed loading node from " << path << ": " << e.message << ", " << e.component);
        }
    }
    return loadedNodes;
};

std::vector<SceneGraphNode*> SceneLoader::addLoadedNodes(Scene& scene, std::vector<SceneLoader::LoadedNode>&& loadedNodes) {
    std::map<std::string, SceneGraphNode*> existingNodes = scene.nodesByName();
    std::map<std::string, SceneGraphNode*> addedNodes;

    // Populate map of nodes to be added.
    // Also track new branches of nodes that are attached
    // to allow for recovery in case an invalid scene is generated.
    for (auto& loadedNode : loadedNodes) {
        std::string name = loadedNode.name;
        if (existingNodes.count(name) > 0) {
            LERROR("Node with name '" + name + "' already exists in scene");
            continue;
        }
        if (addedNodes.count(name) > 0) {
            LERROR("Duplicate node names '" + name + "' among loaded nodes");
        }

        SceneGraphNode* node = loadedNode.node.get();
        addedNodes[name] = node;
    }

    // Find a node by name among the exising nodes and the added nodes.
    auto findNode = [&existingNodes, &addedNodes](const std::string name) {
        std::map<std::string, SceneGraphNode*>::iterator it;
        if ((it = existingNodes.find(name)) != existingNodes.end()) {
            return it->second;
        }
        if ((it = addedNodes.find(name)) != addedNodes.end()) {
            return it->second;
        }
        return static_cast<SceneGraphNode*>(nullptr);
    };

    std::vector<SceneGraphNode*> attachedBranches;
    std::vector<std::unique_ptr<SceneGraphNode>> badNodes;

    // Attach each node to its parent and set up dependencies.
    for (auto& loadedNode : loadedNodes) {
        std::string parentName = loadedNode.parent;
        std::vector<std::string> dependencyNames = loadedNode.dependencies;

        SceneGraphNode* parent = findNode(parentName);
        if (!parent) {
            LERROR("Could not find parent '" + parentName + "' for '" + loadedNode.name + "'");
            badNodes.push_back(std::move(loadedNode.node));
            continue;
        }

        std::vector<SceneGraphNode*> dependencies;
        bool foundAllDeps = true;
        for (const auto& depName : dependencyNames) {
            SceneGraphNode* dep = findNode(depName);
            if (!dep) {
                LERROR("Could not find dependency '" + depName + "' for '" + loadedNode.name + "'");
                foundAllDeps = false;
                continue;
            }
            dependencies.push_back(dep);
        }

        if (!foundAllDeps) {
            badNodes.push_back(std::move(loadedNode.node));
            continue;
        }

        SceneGraphNode* child = loadedNode.node.get();
        parent->attachChild(std::move(loadedNode.node), SceneGraphNode::UpdateScene::No);
        child->setDependencies(dependencies, SceneGraphNode::UpdateScene::No);

        if (existingNodes.find(parentName) != existingNodes.end()) {
            attachedBranches.push_back(child);
        }
    }

    // Remove all bad nodes (parent or deps missing) and all their children and dependent nodes.
    // Use unsorted set `visited` to avoid infinite loop in case of circular deps.
    std::unordered_set<SceneGraphNode*> visited;
    for (size_t i = 0; i < badNodes.size(); i++) {
        auto& badNode = badNodes[i];
        for (auto c : badNode->children()) {
            visited.insert(c);
            badNodes.push_back(badNode->detachChild(*c, SceneGraphNode::UpdateScene::No));
        }
        for (auto& d : badNode->dependentNodes()) {
            SceneGraphNode* parent = d->parent();
            if (visited.count(d) == 0) {
                visited.insert(d);
                if (parent) {
                    badNodes.push_back(parent->detachChild(*d, SceneGraphNode::UpdateScene::No));
                }
            }
        }
    }
    // Warn for nodes that lack connection to the root.
    for (auto& node : addedNodes) {
        if (!node.second->scene()) {
            LWARNING("Node '" << node.first << "' is not connected to the root and will not be added to the scene");
        }
    }

    // Add the nodes to the scene.
    for (auto& node : attachedBranches) {
        scene.addNode(node, Scene::UpdateDependencies::No);
    }

    // Update dependencies: sort nodes topologically.
    scene.updateDependencies();

    // Return a vector of all added nodes.
    std::vector<SceneGraphNode*> addedNodesVector;
    std::transform(addedNodes.begin(), addedNodes.end(), std::back_inserter(addedNodesVector), [] (auto& pair) {
        return pair.second;
    });

    return addedNodesVector;
}
}
<commit_msg>SceneLoader: Print error when trying to load a non-existing directory<commit_after>/*****************************************************************************************
 *                                                                                *
 * OpenSpace                                                                             *
 *                                                                                *
 * Copyright (c) 2014-2017                                                               *
 *                                                                                *
 * 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 <openspace/engine/openspaceengine.h>
#include <openspace/scene/sceneloader.h>
#include <openspace/scene/scene.h>
#include <openspace/scene/scenegraphnode.h>
#include <openspace/documentation/verifier.h>

#include <ghoul/filesystem/filesystem.h>
#include <ghoul/filesystem/file.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/misc/onscopeexit.h>

#include <unordered_set>

namespace {
    const std::string _loggerCat = "SceneLoader";
    const std::string KeyPathScene = "ScenePath";
    const std::string KeyModules = "Modules";
    const std::string ModuleExtension = ".mod";
    const std::string KeyPathModule = "ModulePath";

    const std::string RootNodeName = "Root";
    const std::string KeyName = "Name";
    const std::string KeyParentName = "Parent";
    const std::string KeyDependencies = "Dependencies";
    const std::string KeyCamera = "Camera";
    const std::string KeyCameraFocus = "Focus";
    const std::string KeyCameraPosition = "Position";
    const std::string KeyCameraRotation = "Rotation";
}

struct ModuleInformation {
    ghoul::Dictionary dictionary;
    std::string moduleFile;
    std::string modulePath;
    std::string moduleName;
};

namespace openspace {

std::unique_ptr<Scene> SceneLoader::loadScene(const std::string& path) {
    // Set up lua state.
    lua_State* state = ghoul::lua::createNewLuaState();
    OnExit(
        // Delete the Lua state at the end of the scope, no matter what.
        [state]() {ghoul::lua::destroyLuaState(state); }
    );
    OsEng.scriptEngine().initializeLuaState(state);

    std::string absScenePath = absPath(path);
    ghoul::filesystem::File sceneFile(absScenePath);
    std::string sceneDirectory = sceneFile.directoryName();

    ghoul::Dictionary sceneDictionary;
    if (!FileSys.fileExists(absScenePath)) {
        throw ghoul::FileNotFoundError(absScenePath);
    }
    ghoul::lua::loadDictionaryFromFile(absScenePath, sceneDictionary, state);

    documentation::testSpecificationAndThrow(Scene::Documentation(), sceneDictionary, "Scene");

    std::string relativeSceneDirectory = ".";
    sceneDictionary.getValue<std::string>(KeyPathScene, relativeSceneDirectory);
    std::string modulesPath = FileSys.absPath(sceneDirectory + FileSys.PathSeparator + relativeSceneDirectory);

    ghoul::Dictionary moduleDictionary;
    sceneDictionary.getValue(KeyModules, moduleDictionary);

    // Above we generated a ghoul::Dictionary from the scene file; now we run the scene
    // file again to load any variables defined inside into the state that is passed to
    // the modules. This allows us to specify global variables that can then be used
    // inside the modules to toggle settings.
    ghoul::lua::runScriptFile(state, absScenePath);
    std::vector<std::string> keys = moduleDictionary.keys();
    ghoul::filesystem::Directory oldDirectory = FileSys.currentDirectory();

    std::vector<SceneLoader::LoadedNode> allNodes;

    for (const std::string& key : keys) {
        std::string fullModuleName = moduleDictionary.value<std::string>(key);
        std::replace(fullModuleName.begin(), fullModuleName.end(), '/', FileSys.PathSeparator);
        std::string modulePath = FileSys.pathByAppendingComponent(modulesPath, fullModuleName);

        std::vector<SceneLoader::LoadedNode> nodes = loadDirectory(modulePath, state);
        std::move(nodes.begin(), nodes.end(), std::back_inserter(allNodes));
    }

    FileSys.setCurrentDirectory(oldDirectory);

    std::unique_ptr<Scene> scene = std::make_unique<Scene>();

    std::unique_ptr<SceneGraphNode> rootNode = std::make_unique<SceneGraphNode>();
    rootNode->setName(SceneGraphNode::RootNodeName);
    scene->setRoot(std::move(rootNode));

    addLoadedNodes(*scene, std::move(allNodes));

    ghoul::Dictionary cameraDictionary;
    sceneDictionary.getValue(KeyCamera, cameraDictionary);
    LoadedCamera loadedCamera = loadCamera(cameraDictionary);

    auto& nodeMap = scene->nodesByName();
    auto it = nodeMap.find(loadedCamera.parent);
    if (it != nodeMap.end()) {
        loadedCamera.camera->setParent(it->second);
    } else {
        LWARNING(
            "Could not find the camera parent '" + loadedCamera.parent +
            "'. Attaching camera to root node.");
        loadedCamera.camera->setParent(scene->root());
    }

    scene->setCamera(std::move(loadedCamera.camera));

    return scene;
}

std::vector<SceneGraphNode*> SceneLoader::importDirectory(Scene& scene, const std::string& path) {
    lua_State* state = ghoul::lua::createNewLuaState();
    OnExit(
        // Delete the Lua state at the end of the scope, no matter what.
        [state]() {ghoul::lua::destroyLuaState(state); }
    );
    OsEng.scriptEngine().initializeLuaState(state);

    std::string absDirectoryPath = absPath(path);

    ghoul::filesystem::Directory oldDirectory = FileSys.currentDirectory();
    std::vector<SceneLoader::LoadedNode> nodes = loadDirectory(path, state);
    FileSys.setCurrentDirectory(oldDirectory);
    return addLoadedNodes(scene, std::move(nodes));
}

SceneGraphNode* SceneLoader::importNodeDictionary(Scene& scene, const ghoul::Dictionary& dict) {
    std::vector<SceneLoader::LoadedNode> loadedNodes;
    loadedNodes.push_back(loadNode(dict));
    std::vector<SceneGraphNode*> nodes = addLoadedNodes(scene, std::move(loadedNodes));
    if (nodes.size() == 1) {
        return nodes[0];
    }
    return nullptr;
}

SceneLoader::LoadedCamera SceneLoader::loadCamera(const ghoul::Dictionary& cameraDict) {
    std::string focus;
    glm::vec3 cameraPosition;
    glm::vec4 cameraRotation;

    bool readSuccessful = true;
    readSuccessful &= cameraDict.getValue(KeyCameraFocus, focus);
    readSuccessful &= cameraDict.getValue(KeyCameraPosition, cameraPosition);
    readSuccessful &= cameraDict.getValue(KeyCameraRotation, cameraRotation);

    std::unique_ptr<Camera> camera = std::make_unique<Camera>();

    camera->setPositionVec3(cameraPosition);
    camera->setRotation(glm::dquat(
        cameraRotation.x, cameraRotation.y, cameraRotation.z, cameraRotation.w));

    LoadedCamera loadedCamera(focus, std::move(camera));

    if (!readSuccessful) {
        throw Scene::InvalidSceneError(
            "Position, Rotation and Focus need to be defined for camera dictionary.");
    }

    return loadedCamera;
}

std::vector<SceneLoader::LoadedNode> SceneLoader::loadDirectory(
    const std::string& path,
    lua_State* luaState)
{
    std::string::size_type pos = path.find_last_of(FileSys.PathSeparator);
    if (pos == std::string::npos) {
        LERROR("Error parsing directory name '" << path << "'");
        return std::vector<SceneLoader::LoadedNode>();
    }
    std::string moduleName = path.substr(pos + 1);
    std::string moduleFile = FileSys.pathByAppendingComponent(path, moduleName) + ModuleExtension;

    if (FileSys.fileExists(moduleFile)) {
        // TODO: Get rid of changing the working directory (global state is bad) -- emiax
        // This requires refactoring all renderables to not use relative paths in constructors.
        FileSys.setCurrentDirectory(ghoul::filesystem::Directory(path));

        // We have a module file, so it is a direct include.
        return loadModule(moduleFile, luaState);
    } else {
        std::vector<SceneLoader::LoadedNode> allLoadedNodes;
        // If we do not have a module file, we have to include all subdirectories.
        using ghoul::filesystem::Directory;
        using std::string;

        const Directory directory(path);
        const std::string directoryPath = directory.path();

        if (!FileSys.fileExists(directoryPath)) {
            LERROR("The directory " << directoryPath << " does not exist.");
            return std::vector<SceneLoader::LoadedNode>();
        }

        for (const string& subdirectory : directory.readDirectories()) {
            std::vector<SceneLoader::LoadedNode> loadedNodes = loadDirectory(subdirectory, luaState);
            std::move(loadedNodes.begin(), loadedNodes.end(), std::back_inserter(allLoadedNodes));
        }
        return allLoadedNodes;
    }
}

SceneLoader::LoadedNode SceneLoader::loadNode(const ghoul::Dictionary& dictionary) {
    std::vector<std::string> dependencies;

    std::string nodeName = dictionary.value<std::string>(KeyName);
    std::string parentName = dictionary.value<std::string>(KeyParentName);
    std::unique_ptr<SceneGraphNode> node = SceneGraphNode::createFromDictionary(dictionary);

    if (dictionary.hasKey(SceneGraphNode::KeyDependencies)) {
        if (!dictionary.hasValue<ghoul::Dictionary>(SceneGraphNode::KeyDependencies)) {
            LERROR("Dependencies did not have the corrent type");
        }
        ghoul::Dictionary nodeDependencies;
        dictionary.getValue(SceneGraphNode::KeyDependencies, nodeDependencies);

        std::vector<std::string> keys = nodeDependencies.keys();
        for (const std::string& key : keys) {
            std::string value = nodeDependencies.value<std::string>(key);
            dependencies.push_back(value);
        }
    }
    return SceneLoader::LoadedNode(nodeName, parentName, dependencies, std::move(node));
}

std::vector<SceneLoader::LoadedNode> SceneLoader::loadModule(const std::string& path, lua_State* luaState) {
    ghoul::Dictionary moduleDictionary;
    try {
        ghoul::lua::loadDictionaryFromFile(path, moduleDictionary, luaState);
    } catch (const ghoul::lua::LuaRuntimeException& e) {
        LERRORC(e.component, e.message);
        return std::vector<SceneLoader::LoadedNode>();
    }

    std::vector<SceneLoader::LoadedNode> loadedNodes;
    std::vector<std::string> keys = moduleDictionary.keys();
    for (const std::string& key : keys) {
        ghoul::Dictionary nodeDictionary;
        if (!moduleDictionary.getValue(key, nodeDictionary)) {
            LERROR("Node dictionary did not have the corrent type");
            continue;
        }
        try {
            loadedNodes.push_back(loadNode(nodeDictionary));
        } catch (ghoul::RuntimeError& e) {
            LERROR("Failed loading node from " << path << ": " << e.message << ", " << e.component);
        }
    }
    return loadedNodes;
};

std::vector<SceneGraphNode*> SceneLoader::addLoadedNodes(Scene& scene, std::vector<SceneLoader::LoadedNode>&& loadedNodes) {
    std::map<std::string, SceneGraphNode*> existingNodes = scene.nodesByName();
    std::map<std::string, SceneGraphNode*> addedNodes;

    // Populate map of nodes to be added.
    // Also track new branches of nodes that are attached
    // to allow for recovery in case an invalid scene is generated.
    for (auto& loadedNode : loadedNodes) {
        std::string name = loadedNode.name;
        if (existingNodes.count(name) > 0) {
            LERROR("Node with name '" + name + "' already exists in scene");
            continue;
        }
        if (addedNodes.count(name) > 0) {
            LERROR("Duplicate node names '" + name + "' among loaded nodes");
        }

        SceneGraphNode* node = loadedNode.node.get();
        addedNodes[name] = node;
    }

    // Find a node by name among the exising nodes and the added nodes.
    auto findNode = [&existingNodes, &addedNodes](const std::string name) {
        std::map<std::string, SceneGraphNode*>::iterator it;
        if ((it = existingNodes.find(name)) != existingNodes.end()) {
            return it->second;
        }
        if ((it = addedNodes.find(name)) != addedNodes.end()) {
            return it->second;
        }
        return static_cast<SceneGraphNode*>(nullptr);
    };

    std::vector<SceneGraphNode*> attachedBranches;
    std::vector<std::unique_ptr<SceneGraphNode>> badNodes;

    // Attach each node to its parent and set up dependencies.
    for (auto& loadedNode : loadedNodes) {
        std::string parentName = loadedNode.parent;
        std::vector<std::string> dependencyNames = loadedNode.dependencies;

        SceneGraphNode* parent = findNode(parentName);
        if (!parent) {
            LERROR("Could not find parent '" + parentName + "' for '" + loadedNode.name + "'");
            badNodes.push_back(std::move(loadedNode.node));
            continue;
        }

        std::vector<SceneGraphNode*> dependencies;
        bool foundAllDeps = true;
        for (const auto& depName : dependencyNames) {
            SceneGraphNode* dep = findNode(depName);
            if (!dep) {
                LERROR("Could not find dependency '" + depName + "' for '" + loadedNode.name + "'");
                foundAllDeps = false;
                continue;
            }
            dependencies.push_back(dep);
        }

        if (!foundAllDeps) {
            badNodes.push_back(std::move(loadedNode.node));
            continue;
        }

        SceneGraphNode* child = loadedNode.node.get();
        parent->attachChild(std::move(loadedNode.node), SceneGraphNode::UpdateScene::No);
        child->setDependencies(dependencies, SceneGraphNode::UpdateScene::No);

        if (existingNodes.find(parentName) != existingNodes.end()) {
            attachedBranches.push_back(child);
        }
    }

    // Remove all bad nodes (parent or deps missing) and all their children and dependent nodes.
    // Use unsorted set `visited` to avoid infinite loop in case of circular deps.
    std::unordered_set<SceneGraphNode*> visited;
    for (size_t i = 0; i < badNodes.size(); i++) {
        auto& badNode = badNodes[i];
        for (auto c : badNode->children()) {
            visited.insert(c);
            badNodes.push_back(badNode->detachChild(*c, SceneGraphNode::UpdateScene::No));
        }
        for (auto& d : badNode->dependentNodes()) {
            SceneGraphNode* parent = d->parent();
            if (visited.count(d) == 0) {
                visited.insert(d);
                if (parent) {
                    badNodes.push_back(parent->detachChild(*d, SceneGraphNode::UpdateScene::No));
                }
            }
        }
    }
    // Warn for nodes that lack connection to the root.
    for (auto& node : addedNodes) {
        if (!node.second->scene()) {
            LWARNING("Node '" << node.first << "' is not connected to the root and will not be added to the scene");
        }
    }

    // Add the nodes to the scene.
    for (auto& node : attachedBranches) {
        scene.addNode(node, Scene::UpdateDependencies::No);
    }

    // Update dependencies: sort nodes topologically.
    scene.updateDependencies();

    // Return a vector of all added nodes.
    std::vector<SceneGraphNode*> addedNodesVector;
    std::transform(addedNodes.begin(), addedNodes.end(), std::back_inserter(addedNodesVector), [] (auto& pair) {
        return pair.second;
    });

    return addedNodesVector;
}
}
<|endoftext|>


<commit_before>
#include <scm/core/math/common.h>
#include <scm/core/math/vec_fwd.h>

namespace scm {
namespace math {

// default operators
// unary
template<typename scal_type,
         const unsigned row_dim,
         const unsigned col_dim>
inline mat<scal_type, row_dim, col_dim>&
operator+=(      mat<scal_type, row_dim, col_dim>& lhs,
           const mat<scal_type, row_dim, col_dim>& rhs)
{
    for (unsigned i = 0; i < (row_dim * col_dim); ++i) {
        lhs.data_array[i] += rhs.data_array[i];
    }
    return (lhs);
}

template<typename scal_type,
         const unsigned row_dim,
         const unsigned col_dim>
inline mat<scal_type, row_dim, col_dim>&
operator-=(      mat<scal_type, row_dim, col_dim>& lhs,
           const mat<scal_type, row_dim, col_dim>& rhs)
{
    for (unsigned i = 0; i < (row_dim * col_dim); ++i) {
        lhs.data_array[i] -= rhs.data_array[i];
    }
    return (lhs);
}

template<typename scal_type,
         const unsigned order>
inline mat<scal_type, order, order>&
operator*=(      mat<scal_type, order, order>& lhs,
           const mat<scal_type, order, order>& rhs)
{
    mat<scal_type, order, order> tmp_ret;

    unsigned    dst_off;
    unsigned    row_off;
    unsigned    col_off;

    scal_type   tmp_dp;

    for (unsigned c = 0; c < order; ++c) {
        for (unsigned r = 0; r < order; ++r) {
            dst_off = r + order * c;
            tmp_dp = scal_type(0);

            for (unsigned d = 0; d < order; ++d) {
                row_off = r + d * order;
                col_off = d + c * order;
                tmp_dp += lhs.data_array[row_off] * rhs.data_array[col_off];
            }

            tmp_ret.data_array[dst_off] = tmp_dp;
        }
    }

    lhs = tmp_ret;

    return (lhs);
}

template<typename scal_type,
         const unsigned row_dim,
         const unsigned col_dim>
inline mat<scal_type, row_dim, col_dim>&
operator*=(mat<scal_type, row_dim, col_dim>&   lhs,
           const scal_type                     rhs)
{
    for (unsigned i = 0; i < (row_dim * col_dim); ++i) {
        lhs.data_array[i] *= rhs;
    }
    return (lhs);
}

template<typename scal_type,
         const unsigned row_dim,
         const unsigned col_dim>
inline mat<scal_type, row_dim, col_dim>&
operator/=(mat<scal_type, row_dim, col_dim>& lhs,
           const scal_type rhs)
{
    for (unsigned i = 0; i < (row_dim * col_dim); ++i) {
        lhs.data_array[i] /= rhs;
    }
    return (lhs);
}

// binary operators
template<typename scal_type,
         const unsigned order>
inline const mat<scal_type, order, order>
operator*(const mat<scal_type, order, order>& lhs,
          const mat<scal_type, order, order>& rhs)
{
    mat<scal_type, order, order> tmp(lhs);
    return (tmp *= rhs);
}

template<typename scal_type,
         const unsigned row_dim,
         const unsigned col_dim>
inline const mat<scal_type, row_dim, col_dim>
operator*(const mat<scal_type, row_dim, col_dim>& lhs,
          const scal_type                         rhs)
{
    mat<scal_type, row_dim, col_dim> tmp(lhs);
    return (tmp *= rhs);
}

template<typename scal_type,
         const unsigned row_dim,
         const unsigned col_dim>
inline const mat<scal_type, row_dim, col_dim>
operator/(const mat<scal_type, row_dim, col_dim>& lhs,
          const scal_type                         rhs)
{
    mat<scal_type, row_dim, col_dim> tmp(lhs);
    return (tmp /= rhs);
}

template<typename scal_type,
         const unsigned row_dim,
         const unsigned col_dim>
inline const mat<scal_type, row_dim, col_dim>
operator+(const mat<scal_type, row_dim, col_dim>& lhs,
          const mat<scal_type, row_dim, col_dim>& rhs)
{
    mat<scal_type, row_dim, col_dim> tmp(lhs);
    return (mat<scal_type, row_dim, col_dim>(lhs) += rhs);
}

template<typename scal_type,
         const unsigned row_dim,
         const unsigned col_dim>
inline const mat<scal_type, row_dim, col_dim>
operator-(const mat<scal_type, row_dim, col_dim>& lhs,
          const mat<scal_type, row_dim, col_dim>& rhs)
{
    mat<scal_type, row_dim, col_dim> tmp(lhs);
    return (tmp -= rhs);
}

template<typename scal_type,
         const unsigned order>
inline const vec<scal_type, order>
operator*(const mat<scal_type, order, order>& lhs,
          const vec<scal_type, order>&        rhs)
{
    vec<scal_type, order> tmp_ret(scal_type(0));

    unsigned    row_off;

    scal_type   tmp_dp;

    for (unsigned r = 0; r < order; ++r) {
        tmp_dp = scal_type(0);

        for (unsigned c = 0; c < order; ++c) {
            row_off = r + c * order;
            tmp_dp += lhs.data_array[row_off] * rhs.data_array[c];
        }

        tmp_ret.data_array[r] = tmp_dp;
    }

    return (tmp_ret);
}

template<typename scal_type,
         const unsigned order>
inline const vec<scal_type, order>
operator*(const vec<scal_type, order>&        lhs,
          const mat<scal_type, order, order>& rhs)
{
    vec<scal_type, order> tmp_ret(scal_type(0));

    unsigned    row_off;

    scal_type   tmp_dp;

    for (unsigned r = 0; r < order; ++r) {
        tmp_dp = scal_type(0);

        for (unsigned c = 0; c < order; ++c) {
            row_off = r * order + c;
            tmp_dp += rhs.data_array[row_off] * lhs.data_array[c];
        }

        tmp_ret.data_array[r] = tmp_dp;
    }

    return (tmp_ret);
}

// common functions
template<typename scal_type,
         const unsigned order>
inline void set_identity(mat<scal_type, order, order>& m)
{
    for (unsigned i = 0; i < (order * order); ++i) {
        m.data_array[i] = (i % (order + 1)) == 0 ? scal_type(1) : scal_type(0);
    }
}

template<typename scal_type,
         const unsigned row_dim,
         const unsigned col_dim>
inline const mat<scal_type, row_dim, col_dim> transpose(const mat<scal_type, row_dim, col_dim>& lhs)
{
    mat<scal_type, col_dim, row_dim> tmp_ret;

    unsigned src_off;
    unsigned dst_off;

    for (unsigned c = 0; c < col_dim; ++c) {
        for (unsigned r = 0; r < row_dim; ++r) {
            src_off = r + c * row_dim;
            dst_off = c + r * col_dim;

            tmp_ret.data_array[dst_off] = lhs.data_array[src_off];
        }
    }

    return (tmp_ret);
}

template<typename scal_type,
         const unsigned order>
inline const mat<scal_type, order - 1, order - 1> minor_mat(const mat<scal_type, order, order>& lhs,
                                                            const unsigned row,
                                                            const unsigned col)
{
    mat<scal_type, order - 1, order - 1>   tmp_minor;

    unsigned min_off;
    unsigned src_off;

    unsigned min_row = 0;
    unsigned min_col = 0;

    for (unsigned r = 0; r < order; ++r) {
        if (r != row) {
            min_col = 0;
            for (unsigned c = 0; c < order; ++c) {
                if (c != col) {
                    src_off = r + c * order;
                    min_off = min_row + min_col * (order - 1);

                    tmp_minor.data_array[min_off] = lhs.data_array[src_off];
                    ++min_col;
                }
            }
            ++min_row;
        }
    }

    return (tmp_minor);
}

template<typename scal_type>
inline scal_type determinant(const mat<scal_type, 1, 1>& lhs)
{
    return (lhs.data_array[0]);
}

template<typename scal_type>
inline scal_type determinant(const mat<scal_type, 2, 2>& lhs)
{
    return (lhs.data_array[0] * lhs.data_array[3] - lhs.data_array[1] * lhs.data_array[2]);
}

template<typename scal_type, const unsigned order>
inline scal_type determinant(const mat<scal_type, order, order>& lhs)
{
    scal_type tmp_ret = scal_type(0);

    // determinat development after first column
    for (unsigned r = 0; r < order; ++r) {
        tmp_ret +=  lhs.data_array[r] * sign(-int(r % 2)) * determinant(minor_mat(lhs, r, 0));
    }

    return (tmp_ret);
}

template<typename scal_type, const unsigned order>
inline const mat<scal_type, order, order> inverse(const mat<scal_type, order, order>& lhs)
{
    mat<scal_type, order, order> tmp_ret(mat<scal_type, order, order>::null_mat);
    scal_type                    tmp_det = determinant(lhs);

    unsigned dst_off;

    // ATTENTION!!!! float equal test
    if (tmp_det != scal_type(0)) {
        for (unsigned r = 0; r < order; ++r) {
            for (unsigned c = 0; c < order; ++c) {
                dst_off = c + r * order;
                tmp_ret.data_array[dst_off] = (scal_type(1) / tmp_det) * sign(-int((r+c) % 2)) * determinant(minor_mat(lhs, r, c));
            }
        }
    }

    return (tmp_ret);
}

} // namespace math
} // namespace scm
<commit_msg>math lib linux work (gcc is so FUCKING stupid)<commit_after>
#include <scm/core/math/common.h>
#include <scm/core/math/vec_fwd.h>

namespace scm {
namespace math {

// default operators
// unary
template<typename scal_type,
         const unsigned row_dim,
         const unsigned col_dim>
inline mat<scal_type, row_dim, col_dim>&
operator+=(      mat<scal_type, row_dim, col_dim>& lhs,
           const mat<scal_type, row_dim, col_dim>& rhs)
{
    for (unsigned i = 0; i < (row_dim * col_dim); ++i) {
        lhs.data_array[i] += rhs.data_array[i];
    }
    return (lhs);
}

template<typename scal_type,
         const unsigned row_dim,
         const unsigned col_dim>
inline mat<scal_type, row_dim, col_dim>&
operator-=(      mat<scal_type, row_dim, col_dim>& lhs,
           const mat<scal_type, row_dim, col_dim>& rhs)
{
    for (unsigned i = 0; i < (row_dim * col_dim); ++i) {
        lhs.data_array[i] -= rhs.data_array[i];
    }
    return (lhs);
}

template<typename scal_type,
         const unsigned order>
inline mat<scal_type, order, order>&
operator*=(      mat<scal_type, order, order>& lhs,
           const mat<scal_type, order, order>& rhs)
{
    mat<scal_type, order, order> tmp_ret;

    unsigned    dst_off;
    unsigned    row_off;
    unsigned    col_off;

    scal_type   tmp_dp;

    for (unsigned c = 0; c < order; ++c) {
        for (unsigned r = 0; r < order; ++r) {
            dst_off = r + order * c;
            tmp_dp = scal_type(0);

            for (unsigned d = 0; d < order; ++d) {
                row_off = r + d * order;
                col_off = d + c * order;
                tmp_dp += lhs.data_array[row_off] * rhs.data_array[col_off];
            }

            tmp_ret.data_array[dst_off] = tmp_dp;
        }
    }

    lhs = tmp_ret;

    return (lhs);
}

template<typename scal_type,
         const unsigned row_dim,
         const unsigned col_dim>
inline mat<scal_type, row_dim, col_dim>&
operator*=(mat<scal_type, row_dim, col_dim>&   lhs,
           const scal_type                     rhs)
{
    for (unsigned i = 0; i < (row_dim * col_dim); ++i) {
        lhs.data_array[i] *= rhs;
    }
    return (lhs);
}

template<typename scal_type,
         const unsigned row_dim,
         const unsigned col_dim>
inline mat<scal_type, row_dim, col_dim>&
operator/=(mat<scal_type, row_dim, col_dim>& lhs,
           const scal_type rhs)
{
    for (unsigned i = 0; i < (row_dim * col_dim); ++i) {
        lhs.data_array[i] /= rhs;
    }
    return (lhs);
}

// binary operators
template<typename scal_type,
         const unsigned order>
inline const mat<scal_type, order, order>
operator*(const mat<scal_type, order, order>& lhs,
          const mat<scal_type, order, order>& rhs)
{
    mat<scal_type, order, order> tmp(lhs);
    return (tmp *= rhs);
}

template<typename scal_type,
         const unsigned row_dim,
         const unsigned col_dim>
inline const mat<scal_type, row_dim, col_dim>
operator*(const mat<scal_type, row_dim, col_dim>& lhs,
          const scal_type                         rhs)
{
    mat<scal_type, row_dim, col_dim> tmp(lhs);
    return (tmp *= rhs);
}

template<typename scal_type,
         const unsigned row_dim,
         const unsigned col_dim>
inline const mat<scal_type, row_dim, col_dim>
operator/(const mat<scal_type, row_dim, col_dim>& lhs,
          const scal_type                         rhs)
{
    mat<scal_type, row_dim, col_dim> tmp(lhs);
    return (tmp /= rhs);
}

template<typename scal_type,
         const unsigned row_dim,
         const unsigned col_dim>
inline const mat<scal_type, row_dim, col_dim>
operator+(const mat<scal_type, row_dim, col_dim>& lhs,
          const mat<scal_type, row_dim, col_dim>& rhs)
{
    mat<scal_type, row_dim, col_dim> tmp(lhs);
    return (tmp += rhs);
}

template<typename scal_type,
         const unsigned row_dim,
         const unsigned col_dim>
inline const mat<scal_type, row_dim, col_dim>
operator-(const mat<scal_type, row_dim, col_dim>& lhs,
          const mat<scal_type, row_dim, col_dim>& rhs)
{
    mat<scal_type, row_dim, col_dim> tmp(lhs);
    return (tmp -= rhs);
}

template<typename scal_type,
         const unsigned order>
inline const vec<scal_type, order>
operator*(const mat<scal_type, order, order>& lhs,
          const vec<scal_type, order>&        rhs)
{
    vec<scal_type, order> tmp_ret(scal_type(0));

    unsigned    row_off;

    scal_type   tmp_dp;

    for (unsigned r = 0; r < order; ++r) {
        tmp_dp = scal_type(0);

        for (unsigned c = 0; c < order; ++c) {
            row_off = r + c * order;
            tmp_dp += lhs.data_array[row_off] * rhs.data_array[c];
        }

        tmp_ret.data_array[r] = tmp_dp;
    }

    return (tmp_ret);
}

template<typename scal_type,
         const unsigned order>
inline const vec<scal_type, order>
operator*(const vec<scal_type, order>&        lhs,
          const mat<scal_type, order, order>& rhs)
{
    vec<scal_type, order> tmp_ret(scal_type(0));

    unsigned    row_off;

    scal_type   tmp_dp;

    for (unsigned r = 0; r < order; ++r) {
        tmp_dp = scal_type(0);

        for (unsigned c = 0; c < order; ++c) {
            row_off = r * order + c;
            tmp_dp += rhs.data_array[row_off] * lhs.data_array[c];
        }

        tmp_ret.data_array[r] = tmp_dp;
    }

    return (tmp_ret);
}

// common functions
template<typename scal_type,
         const unsigned order>
inline void set_identity(mat<scal_type, order, order>& m)
{
    for (unsigned i = 0; i < (order * order); ++i) {
        m.data_array[i] = (i % (order + 1)) == 0 ? scal_type(1) : scal_type(0);
    }
}

template<typename scal_type,
         const unsigned row_dim,
         const unsigned col_dim>
inline const mat<scal_type, row_dim, col_dim> transpose(const mat<scal_type, row_dim, col_dim>& lhs)
{
    mat<scal_type, col_dim, row_dim> tmp_ret;

    unsigned src_off;
    unsigned dst_off;

    for (unsigned c = 0; c < col_dim; ++c) {
        for (unsigned r = 0; r < row_dim; ++r) {
            src_off = r + c * row_dim;
            dst_off = c + r * col_dim;

            tmp_ret.data_array[dst_off] = lhs.data_array[src_off];
        }
    }

    return (tmp_ret);
}

template<typename scal_type,
         const unsigned order>
inline const mat<scal_type, order - 1, order - 1> minor_mat(const mat<scal_type, order, order>& lhs,
                                                            const unsigned row,
                                                            const unsigned col)
{
    mat<scal_type, order - 1, order - 1>   tmp_minor;

    unsigned min_off;
    unsigned src_off;

    unsigned min_row = 0;
    unsigned min_col = 0;

    for (unsigned r = 0; r < order; ++r) {
        if (r != row) {
            min_col = 0;
            for (unsigned c = 0; c < order; ++c) {
                if (c != col) {
                    src_off = r + c * order;
                    min_off = min_row + min_col * (order - 1);

                    tmp_minor.data_array[min_off] = lhs.data_array[src_off];
                    ++min_col;
                }
            }
            ++min_row;
        }
    }

    return (tmp_minor);
}

template<typename scal_type>
inline scal_type determinant(const mat<scal_type, 1, 1>& lhs)
{
    return (lhs.data_array[0]);
}

template<typename scal_type>
inline scal_type determinant(const mat<scal_type, 2, 2>& lhs)
{
    return (lhs.data_array[0] * lhs.data_array[3] - lhs.data_array[1] * lhs.data_array[2]);
}

template<typename scal_type, const unsigned order>
inline scal_type determinant(const mat<scal_type, order, order>& lhs)
{
    scal_type tmp_ret = scal_type(0);

    // determinat development after first column
    for (unsigned r = 0; r < order; ++r) {
        tmp_ret +=  lhs.data_array[r] * sign(-int(r % 2)) * determinant(minor_mat(lhs, r, 0));
    }

    return (tmp_ret);
}

template<typename scal_type, const unsigned order>
inline const mat<scal_type, order, order> inverse(const mat<scal_type, order, order>& lhs)
{
    mat<scal_type, order, order> tmp_ret(mat<scal_type, order, order>::null_mat);
    scal_type                    tmp_det = determinant(lhs);

    unsigned dst_off;

    // ATTENTION!!!! float equal test
    if (tmp_det != scal_type(0)) {
        for (unsigned r = 0; r < order; ++r) {
            for (unsigned c = 0; c < order; ++c) {
                dst_off = c + r * order;
                tmp_ret.data_array[dst_off] = (scal_type(1) / tmp_det) * sign(-int((r+c) % 2)) * determinant(minor_mat(lhs, r, c));
            }
        }
    }

    return (tmp_ret);
}

} // namespace math
} // namespace scm
<|endoftext|>


<commit_before>#include "test.hpp"
#include <natus/require.hpp>

static bool freecalled = false;
void hook_free(void* misc) {
	assert(misc == (void*) 0x1234);
	freecalled = true;
}

Value hook(Value& ctx, Require::HookStep step, char* name, void* misc) {
	if (step == Require::HookStepLoad && !strcmp(name, "__internal__")) {
		assert(!ctx.setRecursive("exports.misc", (long) misc).isException());
		return ctx.newBool(true);
	}

	return NULL;
}

int doTest(Engine& engine, Value& global) {
	Value config = global.newObject();
	Value   path = global.newArrayBuilder("./lib");
	config.setRecursive("natus.require.path", path, Value::PropAttrNone, true);

	// Initialize the require system
	Require req(global);
	assert(req.initialize(config));
	assert(req.addHook("__internal__", hook, (void*) 0x1234, hook_free));

	// Load the internal module
	Value modulea = req.require("__internal__");
	assert(!modulea.isException());
	assert(modulea.isObject());
	assert(modulea.get("misc").toLong() == 0x1234);

	// Load the internal module again
	Value moduleb = req.require("__internal__");
	assert(!modulea.isException());
	assert(modulea.isObject());
	assert(modulea.get("misc").toLong() == 0x1234);

	// Check to make sure that the underlying values refer to the same object
	assert(!modulea.set("test", 17L).isException());
	assert(moduleb.get("test").toLong() == 17);

	// Cleanup
	return 0;
}
<commit_msg>add script test<commit_after>#include "test.hpp"
#include <natus/require.hpp>

static bool freecalled = false;
void hook_free(void* misc) {
	assert(misc == (void*) 0x1234);
	freecalled = true;
}

Value hook(Value& ctx, Require::HookStep step, char* name, void* misc) {
	if (step == Require::HookStepLoad && !strcmp(name, "__internal__")) {
		assert(!ctx.setRecursive("exports.misc", (long) misc).isException());
		return ctx.newBool(true);
	}

	return NULL;
}

int doTest(Engine& engine, Value& global) {
	Value config = global.newObject();
	Value   path = global.newArrayBuilder("./lib");
	config.setRecursive("natus.require.path", path, Value::PropAttrNone, true);

	// Initialize the require system
	Require req(global);
	assert(req.initialize(config));
	assert(req.addHook("__internal__", hook, (void*) 0x1234, hook_free));

	// Load the internal module
	Value modulea = req.require("__internal__");
	assert(!modulea.isException());
	assert(modulea.isObject());
	assert(modulea.get("misc").toLong() == 0x1234);

	// Load the internal module again
	Value moduleb = req.require("__internal__");
	assert(!moduleb.isException());
	assert(moduleb.isObject());
	assert(moduleb.get("misc").toLong() == 0x1234);

	// Check to make sure that the underlying values refer to the same object
	assert(!modulea.set("test", 17L).isException());
	assert(moduleb.get("test").toLong() == 17);

	// Load a script file
	Value scriptmod = req.require("script");
	assert(!scriptmod.isException());
	assert(scriptmod.isObject());
	assert(scriptmod.get("number").toLong() == 115);
	assert(scriptmod.get("string").toStringUTF8() == "hello world");

	// Cleanup
	return 0;
}
<|endoftext|>


<commit_before><commit_msg>Switch testnet3's message bytes to avoid connecting to old nodes.<commit_after><|endoftext|>


<commit_before>//
//  MCIMAPMultiDisconnectOperation.cc
//  mailcore2
//
//  Created by Hoa V. DINH on 11/7/13.
//  Copyright (c) 2013 MailCore. All rights reserved.
//

#include "MCIMAPMultiDisconnectOperation.h"

using namespace mailcore;

IMAPMultiDisconnectOperation::IMAPMultiDisconnectOperation()
{
    _count = 0;
    _operations = new Array();
}

IMAPMultiDisconnectOperation::~IMAPMultiDisconnectOperation()
{
    MC_SAFE_RELEASE(_operations);
}

void IMAPMultiDisconnectOperation::addOperation(IMAPOperation * op)
{
    _operations->addObject(op);
}

void IMAPMultiDisconnectOperation::start()
{
    if (_operations->count() == 0) {
        if (callback() != NULL) {
            callback()->operationFinished(this);
        }
        return;
    }

    retain();
    mc_foreacharray(IMAPOperation, op, _operations) {
        op->setCallbackDispatchQueue(this->callbackDispatchQueue());
        op->setCallback(this);
        op->start();
    }
}

void IMAPMultiDisconnectOperation::operationFinished(Operation * op)
{
    _count ++;
    if (_count == _operations->count()) {
        if (callback() != NULL) {
            callback()->operationFinished(this);
        }
        release();
    }
}

<commit_msg>Fixed build for Linux in MCIMAPMultiDisconnectOperation<commit_after>//
//  MCIMAPMultiDisconnectOperation.cc
//  mailcore2
//
//  Created by Hoa V. DINH on 11/7/13.
//  Copyright (c) 2013 MailCore. All rights reserved.
//

#include "MCIMAPMultiDisconnectOperation.h"

using namespace mailcore;

IMAPMultiDisconnectOperation::IMAPMultiDisconnectOperation()
{
    _count = 0;
    _operations = new Array();
}

IMAPMultiDisconnectOperation::~IMAPMultiDisconnectOperation()
{
    MC_SAFE_RELEASE(_operations);
}

void IMAPMultiDisconnectOperation::addOperation(IMAPOperation * op)
{
    _operations->addObject(op);
}

void IMAPMultiDisconnectOperation::start()
{
    if (_operations->count() == 0) {
        if (callback() != NULL) {
            callback()->operationFinished(this);
        }
        return;
    }

    retain();
    mc_foreacharray(IMAPOperation, op, _operations) {
#if __APPLE__
        op->setCallbackDispatchQueue(this->callbackDispatchQueue());
#endif
        op->setCallback(this);
        op->start();
    }
}

void IMAPMultiDisconnectOperation::operationFinished(Operation * op)
{
    _count ++;
    if (_count == _operations->count()) {
        if (callback() != NULL) {
            callback()->operationFinished(this);
        }
        release();
    }
}

<|endoftext|>