<commit_before>// Copyright (c) 2020 The Orbit Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <gmock/gmock-matchers.h>
#include <gtest/gtest.h>

#include <memory>
#include <utility>

#include "OrbitBase/ThreadPool.h"
#include "absl/synchronization/mutex.h"
#include "absl/time/clock.h"

TEST(ThreadPool, Smoke) {
  constexpr size_t kThreadPoolMinSize = 1;
  constexpr size_t kThreadPoolMaxSize = 2;
  constexpr absl::Duration kThreadTtl = absl::Milliseconds(5);
  std::unique_ptr<ThreadPool> thread_pool =
      ThreadPool::Create(kThreadPoolMinSize, kThreadPoolMaxSize, kThreadTtl);

  absl::Mutex mutex;
  bool called = false;

  {
    absl::MutexLock lock(&mutex);
    thread_pool->Schedule([&]() {
      absl::MutexLock lock(&mutex);
      called = true;
    });

    EXPECT_FALSE(called);

    EXPECT_TRUE(mutex.AwaitWithTimeout(absl::Condition(
                                           +[](bool* called) { return *called; }, &called),
                                       absl::Milliseconds(100)));

    EXPECT_TRUE(called);
    called = false;
  }

  thread_pool->ShutdownAndWait();

  EXPECT_FALSE(called);
}

TEST(ThreadPool, QueuedActionsExecutedOnShutdown) {
  constexpr size_t kThreadPoolMinSize = 1;
  constexpr size_t kThreadPoolMaxSize = 2;
  constexpr absl::Duration kThreadTtl = absl::Milliseconds(5);
  std::unique_ptr<ThreadPool> thread_pool =
      ThreadPool::Create(kThreadPoolMinSize, kThreadPoolMaxSize, kThreadTtl);

  absl::Mutex mutex;
  size_t counter = 0;

  constexpr size_t kNumberOfActions = 7;
  {
    absl::MutexLock lock(&mutex);
    for (size_t i = 0; i < kNumberOfActions; ++i) {
      thread_pool->Schedule([&]() {
        absl::MutexLock lock(&mutex);
        counter++;
      });
    }

    // All actions are waiting on the mutex, they won't resume until we unlock
    thread_pool->Shutdown();
  }

  thread_pool->Wait();

  EXPECT_EQ(counter, kNumberOfActions);
}

TEST(ThreadPool, CheckTtl) {
  constexpr size_t kThreadPoolMinSize = 1;
  constexpr size_t kThreadPoolMaxSize = 5;
  constexpr size_t kThreadTtlMillis = 5;
  std::unique_ptr<ThreadPool> thread_pool = ThreadPool::Create(
      kThreadPoolMinSize, kThreadPoolMaxSize, absl::Milliseconds(kThreadTtlMillis));

  absl::Mutex actions_started_mutex;
  size_t actions_started = 0;
  absl::Mutex actions_executed_mutex;
  size_t actions_executed = 0;

  auto action = [&] {
    actions_started_mutex.Lock();
    actions_started++;
    actions_started_mutex.Unlock();

    absl::MutexLock lock(&actions_executed_mutex);
    actions_executed++;
  };

  constexpr size_t kNumberOfActions = 7;
  {
    absl::MutexLock lock(&actions_executed_mutex);
    for (size_t i = 0; i < kNumberOfActions; ++i) {
      thread_pool->Schedule(action);
    }
    // Wait until actions are on worker threads
    actions_started_mutex.Lock();
    EXPECT_TRUE(actions_started_mutex.AwaitWithTimeout(
        absl::Condition(
            +[](size_t* started) { return *started == 5; }, &actions_started),
        absl::Milliseconds(50)));
    actions_started_mutex.Unlock();

    // Now check the thread_pool size
    EXPECT_EQ(thread_pool->GetPoolSize(), kThreadPoolMaxSize);

    // Wait until all actions completed.
    EXPECT_TRUE(actions_executed_mutex.AwaitWithTimeout(
        absl::Condition(
            +[](size_t* executed) { return *executed == 7; }, &actions_executed),
        absl::Milliseconds(50)));
  }

  // +10 because there might be some milliseconds between action is complete and
  // thread went idle
  absl::SleepFor(absl::Milliseconds(kThreadTtlMillis + 10));

  EXPECT_EQ(thread_pool->GetPoolSize(), kThreadPoolMinSize);

  thread_pool->ShutdownAndWait();
}

TEST(ThreadPool, ExtendThreadPool) {
  constexpr size_t kThreadPoolMinSize = 1;
  constexpr size_t kThreadPoolMaxSize = 5;
  constexpr size_t kThreadTtlMillis = 5;
  std::unique_ptr<ThreadPool> thread_pool = ThreadPool::Create(
      kThreadPoolMinSize, kThreadPoolMaxSize, absl::Milliseconds(kThreadTtlMillis));

  absl::Mutex actions_started_mutex;
  size_t actions_started = 0;
  absl::Mutex actions_executed_mutex;
  size_t actions_executed = 0;

  auto action = [&] {
    actions_started_mutex.Lock();
    actions_started++;
    actions_started_mutex.Unlock();

    absl::MutexLock lock(&actions_executed_mutex);
    actions_executed++;
  };

  {
    // Schedule an action and check we have one worker thread
    absl::MutexLock lock(&actions_executed_mutex);
    thread_pool->Schedule(action);

    actions_started_mutex.Lock();
    EXPECT_TRUE(actions_started_mutex.AwaitWithTimeout(
        absl::Condition(
            +[](size_t* started) { return *started == 1; }, &actions_started),
        absl::Milliseconds(100)))
        << "actions_started=" << actions_started << ", expected 1";
    actions_started_mutex.Unlock();

    EXPECT_EQ(thread_pool->GetPoolSize(), 1);

    // Schedule another action and check that there are 2 workers threads now.
    thread_pool->Schedule(action);

    actions_started_mutex.Lock();
    EXPECT_TRUE(actions_started_mutex.AwaitWithTimeout(
        absl::Condition(
            +[](size_t* started) { return *started == 2; }, &actions_started),
        absl::Milliseconds(100)));
    actions_started_mutex.Unlock();

    EXPECT_EQ(thread_pool->GetPoolSize(), 2);

    // Add more and make sure worker thread number remains kThreadPoolMaxSize
    for (size_t i = 0; i < 10; ++i) {
      thread_pool->Schedule(action);
    }

    // I do not think there is a way around it - sleep for 50ms
    absl::SleepFor(absl::Milliseconds(50));

    EXPECT_EQ(thread_pool->GetPoolSize(), kThreadPoolMaxSize);

    actions_started_mutex.Lock();
    EXPECT_EQ(actions_started, kThreadPoolMaxSize);
    actions_started_mutex.Unlock();

    EXPECT_TRUE(actions_executed_mutex.AwaitWithTimeout(
        absl::Condition(
            +[](size_t* executed) { return *executed == 12; }, &actions_executed),
        absl::Milliseconds(100)))
        << "actions_executed=" << actions_executed << ", expected 12";
  }

  // Wait for ttl+10 and check that ThreadPool has reduced
  // number of worker threads
  absl::SleepFor(absl::Milliseconds(kThreadTtlMillis + 10));
  EXPECT_EQ(thread_pool->GetPoolSize(), kThreadPoolMinSize);

  EXPECT_EQ(actions_started, 12);
  EXPECT_EQ(actions_executed, 12);

  // Now make sure thread_pool increases number of threads
  // correctly after reducing number of worker threads.
  {
    absl::MutexLock lock(&actions_executed_mutex);
    thread_pool->Schedule(action);

    actions_started_mutex.Lock();
    EXPECT_TRUE(actions_started_mutex.AwaitWithTimeout(
        absl::Condition(
            +[](size_t* started) { return *started == 13; }, &actions_started),
        absl::Milliseconds(100)));
    actions_started_mutex.Unlock();

    EXPECT_EQ(thread_pool->GetPoolSize(), 1);

    thread_pool->Schedule(action);

    actions_started_mutex.Lock();
    EXPECT_TRUE(actions_started_mutex.AwaitWithTimeout(
        absl::Condition(
            +[](size_t* started) { return *started == 14; }, &actions_started),
        absl::Milliseconds(100)));
    actions_started_mutex.Unlock();

    EXPECT_EQ(thread_pool->GetPoolSize(), 2);

    // Add more and make sure worker thread number remains kThreadPoolMaxSize
    for (size_t i = 0; i < 10; ++i) {
      thread_pool->Schedule(action);
    }

    // I do not think there is a way around it - sleep for 50ms
    absl::SleepFor(absl::Milliseconds(50));

    EXPECT_EQ(thread_pool->GetPoolSize(), kThreadPoolMaxSize);
    actions_started_mutex.Lock();
    EXPECT_EQ(actions_started, 12 + kThreadPoolMaxSize);
    actions_started_mutex.Unlock();
  }

  thread_pool->ShutdownAndWait();

  EXPECT_EQ(actions_started, 24);
  EXPECT_EQ(actions_executed, 24);
}

TEST(ThreadPool, InvalidArguments) {
  EXPECT_DEATH(
      {
        auto thread_pool = ThreadPool::Create(0, 1, absl::Milliseconds(1));
        thread_pool->ShutdownAndWait();
      },
      "");
  EXPECT_DEATH(
      {
        auto thread_pool = ThreadPool::Create(2, 1, absl::Milliseconds(1));
        thread_pool->ShutdownAndWait();
      },
      "");
  EXPECT_DEATH(
      {
        auto thread_pool = ThreadPool::Create(0, 0, absl::Milliseconds(1));
        thread_pool->ShutdownAndWait();
      },
      "");
  EXPECT_DEATH(
      {
        auto thread_pool = ThreadPool::Create(1, 2, absl::Milliseconds(0));
        thread_pool->ShutdownAndWait();
      },
      "");
  EXPECT_DEATH(
      {
        auto thread_pool = ThreadPool::Create(1, 2, absl::Nanoseconds(999));
        thread_pool->ShutdownAndWait();
      },
      "");
}

TEST(ThreadPool, NoShutdown) { EXPECT_DEATH(ThreadPool::Create(1, 4, absl::Milliseconds(10)), ""); }

TEST(ThreadPool, ScheduleAfterShutdown) {
  EXPECT_DEATH(
      {
        constexpr size_t kThreadPoolMinSize = 1;
        constexpr size_t kThreadPoolMaxSize = 2;
        constexpr absl::Duration kThreadTtl = absl::Milliseconds(5);
        std::unique_ptr<ThreadPool> thread_pool =
            ThreadPool::Create(kThreadPoolMinSize, kThreadPoolMaxSize, kThreadTtl);

        thread_pool->Shutdown();
        thread_pool->Schedule([] {});
      },
      "");
}

TEST(ThreadPool, WaitWithoutShutdown) {
  EXPECT_DEATH(
      {
        constexpr size_t kThreadPoolMinSize = 1;
        constexpr size_t kThreadPoolMaxSize = 2;
        constexpr absl::Duration kThreadTtl = absl::Milliseconds(5);
        std::unique_ptr<ThreadPool> thread_pool =
            ThreadPool::Create(kThreadPoolMinSize, kThreadPoolMaxSize, kThreadTtl);

        thread_pool->Wait();
      },
      "");
}
<commit_msg>Add CheckGetNumberOfBusyThreads test<commit_after>// Copyright (c) 2020 The Orbit Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <gmock/gmock-matchers.h>
#include <gtest/gtest.h>

#include <memory>
#include <utility>

#include "OrbitBase/ThreadPool.h"
#include "absl/synchronization/mutex.h"
#include "absl/time/clock.h"

TEST(ThreadPool, Smoke) {
  constexpr size_t kThreadPoolMinSize = 1;
  constexpr size_t kThreadPoolMaxSize = 2;
  constexpr absl::Duration kThreadTtl = absl::Milliseconds(5);
  std::unique_ptr<ThreadPool> thread_pool =
      ThreadPool::Create(kThreadPoolMinSize, kThreadPoolMaxSize, kThreadTtl);

  absl::Mutex mutex;
  bool called = false;

  {
    absl::MutexLock lock(&mutex);
    thread_pool->Schedule([&]() {
      absl::MutexLock lock(&mutex);
      called = true;
    });

    EXPECT_FALSE(called);

    EXPECT_TRUE(mutex.AwaitWithTimeout(absl::Condition(
                                           +[](bool* called) { return *called; }, &called),
                                       absl::Milliseconds(100)));

    EXPECT_TRUE(called);
    called = false;
  }

  thread_pool->ShutdownAndWait();

  EXPECT_FALSE(called);
}

TEST(ThreadPool, QueuedActionsExecutedOnShutdown) {
  constexpr size_t kThreadPoolMinSize = 1;
  constexpr size_t kThreadPoolMaxSize = 2;
  constexpr absl::Duration kThreadTtl = absl::Milliseconds(5);
  std::unique_ptr<ThreadPool> thread_pool =
      ThreadPool::Create(kThreadPoolMinSize, kThreadPoolMaxSize, kThreadTtl);

  absl::Mutex mutex;
  size_t counter = 0;

  constexpr size_t kNumberOfActions = 7;
  {
    absl::MutexLock lock(&mutex);
    for (size_t i = 0; i < kNumberOfActions; ++i) {
      thread_pool->Schedule([&]() {
        absl::MutexLock lock(&mutex);
        counter++;
      });
    }

    // All actions are waiting on the mutex, they won't resume until we unlock
    thread_pool->Shutdown();
  }

  thread_pool->Wait();

  EXPECT_EQ(counter, kNumberOfActions);
}

TEST(ThreadPool, CheckTtl) {
  constexpr size_t kThreadPoolMinSize = 1;
  constexpr size_t kThreadPoolMaxSize = 5;
  constexpr size_t kThreadTtlMillis = 5;
  std::unique_ptr<ThreadPool> thread_pool = ThreadPool::Create(
      kThreadPoolMinSize, kThreadPoolMaxSize, absl::Milliseconds(kThreadTtlMillis));

  absl::Mutex actions_started_mutex;
  size_t actions_started = 0;
  absl::Mutex actions_executed_mutex;
  size_t actions_executed = 0;

  auto action = [&] {
    actions_started_mutex.Lock();
    actions_started++;
    actions_started_mutex.Unlock();

    absl::MutexLock lock(&actions_executed_mutex);
    actions_executed++;
  };

  constexpr size_t kNumberOfActions = 7;
  {
    absl::MutexLock lock(&actions_executed_mutex);
    for (size_t i = 0; i < kNumberOfActions; ++i) {
      thread_pool->Schedule(action);
    }
    // Wait until actions are on worker threads
    actions_started_mutex.Lock();
    EXPECT_TRUE(actions_started_mutex.AwaitWithTimeout(
        absl::Condition(
            +[](size_t* started) { return *started == 5; }, &actions_started),
        absl::Milliseconds(50)));
    actions_started_mutex.Unlock();

    // Now check the thread_pool size
    EXPECT_EQ(thread_pool->GetPoolSize(), kThreadPoolMaxSize);

    // Wait until all actions completed.
    EXPECT_TRUE(actions_executed_mutex.AwaitWithTimeout(
        absl::Condition(
            +[](size_t* executed) { return *executed == 7; }, &actions_executed),
        absl::Milliseconds(50)));
  }

  // +10 because there might be some milliseconds between action is complete and
  // thread went idle
  absl::SleepFor(absl::Milliseconds(kThreadTtlMillis + 10));

  EXPECT_EQ(thread_pool->GetPoolSize(), kThreadPoolMinSize);

  thread_pool->ShutdownAndWait();
}

TEST(ThreadPool, ExtendThreadPool) {
  constexpr size_t kThreadPoolMinSize = 1;
  constexpr size_t kThreadPoolMaxSize = 5;
  constexpr size_t kThreadTtlMillis = 5;
  std::unique_ptr<ThreadPool> thread_pool = ThreadPool::Create(
      kThreadPoolMinSize, kThreadPoolMaxSize, absl::Milliseconds(kThreadTtlMillis));

  absl::Mutex actions_started_mutex;
  size_t actions_started = 0;
  absl::Mutex actions_executed_mutex;
  size_t actions_executed = 0;

  auto action = [&] {
    actions_started_mutex.Lock();
    actions_started++;
    actions_started_mutex.Unlock();

    absl::MutexLock lock(&actions_executed_mutex);
    actions_executed++;
  };

  {
    // Schedule an action and check we have one worker thread
    absl::MutexLock lock(&actions_executed_mutex);
    thread_pool->Schedule(action);

    actions_started_mutex.Lock();
    EXPECT_TRUE(actions_started_mutex.AwaitWithTimeout(
        absl::Condition(
            +[](size_t* started) { return *started == 1; }, &actions_started),
        absl::Milliseconds(100)))
        << "actions_started=" << actions_started << ", expected 1";
    actions_started_mutex.Unlock();

    EXPECT_EQ(thread_pool->GetPoolSize(), 1);

    // Schedule another action and check that there are 2 workers threads now.
    thread_pool->Schedule(action);

    actions_started_mutex.Lock();
    EXPECT_TRUE(actions_started_mutex.AwaitWithTimeout(
        absl::Condition(
            +[](size_t* started) { return *started == 2; }, &actions_started),
        absl::Milliseconds(100)));
    actions_started_mutex.Unlock();

    EXPECT_EQ(thread_pool->GetPoolSize(), 2);

    // Add more and make sure worker thread number remains kThreadPoolMaxSize
    for (size_t i = 0; i < 10; ++i) {
      thread_pool->Schedule(action);
    }

    // I do not think there is a way around it - sleep for 50ms
    absl::SleepFor(absl::Milliseconds(50));

    EXPECT_EQ(thread_pool->GetPoolSize(), kThreadPoolMaxSize);

    actions_started_mutex.Lock();
    EXPECT_EQ(actions_started, kThreadPoolMaxSize);
    actions_started_mutex.Unlock();

    EXPECT_TRUE(actions_executed_mutex.AwaitWithTimeout(
        absl::Condition(
            +[](size_t* executed) { return *executed == 12; }, &actions_executed),
        absl::Milliseconds(100)))
        << "actions_executed=" << actions_executed << ", expected 12";
  }

  // Wait for ttl+10 and check that ThreadPool has reduced
  // number of worker threads
  absl::SleepFor(absl::Milliseconds(kThreadTtlMillis + 10));
  EXPECT_EQ(thread_pool->GetPoolSize(), kThreadPoolMinSize);

  EXPECT_EQ(actions_started, 12);
  EXPECT_EQ(actions_executed, 12);

  // Now make sure thread_pool increases number of threads
  // correctly after reducing number of worker threads.
  {
    absl::MutexLock lock(&actions_executed_mutex);
    thread_pool->Schedule(action);

    actions_started_mutex.Lock();
    EXPECT_TRUE(actions_started_mutex.AwaitWithTimeout(
        absl::Condition(
            +[](size_t* started) { return *started == 13; }, &actions_started),
        absl::Milliseconds(100)));
    actions_started_mutex.Unlock();

    EXPECT_EQ(thread_pool->GetPoolSize(), 1);

    thread_pool->Schedule(action);

    actions_started_mutex.Lock();
    EXPECT_TRUE(actions_started_mutex.AwaitWithTimeout(
        absl::Condition(
            +[](size_t* started) { return *started == 14; }, &actions_started),
        absl::Milliseconds(100)));
    actions_started_mutex.Unlock();

    EXPECT_EQ(thread_pool->GetPoolSize(), 2);

    // Add more and make sure worker thread number remains kThreadPoolMaxSize
    for (size_t i = 0; i < 10; ++i) {
      thread_pool->Schedule(action);
    }

    // I do not think there is a way around it - sleep for 50ms
    absl::SleepFor(absl::Milliseconds(50));

    EXPECT_EQ(thread_pool->GetPoolSize(), kThreadPoolMaxSize);
    actions_started_mutex.Lock();
    EXPECT_EQ(actions_started, 12 + kThreadPoolMaxSize);
    actions_started_mutex.Unlock();
  }

  thread_pool->ShutdownAndWait();

  EXPECT_EQ(actions_started, 24);
  EXPECT_EQ(actions_executed, 24);
}

TEST(ThreadPool, CheckGetNumberOfBusyThreads) {
  constexpr size_t kThreadPoolMinSize = 1;
  constexpr size_t kThreadPoolMaxSize = 2;
  constexpr size_t kThreadTtlMillis = 5;

  std::unique_ptr<ThreadPool> thread_pool = ThreadPool::Create(
      kThreadPoolMinSize, kThreadPoolMaxSize, absl::Milliseconds(kThreadTtlMillis));

  EXPECT_EQ(thread_pool->GetNumberOfBusyThreads(), 0);

  absl::Mutex actions_started_mutex;
  size_t actions_started = 0;
  absl::Mutex actions_executed_mutex;
  size_t actions_executed = 0;

  auto action = [&] {
    actions_started_mutex.Lock();
    actions_started++;
    actions_started_mutex.Unlock();

    absl::MutexLock lock(&actions_executed_mutex);
    actions_executed++;
  };

  {
    // Schedule an action and check we have a busy thread
    absl::MutexLock lock(&actions_executed_mutex);
    thread_pool->Schedule(action);

    actions_started_mutex.Lock();
    EXPECT_TRUE(actions_started_mutex.AwaitWithTimeout(
        absl::Condition(
            +[](size_t* started) { return *started == 1; }, &actions_started),
        absl::Milliseconds(100)))
        << "actions_started=" << actions_started << ", expected 1";
    actions_started_mutex.Unlock();
    EXPECT_EQ(thread_pool->GetNumberOfBusyThreads(), 1);

    // Schedule another action and check that there are 2 workers threads now.
    thread_pool->Schedule(action);

    actions_started_mutex.Lock();
    EXPECT_TRUE(actions_started_mutex.AwaitWithTimeout(
        absl::Condition(
            +[](size_t* started) { return *started == 2; }, &actions_started),
        absl::Milliseconds(100)));
    actions_started_mutex.Unlock();
    EXPECT_EQ(thread_pool->GetNumberOfBusyThreads(), 2);

    EXPECT_TRUE(actions_executed_mutex.AwaitWithTimeout(
        absl::Condition(
            +[](size_t* executed) { return *executed == 2; }, &actions_executed),
        absl::Milliseconds(100)))
        << "actions_executed=" << actions_executed << ", expected 2";
  }

  // Check there are no busy threads anymore
  EXPECT_EQ(thread_pool->GetNumberOfBusyThreads(), 0);

  thread_pool->ShutdownAndWait();
};

TEST(ThreadPool, InvalidArguments) {
  EXPECT_DEATH(
      {
        auto thread_pool = ThreadPool::Create(0, 1, absl::Milliseconds(1));
        thread_pool->ShutdownAndWait();
      },
      "");
  EXPECT_DEATH(
      {
        auto thread_pool = ThreadPool::Create(2, 1, absl::Milliseconds(1));
        thread_pool->ShutdownAndWait();
      },
      "");
  EXPECT_DEATH(
      {
        auto thread_pool = ThreadPool::Create(0, 0, absl::Milliseconds(1));
        thread_pool->ShutdownAndWait();
      },
      "");
  EXPECT_DEATH(
      {
        auto thread_pool = ThreadPool::Create(1, 2, absl::Milliseconds(0));
        thread_pool->ShutdownAndWait();
      },
      "");
  EXPECT_DEATH(
      {
        auto thread_pool = ThreadPool::Create(1, 2, absl::Nanoseconds(999));
        thread_pool->ShutdownAndWait();
      },
      "");
}

TEST(ThreadPool, NoShutdown) { EXPECT_DEATH(ThreadPool::Create(1, 4, absl::Milliseconds(10)), ""); }

TEST(ThreadPool, ScheduleAfterShutdown) {
  EXPECT_DEATH(
      {
        constexpr size_t kThreadPoolMinSize = 1;
        constexpr size_t kThreadPoolMaxSize = 2;
        constexpr absl::Duration kThreadTtl = absl::Milliseconds(5);
        std::unique_ptr<ThreadPool> thread_pool =
            ThreadPool::Create(kThreadPoolMinSize, kThreadPoolMaxSize, kThreadTtl);

        thread_pool->Shutdown();
        thread_pool->Schedule([] {});
      },
      "");
}

TEST(ThreadPool, WaitWithoutShutdown) {
  EXPECT_DEATH(
      {
        constexpr size_t kThreadPoolMinSize = 1;
        constexpr size_t kThreadPoolMaxSize = 2;
        constexpr absl::Duration kThreadTtl = absl::Milliseconds(5);
        std::unique_ptr<ThreadPool> thread_pool =
            ThreadPool::Create(kThreadPoolMinSize, kThreadPoolMaxSize, kThreadTtl);

        thread_pool->Wait();
      },
      "");
}
<|endoftext|>


<commit_before>/** @file
    @brief Implementation

    @date 2016

    @author
    Sensics, Inc.
    <http://sensics.com/osvr>
*/

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

// Internal Includes
#include "FindDriver.h"

// Library/third-party includes
#include <boost/iostreams/stream.hpp>
#include <json/reader.h>
#include <json/value.h>
#include <osvr/Util/Finally.h>
#include <osvr/Util/PlatformConfig.h>

// Standard includes
#include <fstream> // std::ifstream
#include <iostream>
#include <limits.h>
#include <vector>

#if defined(OSVR_USING_FILESYSTEM_HEADER)
#include <filesystem>
#elif defined(OSVR_USING_BOOST_FILESYSTEM)
#include <boost/filesystem.hpp>
#endif

#if defined(OSVR_WINDOWS)
#include <shlobj.h>
#else
#include <cstdlib> // for getenv
#endif

#undef VIVELOADER_VERBOSE

using osvr::util::finally;

namespace osvr {
namespace vive {
#if defined(OSVR_WINDOWS)
    static const auto PLATFORM_DIRNAME_BASE = "win";
    static const auto DRIVER_EXTENSION = ".dll";
    static const auto TOOL_EXTENSION = ".exe";
    static const auto PATH_SEP = "\\";
#elif defined(OSVR_MACOSX)
    /// @todo Note that there are no 64-bit steamvr runtimes or drivers on OS X
    static const auto PLATFORM_DIRNAME_BASE = "osx";
    static const auto DRIVER_EXTENSION = ".dylib";
    static const auto TOOL_EXTENSION = "";
    static const auto PATH_SEP = "/";
#elif defined(OSVR_LINUX)
    static const auto PLATFORM_DIRNAME_BASE = "linux";
    static const auto DRIVER_EXTENSION = ".so";
    static const auto TOOL_EXTENSION = "";
    static const auto PATH_SEP = "/";
#else
#error "Sorry, Valve does not produce a SteamVR runtime for your platform."
#endif

#if defined(OSVR_USING_FILESYSTEM_TR2)
    using std::tr2::sys::path;
    using std::tr2::sys::wpath;
    using std::tr2::sys::exists;
#elif defined(OSVR_USING_FILESYSTEM_EXPERIMENTAL)
    using std::experimental::filesystem::path;
#ifdef _WIN32
    /// Some Windows functions deal in wide strings, easier to just let it cope
    /// with it.
    using wpath = path;
#endif
    using std::experimental::filesystem::exists;
#elif defined(OSVR_USING_BOOST_FILESYSTEM)
    using boost::filesystem::path;
    using boost::filesystem::exists;
#endif

#ifdef OSVR_MACOSX
    inline std::string getPlatformBitSuffix() {
        // they use universal binaries but stick them in "32"
        return "32";
    }
#else
    inline std::string getPlatformBitSuffix() {
        return std::to_string(sizeof(void *) * CHAR_BIT);
    }
#endif

    inline std::string getPlatformDirname() {
        return PLATFORM_DIRNAME_BASE + getPlatformBitSuffix();
    }

    inline void parsePathConfigFile(std::istream &is, Json::Value &ret) {
        Json::Reader reader;
        if (!reader.parse(is, ret)) {
            std::cerr << "Error parsing file containing path configuration - "
                         "have you run SteamVR yet?"
                      << std::endl;
            std::cerr << reader.getFormattedErrorMessages() << std::endl;
        }
    }

#if defined(OSVR_WINDOWS)
    inline Json::Value getPathConfig() {
        PWSTR outString = nullptr;
        Json::Value ret;
        // It's OK to use Vista+ stuff here, since openvr_api.dll uses Vista+
        // stuff too.
        auto hr =
            SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr, &outString);
        if (!SUCCEEDED(hr)) {
            std::cerr << "Could not get local app data directory!" << std::endl;
            return ret;
        }
        // Free the string returned when we're all done.
        auto freeString = [&] { CoTaskMemFree(outString); };
        // Build the path to the file.
        auto vrPaths =
            wpath(outString) / wpath(L"openvr") / wpath(L"openvrpaths.vrpath");
        std::ifstream is(vrPaths.string());
        if (!is) {
            std::wcerr << L"Could not open file containing path configuration "
                          L"- have you run SteamVR yet? "
                       << vrPaths << L"\n";
            return ret;
        }
        parsePathConfigFile(is, ret);
        return ret;
    }

#elif defined(OSVR_MACOSX) || defined(OSVR_LINUX)
    inline Json::Value getPathConfig() {
        auto home = std::getenv("HOME");
        path homePath =
            (nullptr == home
                 ? path{"~"}
                 /*that's weird, should have been in environment...*/
                 : path{home});
        auto vrPaths = homePath / path{".openvr"} / path{"openvrpaths.vrpath"};

        std::ifstream is(vrPaths.string());
        Json::Value ret;
        if (!is) {
            std::cerr << "Could not open file containing path configuration "
                         "- have you run SteamVR yet? "
                      << vrPaths << "\n";
            return ret;
        }
        parsePathConfigFile(is, ret);
        return ret;
    }
#endif

    inline std::vector<std::string> getSteamVRRoots(Json::Value const &json) {
        std::vector<std::string> ret;
        auto &runtimes = json["runtime"];
        if (!runtimes.isArray()) {
            return ret;
        }
        for (auto &runtime : runtimes) {
            ret.emplace_back(runtime.asString());
        }
        return ret;
    }

    inline std::vector<std::string> getSteamVRRoots() {
        return getSteamVRRoots(getPathConfig());
    }

    inline void computeDriverRootAndFilePath(DriverLocationInfo &info,
                                             std::string const &driver) {
        auto p = path{info.steamVrRoot};
        p /= "drivers";
        p /= driver;
        p /= "bin";
        p /= getPlatformDirname();
        info.driverRoot = p.string();
        p /= ("driver_" + driver + DRIVER_EXTENSION);
        info.driverFile = p.string();
    }

    /// Underlying implementation - hand it the preloaded json.
    inline DriverLocationInfo findDriver(Json::Value const &json,
                                         std::string const &driver) {

        DriverLocationInfo info;
        auto &runtimes = json["runtime"];
        if (!runtimes.isArray()) {
            return info;
        }

        for (auto &root : runtimes) {
            info.steamVrRoot = root.asString();
            info.driverName = driver;

            computeDriverRootAndFilePath(info, driver);

#ifdef VIVELOADER_VERBOSE
            std::cout << "Will try to load driver from:\n"
                      << info.driverFile << std::endl;
            if (exists(path{info.driverRoot})) {
                std::cout << "Driver root exists" << std::endl;
            }
            if (exists(path{info.driverFile})) {
                std::cout << "Driver file exists" << std::endl;
            }
#endif

            if (exists(path{info.driverRoot}) &&
                exists(path{info.driverFile})) {
                info.found = true;
                return info;
            }
        }
        info = DriverLocationInfo{};
        return info;
    }

    DriverLocationInfo findDriver(std::string const &driver) {
        return findDriver(getPathConfig(), driver);
    }

    std::string getToolLocation(std::string const &toolName,
                                std::string const &steamVrRoot) {
        std::vector<std::string> searchPath;
        if (!steamVrRoot.empty()) {
            searchPath = {steamVrRoot};
        } else {
            searchPath = getSteamVRRoots();
        }

        for (auto &root : searchPath) {
            auto p = path{root};
            p /= "bin";
            p /= getPlatformDirname();
            p /= (toolName + TOOL_EXTENSION);
            if (exists(p)) {
                return p.string();
            }
        }
        return std::string{};
    }

    /// Underlying implementation - hand it the preloaded json.
    inline ConfigDirs findConfigDirs(Json::Value const &json,
                                     std::string const &driver) {
        ConfigDirs ret;
        auto const &configLocations = json["config"];
        if (!configLocations.isArray()) {
            return ret;
        }
        for (auto &configDir : configLocations) {
            auto configPath = path{configDir.asString()};
            if (!exists(configPath)) {
                continue;
            }
            ret.rootConfigDir = configPath.string();
            ret.driverConfigDir = (configPath / path{driver}).string();
            ret.valid = true;
            return ret;
        }
        ret = ConfigDirs{};
        return ret;
    }

    ConfigDirs findConfigDirs(std::string const & /*steamVrRoot*/,
                              std::string const &driver) {
        return findConfigDirs(getPathConfig(), driver);
    }

    LocationInfo findLocationInfoForDriver(std::string const &driver) {
        auto json = getPathConfig();
        LocationInfo ret;

        auto config = findConfigDirs(json, driver);
        if (config.valid) {
            ret.configFound = true;
            ret.rootConfigDir = config.rootConfigDir;
            ret.driverConfigDir = config.driverConfigDir;
        }
        auto driverLoc = findDriver(json, driver);
        if (driverLoc.found) {
            ret.driverFound = true;
            ret.steamVrRoot = driverLoc.steamVrRoot;
            ret.driverRoot = driverLoc.driverRoot;
            ret.driverName = driverLoc.driverName;
            ret.driverFile = driverLoc.driverFile;
        }
        ret.found = (driverLoc.found && config.valid);
        return ret;
    }

} // namespace vive
} // namespace osvr
<commit_msg>Todo comment and remove suspected unused code.<commit_after>/** @file
    @brief Implementation

    @date 2016

    @author
    Sensics, Inc.
    <http://sensics.com/osvr>
*/

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

// Internal Includes
#include "FindDriver.h"

// Library/third-party includes
#include <boost/iostreams/stream.hpp>
#include <json/reader.h>
#include <json/value.h>
#include <osvr/Util/Finally.h>
#include <osvr/Util/PlatformConfig.h>

// Standard includes
#include <fstream> // std::ifstream
#include <iostream>
#include <limits.h>
#include <vector>

#if defined(OSVR_USING_FILESYSTEM_HEADER)
#include <filesystem>
#elif defined(OSVR_USING_BOOST_FILESYSTEM)
#include <boost/filesystem.hpp>
#endif

#if defined(OSVR_WINDOWS)
#include <shlobj.h>
#else
#include <cstdlib> // for getenv
#endif

#undef VIVELOADER_VERBOSE

using osvr::util::finally;

namespace osvr {
namespace vive {
#if defined(OSVR_WINDOWS)
    static const auto PLATFORM_DIRNAME_BASE = "win";
    static const auto DRIVER_EXTENSION = ".dll";
    static const auto TOOL_EXTENSION = ".exe";
    static const auto PATH_SEP = "\\";
#elif defined(OSVR_MACOSX)
    /// @todo Note that there are no 64-bit steamvr runtimes or drivers on OS X
    static const auto PLATFORM_DIRNAME_BASE = "osx";
    static const auto DRIVER_EXTENSION = ".dylib";
    static const auto TOOL_EXTENSION = "";
    static const auto PATH_SEP = "/";
#elif defined(OSVR_LINUX)
    static const auto PLATFORM_DIRNAME_BASE = "linux";
    static const auto DRIVER_EXTENSION = ".so";
    static const auto TOOL_EXTENSION = "";
    static const auto PATH_SEP = "/";
#else
#error "Sorry, Valve does not produce a SteamVR runtime for your platform."
#endif

#if defined(OSVR_USING_FILESYSTEM_TR2)
    using std::tr2::sys::path;
    using std::tr2::sys::wpath;
    using std::tr2::sys::exists;
#elif defined(OSVR_USING_FILESYSTEM_EXPERIMENTAL)
    using std::experimental::filesystem::path;
#ifdef _WIN32
    /// Some Windows functions deal in wide strings, easier to just let it cope
    /// with it.
    using wpath = path;
#endif
    using std::experimental::filesystem::exists;
#elif defined(OSVR_USING_BOOST_FILESYSTEM)
    using boost::filesystem::path;
    using boost::filesystem::exists;
#endif

#ifdef OSVR_MACOSX
    inline std::string getPlatformBitSuffix() {
        // they use universal binaries but stick them in "32"
        return "32";
    }
#else
    inline std::string getPlatformBitSuffix() {
        return std::to_string(sizeof(void *) * CHAR_BIT);
    }
#endif

    inline std::string getPlatformDirname() {
        return PLATFORM_DIRNAME_BASE + getPlatformBitSuffix();
    }

    inline void parsePathConfigFile(std::istream &is, Json::Value &ret) {
        Json::Reader reader;
        if (!reader.parse(is, ret)) {
            std::cerr << "Error parsing file containing path configuration - "
                         "have you run SteamVR yet?"
                      << std::endl;
            std::cerr << reader.getFormattedErrorMessages() << std::endl;
        }
    }

#if defined(OSVR_WINDOWS)
    inline Json::Value getPathConfig() {
        PWSTR outString = nullptr;
        Json::Value ret;
        // It's OK to use Vista+ stuff here, since openvr_api.dll uses Vista+
        // stuff too.
        auto hr =
            SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr, &outString);
        if (!SUCCEEDED(hr)) {
            std::cerr << "Could not get local app data directory!" << std::endl;
            return ret;
        }
        // Free the string returned when we're all done.
        /// @todo Shouldn't this be in a "finally()" call?
        auto freeString = [&] { CoTaskMemFree(outString); };
        // Build the path to the file.
        auto vrPaths =
            wpath(outString) / wpath(L"openvr") / wpath(L"openvrpaths.vrpath");
        std::ifstream is(vrPaths.string());
        if (!is) {
            std::wcerr << L"Could not open file containing path configuration "
                          L"- have you run SteamVR yet? "
                       << vrPaths << L"\n";
            return ret;
        }
        parsePathConfigFile(is, ret);
        return ret;
    }

#elif defined(OSVR_MACOSX) || defined(OSVR_LINUX)
    inline Json::Value getPathConfig() {
        auto home = std::getenv("HOME");
        path homePath =
            (nullptr == home
                 ? path{"~"}
                 /*that's weird, should have been in environment...*/
                 : path{home});
        auto vrPaths = homePath / path{".openvr"} / path{"openvrpaths.vrpath"};

        std::ifstream is(vrPaths.string());
        Json::Value ret;
        if (!is) {
            std::cerr << "Could not open file containing path configuration "
                         "- have you run SteamVR yet? "
                      << vrPaths << "\n";
            return ret;
        }
        parsePathConfigFile(is, ret);
        return ret;
    }
#endif

    inline std::vector<std::string> getSteamVRRoots(Json::Value const &json) {
        std::vector<std::string> ret;
        auto &runtimes = json["runtime"];
        if (!runtimes.isArray()) {
            return ret;
        }
        for (auto &runtime : runtimes) {
            ret.emplace_back(runtime.asString());
        }
        return ret;
    }

    inline std::vector<std::string> getSteamVRRoots() {
        return getSteamVRRoots(getPathConfig());
    }

    inline void computeDriverRootAndFilePath(DriverLocationInfo &info,
                                             std::string const &driver) {
        auto p = path{info.steamVrRoot};
        p /= "drivers";
        p /= driver;
        p /= "bin";
        p /= getPlatformDirname();
        info.driverRoot = p.string();
        p /= ("driver_" + driver + DRIVER_EXTENSION);
        info.driverFile = p.string();
    }

    /// Underlying implementation - hand it the preloaded json.
    inline DriverLocationInfo findDriver(Json::Value const &json,
                                         std::string const &driver) {

        DriverLocationInfo info;
        auto &runtimes = json["runtime"];
        if (!runtimes.isArray()) {
            return info;
        }

        for (auto &root : runtimes) {
            info.steamVrRoot = root.asString();
            info.driverName = driver;

            computeDriverRootAndFilePath(info, driver);

#ifdef VIVELOADER_VERBOSE
            std::cout << "Will try to load driver from:\n"
                      << info.driverFile << std::endl;
            if (exists(path{info.driverRoot})) {
                std::cout << "Driver root exists" << std::endl;
            }
            if (exists(path{info.driverFile})) {
                std::cout << "Driver file exists" << std::endl;
            }
#endif

            if (exists(path{info.driverRoot}) &&
                exists(path{info.driverFile})) {
                info.found = true;
                return info;
            }
        }
        info = DriverLocationInfo{};
        return info;
    }

    DriverLocationInfo findDriver(std::string const &driver) {
        return findDriver(getPathConfig(), driver);
    }

    std::string getToolLocation(std::string const &toolName,
                                std::string const &steamVrRoot) {
        std::vector<std::string> searchPath;
        if (!steamVrRoot.empty()) {
            searchPath = {steamVrRoot};
        } else {
            searchPath = getSteamVRRoots();
        }

        for (auto &root : searchPath) {
            auto p = path{root};
            p /= "bin";
            p /= getPlatformDirname();
            p /= (toolName + TOOL_EXTENSION);
            if (exists(p)) {
                return p.string();
            }
        }
        return std::string{};
    }

    /// Underlying implementation - hand it the preloaded json.
    inline ConfigDirs findConfigDirs(Json::Value const &json,
                                     std::string const &driver) {
        ConfigDirs ret;
        auto const &configLocations = json["config"];
        if (!configLocations.isArray()) {
            return ret;
        }
        for (auto &configDir : configLocations) {
            auto configPath = path{configDir.asString()};
            if (!exists(configPath)) {
                continue;
            }
            ret.rootConfigDir = configPath.string();
            ret.driverConfigDir = (configPath / path{driver}).string();
            ret.valid = true;
            return ret;
        }
        ret = ConfigDirs{};
        return ret;
    }
#if 0
    ConfigDirs findConfigDirs(std::string const & /*steamVrRoot*/,
                              std::string const &driver) {
        return findConfigDirs(getPathConfig(), driver);
    }
#endif

    LocationInfo findLocationInfoForDriver(std::string const &driver) {
        auto json = getPathConfig();
        LocationInfo ret;

        auto config = findConfigDirs(json, driver);
        if (config.valid) {
            ret.configFound = true;
            ret.rootConfigDir = config.rootConfigDir;
            ret.driverConfigDir = config.driverConfigDir;
        }
        auto driverLoc = findDriver(json, driver);
        if (driverLoc.found) {
            ret.driverFound = true;
            ret.steamVrRoot = driverLoc.steamVrRoot;
            ret.driverRoot = driverLoc.driverRoot;
            ret.driverName = driverLoc.driverName;
            ret.driverFile = driverLoc.driverFile;
        }
        ret.found = (driverLoc.found && config.valid);
        return ret;
    }

} // namespace vive
} // namespace osvr
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: atlwindow.cxx,v $
 *
 *  $Revision: 1.10 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-08 18:31:06 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#include <com/sun/star/uno/Reference.h>
#include <com/sun/star/lang/XComponent.hpp>
#include <com/sun/star/lang/XInitialization.hpp>
#include <com/sun/star/datatransfer/dnd/XDropTarget.hpp>
#include <com/sun/star/datatransfer/dnd/DNDConstants.hpp>

#include <cppuhelper/servicefactory.hxx>
#include <rtl/string.h>

#include "atlwindow.hxx"
#include "targetlistener.hxx"
#include "sourcelistener.hxx"
//#include "transferable.hxx"
#include <map>

#include <winbase.h>
using namespace com::sun::star::lang;
using namespace com::sun::star::datatransfer::dnd;
using namespace com::sun::star::datatransfer::dnd::DNDConstants;
using namespace cppu;
using namespace rtl;
using namespace std;

LRESULT APIENTRY EditSubclassProc( HWND hwnd, UINT uMsg,WPARAM wParam, LPARAM lParam) ;

extern Reference< XMultiServiceFactory > MultiServiceFactory;
DWORD WINAPI MTAFunc(LPVOID pParams);

char* szSTAWin= "XDragSource::executeDrag is called from the same "
                "OLE STA thread that created the window.";
char* szMTAWin= "XDragSource::executeDrag is called from a MTA thread "
                "that did not create the window.";

WNDPROC wpOrigEditProc;

map<HWND, HWND> mapEditToMainWnd;

LRESULT AWindow::OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    Reference<XComponent> xcompSource( m_xDragSource, UNO_QUERY);

    PostQuitMessage(0);

    m_xDropTarget=0;
    m_xDragSource=0;

     // Remove the subclass from the edit control.
    ::SetWindowLong(m_hwndEdit, GWL_WNDPROC,
                (LONG) wpOrigEditProc);

    return 0;
}

LRESULT AWindow::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    // Prepare the EDIT control
    m_hwndEdit = CreateWindowA(
        "EDIT",     // predefined class
        NULL,       // no window title
        WS_CHILD | WS_VISIBLE | WS_VSCROLL |
            ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL,
        0, 0, 0, 0, // set size in WM_SIZE message
        m_hWnd,       // parent window
        (HMENU) NULL, // edit control ID
        (HINSTANCE) GetWindowLong( GWL_HINSTANCE),
        NULL);

    // the map is used in the window procedure for the edit window to associate the
    // it to the right main window ( AWindow)
    mapEditToMainWnd[m_hwndEdit]= m_hWnd;
    // Superclass the edit window, because we want to process mouse messages
    wpOrigEditProc = (WNDPROC) ::SetWindowLongA(m_hwndEdit,
                GWL_WNDPROC, (LONG) EditSubclassProc);

    // Add text to the window.
    if( m_isMTA)
        ::SendMessageA(m_hwndEdit, WM_SETTEXT, 0, (LPARAM) szMTAWin);
    else
        ::SendMessageA(m_hwndEdit, WM_SETTEXT, 0, (LPARAM) szSTAWin);

    // create the DragSource
    Reference< XInterface> xint= MultiServiceFactory->createInstance(OUString(L"com.sun.star.datatransfer.dnd.OleDragSource"));
    m_xDragSource= Reference<XDragSource>( xint, UNO_QUERY);
    Reference<XInitialization> xInit( xint, UNO_QUERY);

    Any ar[2];
    ar[1]<<= (sal_uInt32)m_hWnd;
    xInit->initialize( Sequence<Any>( ar, 2) );

    //create the DropTarget
    Reference< XInterface> xintTarget= MultiServiceFactory->createInstance(OUString(L"com.sun.star.datatransfer.dnd.OleDropTarget"));
    m_xDropTarget= Reference<XDropTarget>( xintTarget, UNO_QUERY);
    Reference<XInitialization> xInitTarget( xintTarget, UNO_QUERY);

    Any any;
    any <<= (sal_uInt32)m_hWnd;
    xInitTarget->initialize( Sequence<Any>( &any, 1) );

    m_xDropTarget->addDropTargetListener( static_cast<XDropTargetListener*>
        ( new DropTargetListener( m_hwndEdit)) );
//  // make this window tho a drop target
    m_xDropTarget->setActive(sal_True);

    return 0;
}

// When the mouse is dragged for a second than a drag is initiated
LRESULT AWindow::OnMouseAction(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    if( uMsg== WM_LBUTTONDOWN)
    {
        SetTimer( 1, 1000);
    }

    else if( uMsg == WM_LBUTTONUP)
    {
        KillTimer(  1);
    }

    return 0;
}

LRESULT AWindow::OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    HRESULT hr;
    USES_CONVERSION;
    KillTimer( 1);
    if(m_xDragSource.is())
    {

        //Get the Text out of the Edit window
        int length= (int)::SendMessageA( m_hwndEdit, WM_GETTEXTLENGTH, 0, 0);
        char * pBuffer= new char[length + 1];
        ZeroMemory( pBuffer, length + 1);
        ::SendMessageA( m_hwndEdit, WM_GETTEXT, length, (LPARAM) pBuffer);

        IDataObject* pData= NULL;
        HRESULT hr= CreateDataCache( NULL, CLSID_NULL, __uuidof(IDataObject),(void**) &pData);
        if( pData)
        {
            FORMATETC format={ CF_TEXT, NULL, DVASPECT_CONTENT, -1, };

            HGLOBAL mem= GlobalAlloc(GHND, length + 1 );
            void* pMem= GlobalLock( mem);
            memcpy( pMem, pBuffer, length+1);
            GlobalUnlock( mem);

            STGMEDIUM medium;
            medium.tymed= TYMED_HGLOBAL;
            medium.hGlobal= mem;
            medium.pUnkForRelease= NULL;

            pData->SetData( &format,  &medium, TRUE); // releases HGLOBAL eventually

            Reference<XTransferable> xTrans= m_aDataConverter.createTransferableFromDataObj(
                                                MultiServiceFactory, pData);

            // call XDragSource::executeDrag from an MTA
            if( m_isMTA )
            {
                DWORD mtaThreadId;
                ThreadData data;
                data.source= m_xDragSource;
                data.transferable= xTrans;

                data.evtThreadReady= CreateEvent( NULL, FALSE, FALSE, NULL);

                HANDLE hThread= CreateThread( NULL, 0, MTAFunc, &data, 0, &mtaThreadId);
                // We must wait until the thread copied the ThreadData structure
                WaitForSingleObject( data.evtThreadReady, INFINITE);
                CloseHandle( data.evtThreadReady);

            }
            else
            {
                m_xDragSource->startDrag( DragGestureEvent(),
                    ACTION_LINK|ACTION_MOVE|ACTION_COPY,
                    0,
                    0,
                    xTrans,
                    Reference<XDragSourceListener>( static_cast<XDragSourceListener*>(new DragSourceListener() ) ) );
            }
        }

        delete[] pBuffer;
    }

    return 0;
}

LRESULT AWindow::OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    // Make the edit control the size of the window's
    // client area.
    ::MoveWindow(m_hwndEdit,
        0, 0,           // starting x- and y-coordinates
        LOWORD(lParam), // width of client area
        HIWORD(lParam), // height of client area
        TRUE);          // repaint window

    return 0;
}
LRESULT AWindow::OnFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    ::SetFocus(m_hwndEdit);
    return 0;
}

// Subclass procedure for EDIT window
LRESULT APIENTRY EditSubclassProc( HWND hwnd, UINT uMsg,WPARAM wParam, LPARAM lParam)
{

    if( uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST)
    {
        HWND hAWindow= mapEditToMainWnd[hwnd];
        ::SendMessage( hAWindow, uMsg, wParam, lParam);

    }
    return CallWindowProc( wpOrigEditProc, hwnd, uMsg,
        wParam, lParam);
}

<commit_msg>INTEGRATION: CWS pchfix02 (1.10.40); FILE MERGED 2006/09/01 17:25:41 kaib 1.10.40.1: #i68856# Added header markers and pch files<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: atlwindow.cxx,v $
 *
 *  $Revision: 1.11 $
 *
 *  last change: $Author: obo $ $Date: 2006-09-17 17:04:07 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_dtrans.hxx"

#include <com/sun/star/uno/Reference.h>
#include <com/sun/star/lang/XComponent.hpp>
#include <com/sun/star/lang/XInitialization.hpp>
#include <com/sun/star/datatransfer/dnd/XDropTarget.hpp>
#include <com/sun/star/datatransfer/dnd/DNDConstants.hpp>

#include <cppuhelper/servicefactory.hxx>
#include <rtl/string.h>

#include "atlwindow.hxx"
#include "targetlistener.hxx"
#include "sourcelistener.hxx"
//#include "transferable.hxx"
#include <map>

#include <winbase.h>
using namespace com::sun::star::lang;
using namespace com::sun::star::datatransfer::dnd;
using namespace com::sun::star::datatransfer::dnd::DNDConstants;
using namespace cppu;
using namespace rtl;
using namespace std;

LRESULT APIENTRY EditSubclassProc( HWND hwnd, UINT uMsg,WPARAM wParam, LPARAM lParam) ;

extern Reference< XMultiServiceFactory > MultiServiceFactory;
DWORD WINAPI MTAFunc(LPVOID pParams);

char* szSTAWin= "XDragSource::executeDrag is called from the same "
                "OLE STA thread that created the window.";
char* szMTAWin= "XDragSource::executeDrag is called from a MTA thread "
                "that did not create the window.";

WNDPROC wpOrigEditProc;

map<HWND, HWND> mapEditToMainWnd;

LRESULT AWindow::OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    Reference<XComponent> xcompSource( m_xDragSource, UNO_QUERY);

    PostQuitMessage(0);

    m_xDropTarget=0;
    m_xDragSource=0;

     // Remove the subclass from the edit control.
    ::SetWindowLong(m_hwndEdit, GWL_WNDPROC,
                (LONG) wpOrigEditProc);

    return 0;
}

LRESULT AWindow::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    // Prepare the EDIT control
    m_hwndEdit = CreateWindowA(
        "EDIT",     // predefined class
        NULL,       // no window title
        WS_CHILD | WS_VISIBLE | WS_VSCROLL |
            ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL,
        0, 0, 0, 0, // set size in WM_SIZE message
        m_hWnd,       // parent window
        (HMENU) NULL, // edit control ID
        (HINSTANCE) GetWindowLong( GWL_HINSTANCE),
        NULL);

    // the map is used in the window procedure for the edit window to associate the
    // it to the right main window ( AWindow)
    mapEditToMainWnd[m_hwndEdit]= m_hWnd;
    // Superclass the edit window, because we want to process mouse messages
    wpOrigEditProc = (WNDPROC) ::SetWindowLongA(m_hwndEdit,
                GWL_WNDPROC, (LONG) EditSubclassProc);

    // Add text to the window.
    if( m_isMTA)
        ::SendMessageA(m_hwndEdit, WM_SETTEXT, 0, (LPARAM) szMTAWin);
    else
        ::SendMessageA(m_hwndEdit, WM_SETTEXT, 0, (LPARAM) szSTAWin);

    // create the DragSource
    Reference< XInterface> xint= MultiServiceFactory->createInstance(OUString(L"com.sun.star.datatransfer.dnd.OleDragSource"));
    m_xDragSource= Reference<XDragSource>( xint, UNO_QUERY);
    Reference<XInitialization> xInit( xint, UNO_QUERY);

    Any ar[2];
    ar[1]<<= (sal_uInt32)m_hWnd;
    xInit->initialize( Sequence<Any>( ar, 2) );

    //create the DropTarget
    Reference< XInterface> xintTarget= MultiServiceFactory->createInstance(OUString(L"com.sun.star.datatransfer.dnd.OleDropTarget"));
    m_xDropTarget= Reference<XDropTarget>( xintTarget, UNO_QUERY);
    Reference<XInitialization> xInitTarget( xintTarget, UNO_QUERY);

    Any any;
    any <<= (sal_uInt32)m_hWnd;
    xInitTarget->initialize( Sequence<Any>( &any, 1) );

    m_xDropTarget->addDropTargetListener( static_cast<XDropTargetListener*>
        ( new DropTargetListener( m_hwndEdit)) );
//  // make this window tho a drop target
    m_xDropTarget->setActive(sal_True);

    return 0;
}

// When the mouse is dragged for a second than a drag is initiated
LRESULT AWindow::OnMouseAction(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    if( uMsg== WM_LBUTTONDOWN)
    {
        SetTimer( 1, 1000);
    }

    else if( uMsg == WM_LBUTTONUP)
    {
        KillTimer(  1);
    }

    return 0;
}

LRESULT AWindow::OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    HRESULT hr;
    USES_CONVERSION;
    KillTimer( 1);
    if(m_xDragSource.is())
    {

        //Get the Text out of the Edit window
        int length= (int)::SendMessageA( m_hwndEdit, WM_GETTEXTLENGTH, 0, 0);
        char * pBuffer= new char[length + 1];
        ZeroMemory( pBuffer, length + 1);
        ::SendMessageA( m_hwndEdit, WM_GETTEXT, length, (LPARAM) pBuffer);

        IDataObject* pData= NULL;
        HRESULT hr= CreateDataCache( NULL, CLSID_NULL, __uuidof(IDataObject),(void**) &pData);
        if( pData)
        {
            FORMATETC format={ CF_TEXT, NULL, DVASPECT_CONTENT, -1, };

            HGLOBAL mem= GlobalAlloc(GHND, length + 1 );
            void* pMem= GlobalLock( mem);
            memcpy( pMem, pBuffer, length+1);
            GlobalUnlock( mem);

            STGMEDIUM medium;
            medium.tymed= TYMED_HGLOBAL;
            medium.hGlobal= mem;
            medium.pUnkForRelease= NULL;

            pData->SetData( &format,  &medium, TRUE); // releases HGLOBAL eventually

            Reference<XTransferable> xTrans= m_aDataConverter.createTransferableFromDataObj(
                                                MultiServiceFactory, pData);

            // call XDragSource::executeDrag from an MTA
            if( m_isMTA )
            {
                DWORD mtaThreadId;
                ThreadData data;
                data.source= m_xDragSource;
                data.transferable= xTrans;

                data.evtThreadReady= CreateEvent( NULL, FALSE, FALSE, NULL);

                HANDLE hThread= CreateThread( NULL, 0, MTAFunc, &data, 0, &mtaThreadId);
                // We must wait until the thread copied the ThreadData structure
                WaitForSingleObject( data.evtThreadReady, INFINITE);
                CloseHandle( data.evtThreadReady);

            }
            else
            {
                m_xDragSource->startDrag( DragGestureEvent(),
                    ACTION_LINK|ACTION_MOVE|ACTION_COPY,
                    0,
                    0,
                    xTrans,
                    Reference<XDragSourceListener>( static_cast<XDragSourceListener*>(new DragSourceListener() ) ) );
            }
        }

        delete[] pBuffer;
    }

    return 0;
}

LRESULT AWindow::OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    // Make the edit control the size of the window's
    // client area.
    ::MoveWindow(m_hwndEdit,
        0, 0,           // starting x- and y-coordinates
        LOWORD(lParam), // width of client area
        HIWORD(lParam), // height of client area
        TRUE);          // repaint window

    return 0;
}
LRESULT AWindow::OnFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    ::SetFocus(m_hwndEdit);
    return 0;
}

// Subclass procedure for EDIT window
LRESULT APIENTRY EditSubclassProc( HWND hwnd, UINT uMsg,WPARAM wParam, LPARAM lParam)
{

    if( uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST)
    {
        HWND hAWindow= mapEditToMainWnd[hwnd];
        ::SendMessage( hAWindow, uMsg, wParam, lParam);

    }
    return CallWindowProc( wpOrigEditProc, hwnd, uMsg,
        wParam, lParam);
}

<|endoftext|>


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

#include "compiler_llvm.h"

#include "class_linker.h"
#include "compilation_unit.h"
#include "compiled_method.h"
#include "compiler.h"
#include "dex_cache.h"
#include "elf_image.h"
#include "elf_loader.h"
#include "ir_builder.h"
#include "jni_compiler.h"
#include "method_compiler.h"
#include "oat_compilation_unit.h"
#include "oat_file.h"
#include "stl_util.h"
#include "upcall_compiler.h"

#include <llvm/LinkAllPasses.h>
#include <llvm/LinkAllVMCore.h>
#include <llvm/Support/CommandLine.h>
#include <llvm/Support/ManagedStatic.h>
#include <llvm/Support/TargetSelect.h>
#include <llvm/Support/Threading.h>

namespace llvm {
  extern bool TimePassesIsEnabled;
}

// NOTE: Although EnableARMLongCalls is defined in llvm/lib/Target/ARM/
// ARMISelLowering.cpp, however, it is not in the llvm namespace.
extern llvm::cl::opt<bool> EnableARMLongCalls;

// ReserveR9 is defined in llvm/lib/Target/ARM/ARMSubtarget.cpp
extern llvm::cl::opt<bool> ReserveR9;

namespace {

pthread_once_t llvm_initialized = PTHREAD_ONCE_INIT;

void InitializeLLVM() {
  // NOTE: Uncomment following line to show the time consumption of LLVM passes
  //llvm::TimePassesIsEnabled = true;

  // Enable -arm-reserve-r9
  ReserveR9 = true;

  // Initialize LLVM target, MC subsystem, asm printer, and asm parser
  llvm::InitializeAllTargets();
  llvm::InitializeAllTargetMCs();
  llvm::InitializeAllAsmPrinters();
  llvm::InitializeAllAsmParsers();
  // TODO: Maybe we don't have to initialize "all" targets.

  // Enable -arm-long-calls
  EnableARMLongCalls = false;

  // Initialize LLVM optimization passes
  llvm::PassRegistry &registry = *llvm::PassRegistry::getPassRegistry();

  llvm::initializeCore(registry);
  llvm::initializeScalarOpts(registry);
  llvm::initializeIPO(registry);
  llvm::initializeAnalysis(registry);
  llvm::initializeIPA(registry);
  llvm::initializeTransformUtils(registry);
  llvm::initializeInstCombine(registry);
  llvm::initializeInstrumentation(registry);
  llvm::initializeTarget(registry);

  // Initialize LLVM internal data structure for multithreading
  llvm::llvm_start_multithreaded();
}

// The Guard to Shutdown LLVM
// llvm::llvm_shutdown_obj llvm_guard;
// TODO: We are commenting out this line because this will cause SEGV from
// time to time.
// Two reasons: (1) the order of the destruction of static objects, or
//              (2) dlopen/dlclose side-effect on static objects.

} // anonymous namespace

namespace art {
namespace compiler_llvm {

llvm::Module* makeLLVMModuleContents(llvm::Module* module);

CompilerLLVM::CompilerLLVM(Compiler* compiler, InstructionSet insn_set)
    : compiler_(compiler), compiler_lock_("llvm_compiler_lock"),
      insn_set_(insn_set), curr_cunit_(NULL) {

  // Initialize LLVM libraries
  pthread_once(&llvm_initialized, InitializeLLVM);
}

CompilerLLVM::~CompilerLLVM() {
  STLDeleteElements(&cunits_);
}

void CompilerLLVM::EnsureCompilationUnit() {
  compiler_lock_.AssertHeld();

  if (curr_cunit_ != NULL) {
    return;
  }

  // Allocate compilation unit
  size_t cunit_idx = cunits_.size();
  curr_cunit_ = new CompilationUnit(insn_set_, cunit_idx);

  // Register compilation unit
  cunits_.push_back(curr_cunit_);
}

void CompilerLLVM::MaterializeRemainder() {
  compiler_lock_.Lock();
  // Localize
  CompilationUnit* cunit = curr_cunit_;
  // Reset the curr_cuit_
  curr_cunit_ = NULL;
  compiler_lock_.Unlock();

  if (cunit != NULL) {
    Materialize(cunit);
  }
}

void CompilerLLVM::MaterializeIfThresholdReached() {
  compiler_lock_.Lock();
  // Localize
  CompilationUnit* cunit = curr_cunit_;

  if (curr_cunit_ != NULL && curr_cunit_->IsMaterializeThresholdReached()) {
    // Delete the compilation unit
    curr_cunit_ = NULL;
  } else {
    // Reset cunit such that Materialize() won't be invoked
    cunit = NULL;
  }

  compiler_lock_.Unlock();

  if (cunit != NULL) {
    Materialize(cunit);
  }
}

void CompilerLLVM::Materialize(CompilationUnit* cunit) {
  DCHECK(cunit != NULL);
  DCHECK(!cunit->IsMaterialized());

  // Write bitcode to file when filename is set
  if (IsBitcodeFileNameAvailable()) {
    const size_t cunit_idx = cunits_.size();
    cunit->WriteBitcodeToFile(
      StringPrintf("%s-%zu", bitcode_filename_.c_str(), cunit_idx));
  }

  // Materialize the llvm::Module into ELF object file
  cunit->Materialize();

  // Load ELF image when automatic ELF loading is enabled
  if (IsAutoElfLoadingEnabled()) {
    LoadElfFromCompilationUnit(cunit);
  }
}

void CompilerLLVM::EnableAutoElfLoading() {
  MutexLock GUARD(compiler_lock_);

  if (IsAutoElfLoadingEnabled()) {
    // If there is an existing ELF loader, then do nothing.
    // Because the existing ELF loader may have returned some code address
    // already.  If we replace the existing ELF loader with
    // elf_loader_.reset(...), then it is possible to have some dangling
    // pointer.
    return;
  }

  // Create ELF loader and load the materialized CompilationUnit
  elf_loader_.reset(new ElfLoader());

  for (size_t i = 0; i < cunits_.size(); ++i) {
    if (cunits_[i]->IsMaterialized()) {
      LoadElfFromCompilationUnit(cunits_[i]);
    }
  }
}

void CompilerLLVM::LoadElfFromCompilationUnit(const CompilationUnit* cunit) {
  compiler_lock_.AssertHeld();
  DCHECK(cunit->IsMaterialized()) << cunit->GetElfIndex();

  if (!elf_loader_->LoadElfAt(cunit->GetElfIndex(),
                              cunit->GetElfImage(),
                              OatFile::kRelocAll)) {
    LOG(ERROR) << "Failed to load ELF from compilation unit "
               << cunit->GetElfIndex();
  }
}

const void* CompilerLLVM::GetMethodCodeAddr(const CompiledMethod* cm) const {
  return elf_loader_->GetMethodCodeAddr(cm->GetElfIndex(),
                                        cm->GetElfFuncIndex());
}

const Method::InvokeStub* CompilerLLVM::
GetMethodInvokeStubAddr(const CompiledInvokeStub* cm) const {
  return elf_loader_->GetMethodInvokeStubAddr(cm->GetElfIndex(),
                                              cm->GetElfFuncIndex());
}

std::vector<ElfImage> CompilerLLVM::GetElfImages() const {
  std::vector<ElfImage> result;

  for (size_t i = 0; i < cunits_.size(); ++i) {
    result.push_back(cunits_[i]->GetElfImage());
  }

  return result;
}

CompiledMethod* CompilerLLVM::
CompileDexMethod(OatCompilationUnit* oat_compilation_unit) {
  MutexLock GUARD(compiler_lock_);

  EnsureCompilationUnit();

  UniquePtr<MethodCompiler> method_compiler(
      new MethodCompiler(curr_cunit_, compiler_, oat_compilation_unit));

  return method_compiler->Compile();
}

CompiledMethod* CompilerLLVM::
CompileNativeMethod(OatCompilationUnit* oat_compilation_unit) {
  MutexLock GUARD(compiler_lock_);

  EnsureCompilationUnit();

  UniquePtr<JniCompiler> jni_compiler(
      new JniCompiler(curr_cunit_, *compiler_, oat_compilation_unit));

  return jni_compiler->Compile();
}

CompiledInvokeStub* CompilerLLVM::CreateInvokeStub(bool is_static,
                                                   char const *shorty) {
  MutexLock GUARD(compiler_lock_);

  EnsureCompilationUnit();

  UniquePtr<UpcallCompiler> upcall_compiler(
    new UpcallCompiler(curr_cunit_, *compiler_));

  return upcall_compiler->CreateStub(is_static, shorty);
}

} // namespace compiler_llvm
} // namespace art

inline static art::compiler_llvm::CompilerLLVM* ContextOf(art::Compiler& compiler) {
  void *compiler_context = compiler.GetCompilerContext();
  CHECK(compiler_context != NULL);
  return reinterpret_cast<art::compiler_llvm::CompilerLLVM*>(compiler_context);
}

inline static const art::compiler_llvm::CompilerLLVM* ContextOf(const art::Compiler& compiler) {
  void *compiler_context = compiler.GetCompilerContext();
  CHECK(compiler_context != NULL);
  return reinterpret_cast<const art::compiler_llvm::CompilerLLVM*>(compiler_context);
}

extern "C" void ArtInitCompilerContext(art::Compiler& compiler) {
  CHECK(compiler.GetCompilerContext() == NULL);

  art::compiler_llvm::CompilerLLVM* compiler_llvm =
      new art::compiler_llvm::CompilerLLVM(&compiler,
                                           compiler.GetInstructionSet());

  compiler.SetCompilerContext(compiler_llvm);
}

extern "C" art::CompiledMethod* ArtCompileMethod(art::Compiler& compiler,
                                                 const art::DexFile::CodeItem* code_item,
                                                 uint32_t access_flags, uint32_t method_idx,
                                                 const art::ClassLoader* class_loader,
                                                 const art::DexFile& dex_file)
{
  art::ClassLinker *class_linker = art::Runtime::Current()->GetClassLinker();
  art::DexCache *dex_cache = class_linker->FindDexCache(dex_file);

  art::OatCompilationUnit oat_compilation_unit(
    class_loader, class_linker, dex_file, *dex_cache, code_item,
    method_idx, access_flags);
  art::compiler_llvm::CompilerLLVM* compiler_llvm = ContextOf(compiler);
  art::CompiledMethod* result = compiler_llvm->CompileDexMethod(&oat_compilation_unit);
  compiler_llvm->MaterializeIfThresholdReached();
  return result;
}

extern "C" art::CompiledMethod* ArtJniCompileMethod(art::Compiler& compiler,
                                                    uint32_t access_flags, uint32_t method_idx,
                                                    const art::DexFile& dex_file) {
  art::ClassLinker *class_linker = art::Runtime::Current()->GetClassLinker();
  art::DexCache *dex_cache = class_linker->FindDexCache(dex_file);

  art::OatCompilationUnit oat_compilation_unit(
    NULL, class_linker, dex_file, *dex_cache, NULL,
    method_idx, access_flags);

  art::compiler_llvm::CompilerLLVM* compiler_llvm = ContextOf(compiler);
  art::CompiledMethod* result = compiler_llvm->CompileNativeMethod(&oat_compilation_unit);
  compiler_llvm->MaterializeIfThresholdReached();
  return result;
}

extern "C" art::CompiledInvokeStub* ArtCreateInvokeStub(art::Compiler& compiler, bool is_static,
                                                        const char* shorty, uint32_t shorty_len) {
  art::compiler_llvm::CompilerLLVM* compiler_llvm = ContextOf(compiler);
  art::CompiledInvokeStub* result = compiler_llvm->CreateInvokeStub(is_static, shorty);
  compiler_llvm->MaterializeIfThresholdReached();
  return result;
}

extern "C" void compilerLLVMSetBitcodeFileName(art::Compiler& compiler,
                                               std::string const& filename) {
  ContextOf(compiler)->SetBitcodeFileName(filename);
}

extern "C" void compilerLLVMMaterializeRemainder(art::Compiler& compiler) {
  ContextOf(compiler)->MaterializeRemainder();
}

extern "C" void compilerLLVMEnableAutoElfLoading(art::Compiler& compiler) {
  art::compiler_llvm::CompilerLLVM* compiler_llvm =
      reinterpret_cast<art::compiler_llvm::CompilerLLVM*>(compiler.GetCompilerContext());
  return compiler_llvm->EnableAutoElfLoading();
}

extern "C" const void* compilerLLVMGetMethodCodeAddr(const art::Compiler& compiler,
                                                     const art::CompiledMethod* cm,
                                                     const art::Method*) {
  const art::compiler_llvm::CompilerLLVM* compiler_llvm =
      reinterpret_cast<const art::compiler_llvm::CompilerLLVM*>(compiler.GetCompilerContext());
  return compiler_llvm->GetMethodCodeAddr(cm);
}

extern "C" const art::Method::InvokeStub* compilerLLVMGetMethodInvokeStubAddr(const art::Compiler& compiler,
                                                                              const art::CompiledInvokeStub* cm,
                                                                              const art::Method*) {
  const art::compiler_llvm::CompilerLLVM* compiler_llvm =
      reinterpret_cast<const art::compiler_llvm::CompilerLLVM*>(compiler.GetCompilerContext());
  return compiler_llvm->GetMethodInvokeStubAddr(cm);
}

extern "C" std::vector<art::ElfImage> compilerLLVMGetElfImages(const art::Compiler& compiler) {
  return ContextOf(compiler)->GetElfImages();
}

extern "C" void compilerLLVMDispose(art::Compiler& compiler) {
  delete ContextOf(compiler);
}
<commit_msg>am 80cd474b: Fix unit test by holding compiler_lock_ on ourselves.<commit_after>/*
 * Copyright (C) 2012 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "compiler_llvm.h"

#include "class_linker.h"
#include "compilation_unit.h"
#include "compiled_method.h"
#include "compiler.h"
#include "dex_cache.h"
#include "elf_image.h"
#include "elf_loader.h"
#include "ir_builder.h"
#include "jni_compiler.h"
#include "method_compiler.h"
#include "oat_compilation_unit.h"
#include "oat_file.h"
#include "stl_util.h"
#include "upcall_compiler.h"

#include <llvm/LinkAllPasses.h>
#include <llvm/LinkAllVMCore.h>
#include <llvm/Support/CommandLine.h>
#include <llvm/Support/ManagedStatic.h>
#include <llvm/Support/TargetSelect.h>
#include <llvm/Support/Threading.h>

namespace llvm {
  extern bool TimePassesIsEnabled;
}

// NOTE: Although EnableARMLongCalls is defined in llvm/lib/Target/ARM/
// ARMISelLowering.cpp, however, it is not in the llvm namespace.
extern llvm::cl::opt<bool> EnableARMLongCalls;

// ReserveR9 is defined in llvm/lib/Target/ARM/ARMSubtarget.cpp
extern llvm::cl::opt<bool> ReserveR9;

namespace {

pthread_once_t llvm_initialized = PTHREAD_ONCE_INIT;

void InitializeLLVM() {
  // NOTE: Uncomment following line to show the time consumption of LLVM passes
  //llvm::TimePassesIsEnabled = true;

  // Enable -arm-reserve-r9
  ReserveR9 = true;

  // Initialize LLVM target, MC subsystem, asm printer, and asm parser
  llvm::InitializeAllTargets();
  llvm::InitializeAllTargetMCs();
  llvm::InitializeAllAsmPrinters();
  llvm::InitializeAllAsmParsers();
  // TODO: Maybe we don't have to initialize "all" targets.

  // Enable -arm-long-calls
  EnableARMLongCalls = false;

  // Initialize LLVM optimization passes
  llvm::PassRegistry &registry = *llvm::PassRegistry::getPassRegistry();

  llvm::initializeCore(registry);
  llvm::initializeScalarOpts(registry);
  llvm::initializeIPO(registry);
  llvm::initializeAnalysis(registry);
  llvm::initializeIPA(registry);
  llvm::initializeTransformUtils(registry);
  llvm::initializeInstCombine(registry);
  llvm::initializeInstrumentation(registry);
  llvm::initializeTarget(registry);

  // Initialize LLVM internal data structure for multithreading
  llvm::llvm_start_multithreaded();
}

// The Guard to Shutdown LLVM
// llvm::llvm_shutdown_obj llvm_guard;
// TODO: We are commenting out this line because this will cause SEGV from
// time to time.
// Two reasons: (1) the order of the destruction of static objects, or
//              (2) dlopen/dlclose side-effect on static objects.

} // anonymous namespace

namespace art {
namespace compiler_llvm {

llvm::Module* makeLLVMModuleContents(llvm::Module* module);

CompilerLLVM::CompilerLLVM(Compiler* compiler, InstructionSet insn_set)
    : compiler_(compiler), compiler_lock_("llvm_compiler_lock"),
      insn_set_(insn_set), curr_cunit_(NULL) {

  // Initialize LLVM libraries
  pthread_once(&llvm_initialized, InitializeLLVM);
}

CompilerLLVM::~CompilerLLVM() {
  STLDeleteElements(&cunits_);
}

void CompilerLLVM::EnsureCompilationUnit() {
  compiler_lock_.AssertHeld();

  if (curr_cunit_ != NULL) {
    return;
  }

  // Allocate compilation unit
  size_t cunit_idx = cunits_.size();
  curr_cunit_ = new CompilationUnit(insn_set_, cunit_idx);

  // Register compilation unit
  cunits_.push_back(curr_cunit_);
}

void CompilerLLVM::MaterializeRemainder() {
  compiler_lock_.Lock();
  // Localize
  CompilationUnit* cunit = curr_cunit_;
  // Reset the curr_cuit_
  curr_cunit_ = NULL;
  compiler_lock_.Unlock();

  if (cunit != NULL) {
    Materialize(cunit);
  }
}

void CompilerLLVM::MaterializeIfThresholdReached() {
  compiler_lock_.Lock();
  // Localize
  CompilationUnit* cunit = curr_cunit_;

  if (curr_cunit_ != NULL && curr_cunit_->IsMaterializeThresholdReached()) {
    // Delete the compilation unit
    curr_cunit_ = NULL;
  } else {
    // Reset cunit such that Materialize() won't be invoked
    cunit = NULL;
  }

  compiler_lock_.Unlock();

  if (cunit != NULL) {
    Materialize(cunit);
  }
}

void CompilerLLVM::Materialize(CompilationUnit* cunit) {
  DCHECK(cunit != NULL);
  DCHECK(!cunit->IsMaterialized());

  // Write bitcode to file when filename is set
  if (IsBitcodeFileNameAvailable()) {
    const size_t cunit_idx = cunits_.size();
    cunit->WriteBitcodeToFile(
      StringPrintf("%s-%zu", bitcode_filename_.c_str(), cunit_idx));
  }

  // Materialize the llvm::Module into ELF object file
  cunit->Materialize();

  // Load ELF image when automatic ELF loading is enabled
  if (IsAutoElfLoadingEnabled()) {
    LoadElfFromCompilationUnit(cunit);
  }
}

void CompilerLLVM::EnableAutoElfLoading() {
  MutexLock GUARD(compiler_lock_);

  if (IsAutoElfLoadingEnabled()) {
    // If there is an existing ELF loader, then do nothing.
    // Because the existing ELF loader may have returned some code address
    // already.  If we replace the existing ELF loader with
    // elf_loader_.reset(...), then it is possible to have some dangling
    // pointer.
    return;
  }

  // Create ELF loader and load the materialized CompilationUnit
  elf_loader_.reset(new ElfLoader());

  for (size_t i = 0; i < cunits_.size(); ++i) {
    if (cunits_[i]->IsMaterialized()) {
      LoadElfFromCompilationUnit(cunits_[i]);
    }
  }
}

void CompilerLLVM::LoadElfFromCompilationUnit(const CompilationUnit* cunit) {
  MutexLock GUARD(compiler_lock_);
  DCHECK(cunit->IsMaterialized()) << cunit->GetElfIndex();

  if (!elf_loader_->LoadElfAt(cunit->GetElfIndex(),
                              cunit->GetElfImage(),
                              OatFile::kRelocAll)) {
    LOG(ERROR) << "Failed to load ELF from compilation unit "
               << cunit->GetElfIndex();
  }
}

const void* CompilerLLVM::GetMethodCodeAddr(const CompiledMethod* cm) const {
  return elf_loader_->GetMethodCodeAddr(cm->GetElfIndex(),
                                        cm->GetElfFuncIndex());
}

const Method::InvokeStub* CompilerLLVM::
GetMethodInvokeStubAddr(const CompiledInvokeStub* cm) const {
  return elf_loader_->GetMethodInvokeStubAddr(cm->GetElfIndex(),
                                              cm->GetElfFuncIndex());
}

std::vector<ElfImage> CompilerLLVM::GetElfImages() const {
  std::vector<ElfImage> result;

  for (size_t i = 0; i < cunits_.size(); ++i) {
    result.push_back(cunits_[i]->GetElfImage());
  }

  return result;
}

CompiledMethod* CompilerLLVM::
CompileDexMethod(OatCompilationUnit* oat_compilation_unit) {
  MutexLock GUARD(compiler_lock_);

  EnsureCompilationUnit();

  UniquePtr<MethodCompiler> method_compiler(
      new MethodCompiler(curr_cunit_, compiler_, oat_compilation_unit));

  return method_compiler->Compile();
}

CompiledMethod* CompilerLLVM::
CompileNativeMethod(OatCompilationUnit* oat_compilation_unit) {
  MutexLock GUARD(compiler_lock_);

  EnsureCompilationUnit();

  UniquePtr<JniCompiler> jni_compiler(
      new JniCompiler(curr_cunit_, *compiler_, oat_compilation_unit));

  return jni_compiler->Compile();
}

CompiledInvokeStub* CompilerLLVM::CreateInvokeStub(bool is_static,
                                                   char const *shorty) {
  MutexLock GUARD(compiler_lock_);

  EnsureCompilationUnit();

  UniquePtr<UpcallCompiler> upcall_compiler(
    new UpcallCompiler(curr_cunit_, *compiler_));

  return upcall_compiler->CreateStub(is_static, shorty);
}

} // namespace compiler_llvm
} // namespace art

inline static art::compiler_llvm::CompilerLLVM* ContextOf(art::Compiler& compiler) {
  void *compiler_context = compiler.GetCompilerContext();
  CHECK(compiler_context != NULL);
  return reinterpret_cast<art::compiler_llvm::CompilerLLVM*>(compiler_context);
}

inline static const art::compiler_llvm::CompilerLLVM* ContextOf(const art::Compiler& compiler) {
  void *compiler_context = compiler.GetCompilerContext();
  CHECK(compiler_context != NULL);
  return reinterpret_cast<const art::compiler_llvm::CompilerLLVM*>(compiler_context);
}

extern "C" void ArtInitCompilerContext(art::Compiler& compiler) {
  CHECK(compiler.GetCompilerContext() == NULL);

  art::compiler_llvm::CompilerLLVM* compiler_llvm =
      new art::compiler_llvm::CompilerLLVM(&compiler,
                                           compiler.GetInstructionSet());

  compiler.SetCompilerContext(compiler_llvm);
}

extern "C" art::CompiledMethod* ArtCompileMethod(art::Compiler& compiler,
                                                 const art::DexFile::CodeItem* code_item,
                                                 uint32_t access_flags, uint32_t method_idx,
                                                 const art::ClassLoader* class_loader,
                                                 const art::DexFile& dex_file)
{
  art::ClassLinker *class_linker = art::Runtime::Current()->GetClassLinker();
  art::DexCache *dex_cache = class_linker->FindDexCache(dex_file);

  art::OatCompilationUnit oat_compilation_unit(
    class_loader, class_linker, dex_file, *dex_cache, code_item,
    method_idx, access_flags);
  art::compiler_llvm::CompilerLLVM* compiler_llvm = ContextOf(compiler);
  art::CompiledMethod* result = compiler_llvm->CompileDexMethod(&oat_compilation_unit);
  compiler_llvm->MaterializeIfThresholdReached();
  return result;
}

extern "C" art::CompiledMethod* ArtJniCompileMethod(art::Compiler& compiler,
                                                    uint32_t access_flags, uint32_t method_idx,
                                                    const art::DexFile& dex_file) {
  art::ClassLinker *class_linker = art::Runtime::Current()->GetClassLinker();
  art::DexCache *dex_cache = class_linker->FindDexCache(dex_file);

  art::OatCompilationUnit oat_compilation_unit(
    NULL, class_linker, dex_file, *dex_cache, NULL,
    method_idx, access_flags);

  art::compiler_llvm::CompilerLLVM* compiler_llvm = ContextOf(compiler);
  art::CompiledMethod* result = compiler_llvm->CompileNativeMethod(&oat_compilation_unit);
  compiler_llvm->MaterializeIfThresholdReached();
  return result;
}

extern "C" art::CompiledInvokeStub* ArtCreateInvokeStub(art::Compiler& compiler, bool is_static,
                                                        const char* shorty, uint32_t shorty_len) {
  art::compiler_llvm::CompilerLLVM* compiler_llvm = ContextOf(compiler);
  art::CompiledInvokeStub* result = compiler_llvm->CreateInvokeStub(is_static, shorty);
  compiler_llvm->MaterializeIfThresholdReached();
  return result;
}

extern "C" void compilerLLVMSetBitcodeFileName(art::Compiler& compiler,
                                               std::string const& filename) {
  ContextOf(compiler)->SetBitcodeFileName(filename);
}

extern "C" void compilerLLVMMaterializeRemainder(art::Compiler& compiler) {
  ContextOf(compiler)->MaterializeRemainder();
}

extern "C" void compilerLLVMEnableAutoElfLoading(art::Compiler& compiler) {
  art::compiler_llvm::CompilerLLVM* compiler_llvm =
      reinterpret_cast<art::compiler_llvm::CompilerLLVM*>(compiler.GetCompilerContext());
  return compiler_llvm->EnableAutoElfLoading();
}

extern "C" const void* compilerLLVMGetMethodCodeAddr(const art::Compiler& compiler,
                                                     const art::CompiledMethod* cm,
                                                     const art::Method*) {
  const art::compiler_llvm::CompilerLLVM* compiler_llvm =
      reinterpret_cast<const art::compiler_llvm::CompilerLLVM*>(compiler.GetCompilerContext());
  return compiler_llvm->GetMethodCodeAddr(cm);
}

extern "C" const art::Method::InvokeStub* compilerLLVMGetMethodInvokeStubAddr(const art::Compiler& compiler,
                                                                              const art::CompiledInvokeStub* cm,
                                                                              const art::Method*) {
  const art::compiler_llvm::CompilerLLVM* compiler_llvm =
      reinterpret_cast<const art::compiler_llvm::CompilerLLVM*>(compiler.GetCompilerContext());
  return compiler_llvm->GetMethodInvokeStubAddr(cm);
}

extern "C" std::vector<art::ElfImage> compilerLLVMGetElfImages(const art::Compiler& compiler) {
  return ContextOf(compiler)->GetElfImages();
}

extern "C" void compilerLLVMDispose(art::Compiler& compiler) {
  delete ContextOf(compiler);
}
<|endoftext|>