<commit_before>/**
 * @file launch_box.hxx
 * @author Cameron Shinn (ctshinn@ucdavis.edu)
 * @brief
 * @version 0.1
 * @date 2020-11-09
 *
 * @copyright Copyright (c) 2020
 *
 */
#pragma once

#include <gunrock/cuda/detail/launch_box.hxx>
#include <gunrock/cuda/sm.hxx>
#include <gunrock/cuda/device_properties.hxx>
#include <gunrock/cuda/context.hxx>
#include <gunrock/error.hxx>

#include <tuple>
#include <type_traits>

#ifndef SM_TARGET
#define SM_TARGET 0
#endif

namespace gunrock {
namespace cuda {
namespace launch_box {

struct dimensions_t {
  unsigned int x, y, z;

  __host__ __device__ constexpr dimensions_t(const unsigned int _x = 1,
                                             const unsigned int _y = 1,
                                             const unsigned int _z = 1)
      : x(_x), y(_y), z(_z) {}

  __host__ __device__ constexpr unsigned int size() const { return x * y * z; }

#ifdef _MSC_VER
  __host__ __device__ operator dim3(void) const { return uint3{x, y, z}; }
#else
  __host__ __device__ constexpr operator dim3(void) const {
    return uint3{x, y, z};
  }
#endif
};

/**
 * @brief CUDA dim3 template representation, since dim3 cannot be used as a
 * template argument.
 *
 * @tparam x_ Dimension in the X direction.
 * @tparam y_ (default = `1`) Dimension in the Y direction.
 * @tparam z_ (default = `1`) Dimension in the Z direction.
 */
template <unsigned int x_ = 1, unsigned int y_ = 1, unsigned int z_ = 1>
struct dim3_t {
  enum : unsigned int { x = x_, y = y_, z = z_ };
  static constexpr unsigned int size() { return x * y * z; }
  static constexpr dimensions_t dimensions() { return {x, y, z}; }

  // Convertors must be non-static members.
  constexpr operator dimensions_t(void) { return {x, y, z}; }
};

/**
 * @brief Collection of kernel launch parameters for different architectures.
 *
 * @par Overview
 * A launch box is a collection of sets of CUDA kernel launch parameters each
 * corresponding to one or more SM architectures. At compile time, the launch
 * box's type resolves to the **first** launch parameters type (derived from
 * `launch_param_base_t`) that match the SM architecture that Gunrock is being
 * compiled for. If there isn't an explicit match, launch parameters for any SM
 * version can be specified at the end of the parameter pack using the
 * `fallback` enum for the `sm_flag_t` template parameter (note that this will
 * invalidate any launch parameter types later in the parameter pack). In the
 * case that there isn't a fallback and the compiler can't find launch
 * parameters for the architecture being compiled for, a static assert will be
 * raised. All launch parameters *should* use the same struct template so there
 * isn't any ambiguity as to what the launch box's constructor is, though this
 * isn't enforced.
 *
 * @par Example
 * The following code is an example of how to instantiate a launch box.
 *
 * \code
 * typedef launch_box_t<
 *     launch_params_t<sm_86 | sm_80, dim3_t<16, 2, 2>, dim3_t<64, 1, 4>, 2>,
 *     launch_params_t<sm_75 | sm_70, dim3_t<32, 2, 4>, dim3_t<64, 8, 8>>,
 *     launch_params_t<sm_61 | sm_60, dim3_t<8, 4, 4>, dim3_t<32, 1, 4>, 2>,
 *     launch_params_t<sm_35, dim3_t<64>, dim3_t<64>, 16>,
 *     launch_params_t<fallback, dim3_t<16>, dim3_t<2>, 4>>
 *     launch_t;
 *
 * launch_t my_launch_box(context);
 * \endcode
 *
 * @tparam lp_v Pack of `launch_params_t` types for each corresponding
 * architecture(s).
 */
template <typename... lp_v>
using launch_box_t = std::conditional_t<
    (std::tuple_size<detail::match_launch_params_t<lp_v...>>::value == 0),
    detail::raise_not_found_error_t<void>,  // Couldn't find params for SM ver
    std::tuple_element_t<0, detail::match_launch_params_t<lp_v...>>>;

/**
 * @brief Set of launch parameters for a CUDA kernel.
 *
 * @tparam sm_flags_ Bit flags for the SM architectures the launch parameters
 * correspond to.
 * @tparam block_dimensions_ A `dim3_t` type representing the block dimensions.
 * @tparam grid_dimensions_ A `dim3_t` type representing the grid dimensions.
 * @tparam shared_memory_bytes_ Number of bytes of shared memory to allocate.
 */
template <sm_flag_t sm_flags_,
          typename block_dimensions_,
          typename grid_dimensions_,
          size_t shared_memory_bytes_ = 0>
struct launch_params_t
    : detail::launch_params_base_t<sm_flags_, shared_memory_bytes_> {
  typedef detail::launch_params_base_t<sm_flags_, shared_memory_bytes_> base_t;
  typedef block_dimensions_ block_dimensions_t;
  typedef grid_dimensions_ grid_dimensions_t;

  static constexpr dimensions_t block_dimensions =
      block_dimensions_t::dimensions();
  static constexpr dimensions_t grid_dimensions =
      grid_dimensions_t::dimensions();

  /**
   * @brief Launch a kernel within the given launch box.
   *
   * @par Overview
   * This function is a reimplementation of `std::apply`, that allows for
   * launching cuda kernels with launch param members of the class and a context
   * argument. It follows the "possible implementation" of `std::apply` in the
   * C++ reference: https://en.cppreference.com/w/cpp/utility/apply.
   *
   * @tparam func_t The type of the kernel function being passed in.
   * @tparam args_tuple_t The type of the tuple of arguments being passed in.
   * @param __ Kernel function to call.
   * @param args Tuple of arguments to be expanded as the arguments of the
   * kernel function.
   * @param context Reference to the context used to launch the kernel (used for
   * the context's stream).
   */
  template <typename func_t, typename args_tuple_t>
  void launch(func_t&& __, args_tuple_t&& args, standard_context_t& context) {
    expand_launch(
        std::forward<func_t>(__), std::forward<args_tuple_t>(args), context,
        std::make_index_sequence<
            std::tuple_size_v<std::remove_reference_t<args_tuple_t>>>{});
  }

 private:
  template <typename func_t, typename args_tuple_t, std::size_t... I>
  void expand_launch(func_t&& __,
                     args_tuple_t&& args,
                     standard_context_t& context,
                     std::index_sequence<I...>) {
    __<<<grid_dimensions, block_dimensions, base_t::shared_memory_bytes,
         context.stream()>>>(std::get<I>(std::forward<args_tuple_t>(args))...);
  }
};

/**
 * @brief Set of launch parameters for a CUDA kernel (with dynamic grid
 * dimensions).
 *
 * @tparam sm_flags_ Bit flags for the SM architectures the launch parameters
 * correspond to.
 * @tparam block_dimensions_ A `dim3_t` type representing the block dimensions.
 * @tparam shared_memory_bytes_ Number of bytes of shared memory to allocate.
 */
template <sm_flag_t sm_flags_,
          typename block_dimensions_,
          size_t shared_memory_bytes_ = 0>
struct launch_params_dynamic_grid_t
    : detail::launch_params_base_t<sm_flags_, shared_memory_bytes_> {
  typedef detail::launch_params_base_t<sm_flags_, shared_memory_bytes_> base_t;
  typedef block_dimensions_ block_dimensions_t;

  static constexpr dimensions_t block_dimensions =
      block_dimensions_t::dimensions();

  dimensions_t grid_dimensions;

  void calculate_grid_dimensions(std::size_t num_elements) {
    grid_dimensions = dimensions_t(
        (num_elements + block_dimensions.x - 1) / block_dimensions.x, 1, 1);
  }

  /**
   * @brief Launch a kernel within the given launch box.
   *
   * @par Overview
   * This function is a reimplementation of `std::apply`, that allows for
   * launching cuda kernels with launch param members of the class and a context
   * argument. It follows the "possible implementation" of `std::apply` in the
   * C++ reference: https://en.cppreference.com/w/cpp/utility/apply.
   *
   * @tparam func_t The type of the kernel function being passed in.
   * @tparam args_tuple_t The type of the tuple of arguments being passed in.
   * @param __ Kernel function to call.
   * @param args Tuple of arguments to be expanded as the arguments of the
   * kernel function.
   * @param context Reference to the context used to launch the kernel (used for
   * the context's stream).
   */
  template <typename func_t, typename args_tuple_t>
  void launch(func_t&& __, args_tuple_t&& args, standard_context_t& context) {
    expand_launch(
        std::forward<func_t>(__), std::forward<args_tuple_t>(args), context,
        std::make_index_sequence<
            std::tuple_size_v<std::remove_reference_t<args_tuple_t>>>{});
  }

 private:
  template <typename func_t, typename args_tuple_t, std::size_t... I>
  void expand_launch(func_t&& __,
                     args_tuple_t&& args,
                     standard_context_t& context,
                     std::index_sequence<I...>) {
    __<<<grid_dimensions, block_dimensions, base_t::shared_memory_bytes,
         context.stream()>>>(std::get<I>(std::forward<args_tuple_t>(args))...);
  }
};

/**
 * @brief Calculator for ratio of active to maximum warps per multiprocessor.
 *
 * @tparam launch_box_t Launch box for the corresponding kernel.
 * @param kernel CUDA kernel for which to calculate the occupancy.
 * \return float
 */
template <typename launch_box_t, typename func_t>
inline float occupancy(func_t kernel) {
  int max_active_blocks;
  int block_size = launch_box_t::block_dimensions_t::size();
  int device;
  cudaDeviceProp props;

  cudaGetDevice(&device);
  cudaGetDeviceProperties(&props, device);
  error::error_t status = cudaOccupancyMaxActiveBlocksPerMultiprocessor(
      &max_active_blocks, kernel, block_size, (size_t)0);
  error::throw_if_exception(status);
  float occupancy = (max_active_blocks * block_size / props.warpSize) /
                    (float)(props.maxThreadsPerMultiProcessor / props.warpSize);
  return occupancy;
}

}  // namespace launch_box

}  // namespace cuda
}  // namespace gunrock
<commit_msg>Define launch func in launch box<commit_after>/**
 * @file launch_box.hxx
 * @author Cameron Shinn (ctshinn@ucdavis.edu)
 * @brief
 * @version 0.1
 * @date 2020-11-09
 *
 * @copyright Copyright (c) 2020
 *
 */
#pragma once

#include <gunrock/cuda/detail/launch_box.hxx>
#include <gunrock/cuda/sm.hxx>
#include <gunrock/cuda/device_properties.hxx>
#include <gunrock/cuda/context.hxx>
#include <gunrock/error.hxx>

#include <tuple>
#include <type_traits>

#ifndef SM_TARGET
#define SM_TARGET 0
#endif

namespace gunrock {
namespace cuda {
namespace launch_box {

struct dimensions_t {
  unsigned int x, y, z;

  __host__ __device__ constexpr dimensions_t(const unsigned int _x = 1,
                                             const unsigned int _y = 1,
                                             const unsigned int _z = 1)
      : x(_x), y(_y), z(_z) {}

  __host__ __device__ constexpr unsigned int size() const { return x * y * z; }

#ifdef _MSC_VER
  __host__ __device__ operator dim3(void) const { return uint3{x, y, z}; }
#else
  __host__ __device__ constexpr operator dim3(void) const {
    return uint3{x, y, z};
  }
#endif
};

/**
 * @brief CUDA dim3 template representation, since dim3 cannot be used as a
 * template argument.
 *
 * @tparam x_ Dimension in the X direction.
 * @tparam y_ (default = `1`) Dimension in the Y direction.
 * @tparam z_ (default = `1`) Dimension in the Z direction.
 */
template <unsigned int x_ = 1, unsigned int y_ = 1, unsigned int z_ = 1>
struct dim3_t {
  enum : unsigned int { x = x_, y = y_, z = z_ };
  static constexpr unsigned int size() { return x * y * z; }
  static constexpr dimensions_t dimensions() { return {x, y, z}; }

  // Convertors must be non-static members.
  constexpr operator dimensions_t(void) { return {x, y, z}; }
};

/**
 * @brief Set of launch parameters for a CUDA kernel.
 *
 * @tparam sm_flags_ Bit flags for the SM architectures the launch parameters
 * correspond to.
 * @tparam block_dimensions_ A `dim3_t` type representing the block dimensions.
 * @tparam grid_dimensions_ A `dim3_t` type representing the grid dimensions.
 * @tparam shared_memory_bytes_ Number of bytes of shared memory to allocate.
 */
template <sm_flag_t sm_flags_,
          typename block_dimensions_,
          typename grid_dimensions_,
          size_t shared_memory_bytes_ = 0>
struct launch_params_t
    : detail::launch_params_base_t<sm_flags_, shared_memory_bytes_> {
  typedef detail::launch_params_base_t<sm_flags_, shared_memory_bytes_> base_t;
  typedef block_dimensions_ block_dimensions_t;
  typedef grid_dimensions_ grid_dimensions_t;

  static constexpr dimensions_t block_dimensions =
      block_dimensions_t::dimensions();
  static constexpr dimensions_t grid_dimensions =
      grid_dimensions_t::dimensions();
};

/**
 * @brief Set of launch parameters for a CUDA kernel (with dynamic grid
 * dimensions).
 *
 * @tparam sm_flags_ Bit flags for the SM architectures the launch parameters
 * correspond to.
 * @tparam block_dimensions_ A `dim3_t` type representing the block dimensions.
 * @tparam shared_memory_bytes_ Number of bytes of shared memory to allocate.
 */
template <sm_flag_t sm_flags_,
          typename block_dimensions_,
          size_t shared_memory_bytes_ = 0>
struct launch_params_dynamic_grid_t
    : detail::launch_params_base_t<sm_flags_, shared_memory_bytes_> {
  typedef detail::launch_params_base_t<sm_flags_, shared_memory_bytes_> base_t;
  typedef block_dimensions_ block_dimensions_t;

  static constexpr dimensions_t block_dimensions =
      block_dimensions_t::dimensions();

  dimensions_t grid_dimensions;

  void calculate_grid_dimensions(std::size_t num_elements) {
    grid_dimensions = dimensions_t(
        (num_elements + block_dimensions.x - 1) / block_dimensions.x, 1, 1);
  }
};

/**
 * @brief Alias a selected a launch params type from valid options on
 * architecture being compiled for.
 *
 * @tparam lp_v
 */
template <typename... lp_v>
using select_launch_params_t = std::conditional_t<
    (std::tuple_size<detail::match_launch_params_t<lp_v...>>::value == 0),
    detail::raise_not_found_error_t<void>,  // Couldn't find arch params
    std::tuple_element_t<0, detail::match_launch_params_t<lp_v...>>>;

/**
 * @brief Collection of kernel launch parameters for different
 * architectures.
 *
 * @par Overview
 * A launch box is a collection of sets of CUDA kernel launch parameters
 * each corresponding to one or more SM architectures. At compile time, the
 * launch box's type resolves to the **first** launch parameters type
 * (derived from `launch_param_base_t`) that match the SM architecture that
 * Gunrock is being compiled for. If there isn't an explicit match, launch
 * parameters for any SM version can be specified at the end of the
 * parameter pack using the `fallback` enum for the `sm_flag_t` template
 * parameter (note that this will invalidate any launch parameter types
 * later in the parameter pack). In the case that there isn't a fallback and
 * the compiler can't find launch parameters for the architecture being
 * compiled for, a static assert will be raised. All launch parameters
 * *should* use the same struct template so there isn't any ambiguity as to
 * what the launch box's constructor is, though this isn't enforced.
 *
 * @par Example
 * The following code is an example of how to instantiate a launch box.
 *
 * \code
 * typedef launch_box_t<
 *     launch_params_t<sm_86 | sm_80, dim3_t<16, 2, 2>, dim3_t<64, 1, 4>,
 * 2>, launch_params_t<sm_75 | sm_70, dim3_t<32, 2, 4>, dim3_t<64, 8, 8>>,
 *     launch_params_t<sm_61 | sm_60, dim3_t<8, 4, 4>, dim3_t<32, 1, 4>, 2>,
 *     launch_params_t<sm_35, dim3_t<64>, dim3_t<64>, 16>,
 *     launch_params_t<fallback, dim3_t<16>, dim3_t<2>, 4>>
 *     launch_t;
 *
 * launch_t my_launch_box(context);
 * \endcode
 *
 * @tparam lp_v Pack of `launch_params_t` types for each corresponding
 * architecture(s).
 */
template <typename... lp_v>
struct launch_box_t : select_launch_params_t<lp_v...> {
  typedef select_launch_params_t<lp_v...> base_t;

  /**
   * @brief Launch a kernel within the given launch box.
   *
   * @par Overview
   * This function is a reimplementation of `std::apply`, that allows
   * for launching cuda kernels with launch param members of the class
   * and a context argument. It follows the "possible implementation" of
   * `std::apply` in the C++ reference:
   * https://en.cppreference.com/w/cpp/utility/apply.
   *
   * @tparam func_t The type of the kernel function being passed in.
   * @tparam args_tuple_t The type of the tuple of arguments being
   * passed in.
   * @param __ Kernel function to call.
   * @param args Tuple of arguments to be expanded as the arguments of
   * the kernel function.
   * @param context Reference to the context used to launch the kernel
   * (used for the context's stream).
   */
  template <typename func_t, typename args_tuple_t>
  void launch(func_t&& __, args_tuple_t&& args, standard_context_t& context) {
    launch_impl(
        std::forward<func_t>(__), std::forward<args_tuple_t>(args), context,
        std::make_index_sequence<
            std::tuple_size_v<std::remove_reference_t<args_tuple_t>>>{});
  }

 private:
  template <typename func_t, typename args_tuple_t, std::size_t... I>
  void launch_impl(func_t&& __,
                   args_tuple_t&& args,
                   standard_context_t& context,
                   std::index_sequence<I...>) {
    __<<<base_t::grid_dimensions, base_t::block_dimensions,
         base_t::shared_memory_bytes, context.stream()>>>(
        std::get<I>(std::forward<args_tuple_t>(args))...);
  }
};

/**
 * @brief Calculator for ratio of active to maximum warps per multiprocessor.
 *
 * @tparam launch_box_t Launch box for the corresponding kernel.
 * @param kernel CUDA kernel for which to calculate the occupancy.
 * \return float
 */
template <typename launch_box_t, typename func_t>
inline float occupancy(func_t kernel) {
  int max_active_blocks;
  int block_size = launch_box_t::block_dimensions_t::size();
  int device;
  cudaDeviceProp props;

  cudaGetDevice(&device);
  cudaGetDeviceProperties(&props, device);
  error::error_t status = cudaOccupancyMaxActiveBlocksPerMultiprocessor(
      &max_active_blocks, kernel, block_size, (size_t)0);
  error::throw_if_exception(status);
  float occupancy = (max_active_blocks * block_size / props.warpSize) /
                    (float)(props.maxThreadsPerMultiProcessor / props.warpSize);
  return occupancy;
}

}  // namespace launch_box
}  // namespace cuda
}  // namespace gunrock
<|endoftext|>


<commit_before>// Copyright (c) 2016-2018 The Gulden developers
// Authored by: Malcolm MacLeod (mmacleod@webmail.co.za)
// Distributed under the GULDEN software license, see the accompanying
// file COPYING

#include "appmanager.h"
#include "util.h"
#include "init.h"
#include "warnings.h"

// Below  includes is for the socketpair that controls shutdown.
#ifdef WIN32
#include <sys/types.h>
#endif

GuldenAppManager* GuldenAppManager::gApp = nullptr;

GuldenAppManager::GuldenAppManager()
: fShutDownHasBeenInitiated(false)
{
    if (gApp)
        assert(0);

    gApp = this;

    // Create the shutdown handler thread.
    shutdownThread();
}

GuldenAppManager::~GuldenAppManager()
{
    // Refuse to close if initialize() or shutdown() is still busy.
    std::lock_guard<std::mutex> lock(appManagerInitShutDownMutex);
}

void GuldenAppManager::handleRunawayException(const std::exception *e)
{
    PrintExceptionContinue(e, "Runaway exception");
    signalRunawayException((GetWarnings("gui")));
}

void GuldenAppManager::initialize()
{
    std::thread([=]
    {
        //RenameThread("Gulden-initialise");
        std::lock_guard<std::mutex> lock(appManagerInitShutDownMutex);
        try
        {
            LogPrintf("%s: Running initialization in thread\n", __func__);
            if (fShutDownHasBeenInitiated)
                return;
            if (!AppInitBasicSetup())
            {
                signalAppInitializeResult(false);
                return;
            }
            if (fShutDownHasBeenInitiated)
                return;
            if (!AppInitParameterInteraction())
            {
                signalAppInitializeResult(false);
                return;
            }
            if (fShutDownHasBeenInitiated)
                return;
            if (!AppInitSanityChecks())
            {
                signalAppInitializeResult(false);
                return;
            }
            if (fShutDownHasBeenInitiated)
                return;

            //fixme: (UNITY) - We handle only the last slot return here - this is fine for now as there -is- only one.
            //However we should just use a custom combiner and boolean && the results to be future safe for other ports.
            if (!signalAboutToInitMain())
            {
                //Start shutdown process.
                shutdown();
                return;
            }

            if (fShutDownHasBeenInitiated)
                return;
            bool rv = AppInitMain(threadGroup, scheduler);
            signalAppInitializeResult(rv);
        }
        catch (const std::exception& e)
        {
            handleRunawayException(&e);
        }
        catch (...)
        {
            handleRunawayException(NULL);
        }
    }).detach();
}

// We use a socket here to signal shutdown to the main app.
// As we (may) have been called from sigterm it is not safe to do anything else here.
// See http://doc.qt.io/qt-5/unix-signals.html for more information.
void GuldenAppManager::shutdown()
{
    // Let the core know that we are in the early process of shutting down.
    // Do this before the mutex so that if we are still in init we can abandon the init.
    fShutDownHasBeenInitiated = true;

    #ifdef WIN32
    sigtermCv.notify_one();
    #else
    char signalClose = 1;
    ::write(sigtermFd[0], &signalClose, sizeof(signalClose));
    #endif
}

bool GuldenAppManager::daemonise()
{
    #if HAVE_DECL_DAEMON
    {
        // Daemonize - don't chdir (1), do close FDs (0)
        bool managedToDeamonise = (daemon(1, 0) != -1);

        if (!managedToDeamonise)
        {
            fprintf(stderr, "Error: daemon() failed: %s\n", strerror(errno));
        }

        // Create a new shutdownThread
        shutdownThread();
        return managedToDeamonise;
    }
    #else
    fprintf(stderr, "Error: -daemon is not supported on this operating system\n");
    return false
    #endif
}

void GuldenAppManager::shutdownThread()
{
    #ifndef WIN32
    if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sigtermFd) == -1)
    {
        LogPrintf("shutdown thread: Failed to create socket pair\n");
        assert(0);
    }
    #endif

    std::thread([=]
    {
        RenameThread("Gulden-shutdown");

        // Block until we are signalled to commence
        #ifdef WIN32
        std::unique_lock<std::mutex> lk(appManagerInitShutDownMutex);
        sigtermCv.wait(lk, [this]{ return fShutDownHasBeenInitiated == true; });
        #else
        char signalClose = 0;
        ::read(sigtermFd[1], &signalClose, sizeof(signalClose));
        LogPrintf("shutdown thread: App shutdown requested\n");
        std::lock_guard<std::mutex> lock(appManagerInitShutDownMutex);
        #endif

        LogPrintf("shutdown thread: Commence app shutdown\n");

        try
        {
            // Allow UI to visually alert start of shutdown progress.
            LogPrintf("shutdown thread: Signal start of shutdown to UI\n");
            signalAppShutdownStarted();
            MilliSleep(200);

            LogPrintf("shutdown thread: Signal UI to alert user of shutdown\n");
            signalAppShutdownAlertUser();
            MilliSleep(50);

            // Notify all core and network threads to start "wrapping up".
            LogPrintf("shutdown thread: Interrupt core\n");
            CoreInterrupt(threadGroup);
            MilliSleep(50);

            // Notify UI that core shutdown has begun and that it should start disconnecting the various models/signals.
            LogPrintf("shutdown thread: Signal core interrupt to UI\n");
            signalAppShutdownCoreInterrupted();
            MilliSleep(50);

            // Terminate all core threads.
            LogPrintf("shutdown thread: Shut down core\n");
            CoreShutdown(threadGroup);
            MilliSleep(50);

            LogPrintf("shutdown thread: Core shutdown finished, signaling UI to shut itself down\n");
            signalAppShutdownFinished();
            MilliSleep(50);

            LogPrintf("shutdown thread: Exiting shutdown thread\n");
        }
        catch (const std::exception& e)
        {
            LogPrintf("%s: App shutdown exception [%s]\n", __func__, e.what());
            handleRunawayException(&e);
        }
        catch (...)
        {
            LogPrintf("%s: App shutdown exception\n", __func__);
            handleRunawayException(NULL);
        }
    }).detach();
}

void GuldenAppManager::setRecoveryPhrase(const SecureString& recoveryPhrase_)
{
    recoveryPhrase = recoveryPhrase_;
}

SecureString GuldenAppManager::getRecoveryPhrase()
{
    return recoveryPhrase;
}

void GuldenAppManager::BurnRecoveryPhrase()
{
    // The below is a 'SecureString' - so no memory burn necessary, it should burn itself.
    recoveryPhrase = "";
}

bool ShutdownRequested()
{
    return GuldenAppManager::gApp ? (bool)GuldenAppManager::gApp->fShutDownHasBeenInitiated : false;
}
<commit_msg>Windows build fix.<commit_after>// Copyright (c) 2016-2018 The Gulden developers
// Authored by: Malcolm MacLeod (mmacleod@webmail.co.za)
// Distributed under the GULDEN software license, see the accompanying
// file COPYING

#include "appmanager.h"
#include "util.h"
#include "init.h"
#include "warnings.h"

// Below  includes is for the socketpair that controls shutdown.
#ifdef WIN32
#include <sys/types.h>
#endif

GuldenAppManager* GuldenAppManager::gApp = nullptr;

GuldenAppManager::GuldenAppManager()
: fShutDownHasBeenInitiated(false)
{
    if (gApp)
        assert(0);

    gApp = this;

    // Create the shutdown handler thread.
    shutdownThread();
}

GuldenAppManager::~GuldenAppManager()
{
    // Refuse to close if initialize() or shutdown() is still busy.
    std::lock_guard<std::mutex> lock(appManagerInitShutDownMutex);
}

void GuldenAppManager::handleRunawayException(const std::exception *e)
{
    PrintExceptionContinue(e, "Runaway exception");
    signalRunawayException((GetWarnings("gui")));
}

void GuldenAppManager::initialize()
{
    std::thread([=]
    {
        //RenameThread("Gulden-initialise");
        std::lock_guard<std::mutex> lock(appManagerInitShutDownMutex);
        try
        {
            LogPrintf("%s: Running initialization in thread\n", __func__);
            if (fShutDownHasBeenInitiated)
                return;
            if (!AppInitBasicSetup())
            {
                signalAppInitializeResult(false);
                return;
            }
            if (fShutDownHasBeenInitiated)
                return;
            if (!AppInitParameterInteraction())
            {
                signalAppInitializeResult(false);
                return;
            }
            if (fShutDownHasBeenInitiated)
                return;
            if (!AppInitSanityChecks())
            {
                signalAppInitializeResult(false);
                return;
            }
            if (fShutDownHasBeenInitiated)
                return;

            //fixme: (UNITY) - We handle only the last slot return here - this is fine for now as there -is- only one.
            //However we should just use a custom combiner and boolean && the results to be future safe for other ports.
            if (!signalAboutToInitMain())
            {
                //Start shutdown process.
                shutdown();
                return;
            }

            if (fShutDownHasBeenInitiated)
                return;
            bool rv = AppInitMain(threadGroup, scheduler);
            signalAppInitializeResult(rv);
        }
        catch (const std::exception& e)
        {
            handleRunawayException(&e);
        }
        catch (...)
        {
            handleRunawayException(NULL);
        }
    }).detach();
}

// We use a socket here to signal shutdown to the main app.
// As we (may) have been called from sigterm it is not safe to do anything else here.
// See http://doc.qt.io/qt-5/unix-signals.html for more information.
void GuldenAppManager::shutdown()
{
    // Let the core know that we are in the early process of shutting down.
    // Do this before the mutex so that if we are still in init we can abandon the init.
    fShutDownHasBeenInitiated = true;

    #ifdef WIN32
    sigtermCv.notify_one();
    #else
    char signalClose = 1;
    ::write(sigtermFd[0], &signalClose, sizeof(signalClose));
    #endif
}

bool GuldenAppManager::daemonise()
{
    #if HAVE_DECL_DAEMON
    {
        // Daemonize - don't chdir (1), do close FDs (0)
        bool managedToDeamonise = (daemon(1, 0) != -1);

        if (!managedToDeamonise)
        {
            fprintf(stderr, "Error: daemon() failed: %s\n", strerror(errno));
        }

        // Create a new shutdownThread
        shutdownThread();
        return managedToDeamonise;
    }
    #else
    fprintf(stderr, "Error: -daemon is not supported on this operating system\n");
    return false;
    #endif
}

void GuldenAppManager::shutdownThread()
{
    #ifndef WIN32
    if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sigtermFd) == -1)
    {
        LogPrintf("shutdown thread: Failed to create socket pair\n");
        assert(0);
    }
    #endif

    std::thread([=]
    {
        RenameThread("Gulden-shutdown");

        // Block until we are signalled to commence
        #ifdef WIN32
        std::unique_lock<std::mutex> lk(appManagerInitShutDownMutex);
        sigtermCv.wait(lk, [this]{ return fShutDownHasBeenInitiated == true; });
        #else
        char signalClose = 0;
        ::read(sigtermFd[1], &signalClose, sizeof(signalClose));
        LogPrintf("shutdown thread: App shutdown requested\n");
        std::lock_guard<std::mutex> lock(appManagerInitShutDownMutex);
        #endif

        LogPrintf("shutdown thread: Commence app shutdown\n");

        try
        {
            // Allow UI to visually alert start of shutdown progress.
            LogPrintf("shutdown thread: Signal start of shutdown to UI\n");
            signalAppShutdownStarted();
            MilliSleep(200);

            LogPrintf("shutdown thread: Signal UI to alert user of shutdown\n");
            signalAppShutdownAlertUser();
            MilliSleep(50);

            // Notify all core and network threads to start "wrapping up".
            LogPrintf("shutdown thread: Interrupt core\n");
            CoreInterrupt(threadGroup);
            MilliSleep(50);

            // Notify UI that core shutdown has begun and that it should start disconnecting the various models/signals.
            LogPrintf("shutdown thread: Signal core interrupt to UI\n");
            signalAppShutdownCoreInterrupted();
            MilliSleep(50);

            // Terminate all core threads.
            LogPrintf("shutdown thread: Shut down core\n");
            CoreShutdown(threadGroup);
            MilliSleep(50);

            LogPrintf("shutdown thread: Core shutdown finished, signaling UI to shut itself down\n");
            signalAppShutdownFinished();
            MilliSleep(50);

            LogPrintf("shutdown thread: Exiting shutdown thread\n");
        }
        catch (const std::exception& e)
        {
            LogPrintf("%s: App shutdown exception [%s]\n", __func__, e.what());
            handleRunawayException(&e);
        }
        catch (...)
        {
            LogPrintf("%s: App shutdown exception\n", __func__);
            handleRunawayException(NULL);
        }
    }).detach();
}

void GuldenAppManager::setRecoveryPhrase(const SecureString& recoveryPhrase_)
{
    recoveryPhrase = recoveryPhrase_;
}

SecureString GuldenAppManager::getRecoveryPhrase()
{
    return recoveryPhrase;
}

void GuldenAppManager::BurnRecoveryPhrase()
{
    // The below is a 'SecureString' - so no memory burn necessary, it should burn itself.
    recoveryPhrase = "";
}

bool ShutdownRequested()
{
    return GuldenAppManager::gApp ? (bool)GuldenAppManager::gApp->fShutDownHasBeenInitiated : false;
}
<|endoftext|>


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

#include "unix/ibus/session.h"

#include "base/base.h"
#include "base/singleton.h"
#include "session/commands.pb.h"
#include "session/session_handler.h"

namespace mozc {
namespace ibus {

// Session Handler must be singleton per user.
class StandaloneSessionHandler {
public:
  StandaloneSessionHandler()
    : handler_(new SessionHandler) {}

  bool EvalCommand(commands::Command *command) {
    // SessionHandler::EvalCommand is not thread safe.
    // When IBusMozc creates multiple Session instances and
    // calls EvalCommand simultaneously, EvalCommand will crashes.
    // To prevent such situation, do a giant lock on EvalCommand
    // just in case. Seems that IBusMozc will never create
    // multiple instances.
    scoped_lock l(&mutex_);
    return handler_->EvalCommand(command);
  }

private:
  Mutex mutex_;
  scoped_ptr<SessionHandler> handler_;
};

Session::Session()
    : id_(0),
      handler_(Singleton<StandaloneSessionHandler>::get()) {
}

Session::~Session() {
  DeleteSession();
}

bool Session::IsValidRunLevel() const {
  return true;
}

bool Session::EnsureSession() {
  if (id_ != 0) {
    VLOG(1) << "session has been already created.";
    return true;
  }
  return CreateSession();
}

bool Session::EnsureConnection() {
  return true;
}

bool Session::SendKey(const commands::KeyEvent &key,
                      commands::Output *output) {
  commands::Input input;
  input.set_type(commands::Input::SEND_KEY);
  input.mutable_key()->CopyFrom(key);
  return EnsureCallCommand(&input, output);
}

bool Session::TestSendKey(const commands::KeyEvent &key,
                          commands::Output *output) {
  commands::Input input;
  input.set_type(commands::Input::TEST_SEND_KEY);
  input.mutable_key()->CopyFrom(key);
  return EnsureCallCommand(&input, output);
}

bool Session::SendCommand(const commands::SessionCommand &command,
                          commands::Output *output) {
  commands::Input input;
  input.set_type(commands::Input::SEND_COMMAND);
  input.mutable_command()->CopyFrom(command);
  return EnsureCallCommand(&input, output);
}

bool Session::CheckVersionOrRestartServer() {
  return true;
}

bool Session::EnsureCallCommand(commands::Input *input,
                                commands::Output *output) {
  if (!EnsureSession()) {
    LOG(ERROR) << "EnsureSession failed";
    return false;
  }

  input->set_id(id_);
  output->set_id(0);
  return Call(*input, output);
}

void Session::EnableCascadingWindow(bool enable) {
  // TODO(mazda): implement this
}

void Session::set_timeout(int timeout) {
}

void Session::set_restricted(bool restricted) {
}

void Session::set_server_program(const string &program_path) {
}

void Session::set_client_capability(const commands::Capability &capability) {
  client_capability_.CopyFrom(capability);
}

bool Session::LaunchTool(const string &mode,
                         const string &extra_arg) {
  return true;
}

bool Session::OpenBrowser(const string &url) {
  return true;
}

bool Session::CreateSession() {
  if (id_ != 0) {
    VLOG(1) << "session has been already created.";
    return true;
  }

  commands::Command command;
  command.mutable_input()->set_type(commands::Input::CREATE_SESSION);
  command.mutable_input()->set_capability(client_capability_);
  if (!handler_->EvalCommand(&command)) {
    LOG(ERROR) << "EvalCommand failed";
    return false;
  }
  id_ = command.output().id();
  return true;
}

bool Session::DeleteSession() {
  // No need to delete session
  if (id_ == 0) {
    VLOG(1) << "session has been already deleted.";
    return true;
  }

  commands::Command command;
  command.mutable_input()->set_id(id_);
  command.mutable_input()->set_type(commands::Input::DELETE_SESSION);
  bool result = handler_->EvalCommand(&command);
  id_ = 0;
  return result;
}

bool Session::GetConfig(config::Config *config) {
  commands::Input input;
  input.set_id(id_);
  input.set_type(commands::Input::GET_CONFIG);

  commands::Output output;
  if (!Call(input, &output)) {
    return false;
  }

  if (!output.has_config()) {
    return false;
  }

  config->Clear();
  config->CopyFrom(output.config());
  return true;
}

bool Session::SetConfig(const config::Config &config) {
  commands::Input input;
  input.set_id(id_);
  input.set_type(commands::Input::SET_CONFIG);
  input.mutable_config()->CopyFrom(config);

  commands::Output output;
  if (!Call(input, &output)) {
    return false;
  }

  return true;
}

bool Session::ClearUserHistory() {
  return CallCommand(commands::Input::CLEAR_USER_HISTORY);
}

bool Session::ClearUserPrediction() {
  return CallCommand(commands::Input::CLEAR_USER_PREDICTION);
}

bool Session::ClearUnusedUserPrediction() {
  return CallCommand(commands::Input::CLEAR_UNUSED_USER_PREDICTION);
}

bool Session::Shutdown() {
  CallCommand(commands::Input::SHUTDOWN);
  return true;
}

bool Session::SyncData() {
  return CallCommand(commands::Input::SYNC_DATA);
}

bool Session::Reload() {
  return CallCommand(commands::Input::RELOAD);
}

bool Session::Cleanup() {
  return CallCommand(commands::Input::CLEANUP);
}

bool Session::NoOperation() {
  return CallCommand(commands::Input::NO_OPERATION);
}

bool Session::PingServer() const {
  return true;
}

bool Session::CallCommand(commands::Input::CommandType type) {
  commands::Input input;
  input.set_id(id_);
  input.set_type(type);
  commands::Output output;
  return Call(input, &output);
}

bool Session::Call(const commands::Input &input,
                   commands::Output *output) {
  DCHECK(output) << "output is null";

  commands::Command command;
  command.mutable_input()->CopyFrom(input);
  if (!handler_->EvalCommand(&command)) {
    LOG(ERROR) << "EvalCommand failed.";
    return false;
  }
  output->CopyFrom(command.output());
  return true;
}

void Session::Reset() {
  // TODO(mazda): Implement this
}

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

#include "unix/ibus/session.h"

#include "base/base.h"
#include "base/singleton.h"
#include "session/commands.pb.h"
#include "session/session_handler.h"

namespace mozc {
namespace ibus {

// Session Handler must be singleton per user.
class StandaloneSessionHandler {
public:
  StandaloneSessionHandler()
    : handler_(new SessionHandler) {}

  bool EvalCommand(commands::Command *command) {
    // SessionHandler::EvalCommand is not thread safe.
    // When IBusMozc creates multiple Session instances and
    // calls EvalCommand simultaneously, EvalCommand will crashes.
    // To prevent such situation, do a giant lock on EvalCommand
    // just in case. Seems that IBusMozc will never create
    // multiple instances.
    scoped_lock l(&mutex_);
    return handler_->EvalCommand(command);
  }

private:
  Mutex mutex_;
  scoped_ptr<SessionHandler> handler_;
};

Session::Session()
    : id_(0),
      handler_(Singleton<StandaloneSessionHandler>::get()) {
}

Session::~Session() {
  DeleteSession();
}

bool Session::IsValidRunLevel() const {
  return true;
}

bool Session::EnsureSession() {
  if (id_ != 0) {
    VLOG(1) << "session has been already created.";
    return true;
  }
  return CreateSession();
}

bool Session::EnsureConnection() {
  return true;
}

bool Session::SendKey(const commands::KeyEvent &key,
                      commands::Output *output) {
  commands::Input input;
  input.set_type(commands::Input::SEND_KEY);
  input.mutable_key()->CopyFrom(key);
  return EnsureCallCommand(&input, output);
}

bool Session::TestSendKey(const commands::KeyEvent &key,
                          commands::Output *output) {
  commands::Input input;
  input.set_type(commands::Input::TEST_SEND_KEY);
  input.mutable_key()->CopyFrom(key);
  return EnsureCallCommand(&input, output);
}

bool Session::SendCommand(const commands::SessionCommand &command,
                          commands::Output *output) {
  commands::Input input;
  input.set_type(commands::Input::SEND_COMMAND);
  input.mutable_command()->CopyFrom(command);
  return EnsureCallCommand(&input, output);
}

bool Session::CheckVersionOrRestartServer() {
  return true;
}

bool Session::EnsureCallCommand(commands::Input *input,
                                commands::Output *output) {
  if (!EnsureSession()) {
    LOG(ERROR) << "EnsureSession failed";
    return false;
  }

  input->set_id(id_);
  output->set_id(0);
  return Call(*input, output);
}

void Session::EnableCascadingWindow(bool enable) {
  // TODO(mazda): implement this
}

void Session::set_timeout(int timeout) {
}

void Session::set_restricted(bool restricted) {
}

void Session::set_server_program(const string &program_path) {
}

void Session::set_client_capability(const commands::Capability &capability) {
  client_capability_.CopyFrom(capability);
}

bool Session::LaunchTool(const string &mode,
                         const string &extra_arg) {
  return true;
}

bool Session::OpenBrowser(const string &url) {
  return true;
}

bool Session::CreateSession() {
  if (id_ != 0) {
    VLOG(1) << "session has been already created.";
    return true;
  }

  commands::Command command;
  command.mutable_input()->set_type(commands::Input::CREATE_SESSION);
  command.mutable_input()->mutable_capability()->CopyFrom(client_capability_);
  if (!handler_->EvalCommand(&command)) {
    LOG(ERROR) << "EvalCommand failed";
    return false;
  }
  id_ = command.output().id();
  return true;
}

bool Session::DeleteSession() {
  // No need to delete session
  if (id_ == 0) {
    VLOG(1) << "session has been already deleted.";
    return true;
  }

  commands::Command command;
  command.mutable_input()->set_id(id_);
  command.mutable_input()->set_type(commands::Input::DELETE_SESSION);
  bool result = handler_->EvalCommand(&command);
  id_ = 0;
  return result;
}

bool Session::GetConfig(config::Config *config) {
  commands::Input input;
  input.set_id(id_);
  input.set_type(commands::Input::GET_CONFIG);

  commands::Output output;
  if (!Call(input, &output)) {
    return false;
  }

  if (!output.has_config()) {
    return false;
  }

  config->Clear();
  config->CopyFrom(output.config());
  return true;
}

bool Session::SetConfig(const config::Config &config) {
  commands::Input input;
  input.set_id(id_);
  input.set_type(commands::Input::SET_CONFIG);
  input.mutable_config()->CopyFrom(config);

  commands::Output output;
  if (!Call(input, &output)) {
    return false;
  }

  return true;
}

bool Session::ClearUserHistory() {
  return CallCommand(commands::Input::CLEAR_USER_HISTORY);
}

bool Session::ClearUserPrediction() {
  return CallCommand(commands::Input::CLEAR_USER_PREDICTION);
}

bool Session::ClearUnusedUserPrediction() {
  return CallCommand(commands::Input::CLEAR_UNUSED_USER_PREDICTION);
}

bool Session::Shutdown() {
  CallCommand(commands::Input::SHUTDOWN);
  return true;
}

bool Session::SyncData() {
  return CallCommand(commands::Input::SYNC_DATA);
}

bool Session::Reload() {
  return CallCommand(commands::Input::RELOAD);
}

bool Session::Cleanup() {
  return CallCommand(commands::Input::CLEANUP);
}

bool Session::NoOperation() {
  return CallCommand(commands::Input::NO_OPERATION);
}

bool Session::PingServer() const {
  return true;
}

bool Session::CallCommand(commands::Input::CommandType type) {
  commands::Input input;
  input.set_id(id_);
  input.set_type(type);
  commands::Output output;
  return Call(input, &output);
}

bool Session::Call(const commands::Input &input,
                   commands::Output *output) {
  DCHECK(output) << "output is null";

  commands::Command command;
  command.mutable_input()->CopyFrom(input);
  if (!handler_->EvalCommand(&command)) {
    LOG(ERROR) << "EvalCommand failed.";
    return false;
  }
  output->CopyFrom(command.output());
  return true;
}

void Session::Reset() {
  // TODO(mazda): Implement this
}

}  // namespace ibus
}  // namespace mozc
<|endoftext|>


<commit_before>// Copyright 2020 The Google Research Authors.
//
// 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 "lossy_weight.h"

#include "sketch.h"
#include "utils.h"

namespace sketch {

LossyWeight::LossyWeight(uint window_size, uint hash_count, uint hash_size)
    : window_size_(window_size), accumulated_counters_(0),
      cm_(CountMinCU(hash_count, hash_size)) {
  counters_.reserve(window_size * 2);
}

void LossyWeight::Reset() {
  accumulated_counters_ = 0;
  counters_.resize(0);
  cm_.Reset();
}

void LossyWeight::Add(uint item, float delta) {
  counters_.push_back(std::make_pair(item, delta));
  if (counters_.size() >= window_size_ + accumulated_counters_) {
    MergeCounters();
  }
}

void LossyWeight::ReadyToEstimate() { MergeCounters(); }

float LossyWeight::Estimate(uint item) const {
  const auto& pos = lower_bound(counters_.begin(),
                                counters_.begin() + accumulated_counters_,
                                std::make_pair(item, 0), cmpByItem);
  if (pos != counters_.end() && pos->first == item) return pos->second;
  return cm_.Estimate(item);
}

std::vector<uint> LossyWeight::HeavyHitters(float threshold) const {
  return FilterOutAboveThreshold(counters_, threshold);
}

unsigned int LossyWeight::Size() const {
  return sizeof(LossyWeight) - sizeof(CountMinCU) + cm_.Size() +
      counters_.capacity() * sizeof(IntFloatPair);
}

bool LossyWeight::Compatible(const Sketch& other_sketch) const {
  const LossyWeight* other = dynamic_cast<const LossyWeight*>(&other_sketch);
  if (other == nullptr) return false;
  if (window_size_ != other->window_size_) return false;
  if (!cm_.Compatible(other->cm_)) return false;
  return true;
}

void LossyWeight::Merge(const Sketch& other_sketch) {
  if (!Compatible(other_sketch)) return;
  const LossyWeight& other = static_cast<const LossyWeight &>(other_sketch);
  MergeCounters();
  int i = 0;
  int j = 0;
  while (i < accumulated_counters_ || j < other.accumulated_counters_) {
    if (i < accumulated_counters_ && j < other.accumulated_counters_) {
      if (counters_[i].first == other.counters_[j].first) {
        counters_[i].second += other.counters_[j].second;
        i++; j++;
      } else if (counters_[i].first < other.counters_[j].first) {
        counters_[i].second += other.cm_.Estimate(counters_[i].first);
        i++;
      } else {
        counters_.push_back(other.counters_[j++]);
        counters_.back().second += cm_.Estimate(counters_.back().first);
      }
    } else if (i < accumulated_counters_) {
      counters_[i].second += other.cm_.Estimate(counters_[i].first);
      i++;
    } else {
      counters_.push_back(other.counters_[j++]);
      counters_.back().second += cm_.Estimate(counters_.back().first);
    }
  }
  cm_.Merge(other.cm_);
  if (counters_.size() > window_size_) {
    sort(counters_.begin(), counters_.end(), cmpByValue);
    for (int i = window_size_; i < counters_.size(); ++i) {
      cm_.Update(counters_[i].first, counters_[i].second);
    }
    counters_.resize(window_size_);
  }
  sort(counters_.begin(), counters_.end(), cmpByItem);
  // Also add any unmerged items in other
  for (j = other.accumulated_counters_; j < other.counters_.size(); ++j) {
    Add(other.counters_[j].first, other.counters_[j].second);
  }
}

void LossyWeight::MergeCounters() {
  if (counters_.size() <= accumulated_counters_) return;  // nothing to merge
  sort(counters_.begin() + accumulated_counters_, counters_.end(), cmpByItem);
  int i = 0;
  int j = accumulated_counters_;
  int m = accumulated_counters_;
  while (i < accumulated_counters_ || j < counters_.size()) {
    if (i < accumulated_counters_ &&
        (j >= counters_.size() || counters_[i].first <= counters_[j].first)) {
      for (; j < counters_.size() && counters_[j].first == counters_[i].first;
           ++j) {
        counters_[i].second += counters_[j].second;
      }
      i++;
    } else {
      counters_[m].first = counters_[j].first;
      counters_[m].second = cm_.Estimate(counters_[m].first) +
                            counters_[j++].second;
      for (; j < counters_.size() && counters_[j].first == counters_[m].first;
           ++j) {
        counters_[m].second += counters_[j].second;
      }
      m++;
    }
  }

  if (m > window_size_) {
    sort(counters_.begin(), counters_.begin() + m, cmpByValue);
    for (i = window_size_; i < m; ++i) {
      cm_.Update(counters_[i].first, counters_[i].second);
    }
    m = window_size_;
  }

  counters_.resize(m);
  sort(counters_.begin(), counters_.end(), cmpByItem);
  accumulated_counters_ = counters_.size();
}

}  // namespace sketch
<commit_msg>Applies selection algorithm to improve the removing items part.<commit_after>// Copyright 2020 The Google Research Authors.
//
// 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 "lossy_weight.h"

#include <algorithm>

#include "sketch.h"
#include "utils.h"

namespace sketch {

LossyWeight::LossyWeight(uint window_size, uint hash_count, uint hash_size)
    : window_size_(window_size), accumulated_counters_(0),
      cm_(CountMinCU(hash_count, hash_size)) {
  counters_.reserve(window_size * 2);
}

void LossyWeight::Reset() {
  accumulated_counters_ = 0;
  counters_.resize(0);
  cm_.Reset();
}

void LossyWeight::Add(uint item, float delta) {
  counters_.push_back(std::make_pair(item, delta));
  if (counters_.size() >= window_size_ + accumulated_counters_) {
    MergeCounters();
  }
}

void LossyWeight::ReadyToEstimate() { MergeCounters(); }

float LossyWeight::Estimate(uint item) const {
  const auto& pos = std::lower_bound(counters_.begin(),
                                     counters_.begin() + accumulated_counters_,
                                     std::make_pair(item, 0), cmpByItem);
  if (pos != counters_.end() && pos->first == item) return pos->second;
  return cm_.Estimate(item);
}

std::vector<uint> LossyWeight::HeavyHitters(float threshold) const {
  return FilterOutAboveThreshold(counters_, threshold);
}

unsigned int LossyWeight::Size() const {
  return sizeof(LossyWeight) - sizeof(CountMinCU) + cm_.Size() +
      counters_.capacity() * sizeof(IntFloatPair);
}

bool LossyWeight::Compatible(const Sketch& other_sketch) const {
  const LossyWeight* other = dynamic_cast<const LossyWeight*>(&other_sketch);
  if (other == nullptr) return false;
  if (window_size_ != other->window_size_) return false;
  if (!cm_.Compatible(other->cm_)) return false;
  return true;
}

void LossyWeight::Merge(const Sketch& other_sketch) {
  if (!Compatible(other_sketch)) return;
  const LossyWeight& other = static_cast<const LossyWeight &>(other_sketch);
  MergeCounters();
  int i = 0;
  int j = 0;
  while (i < accumulated_counters_ || j < other.accumulated_counters_) {
    if (i < accumulated_counters_ && j < other.accumulated_counters_) {
      if (counters_[i].first == other.counters_[j].first) {
        counters_[i].second += other.counters_[j].second;
        i++; j++;
      } else if (counters_[i].first < other.counters_[j].first) {
        counters_[i].second += other.cm_.Estimate(counters_[i].first);
        i++;
      } else {
        counters_.push_back(other.counters_[j++]);
        counters_.back().second += cm_.Estimate(counters_.back().first);
      }
    } else if (i < accumulated_counters_) {
      counters_[i].second += other.cm_.Estimate(counters_[i].first);
      i++;
    } else {
      counters_.push_back(other.counters_[j++]);
      counters_.back().second += cm_.Estimate(counters_.back().first);
    }
  }
  cm_.Merge(other.cm_);
  if (counters_.size() > window_size_) {
    std::sort(counters_.begin(), counters_.end(), cmpByValue);
    for (int i = window_size_; i < counters_.size(); ++i) {
      cm_.Update(counters_[i].first, counters_[i].second);
    }
    counters_.resize(window_size_);
  }
  std::sort(counters_.begin(), counters_.end(), cmpByItem);
  // Also add any unmerged items in other
  for (j = other.accumulated_counters_; j < other.counters_.size(); ++j) {
    Add(other.counters_[j].first, other.counters_[j].second);
  }
}

void LossyWeight::MergeCounters() {
  if (counters_.size() <= accumulated_counters_) return;  // nothing to merge
  std::sort(counters_.begin() + accumulated_counters_, counters_.end(),
            cmpByItem);
  int i = 0;
  int j = accumulated_counters_;
  int m = accumulated_counters_;
  while (i < accumulated_counters_ || j < counters_.size()) {
    if (i < accumulated_counters_ &&
        (j >= counters_.size() || counters_[i].first <= counters_[j].first)) {
      for (; j < counters_.size() && counters_[j].first == counters_[i].first;
           ++j) {
        counters_[i].second += counters_[j].second;
      }
      i++;
    } else {
      counters_[m].first = counters_[j].first;
      counters_[m].second = cm_.Estimate(counters_[m].first) +
                            counters_[j++].second;
      for (; j < counters_.size() && counters_[j].first == counters_[m].first;
           ++j) {
        counters_[m].second += counters_[j].second;
      }
      m++;
    }
  }

  if (m > window_size_) {
    static auto comp = [](const IntFloatPair& a, const IntFloatPair& b) {
      if (a.second != b.second) {
        return a.second > b.second;
      }
      return a.first < b.first;
    };

    std::nth_element(
        counters_.begin(),
        counters_.begin() + window_size_ - 1, counters_.begin() + m, comp);
    for (int i = window_size_; i < counters_.size(); ++i) {
      cm_.Update(counters_[i].first, counters_[i].second);
    }
    m = window_size_;
  }

  counters_.resize(m);
  std::sort(counters_.begin(), counters_.end(), cmpByItem);
  accumulated_counters_ = counters_.size();
}

}  // namespace sketch
<|endoftext|>


<commit_before>/**
* @file lllandmarkactions.cpp
* @brief LLLandmarkActions class implementation
*
* $LicenseInfo:firstyear=2001&license=viewergpl$
*
* Copyright (c) 2001-2009, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab.  Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/

#include "llviewerprecompiledheaders.h"
#include "lllandmarkactions.h"

#include "roles_constants.h"

#include "llinventory.h"
#include "lllandmark.h"
#include "llparcel.h"

#include "llnotifications.h"

#include "llagent.h"
#include "llinventorymodel.h"
#include "lllandmarklist.h"
#include "llslurl.h"
#include "llviewerinventory.h"
#include "llviewerparcelmgr.h"
#include "llworldmap.h"
#include "lllandmark.h"
#include "llinventorymodel.h"
#include "llagentui.h"

class LLFetchlLandmarkByPos : public LLInventoryCollectFunctor
{
private:
	LLVector3d mPos;
public:
	LLFetchlLandmarkByPos(const LLVector3d& pos) :
		mPos(pos)
	{}

	/*virtual*/ bool operator()(LLInventoryCategory* cat, LLInventoryItem* item)
	{
		if (!item || item->getType() != LLAssetType::AT_LANDMARK)
			return false;

		LLLandmark* landmark = gLandmarkList.getAsset(item->getAssetUUID());
		if (!landmark) // the landmark not been loaded yet
			return false;

		LLVector3d landmark_global_pos;
		if (!landmark->getGlobalPos(landmark_global_pos))
			return false;
		//we have to round off each coordinates to compare positions properly
		return llround(mPos.mdV[VX]) ==  llround(landmark_global_pos.mdV[VX])
				&& llround(mPos.mdV[VY]) ==  llround(landmark_global_pos.mdV[VY])
				&& llround(mPos.mdV[VZ]) ==  llround(landmark_global_pos.mdV[VZ]);
	}
};

class LLFetchLandmarksByName : public LLInventoryCollectFunctor
{
private:
	std::string name;
	BOOL use_substring;
	//this member will be contain copy of founded items to keep the result unique
	std::set<std::string> check_duplicate;

public:
LLFetchLandmarksByName(std::string &landmark_name, BOOL if_use_substring)
:name(landmark_name),
use_substring(if_use_substring)
	{
	LLStringUtil::toLower(name);
	}

public:
	/*virtual*/ bool operator()(LLInventoryCategory* cat, LLInventoryItem* item)
	{
		if (!item || item->getType() != LLAssetType::AT_LANDMARK)
			return false;

		LLLandmark* landmark = gLandmarkList.getAsset(item->getAssetUUID());
		if (!landmark) // the landmark not been loaded yet
			return false;

		bool acceptable = false;
		std::string landmark_name = item->getName();
		LLStringUtil::toLower(landmark_name);
		if(use_substring)
		{
			acceptable =  landmark_name.find( name ) != std::string::npos;
		}
		else
		{
			acceptable = landmark_name == name;
		}
		if(acceptable){
			if(check_duplicate.find(landmark_name) != check_duplicate.end()){
				// we have duplicated items in landmarks
				acceptable = false;
			}else{
				check_duplicate.insert(landmark_name);
			}
		}

		return acceptable;
	}
};

LLInventoryModel::item_array_t LLLandmarkActions::fetchLandmarksByName(std::string& name, BOOL use_substring)
{
	LLInventoryModel::cat_array_t cats;
	LLInventoryModel::item_array_t items;
	LLFetchLandmarksByName fetchLandmarks(name, use_substring);
	gInventory.collectDescendentsIf(gInventory.getRootFolderID(),
			cats,
			items,
			LLInventoryModel::EXCLUDE_TRASH,
			fetchLandmarks);
	return items;
}

bool LLLandmarkActions::landmarkAlreadyExists()
{
	// Determine whether there are landmarks pointing to the current global  agent position.
	return findLandmarkForAgentPos() != NULL;
}

LLViewerInventoryItem* LLLandmarkActions::findLandmarkForGlobalPos(const LLVector3d &pos)
{
	// Determine whether there are landmarks pointing to the current parcel.
	LLInventoryModel::cat_array_t cats;
	LLInventoryModel::item_array_t items;
	LLFetchlLandmarkByPos is_current_pos_landmark(pos);
	gInventory.collectDescendentsIf(gInventory.getRootFolderID(),
		cats,
		items,
		LLInventoryModel::EXCLUDE_TRASH,
		is_current_pos_landmark);

	if(items.empty())
	{
		return NULL;
	}

	return items[0];
}

LLViewerInventoryItem* LLLandmarkActions::findLandmarkForAgentPos()
{
	return findLandmarkForGlobalPos(gAgent.getPositionGlobal());
}

bool LLLandmarkActions::canCreateLandmarkHere()
{
	LLParcel* agent_parcel = LLViewerParcelMgr::getInstance()->getAgentParcel();
	if(!agent_parcel)
	{
		llwarns << "No agent region" << llendl;
		return false;
	}
	if (agent_parcel->getAllowLandmark()
		|| LLViewerParcelMgr::isParcelOwnedByAgent(agent_parcel, GP_LAND_ALLOW_LANDMARK))
	{
		return true;
	}

	return false;
}

void LLLandmarkActions::createLandmarkHere(
	const std::string& name,
	const std::string& desc,
	const LLUUID& folder_id)
{
	if(!gAgent.getRegion())
	{
		llwarns << "No agent region" << llendl;
		return;
	}
	LLParcel* agent_parcel = LLViewerParcelMgr::getInstance()->getAgentParcel();
	if (!agent_parcel)
	{
		llwarns << "No agent parcel" << llendl;
		return;
	}
	if (!canCreateLandmarkHere())
	{
		LLNotifications::instance().add("CannotCreateLandmarkNotOwner");
		return;
	}

	create_inventory_item(gAgent.getID(), gAgent.getSessionID(),
		folder_id, LLTransactionID::tnull,
		name, desc,
		LLAssetType::AT_LANDMARK,
		LLInventoryType::IT_LANDMARK,
		NOT_WEARABLE, PERM_ALL,
		NULL);
}

void LLLandmarkActions::createLandmarkHere()
{
	std::string landmark_name, landmark_desc;

	LLAgentUI::buildLocationString(landmark_name, LLAgentUI::LOCATION_FORMAT_LANDMARK);
	LLAgentUI::buildLocationString(landmark_desc, LLAgentUI::LOCATION_FORMAT_FULL);
	LLUUID folder_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_LANDMARK);

	createLandmarkHere(landmark_name, landmark_desc, folder_id);
}

void LLLandmarkActions::getSLURLfromPosGlobal(const LLVector3d& global_pos, slurl_callback_t cb, bool escaped /* = true */)
{
	std::string sim_name;
	bool gotSimName = LLWorldMap::getInstance()->simNameFromPosGlobal(global_pos, sim_name);
	if (gotSimName)
	{
		std::string slurl = LLSLURL::buildSLURLfromPosGlobal(sim_name, global_pos, escaped);
		cb(slurl);

		return;
	}
	else
	{
		U64 new_region_handle = to_region_handle(global_pos);

		LLWorldMap::url_callback_t url_cb = boost::bind(&LLLandmarkActions::onRegionResponse,
														cb,
														global_pos,
														escaped,
														_1, _2, _3, _4);

		LLWorldMap::getInstance()->sendHandleRegionRequest(new_region_handle, url_cb, std::string("unused"), false);
	}
}

void LLLandmarkActions::onRegionResponse(slurl_callback_t cb,
										 const LLVector3d& global_pos,
										 bool escaped,
										 U64 region_handle,
										 const std::string& url,
										 const LLUUID& snapshot_id,
										 bool teleport)
{
	std::string sim_name;
	std::string slurl;
	bool gotSimName = LLWorldMap::getInstance()->simNameFromPosGlobal(global_pos, sim_name);
	if (gotSimName)
	{
		slurl = LLSLURL::buildSLURLfromPosGlobal(sim_name, global_pos, escaped);
	}
	else
	{
		slurl = "";
	}

	cb(slurl);
}

bool LLLandmarkActions::getLandmarkGlobalPos(const LLUUID& landmarkInventoryItemID, LLVector3d& posGlobal)
{
	LLViewerInventoryItem* item = gInventory.getItem(landmarkInventoryItemID);
	if (NULL == item)
		return false;

	const LLUUID& asset_id = item->getAssetUUID();
	LLLandmark* landmark = gLandmarkList.getAsset(asset_id, NULL);

	if (NULL == landmark)
		return false;

	return landmark->getGlobalPos(posGlobal);
}
<commit_msg>Nav bar only searches for existing landmarks in "My Favorites" and "Landmarks" folders.  This is a partial fix for EXT-1402 Thousands of "WARNING: LLAssetStorage::getAssetData: Discarding duplicate request for asset".  Somewhat reduces the number of asset downloads on login.  Reviewed with Steve.<commit_after>/**
* @file lllandmarkactions.cpp
* @brief LLLandmarkActions class implementation
*
* $LicenseInfo:firstyear=2001&license=viewergpl$
*
* Copyright (c) 2001-2009, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab.  Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/

#include "llviewerprecompiledheaders.h"
#include "lllandmarkactions.h"

#include "roles_constants.h"

#include "llinventory.h"
#include "lllandmark.h"
#include "llparcel.h"

#include "llnotifications.h"

#include "llagent.h"
#include "llinventorymodel.h"
#include "lllandmarklist.h"
#include "llslurl.h"
#include "llviewerinventory.h"
#include "llviewerparcelmgr.h"
#include "llworldmap.h"
#include "lllandmark.h"
#include "llinventorymodel.h"
#include "llagentui.h"

class LLFetchlLandmarkByPos : public LLInventoryCollectFunctor
{
private:
	LLVector3d mPos;
public:
	LLFetchlLandmarkByPos(const LLVector3d& pos) :
		mPos(pos)
	{}

	/*virtual*/ bool operator()(LLInventoryCategory* cat, LLInventoryItem* item)
	{
		if (!item || item->getType() != LLAssetType::AT_LANDMARK)
			return false;

		LLLandmark* landmark = gLandmarkList.getAsset(item->getAssetUUID());
		if (!landmark) // the landmark not been loaded yet
			return false;

		LLVector3d landmark_global_pos;
		if (!landmark->getGlobalPos(landmark_global_pos))
			return false;
		//we have to round off each coordinates to compare positions properly
		return llround(mPos.mdV[VX]) ==  llround(landmark_global_pos.mdV[VX])
				&& llround(mPos.mdV[VY]) ==  llround(landmark_global_pos.mdV[VY])
				&& llround(mPos.mdV[VZ]) ==  llround(landmark_global_pos.mdV[VZ]);
	}
};

class LLFetchLandmarksByName : public LLInventoryCollectFunctor
{
private:
	std::string name;
	BOOL use_substring;
	//this member will be contain copy of founded items to keep the result unique
	std::set<std::string> check_duplicate;

public:
LLFetchLandmarksByName(std::string &landmark_name, BOOL if_use_substring)
:name(landmark_name),
use_substring(if_use_substring)
	{
	LLStringUtil::toLower(name);
	}

public:
	/*virtual*/ bool operator()(LLInventoryCategory* cat, LLInventoryItem* item)
	{
		if (!item || item->getType() != LLAssetType::AT_LANDMARK)
			return false;

		LLLandmark* landmark = gLandmarkList.getAsset(item->getAssetUUID());
		if (!landmark) // the landmark not been loaded yet
			return false;

		bool acceptable = false;
		std::string landmark_name = item->getName();
		LLStringUtil::toLower(landmark_name);
		if(use_substring)
		{
			acceptable =  landmark_name.find( name ) != std::string::npos;
		}
		else
		{
			acceptable = landmark_name == name;
		}
		if(acceptable){
			if(check_duplicate.find(landmark_name) != check_duplicate.end()){
				// we have duplicated items in landmarks
				acceptable = false;
			}else{
				check_duplicate.insert(landmark_name);
			}
		}

		return acceptable;
	}
};

static void fetch_landmarks(LLInventoryModel::cat_array_t& cats,
							LLInventoryModel::item_array_t& items,
							LLInventoryCollectFunctor& add)
{
	// Look in "My Favorites"
	LLUUID favorites_folder_id =
		gInventory.findCategoryUUIDForType(LLAssetType::AT_FAVORITE);
	gInventory.collectDescendentsIf(favorites_folder_id,
		cats,
		items,
		LLInventoryModel::EXCLUDE_TRASH,
		add);

	// Look in "Landmarks"
	LLUUID landmarks_folder_id =
		gInventory.findCategoryUUIDForType(LLAssetType::AT_LANDMARK);
	gInventory.collectDescendentsIf(landmarks_folder_id,
		cats,
		items,
		LLInventoryModel::EXCLUDE_TRASH,
		add);
}

LLInventoryModel::item_array_t LLLandmarkActions::fetchLandmarksByName(std::string& name, BOOL use_substring)
{
	LLInventoryModel::cat_array_t cats;
	LLInventoryModel::item_array_t items;
	LLFetchLandmarksByName by_name(name, use_substring);
	fetch_landmarks(cats, items, by_name);

	return items;
}

bool LLLandmarkActions::landmarkAlreadyExists()
{
	// Determine whether there are landmarks pointing to the current global  agent position.
	return findLandmarkForAgentPos() != NULL;
}

// *TODO: This could be made more efficient by only fetching the FIRST
// landmark that meets the criteria
LLViewerInventoryItem* LLLandmarkActions::findLandmarkForGlobalPos(const LLVector3d &pos)
{
	// Determine whether there are landmarks pointing to the current parcel.
	LLInventoryModel::cat_array_t cats;
	LLInventoryModel::item_array_t items;
	LLFetchlLandmarkByPos is_current_pos_landmark(pos);
	fetch_landmarks(cats, items, is_current_pos_landmark);

	if(items.empty())
	{
		return NULL;
	}

	return items[0];
}

LLViewerInventoryItem* LLLandmarkActions::findLandmarkForAgentPos()
{
	return findLandmarkForGlobalPos(gAgent.getPositionGlobal());
}

bool LLLandmarkActions::canCreateLandmarkHere()
{
	LLParcel* agent_parcel = LLViewerParcelMgr::getInstance()->getAgentParcel();
	if(!agent_parcel)
	{
		llwarns << "No agent region" << llendl;
		return false;
	}
	if (agent_parcel->getAllowLandmark()
		|| LLViewerParcelMgr::isParcelOwnedByAgent(agent_parcel, GP_LAND_ALLOW_LANDMARK))
	{
		return true;
	}

	return false;
}

void LLLandmarkActions::createLandmarkHere(
	const std::string& name,
	const std::string& desc,
	const LLUUID& folder_id)
{
	if(!gAgent.getRegion())
	{
		llwarns << "No agent region" << llendl;
		return;
	}
	LLParcel* agent_parcel = LLViewerParcelMgr::getInstance()->getAgentParcel();
	if (!agent_parcel)
	{
		llwarns << "No agent parcel" << llendl;
		return;
	}
	if (!canCreateLandmarkHere())
	{
		LLNotifications::instance().add("CannotCreateLandmarkNotOwner");
		return;
	}

	create_inventory_item(gAgent.getID(), gAgent.getSessionID(),
		folder_id, LLTransactionID::tnull,
		name, desc,
		LLAssetType::AT_LANDMARK,
		LLInventoryType::IT_LANDMARK,
		NOT_WEARABLE, PERM_ALL,
		NULL);
}

void LLLandmarkActions::createLandmarkHere()
{
	std::string landmark_name, landmark_desc;

	LLAgentUI::buildLocationString(landmark_name, LLAgentUI::LOCATION_FORMAT_LANDMARK);
	LLAgentUI::buildLocationString(landmark_desc, LLAgentUI::LOCATION_FORMAT_FULL);
	LLUUID folder_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_LANDMARK);

	createLandmarkHere(landmark_name, landmark_desc, folder_id);
}

void LLLandmarkActions::getSLURLfromPosGlobal(const LLVector3d& global_pos, slurl_callback_t cb, bool escaped /* = true */)
{
	std::string sim_name;
	bool gotSimName = LLWorldMap::getInstance()->simNameFromPosGlobal(global_pos, sim_name);
	if (gotSimName)
	{
		std::string slurl = LLSLURL::buildSLURLfromPosGlobal(sim_name, global_pos, escaped);
		cb(slurl);

		return;
	}
	else
	{
		U64 new_region_handle = to_region_handle(global_pos);

		LLWorldMap::url_callback_t url_cb = boost::bind(&LLLandmarkActions::onRegionResponse,
														cb,
														global_pos,
														escaped,
														_1, _2, _3, _4);

		LLWorldMap::getInstance()->sendHandleRegionRequest(new_region_handle, url_cb, std::string("unused"), false);
	}
}

void LLLandmarkActions::onRegionResponse(slurl_callback_t cb,
										 const LLVector3d& global_pos,
										 bool escaped,
										 U64 region_handle,
										 const std::string& url,
										 const LLUUID& snapshot_id,
										 bool teleport)
{
	std::string sim_name;
	std::string slurl;
	bool gotSimName = LLWorldMap::getInstance()->simNameFromPosGlobal(global_pos, sim_name);
	if (gotSimName)
	{
		slurl = LLSLURL::buildSLURLfromPosGlobal(sim_name, global_pos, escaped);
	}
	else
	{
		slurl = "";
	}

	cb(slurl);
}

bool LLLandmarkActions::getLandmarkGlobalPos(const LLUUID& landmarkInventoryItemID, LLVector3d& posGlobal)
{
	LLViewerInventoryItem* item = gInventory.getItem(landmarkInventoryItemID);
	if (NULL == item)
		return false;

	const LLUUID& asset_id = item->getAssetUUID();
	LLLandmark* landmark = gLandmarkList.getAsset(asset_id, NULL);

	if (NULL == landmark)
		return false;

	return landmark->getGlobalPos(posGlobal);
}
<|endoftext|>


<commit_before>///
/// @file  pod_vector.hpp
///
/// Copyright (C) 2022 Kim Walisch, <kim.walisch@gmail.com>
///
/// This file is distributed under the BSD License. See the COPYING
/// file in the top level directory.
///

#ifndef POD_VECTOR_HPP
#define POD_VECTOR_HPP

#include "macros.hpp"

#include <algorithm>
#include <cassert>
#include <cstddef>
#include <type_traits>
#include <utility>

namespace primesieve {

/// pod_vector is a dynamically growing array.
/// It has the same API (though not complete) as std::vector but its
/// resize() method does not default initialize memory for built-in
/// integer types. It does however default initialize classes and
/// struct types if they have a constructor. It also prevents
/// bounds checks which is important for primesieve's performance, e.g.
/// the Fedora Linux distribution compiles with -D_GLIBCXX_ASSERTIONS
/// which enables std::vector bounds checks.
///
template <typename T>
class pod_vector
{
public:
  static_assert(std::is_trivially_destructible<T>::value,
                "pod_vector<T> only supports types with trivial destructors!");

  pod_vector() noexcept = default;

  pod_vector(std::size_t size)
  {
    resize(size);
  }

  ~pod_vector()
  {
    delete [] array_;
  }

  /// Free all memory, the pod_vector
  /// can be reused afterwards.
  void free() noexcept
  {
    delete [] array_;
    array_ = nullptr;
    end_ = nullptr;
    capacity_ = nullptr;
  }

  /// Reset the pod_vector, but do not free its
  /// memory. Same as std::vector.clear().
  void clear() noexcept
  {
    end_ = array_;
  }

  /// Copying is slow, we prevent it
  pod_vector(const pod_vector&) = delete;
  pod_vector& operator=(const pod_vector&) = delete;

  /// Move constructor
  pod_vector(pod_vector&& other) noexcept
  {
    this->swap(other);
  }

  /// Move assignment operator
  pod_vector& operator=(pod_vector&& other) noexcept
  {
    if (this != &other)
      this->swap(other);

    return *this;
  }

  void swap(pod_vector& other) noexcept
  {
    std::swap(array_, other.array_);
    std::swap(end_, other.end_);
    std::swap(capacity_, other.capacity_);
  }

  bool empty() const noexcept
  {
    return array_ == end_;
  }

  T& operator[] (std::size_t pos) noexcept
  {
    return array_[pos];
  }

  T& operator[] (std::size_t pos) const noexcept
  {
    return array_[pos];
  }

  T* data() noexcept
  {
    return array_;
  }

  T* data() const noexcept
  {
    return array_;
  }

  std::size_t size() const noexcept
  {
    assert(end_ >= array_);
    return (std::size_t)(end_ - array_);
  }

  std::size_t capacity() const noexcept
  {
    assert(capacity_ >= array_);
    return (std::size_t)(capacity_ - array_);
  }

  T* begin() noexcept
  {
    return array_;
  }

  T* begin() const noexcept
  {
    return array_;
  }

  T* end() noexcept
  {
    return end_;
  }

  T* end() const noexcept
  {
    return end_;
  }

  T& front() noexcept
  {
    return *array_;
  }

  T& front() const noexcept
  {
    return *array_;
  }

  T& back() noexcept
  {
    assert(end_ != nullptr);
    return *(end_ - 1);
  }

  T& back() const noexcept
  {
    assert(end_ != nullptr);
    return *(end_ - 1);
  }

  ALWAYS_INLINE void push_back(const T& value)
  {
    if_unlikely(end_ >= capacity_)
      reserve(capacity() * 2);
    *end_++ = value;
  }

  ALWAYS_INLINE void push_back(T&& value)
  {
    if_unlikely(end_ >= capacity_)
      reserve(capacity() * 2);
    *end_++ = value;
  }

  template <class... Args>
  ALWAYS_INLINE void emplace_back(Args&&... args)
  {
    if_unlikely(end_ >= capacity_)
      reserve(capacity() * 2);
    *end_++ = T(std::forward<Args>(args)...);
  }

  void reserve(std::size_t n)
  {
    if (n > capacity())
    {
      std::size_t old_size = size();
      resize(n);
      end_ = array_ + old_size;
    }
  }

  template <typename TT>
  ALWAYS_INLINE typename std::enable_if<std::is_pod<TT>::value>::type
  default_initialize_range(TT*, TT*)
  { }

  template <typename TT>
  ALWAYS_INLINE typename std::enable_if<!std::is_pod<TT>::value>::type
  default_initialize_range(TT* first, TT* last)
  {
    for (; first < last; first++)
      *first = TT();
  }

  /// Resize without default initializing memory.
  /// If the pod_vector is not empty the current content
  /// will be copied into the new array.
  ///
  void resize(std::size_t n)
  {
    if (n == size())
      return;
    else if (n <= capacity())
    {
      assert(capacity() > 0);

      // This will only be used for classes
      // and structs with constructors.
      if (n > size())
        default_initialize_range(end_, array_ + n);

      end_ = array_ + n;
    }
    else
    {
      // GCC & Clang's std::vector grow the capacity by at least
      // 2x for every call to resize() with n > capacity(). We
      // grow by at least 1.5x as we tend to accurately calculate
      // the amount of memory we need upfront.
      assert(n > capacity());
      n = std::max(n, (std::size_t)(capacity() * 1.5));

      // This default initializes memory of classes and
      // structs with constructors. But it does not default
      // initialize memory for POD types like int, long.
      T* new_array = new T[n];

      if (array_)
      {
        std::copy(array_, end_, new_array);
        delete [] array_;
      }

      array_ = new_array;
      end_ = array_ + n;
      capacity_ = end_;
    }
  }

  using value_type = T;

private:
  T* array_ = nullptr;
  T* end_ = nullptr;
  T* capacity_ = nullptr;
};

} // namespace

#endif
<commit_msg>Fix reserve()<commit_after>///
/// @file  pod_vector.hpp
///
/// Copyright (C) 2022 Kim Walisch, <kim.walisch@gmail.com>
///
/// This file is distributed under the BSD License. See the COPYING
/// file in the top level directory.
///

#ifndef POD_VECTOR_HPP
#define POD_VECTOR_HPP

#include "macros.hpp"

#include <algorithm>
#include <cassert>
#include <cstddef>
#include <type_traits>
#include <utility>

namespace primesieve {

/// pod_vector is a dynamically growing array.
/// It has the same API (though not complete) as std::vector but its
/// resize() method does not default initialize memory for built-in
/// integer types. It does however default initialize classes and
/// struct types if they have a constructor. It also prevents
/// bounds checks which is important for primesieve's performance, e.g.
/// the Fedora Linux distribution compiles with -D_GLIBCXX_ASSERTIONS
/// which enables std::vector bounds checks.
///
template <typename T>
class pod_vector
{
public:
  static_assert(std::is_trivially_destructible<T>::value,
                "pod_vector<T> only supports types with trivial destructors!");

  pod_vector() noexcept = default;

  pod_vector(std::size_t size)
  {
    resize(size);
  }

  ~pod_vector()
  {
    delete [] array_;
  }

  /// Free all memory, the pod_vector
  /// can be reused afterwards.
  void free() noexcept
  {
    delete [] array_;
    array_ = nullptr;
    end_ = nullptr;
    capacity_ = nullptr;
  }

  /// Reset the pod_vector, but do not free its
  /// memory. Same as std::vector.clear().
  void clear() noexcept
  {
    end_ = array_;
  }

  /// Copying is slow, we prevent it
  pod_vector(const pod_vector&) = delete;
  pod_vector& operator=(const pod_vector&) = delete;

  /// Move constructor
  pod_vector(pod_vector&& other) noexcept
  {
    this->swap(other);
  }

  /// Move assignment operator
  pod_vector& operator=(pod_vector&& other) noexcept
  {
    if (this != &other)
      this->swap(other);

    return *this;
  }

  void swap(pod_vector& other) noexcept
  {
    std::swap(array_, other.array_);
    std::swap(end_, other.end_);
    std::swap(capacity_, other.capacity_);
  }

  bool empty() const noexcept
  {
    return array_ == end_;
  }

  T& operator[] (std::size_t pos) noexcept
  {
    return array_[pos];
  }

  T& operator[] (std::size_t pos) const noexcept
  {
    return array_[pos];
  }

  T* data() noexcept
  {
    return array_;
  }

  T* data() const noexcept
  {
    return array_;
  }

  std::size_t size() const noexcept
  {
    assert(end_ >= array_);
    return (std::size_t)(end_ - array_);
  }

  std::size_t capacity() const noexcept
  {
    assert(capacity_ >= array_);
    return (std::size_t)(capacity_ - array_);
  }

  T* begin() noexcept
  {
    return array_;
  }

  T* begin() const noexcept
  {
    return array_;
  }

  T* end() noexcept
  {
    return end_;
  }

  T* end() const noexcept
  {
    return end_;
  }

  T& front() noexcept
  {
    return *array_;
  }

  T& front() const noexcept
  {
    return *array_;
  }

  T& back() noexcept
  {
    assert(end_ != nullptr);
    return *(end_ - 1);
  }

  T& back() const noexcept
  {
    assert(end_ != nullptr);
    return *(end_ - 1);
  }

  ALWAYS_INLINE void push_back(const T& value)
  {
    if_unlikely(end_ >= capacity_)
      reserve(std::max((std::size_t) 1, capacity() * 2));
    *end_++ = value;
  }

  ALWAYS_INLINE void push_back(T&& value)
  {
    if_unlikely(end_ >= capacity_)
      reserve(std::max((std::size_t) 1, capacity() * 2));
    *end_++ = value;
  }

  template <class... Args>
  ALWAYS_INLINE void emplace_back(Args&&... args)
  {
    if_unlikely(end_ >= capacity_)
      reserve(std::max((std::size_t) 1, capacity() * 2));
    *end_++ = T(std::forward<Args>(args)...);
  }

  void reserve(std::size_t n)
  {
    if (n > capacity())
    {
      std::size_t old_size = size();
      resize(n);
      end_ = array_ + old_size;
    }
  }

  template <typename TT>
  ALWAYS_INLINE typename std::enable_if<std::is_pod<TT>::value>::type
  default_initialize_range(TT*, TT*)
  { }

  template <typename TT>
  ALWAYS_INLINE typename std::enable_if<!std::is_pod<TT>::value>::type
  default_initialize_range(TT* first, TT* last)
  {
    for (; first < last; first++)
      *first = TT();
  }

  /// Resize without default initializing memory.
  /// If the pod_vector is not empty the current content
  /// will be copied into the new array.
  ///
  void resize(std::size_t n)
  {
    if (n == size())
      return;
    else if (n <= capacity())
    {
      assert(capacity() > 0);

      // This will only be used for classes
      // and structs with constructors.
      if (n > size())
        default_initialize_range(end_, array_ + n);

      end_ = array_ + n;
    }
    else
    {
      // GCC & Clang's std::vector grow the capacity by at least
      // 2x for every call to resize() with n > capacity(). We
      // grow by at least 1.5x as we tend to accurately calculate
      // the amount of memory we need upfront.
      assert(n > capacity());
      n = std::max(n, (std::size_t)(capacity() * 1.5));

      // This default initializes memory of classes and
      // structs with constructors. But it does not default
      // initialize memory for POD types like int, long.
      T* new_array = new T[n];

      if (array_)
      {
        std::copy(array_, end_, new_array);
        delete [] array_;
      }

      array_ = new_array;
      end_ = array_ + n;
      capacity_ = end_;
    }
  }

  using value_type = T;

private:
  T* array_ = nullptr;
  T* end_ = nullptr;
  T* capacity_ = nullptr;
};

} // namespace

#endif
<|endoftext|>