<commit_before>#pragma once

#include <cstdint>
#include <array>

namespace Utility
{
    namespace MemoryManager
    {
        class AllocationHeaderBuilder
        {
            private:
            AllocationHeaderBuilder();
            virtual ~AllocationHeaderBuilder();

            public:
            /**
            Sets the byte to zero
            */
            // TODORT move to .cpp file
            static void SetByteAsZero(void* p_pointerToByte) { *(uint8_t*)p_pointerToByte = 0; }

            /**
                Sets the byte to a specific value;
            */
            // TODORT move to .cpp file
            static void SetByte(void* p_pointerToByte, const uint8_t& value) { *(uint8_t*)p_pointerToByte = value; }

            // TODORT move to .cpp file
            static void BuildCustomByteBasedOnFlags(void* p_pointerToByte, const bool& f0, const bool& f1, const bool& f2, const bool& f3,
                                                    const bool& f4, const bool& f5, const bool& f6, const bool& f7)
            {
                uint8_t flagValue = 0;
                if(f0)
                {
                    flagValue |= 1;
                }
                if(f1)
                {
                    flagValue |= 2;
                }
                if(f2)
                {
                    flagValue |= 4;
                }
                if(f3)
                {
                    flagValue |= 8;
                }
                if(f4)
                {
                    flagValue |= 16;
                }
                if(f5)
                {
                    flagValue |= 32;
                }
                if(f6)
                {
                    flagValue |= 64;
                }
                if(f7)
                {
                    flagValue |= 128;
                }
                *(uint8_t*)p_pointerToByte = flagValue;
            }

            // TODORT move to .cpp file
            static void MarkByteBasedOnFlags(void* p_pointerToByte, const std::array<bool, 8>& flags)
            {
                uint8_t flagValue = 0;
                if(flags[0])
                {
                    flagValue |= 1;
                }
                if(flags[1])
                {
                    flagValue |= 2;
                }
                if(flags[2])
                {
                    flagValue |= 4;
                }
                if(flags[3])
                {
                    flagValue |= 8;
                }
                if(flags[4])
                {
                    flagValue |= 16;
                }
                if(flags[5])
                {
                    flagValue |= 32;
                }
                if(flags[6])
                {
                    flagValue |= 64;
                }
                if(flags[7])
                {
                    flagValue |= 128;
                }
                *(uint8_t*)p_pointerToByte = flagValue;
            }

            // TODORT move to .cpp file
            static void MarkByteAsOccupied(void* p_pointerToByte)
            {
                /*
                   XXXX XXXX
                 | 0000 0001
                */
                *(uint8_t*)p_pointerToByte |= 1;
            }

            // TODORT move to .cpp file
            static void MarkByteAsFree(void* p_pointerToByte)
            {
                /*
                    XXXX XXXX
                  & 1111 1110
                */
                *(uint8_t*)p_pointerToByte &= 254;
            }

            // TODORT move to .cpp file
            static std::array<bool, 8>& FetchAllFlagsFromByte(const void const* p_pointerToByte)
            {
                const uint8_t& value = *(uint8_t*)p_pointerToByte;
                std::array<bool, 8> returnValue; // All false at the beginning
                if((value & 1) == 1)
                {
                    returnValue[0] = true;
                }
                if((value & 2) == 2)
                {
                    returnValue[1] = true;
                }
                if((value & 4) == 4)
                {
                    returnValue[2] = true;
                }
                if((value & 8) == 8)
                {
                    returnValue[3] = true;
                }
                if((value & 16) == 16)
                {
                    returnValue[4] = true;
                }
                if((value & 32) == 32)
                {
                    returnValue[5] = true;
                }
                if((value & 64) == 64)
                {
                    returnValue[6] = true;
                }
                if((value & 128) == 128)
                {
                    returnValue[7] = true;
                }
                return returnValue;
            }
        };
    }
}<commit_msg>Fixed warning by moving const keyword<commit_after>#pragma once

#include <cstdint>
#include <array>

namespace Utility
{
    namespace MemoryManager
    {
        class AllocationHeaderBuilder
        {
            private:
            AllocationHeaderBuilder();
            virtual ~AllocationHeaderBuilder();

            public:
            /**
            Sets the byte to zero
            */
            // TODORT move to .cpp file
            static void SetByteAsZero(void* p_pointerToByte) { *(uint8_t*)p_pointerToByte = 0; }

            /**
                Sets the byte to a specific value;
            */
            // TODORT move to .cpp file
            static void SetByte(void* p_pointerToByte, const uint8_t& value) { *(uint8_t*)p_pointerToByte = value; }

            // TODORT move to .cpp file
            static void BuildCustomByteBasedOnFlags(void* p_pointerToByte, const bool& f0, const bool& f1, const bool& f2, const bool& f3,
                                                    const bool& f4, const bool& f5, const bool& f6, const bool& f7)
            {
                uint8_t flagValue = 0;
                if(f0)
                {
                    flagValue |= 1;
                }
                if(f1)
                {
                    flagValue |= 2;
                }
                if(f2)
                {
                    flagValue |= 4;
                }
                if(f3)
                {
                    flagValue |= 8;
                }
                if(f4)
                {
                    flagValue |= 16;
                }
                if(f5)
                {
                    flagValue |= 32;
                }
                if(f6)
                {
                    flagValue |= 64;
                }
                if(f7)
                {
                    flagValue |= 128;
                }
                *(uint8_t*)p_pointerToByte = flagValue;
            }

            // TODORT move to .cpp file
            static void MarkByteBasedOnFlags(void* p_pointerToByte, const std::array<bool, 8>& flags)
            {
                uint8_t flagValue = 0;
                if(flags[0])
                {
                    flagValue |= 1;
                }
                if(flags[1])
                {
                    flagValue |= 2;
                }
                if(flags[2])
                {
                    flagValue |= 4;
                }
                if(flags[3])
                {
                    flagValue |= 8;
                }
                if(flags[4])
                {
                    flagValue |= 16;
                }
                if(flags[5])
                {
                    flagValue |= 32;
                }
                if(flags[6])
                {
                    flagValue |= 64;
                }
                if(flags[7])
                {
                    flagValue |= 128;
                }
                *(uint8_t*)p_pointerToByte = flagValue;
            }

            // TODORT move to .cpp file
            static void MarkByteAsOccupied(void* p_pointerToByte)
            {
                /*
                   XXXX XXXX
                 | 0000 0001
                */
                *(uint8_t*)p_pointerToByte |= 1;
            }

            // TODORT move to .cpp file
            static void MarkByteAsFree(void* p_pointerToByte)
            {
                /*
                    XXXX XXXX
                  & 1111 1110
                */
                *(uint8_t*)p_pointerToByte &= 254;
            }

            // TODORT move to .cpp file
            static std::array<bool, 8>& FetchAllFlagsFromByte(const void* const p_pointerToByte)
            {
                const uint8_t& value = *(uint8_t*)p_pointerToByte;
                std::array<bool, 8> returnValue; // All false at the beginning
                if((value & 1) == 1)
                {
                    returnValue[0] = true;
                }
                if((value & 2) == 2)
                {
                    returnValue[1] = true;
                }
                if((value & 4) == 4)
                {
                    returnValue[2] = true;
                }
                if((value & 8) == 8)
                {
                    returnValue[3] = true;
                }
                if((value & 16) == 16)
                {
                    returnValue[4] = true;
                }
                if((value & 32) == 32)
                {
                    returnValue[5] = true;
                }
                if((value & 64) == 64)
                {
                    returnValue[6] = true;
                }
                if((value & 128) == 128)
                {
                    returnValue[7] = true;
                }
                return returnValue;
            }
        };
    }
}<|endoftext|>


<commit_before>/*ckwg +5
 * Copyright 2011-2013 by Kitware, Inc. All Rights Reserved. Please refer to
 * KITWARE_LICENSE.TXT for licensing information, or contact General Counsel,
 * Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065.
 */

#include "utils.h"

#ifdef HAVE_PTHREAD_NAMING
#define NAME_THREAD_USING_PTHREAD
#ifdef HAVE_PTHREAD_SET_NAME_NP
#ifdef NDEBUG
// The mechanism only make sense in debugging mode.
#undef NAME_THREAD_USING_PTHREAD
#endif
#endif
#endif

#ifdef HAVE_SETPROCTITLE
#define NAME_THREAD_USING_SETPROCTITLE
#endif

#ifdef __linux__
#define NAME_THREAD_USING_PRCTL
#endif

#if defined(_WIN32) || defined(_WIN64)
#ifndef NDEBUG
#define NAME_THREAD_USING_WIN32
#endif
#endif

#ifdef NAME_THREAD_USING_PTHREAD
#include <pthread.h>
#ifdef HAVE_PTHREAD_SET_NAME_NP
#include <pthread_np.h>
#endif
#endif

#ifdef NAME_THREAD_USING_PRCTL
#include <sys/prctl.h>
#endif

#if defined(_WIN32) || defined(_WIN64)
#include <boost/scoped_array.hpp>

#include <windows.h>
#else
#include <cstdlib>
#endif

/**
 * \file utils.cxx
 *
 * \brief Implementation of pipeline utilities.
 */

namespace vistk
{

#ifdef NAME_THREAD_USING_PTHREAD
static bool name_thread_pthread(thread_name_t const& name);
#endif

#ifdef NAME_THREAD_USING_SETPROCTITLE
static bool name_thread_setproctitle(thread_name_t const& name);
#endif

#ifdef NAME_THREAD_USING_PRCTL
static bool name_thread_prctl(thread_name_t const& name);
#endif

#ifdef NAME_THREAD_USING_WIN32
static bool name_thread_win32(thread_name_t const& name);
#endif

bool
name_thread(thread_name_t const& name)
{
  bool ret = false;

#ifdef NAME_THREAD_USING_PTHREAD
  if (!ret)
  {
    ret = name_thread_pthread(name);
  }
#endif

#ifdef NAME_THREAD_USING_SETPROCTITLE
  if (!ret)
  {
    ret = name_thread_setproctitle(name);
  }
#endif

#ifdef NAME_THREAD_USING_PRCTL
  if (!ret)
  {
    ret = name_thread_prctl(name);
  }
#endif

#ifdef NAME_THREAD_USING_WIN32
  if (!ret)
  {
    ret = name_thread_win32(name);
  }
#endif

  return ret;
}

envvar_value_t
get_envvar(envvar_name_t const& name)
{
  envvar_value_t value;

#if defined(_WIN32) || defined(_WIN64)
  char const* const name_cstr = name.c_str();

  DWORD sz = GetEnvironmentVariable(name_cstr, NULL, 0);

  if (sz)
  {
    typedef boost::scoped_array<char> raw_envvar_value_t;
    raw_envvar_value_t const envvalue(new char[sz]);

    sz = GetEnvironmentVariable(name_cstr, envvalue.get(), sz);

    value = envvalue.get();
  }

  if (!sz)
  {
    /// \todo Log error that the environment reading failed.
  }
#else
  char const* const envvalue = getenv(name.c_str());

  if (envvalue)
  {
    value = envvalue;
  }
#endif

  return value;
}

#ifdef NAME_THREAD_USING_PTHREAD
bool
name_thread_pthread(thread_name_t const& name)
{
  int ret;

#ifdef HAVE_PTHREAD_SETNAME_NP
#ifdef PTHREAD_SETNAME_NP_TAKES_ID
  pthread_t const tid = pthread_self();

  ret = pthread_setname_np(tid, name.c_str());
#else
  ret = pthread_setname_np(name.c_str());
#endif
#elif defined(HAVE_PTHREAD_SET_NAME_NP)
// The documentation states that it only makes sense in debugging; respect it.
#ifndef NDEBUG
  pthread_t const tid = pthread_self();

  ret = pthread_set_name_np(tid, name.c_str());
#else
  // Fail if not debugging.
  ret = 1;
#endif
#endif

  return (ret == 0);
}
#endif

#ifdef NAME_THREAD_USING_SETPROCTITLE
bool
name_thread_setproctitle(thread_name_t const& name)
{
  setproctitle("%s", name.c_str());
}
#endif

#ifdef NAME_THREAD_USING_PRCTL
bool
name_thread_prctl(thread_name_t const& name)
{
  int const ret = prctl(PR_SET_NAME, reinterpret_cast<unsigned long>(name.c_str()), 0, 0, 0);

  return (ret == 0);
}
#endif

#ifdef NAME_THREAD_USING_WIN32
// The mechanism only make sense in debugging mode.
#ifndef NDEBUG
static void SetThreadName(DWORD dwThreadID, LPCSTR threadName);
#endif

bool
name_thread_win32(thread_name_t const& name)
{
  bool ret;

#ifndef NDEBUG
  static DWORD const current_thread = -1;

  set_thread_name(current_thread, name.c_str());

  ret = true;
#else
  ret = false;
#endif

  return ret;
}

// Code obtained from http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
#pragma pack(push,8)
typedef struct tagTHREADNAME_INFO
{
   DWORD dwType; // Must be 0x1000.
   LPCSTR szName; // Pointer to name (in user addr space).
   DWORD dwThreadID; // Thread ID (-1 = caller thread).
   DWORD dwFlags; // Reserved for future use, must be zero.
} THREADNAME_INFO;
#pragma pack(pop)

void
SetThreadName(DWORD dwThreadID, LPCSTR threadName)
{
   THREADNAME_INFO info;
   info.dwType = 0x1000;
   info.szName = threadName;
   info.dwThreadID = dwThreadID;
   info.dwFlags = 0;

   __try
   {
      static DWORD const MS_VC_EXCEPTION = 0x406D1388;

      RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info);
   }
   __except(EXCEPTION_EXECUTE_HANDLER)
   {
   }
}
#endif

}
<commit_msg>Clean up some style with the Windows support<commit_after>/*ckwg +5
 * Copyright 2011-2013 by Kitware, Inc. All Rights Reserved. Please refer to
 * KITWARE_LICENSE.TXT for licensing information, or contact General Counsel,
 * Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065.
 */

#include "utils.h"

#ifdef HAVE_PTHREAD_NAMING
#define NAME_THREAD_USING_PTHREAD
#ifdef HAVE_PTHREAD_SET_NAME_NP
#ifdef NDEBUG
// The mechanism only make sense in debugging mode.
#undef NAME_THREAD_USING_PTHREAD
#endif
#endif
#endif

#ifdef HAVE_SETPROCTITLE
#define NAME_THREAD_USING_SETPROCTITLE
#endif

#ifdef __linux__
#define NAME_THREAD_USING_PRCTL
#endif

#if defined(_WIN32) || defined(_WIN64)
#ifndef NDEBUG
#define NAME_THREAD_USING_WIN32
#endif
#endif

#ifdef NAME_THREAD_USING_PTHREAD
#include <pthread.h>
#ifdef HAVE_PTHREAD_SET_NAME_NP
#include <pthread_np.h>
#endif
#endif

#ifdef NAME_THREAD_USING_PRCTL
#include <sys/prctl.h>
#endif

#if defined(_WIN32) || defined(_WIN64)
#include <boost/scoped_array.hpp>

#include <windows.h>
#else
#include <cstdlib>
#endif

/**
 * \file utils.cxx
 *
 * \brief Implementation of pipeline utilities.
 */

namespace vistk
{

#ifdef NAME_THREAD_USING_PTHREAD
static bool name_thread_pthread(thread_name_t const& name);
#endif

#ifdef NAME_THREAD_USING_SETPROCTITLE
static bool name_thread_setproctitle(thread_name_t const& name);
#endif

#ifdef NAME_THREAD_USING_PRCTL
static bool name_thread_prctl(thread_name_t const& name);
#endif

#ifdef NAME_THREAD_USING_WIN32
static bool name_thread_win32(thread_name_t const& name);
#endif

bool
name_thread(thread_name_t const& name)
{
  bool ret = false;

#ifdef NAME_THREAD_USING_PTHREAD
  if (!ret)
  {
    ret = name_thread_pthread(name);
  }
#endif

#ifdef NAME_THREAD_USING_SETPROCTITLE
  if (!ret)
  {
    ret = name_thread_setproctitle(name);
  }
#endif

#ifdef NAME_THREAD_USING_PRCTL
  if (!ret)
  {
    ret = name_thread_prctl(name);
  }
#endif

#ifdef NAME_THREAD_USING_WIN32
  if (!ret)
  {
    ret = name_thread_win32(name);
  }
#endif

  return ret;
}

envvar_value_t
get_envvar(envvar_name_t const& name)
{
  envvar_value_t value;

#if defined(_WIN32) || defined(_WIN64)
  char const* const name_cstr = name.c_str();

  DWORD sz = GetEnvironmentVariable(name_cstr, NULL, 0);

  if (sz)
  {
    typedef boost::scoped_array<char> raw_envvar_value_t;
    raw_envvar_value_t const envvalue(new char[sz]);

    sz = GetEnvironmentVariable(name_cstr, envvalue.get(), sz);

    value = envvalue.get();
  }

  if (!sz)
  {
    /// \todo Log error that the environment reading failed.
  }
#else
  char const* const envvalue = getenv(name.c_str());

  if (envvalue)
  {
    value = envvalue;
  }
#endif

  return value;
}

#ifdef NAME_THREAD_USING_PTHREAD
bool
name_thread_pthread(thread_name_t const& name)
{
  int ret;

#ifdef HAVE_PTHREAD_SETNAME_NP
#ifdef PTHREAD_SETNAME_NP_TAKES_ID
  pthread_t const tid = pthread_self();

  ret = pthread_setname_np(tid, name.c_str());
#else
  ret = pthread_setname_np(name.c_str());
#endif
#elif defined(HAVE_PTHREAD_SET_NAME_NP)
// The documentation states that it only makes sense in debugging; respect it.
#ifndef NDEBUG
  pthread_t const tid = pthread_self();

  ret = pthread_set_name_np(tid, name.c_str());
#else
  // Fail if not debugging.
  ret = 1;
#endif
#endif

  return (ret == 0);
}
#endif

#ifdef NAME_THREAD_USING_SETPROCTITLE
bool
name_thread_setproctitle(thread_name_t const& name)
{
  setproctitle("%s", name.c_str());
}
#endif

#ifdef NAME_THREAD_USING_PRCTL
bool
name_thread_prctl(thread_name_t const& name)
{
  int const ret = prctl(PR_SET_NAME, reinterpret_cast<unsigned long>(name.c_str()), 0, 0, 0);

  return (ret == 0);
}
#endif

#ifdef NAME_THREAD_USING_WIN32
// The mechanism only make sense in debugging mode.
#ifndef NDEBUG
static void set_thread_name(DWORD thread_id, LPCSTR name);
#endif

bool
name_thread_win32(thread_name_t const& name)
{
  bool ret;

#ifndef NDEBUG
  static DWORD const current_thread = -1;

  set_thread_name(current_thread, name.c_str());

  ret = true;
#else
  ret = false;
#endif

  return ret;
}

// Code obtained from http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
#pragma pack(push,8)
typedef struct tagTHREADNAME_INFO
{
   DWORD dwType;     // Must be 0x1000.
   LPCSTR szName;    // Pointer to name (in user addr space).
   DWORD dwThreadID; // Thread ID (-1 = caller thread).
   DWORD dwFlags;    // Reserved for future use, must be zero.
} THREADNAME_INFO;
#pragma pack(pop)

void
set_thread_name(DWORD thread_id, LPCSTR name)
{
   THREADNAME_INFO info;
   info.dwType = 0x1000;
   info.szName = name;
   info.dwThreadID = thread_id;
   info.dwFlags = 0;

   __try
   {
      static DWORD const MS_VC_EXCEPTION = 0x406D1388;

      RaiseException(MS_VC_EXCEPTION,
        0,
        sizeof(info) / sizeof(ULONG_PTR),
        reinterpret_cast<ULONG_PTR*>&info);
   }
   __except(EXCEPTION_EXECUTE_HANDLER)
   {
   }
}
#endif

}
<|endoftext|>


<commit_before>/**************************************************************************************/
/*                                                                                */
/*  Visualization Library                                                             */
/*  http://visualizationlibrary.org                                                   */
/*                                                                                */
/*  Copyright (c) 2005-2017, Michele Bosi                                             */
/*  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.                                   */
/*                                                                                */
/*  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 HOLDER 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.                      */
/*                                                                                */
/**************************************************************************************/

#ifndef CRC32CheckSum_INCLUDE_ONCE
#define CRC32CheckSum_INCLUDE_ONCE

#include <vlCore/VirtualFile.hpp>

namespace vl
{
  /**
   * Computes the a CRC32 checksum of a given buffer or VirtualFile
  */
  class CRC32CheckSum
  {
    // Lower this if you need to limit the amount of data allocated to the stack, for example to 16K.
    static const int CHUNK_SIZE = 128*1024;

  public:
    //! Constructor.
    CRC32CheckSum() { crc32_init(); }

    unsigned int compute(const void* buf, int length)
    {
      const unsigned char* buffer = (const unsigned char*)buf;
      unsigned int mCRC32 = 0xffffffff;
      while(length--)
        mCRC32 = (mCRC32 >> 8) ^ crc32_table[(mCRC32 & 0xFF) ^ *buffer++];
      return mCRC32 ^ 0xffffffff;
    }

    unsigned int compute(VirtualFile* stream)
    {
      std::vector<unsigned char> buffer;
      buffer.resize(CHUNK_SIZE);
      unsigned char *ptr = &buffer.front();
      startCRC32();

      for( int length = (int)stream->read(ptr, CHUNK_SIZE); length; length = (int)stream->read(ptr, CHUNK_SIZE) )
        continueCRC32(ptr,length);

      return finalizeCRC32();
    }

    void startCRC32() { mCRC32 = 0xffffffff; }
    unsigned int finalizeCRC32() { return mCRC32 ^ 0xffffffff; }
    void continueCRC32(unsigned char* ptr, int length)
    {
      while(length--)
        mCRC32 = (mCRC32 >> 8) ^ crc32_table[(mCRC32 & 0xFF) ^ *ptr++];
    }

  protected:
    void crc32_init()
    {
      mCRC32 = 0;
      unsigned int ulPolynomial = 0x04c11db7;
      for(int i = 0; i <= 0xFF; i++)
      {
        crc32_table[i]=reflect(i, 8) << 24;
        for (int j = 0; j < 8; j++)
          crc32_table[i] = (crc32_table[i] << 1) ^ (crc32_table[i] & (1 << 31) ? ulPolynomial : 0);
        crc32_table[i] = reflect(crc32_table[i], 32);
      }
    }

    unsigned int reflect(unsigned int val, char ch)
    {
      unsigned int refl = 0;
      for(int i = 1; i < (ch + 1); i++)
      {
        if(val & 1)
          refl |= 1 << (ch - i);
        val >>= 1;
      }
      return refl;
    }

  protected:
    unsigned int mCRC32;
    unsigned int crc32_table[256];
  };
}

#endif
<commit_msg>Fixed CRC32CheckSum::mCRC32 initialization<commit_after>/**************************************************************************************/
/*                                                                                */
/*  Visualization Library                                                             */
/*  http://visualizationlibrary.org                                                   */
/*                                                                                */
/*  Copyright (c) 2005-2017, Michele Bosi                                             */
/*  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.                                   */
/*                                                                                */
/*  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 HOLDER 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.                      */
/*                                                                                */
/**************************************************************************************/

#ifndef CRC32CheckSum_INCLUDE_ONCE
#define CRC32CheckSum_INCLUDE_ONCE

#include <vlCore/VirtualFile.hpp>

namespace vl
{
  /**
   * Computes the a CRC32 checksum of a given buffer or VirtualFile
  */
  class CRC32CheckSum
  {
    // Lower this if you need to limit the amount of data allocated to the stack, for example to 16K.
    static const int CHUNK_SIZE = 128*1024;

  public:
    //! Constructor.
    CRC32CheckSum() { crc32_init(); }

    unsigned int compute(const void* buf, int length)
    {
      const unsigned char* buffer = (const unsigned char*)buf;
      mCRC32 = 0xffffffff;
      while(length--)
        mCRC32 = (mCRC32 >> 8) ^ crc32_table[(mCRC32 & 0xFF) ^ *buffer++];
      return mCRC32 ^ 0xffffffff;
    }

    unsigned int compute(VirtualFile* stream)
    {
      std::vector<unsigned char> buffer;
      buffer.resize(CHUNK_SIZE);
      unsigned char *ptr = &buffer.front();
      startCRC32();

      for( int length = (int)stream->read(ptr, CHUNK_SIZE); length; length = (int)stream->read(ptr, CHUNK_SIZE) )
        continueCRC32(ptr,length);

      return finalizeCRC32();
    }

    void startCRC32() { mCRC32 = 0xffffffff; }
    unsigned int finalizeCRC32() { return mCRC32 ^ 0xffffffff; }
    void continueCRC32(unsigned char* ptr, int length)
    {
      while(length--)
        mCRC32 = (mCRC32 >> 8) ^ crc32_table[(mCRC32 & 0xFF) ^ *ptr++];
    }

  protected:
    void crc32_init()
    {
      mCRC32 = 0;
      unsigned int ulPolynomial = 0x04c11db7;
      for(int i = 0; i <= 0xFF; i++)
      {
        crc32_table[i]=reflect(i, 8) << 24;
        for (int j = 0; j < 8; j++)
          crc32_table[i] = (crc32_table[i] << 1) ^ (crc32_table[i] & (1 << 31) ? ulPolynomial : 0);
        crc32_table[i] = reflect(crc32_table[i], 32);
      }
    }

    unsigned int reflect(unsigned int val, char ch)
    {
      unsigned int refl = 0;
      for(int i = 1; i < (ch + 1); i++)
      {
        if(val & 1)
          refl |= 1 << (ch - i);
        val >>= 1;
      }
      return refl;
    }

  protected:
    unsigned int mCRC32;
    unsigned int crc32_table[256];
  };
}

#endif
<|endoftext|>


<commit_before>#include "widgets/notebookpage.hpp"
#include "colorscheme.hpp"
#include "widgets/chatwidget.hpp"
#include "widgets/notebook.hpp"
#include "widgets/notebooktab.hpp"

#include <QDebug>
#include <QHBoxLayout>
#include <QMimeData>
#include <QObject>
#include <QPainter>
#include <QVBoxLayout>
#include <QWidget>
#include <boost/foreach.hpp>

#include <algorithm>

namespace chatterino {
namespace widgets {

bool NotebookPage::isDraggingSplit = false;
ChatWidget *NotebookPage::draggingSplit = nullptr;
std::pair<int, int> NotebookPage::dropPosition = std::pair<int, int>(-1, -1);

NotebookPage::NotebookPage(ChannelManager &_channelManager, Notebook *parent, NotebookTab *_tab)
    : BaseWidget(parent->colorScheme, parent)
    , channelManager(_channelManager)
    , completionManager(parent->completionManager)
    , tab(_tab)
    , dropPreview(this)
{
    this->tab->page = this;

    this->setLayout(&this->ui.parentLayout);

    this->setHidden(true);
    this->setAcceptDrops(true);

    this->ui.parentLayout.addSpacing(2);
    this->ui.parentLayout.addLayout(&this->ui.hbox);
    this->ui.parentLayout.setMargin(0);

    this->ui.hbox.setSpacing(1);
    this->ui.hbox.setMargin(0);
}

std::pair<int, int> NotebookPage::removeFromLayout(ChatWidget *widget)
{
    // remove reference to chat widget from chatWidgets vector
    auto it = std::find(std::begin(this->chatWidgets), std::end(this->chatWidgets), widget);
    if (it != std::end(this->chatWidgets)) {
        this->chatWidgets.erase(it);
    }

    // remove from box and return location
    for (int i = 0; i < this->ui.hbox.count(); ++i) {
        auto vbox = static_cast<QVBoxLayout *>(this->ui.hbox.itemAt(i));

        for (int j = 0; j < vbox->count(); ++j) {
            if (vbox->itemAt(j)->widget() != widget) {
                continue;
            }

            widget->setParent(nullptr);

            bool isLastItem = vbox->count() == 0;

            if (isLastItem) {
                this->ui.hbox.removeItem(vbox);

                delete vbox;
            }

            return std::pair<int, int>(i, isLastItem ? -1 : j);
        }
    }

    return std::pair<int, int>(-1, -1);
}

void NotebookPage::addToLayout(ChatWidget *widget,
                               std::pair<int, int> position = std::pair<int, int>(-1, -1))
{
    this->chatWidgets.push_back(widget);
    widget->giveFocus();

    // add vbox at the end
    if (position.first < 0 || position.first >= this->ui.hbox.count()) {
        auto vbox = new QVBoxLayout();
        vbox->addWidget(widget);

        this->ui.hbox.addLayout(vbox, 1);
        return;
    }

    // insert vbox
    if (position.second == -1) {
        auto vbox = new QVBoxLayout();
        vbox->addWidget(widget);

        this->ui.hbox.insertLayout(position.first, vbox, 1);
        return;
    }

    // add to existing vbox
    auto vbox = static_cast<QVBoxLayout *>(this->ui.hbox.itemAt(position.first));

    vbox->insertWidget(std::max(0, std::min(vbox->count(), position.second)), widget);
}

const std::vector<ChatWidget *> &NotebookPage::getChatWidgets() const
{
    return this->chatWidgets;
}

NotebookTab *NotebookPage::getTab() const
{
    return this->tab;
}

void NotebookPage::addChat(bool openChannelNameDialog)
{
    ChatWidget *w = this->createChatWidget();

    if (openChannelNameDialog) {
        bool ret = w->showChangeChannelPopup("Open channel", true);

        if (!ret) {
            delete w;
            return;
        }
    }

    this->addToLayout(w, std::pair<int, int>(-1, -1));
}

void NotebookPage::enterEvent(QEvent *)
{
    if (this->ui.hbox.count() == 0) {
        this->setCursor(QCursor(Qt::PointingHandCursor));
    } else {
        this->setCursor(QCursor(Qt::ArrowCursor));
    }
}

void NotebookPage::leaveEvent(QEvent *)
{
}

void NotebookPage::mouseReleaseEvent(QMouseEvent *event)
{
    if (this->ui.hbox.count() == 0 && event->button() == Qt::LeftButton) {
        // "Add Chat" was clicked
        this->addChat(true);

        this->setCursor(QCursor(Qt::ArrowCursor));
    }
}

void NotebookPage::dragEnterEvent(QDragEnterEvent *event)
{
    if (!event->mimeData()->hasFormat("chatterino/split"))
        return;

    if (isDraggingSplit) {
        return;
    }

    this->dropRegions.clear();

    if (this->ui.hbox.count() == 0) {
        this->dropRegions.push_back(DropRegion(rect(), std::pair<int, int>(-1, -1)));
    } else {
        for (int i = 0; i < this->ui.hbox.count() + 1; ++i) {
            this->dropRegions.push_back(
                DropRegion(QRect(((i * 4 - 1) * width() / this->ui.hbox.count()) / 4, 0,
                                 width() / this->ui.hbox.count() / 2 + 1, height() + 1),
                           std::pair<int, int>(i, -1)));
        }

        for (int i = 0; i < this->ui.hbox.count(); ++i) {
            auto vbox = static_cast<QVBoxLayout *>(this->ui.hbox.itemAt(i));

            for (int j = 0; j < vbox->count() + 1; ++j) {
                this->dropRegions.push_back(DropRegion(
                    QRect(i * width() / this->ui.hbox.count(),
                          ((j * 2 - 1) * height() / vbox->count()) / 2,
                          width() / this->ui.hbox.count() + 1, height() / vbox->count() + 1),

                    std::pair<int, int>(i, j)));
            }
        }
    }

    setPreviewRect(event->pos());

    event->acceptProposedAction();
}

void NotebookPage::dragMoveEvent(QDragMoveEvent *event)
{
    setPreviewRect(event->pos());
}

void NotebookPage::setPreviewRect(QPoint mousePos)
{
    for (DropRegion region : this->dropRegions) {
        if (region.rect.contains(mousePos)) {
            this->dropPreview.setBounds(region.rect);

            if (!this->dropPreview.isVisible()) {
                this->dropPreview.show();
                this->dropPreview.raise();
            }

            dropPosition = region.position;

            return;
        }
    }

    this->dropPreview.hide();
}

void NotebookPage::dragLeaveEvent(QDragLeaveEvent *event)
{
    this->dropPreview.hide();
}

void NotebookPage::dropEvent(QDropEvent *event)
{
    if (isDraggingSplit) {
        event->acceptProposedAction();

        NotebookPage::draggingSplit->setParent(this);

        addToLayout(NotebookPage::draggingSplit, dropPosition);
    }

    this->dropPreview.hide();
}

void NotebookPage::paintEvent(QPaintEvent *)
{
    QPainter painter(this);

    if (this->ui.hbox.count() == 0) {
        painter.fillRect(rect(), this->colorScheme.ChatBackground);

        painter.fillRect(0, 0, width(), 2, this->colorScheme.TabSelectedBackground);

        painter.setPen(this->colorScheme.Text);
        painter.drawText(rect(), "Add Chat", QTextOption(Qt::AlignCenter));
    } else {
        //painter.fillRect(rect(), this->colorScheme.TabSelectedBackground);
        painter.fillRect(rect(), QColor(127, 127, 127));

        painter.fillRect(0, 0, width(), 2, this->colorScheme.TabSelectedBackground);
    }
}

static std::pair<int, int> getWidgetPositionInLayout(QLayout *layout, const ChatWidget *chatWidget)
{
    for (int i = 0; i < layout->count(); ++i) {
        printf("xD\n");
    }

    return std::make_pair(-1, -1);
}

std::pair<int, int> NotebookPage::getChatPosition(const ChatWidget *chatWidget)
{
    auto layout = this->ui.hbox.layout();

    if (layout == nullptr) {
        return std::make_pair(-1, -1);
    }

    return getWidgetPositionInLayout(layout, chatWidget);
}

ChatWidget *NotebookPage::createChatWidget()
{
    return new ChatWidget(this->channelManager, this);
}

void NotebookPage::load(const boost::property_tree::ptree &tree)
{
    try {
        int column = 0;
        for (const auto &v : tree.get_child("columns.")) {
            int row = 0;
            for (const auto &innerV : v.second.get_child("")) {
                auto widget = this->createChatWidget();
                widget->load(innerV.second);
                addToLayout(widget, std::pair<int, int>(column, row));
                ++row;
            }
            ++column;
        }
    } catch (boost::property_tree::ptree_error &) {
        // can't read tabs
    }
}

static void saveFromLayout(QLayout *layout, boost::property_tree::ptree &tree)
{
    for (int i = 0; i < layout->count(); ++i) {
        auto item = layout->itemAt(i);

        auto innerLayout = item->layout();
        if (innerLayout != nullptr) {
            boost::property_tree::ptree innerLayoutTree;

            saveFromLayout(innerLayout, innerLayoutTree);

            if (innerLayoutTree.size() > 0) {
                tree.push_back(std::make_pair("", innerLayoutTree));
            }

            continue;
        }

        auto widget = item->widget();

        if (widget == nullptr) {
            // This layoutitem does not manage a widget for some reason
            continue;
        }

        ChatWidget *chatWidget = qobject_cast<ChatWidget *>(widget);

        if (chatWidget != nullptr) {
            boost::property_tree::ptree chat = chatWidget->save();

            tree.push_back(std::make_pair("", chat));
            continue;
        }
    }
}

boost::property_tree::ptree NotebookPage::save()
{
    boost::property_tree::ptree tree;

    auto layout = this->ui.hbox.layout();

    saveFromLayout(layout, tree);

    /*
    for (const auto &chat : this->chatWidgets) {
        boost::property_tree::ptree child = chat->save();

        // Set child position
        child.put("position", "5,3");

        tree.push_back(std::make_pair("", child));
    }
    */

    return tree;
}

}  // namespace widgets
}  // namespace chatterino
<commit_msg>fixed split drag n drop<commit_after>#include "widgets/notebookpage.hpp"
#include "colorscheme.hpp"
#include "widgets/chatwidget.hpp"
#include "widgets/notebook.hpp"
#include "widgets/notebooktab.hpp"

#include <QDebug>
#include <QHBoxLayout>
#include <QMimeData>
#include <QObject>
#include <QPainter>
#include <QVBoxLayout>
#include <QWidget>
#include <boost/foreach.hpp>

#include <algorithm>

namespace chatterino {
namespace widgets {

bool NotebookPage::isDraggingSplit = false;
ChatWidget *NotebookPage::draggingSplit = nullptr;
std::pair<int, int> NotebookPage::dropPosition = std::pair<int, int>(-1, -1);

NotebookPage::NotebookPage(ChannelManager &_channelManager, Notebook *parent, NotebookTab *_tab)
    : BaseWidget(parent->colorScheme, parent)
    , channelManager(_channelManager)
    , completionManager(parent->completionManager)
    , tab(_tab)
    , dropPreview(this)
{
    this->tab->page = this;

    this->setLayout(&this->ui.parentLayout);

    this->setHidden(true);
    this->setAcceptDrops(true);

    this->ui.parentLayout.addSpacing(2);
    this->ui.parentLayout.addLayout(&this->ui.hbox);
    this->ui.parentLayout.setMargin(0);

    this->ui.hbox.setSpacing(1);
    this->ui.hbox.setMargin(0);
}

std::pair<int, int> NotebookPage::removeFromLayout(ChatWidget *widget)
{
    // remove reference to chat widget from chatWidgets vector
    auto it = std::find(std::begin(this->chatWidgets), std::end(this->chatWidgets), widget);
    if (it != std::end(this->chatWidgets)) {
        this->chatWidgets.erase(it);
    }

    // remove from box and return location
    for (int i = 0; i < this->ui.hbox.count(); ++i) {
        auto vbox = static_cast<QVBoxLayout *>(this->ui.hbox.itemAt(i));

        for (int j = 0; j < vbox->count(); ++j) {
            if (vbox->itemAt(j)->widget() != widget) {
                continue;
            }

            widget->setParent(nullptr);

            bool isLastItem = vbox->count() == 0;

            if (isLastItem) {
                this->ui.hbox.removeItem(vbox);

                delete vbox;
            }

            return std::pair<int, int>(i, isLastItem ? -1 : j);
        }
    }

    return std::pair<int, int>(-1, -1);
}

void NotebookPage::addToLayout(ChatWidget *widget,
                               std::pair<int, int> position = std::pair<int, int>(-1, -1))
{
    this->chatWidgets.push_back(widget);
    widget->giveFocus();

    // add vbox at the end
    if (position.first < 0 || position.first >= this->ui.hbox.count()) {
        auto vbox = new QVBoxLayout();
        vbox->addWidget(widget);

        this->ui.hbox.addLayout(vbox, 1);
        return;
    }

    // insert vbox
    if (position.second == -1) {
        auto vbox = new QVBoxLayout();
        vbox->addWidget(widget);

        this->ui.hbox.insertLayout(position.first, vbox, 1);
        return;
    }

    // add to existing vbox
    auto vbox = static_cast<QVBoxLayout *>(this->ui.hbox.itemAt(position.first));

    vbox->insertWidget(std::max(0, std::min(vbox->count(), position.second)), widget);
}

const std::vector<ChatWidget *> &NotebookPage::getChatWidgets() const
{
    return this->chatWidgets;
}

NotebookTab *NotebookPage::getTab() const
{
    return this->tab;
}

void NotebookPage::addChat(bool openChannelNameDialog)
{
    ChatWidget *w = this->createChatWidget();

    if (openChannelNameDialog) {
        bool ret = w->showChangeChannelPopup("Open channel", true);

        if (!ret) {
            delete w;
            return;
        }
    }

    this->addToLayout(w, std::pair<int, int>(-1, -1));
}

void NotebookPage::enterEvent(QEvent *)
{
    if (this->ui.hbox.count() == 0) {
        this->setCursor(QCursor(Qt::PointingHandCursor));
    } else {
        this->setCursor(QCursor(Qt::ArrowCursor));
    }
}

void NotebookPage::leaveEvent(QEvent *)
{
}

void NotebookPage::mouseReleaseEvent(QMouseEvent *event)
{
    if (this->ui.hbox.count() == 0 && event->button() == Qt::LeftButton) {
        // "Add Chat" was clicked
        this->addChat(true);

        this->setCursor(QCursor(Qt::ArrowCursor));
    }
}

void NotebookPage::dragEnterEvent(QDragEnterEvent *event)
{
    if (!event->mimeData()->hasFormat("chatterino/split"))
        return;

    if (!isDraggingSplit) {
        return;
    }

    this->dropRegions.clear();

    if (this->ui.hbox.count() == 0) {
        this->dropRegions.push_back(DropRegion(rect(), std::pair<int, int>(-1, -1)));
    } else {
        for (int i = 0; i < this->ui.hbox.count() + 1; ++i) {
            this->dropRegions.push_back(
                DropRegion(QRect(((i * 4 - 1) * width() / this->ui.hbox.count()) / 4, 0,
                                 width() / this->ui.hbox.count() / 2 + 1, height() + 1),
                           std::pair<int, int>(i, -1)));
        }

        for (int i = 0; i < this->ui.hbox.count(); ++i) {
            auto vbox = static_cast<QVBoxLayout *>(this->ui.hbox.itemAt(i));

            for (int j = 0; j < vbox->count() + 1; ++j) {
                this->dropRegions.push_back(DropRegion(
                    QRect(i * width() / this->ui.hbox.count(),
                          ((j * 2 - 1) * height() / vbox->count()) / 2,
                          width() / this->ui.hbox.count() + 1, height() / vbox->count() + 1),

                    std::pair<int, int>(i, j)));
            }
        }
    }

    setPreviewRect(event->pos());

    event->acceptProposedAction();
}

void NotebookPage::dragMoveEvent(QDragMoveEvent *event)
{
    setPreviewRect(event->pos());
}

void NotebookPage::setPreviewRect(QPoint mousePos)
{
    for (DropRegion region : this->dropRegions) {
        if (region.rect.contains(mousePos)) {
            this->dropPreview.setBounds(region.rect);

            if (!this->dropPreview.isVisible()) {
                this->dropPreview.show();
                this->dropPreview.raise();
            }

            dropPosition = region.position;

            return;
        }
    }

    this->dropPreview.hide();
}

void NotebookPage::dragLeaveEvent(QDragLeaveEvent *event)
{
    this->dropPreview.hide();
}

void NotebookPage::dropEvent(QDropEvent *event)
{
    if (isDraggingSplit) {
        event->acceptProposedAction();

        NotebookPage::draggingSplit->setParent(this);

        addToLayout(NotebookPage::draggingSplit, dropPosition);
    }

    this->dropPreview.hide();
}

void NotebookPage::paintEvent(QPaintEvent *)
{
    QPainter painter(this);

    if (this->ui.hbox.count() == 0) {
        painter.fillRect(rect(), this->colorScheme.ChatBackground);

        painter.fillRect(0, 0, width(), 2, this->colorScheme.TabSelectedBackground);

        painter.setPen(this->colorScheme.Text);
        painter.drawText(rect(), "Add Chat", QTextOption(Qt::AlignCenter));
    } else {
        //painter.fillRect(rect(), this->colorScheme.TabSelectedBackground);
        painter.fillRect(rect(), QColor(127, 127, 127));

        painter.fillRect(0, 0, width(), 2, this->colorScheme.TabSelectedBackground);
    }
}

static std::pair<int, int> getWidgetPositionInLayout(QLayout *layout, const ChatWidget *chatWidget)
{
    for (int i = 0; i < layout->count(); ++i) {
        printf("xD\n");
    }

    return std::make_pair(-1, -1);
}

std::pair<int, int> NotebookPage::getChatPosition(const ChatWidget *chatWidget)
{
    auto layout = this->ui.hbox.layout();

    if (layout == nullptr) {
        return std::make_pair(-1, -1);
    }

    return getWidgetPositionInLayout(layout, chatWidget);
}

ChatWidget *NotebookPage::createChatWidget()
{
    return new ChatWidget(this->channelManager, this);
}

void NotebookPage::load(const boost::property_tree::ptree &tree)
{
    try {
        int column = 0;
        for (const auto &v : tree.get_child("columns.")) {
            int row = 0;
            for (const auto &innerV : v.second.get_child("")) {
                auto widget = this->createChatWidget();
                widget->load(innerV.second);
                addToLayout(widget, std::pair<int, int>(column, row));
                ++row;
            }
            ++column;
        }
    } catch (boost::property_tree::ptree_error &) {
        // can't read tabs
    }
}

static void saveFromLayout(QLayout *layout, boost::property_tree::ptree &tree)
{
    for (int i = 0; i < layout->count(); ++i) {
        auto item = layout->itemAt(i);

        auto innerLayout = item->layout();
        if (innerLayout != nullptr) {
            boost::property_tree::ptree innerLayoutTree;

            saveFromLayout(innerLayout, innerLayoutTree);

            if (innerLayoutTree.size() > 0) {
                tree.push_back(std::make_pair("", innerLayoutTree));
            }

            continue;
        }

        auto widget = item->widget();

        if (widget == nullptr) {
            // This layoutitem does not manage a widget for some reason
            continue;
        }

        ChatWidget *chatWidget = qobject_cast<ChatWidget *>(widget);

        if (chatWidget != nullptr) {
            boost::property_tree::ptree chat = chatWidget->save();

            tree.push_back(std::make_pair("", chat));
            continue;
        }
    }
}

boost::property_tree::ptree NotebookPage::save()
{
    boost::property_tree::ptree tree;

    auto layout = this->ui.hbox.layout();

    saveFromLayout(layout, tree);

    /*
    for (const auto &chat : this->chatWidgets) {
        boost::property_tree::ptree child = chat->save();

        // Set child position
        child.put("position", "5,3");

        tree.push_back(std::make_pair("", child));
    }
    */

    return tree;
}

}  // namespace widgets
}  // namespace chatterino
<|endoftext|>


<commit_before>/**
 ******************************************************************************
 * Xenia : Xbox 360 Emulator Research Project                                 *
 ******************************************************************************
 * Copyright 2015 Ben Vanik. All rights reserved.                             *
 * Released under the BSD license - see LICENSE in the root for more details. *
 ******************************************************************************
 */

#include "xenia/gpu/trace_dump.h"

#include <gflags/gflags.h>

#include "third_party/stb/stb_image_write.h"
#include "xenia/base/logging.h"
#include "xenia/base/profiling.h"
#include "xenia/base/string.h"
#include "xenia/base/threading.h"
#include "xenia/gpu/command_processor.h"
#include "xenia/gpu/graphics_system.h"
#include "xenia/memory.h"
#include "xenia/ui/file_picker.h"
#include "xenia/ui/window.h"
#include "xenia/xbox.h"

#define STB_IMAGE_WRITE_IMPLEMENTATION
#undef _CRT_SECURE_NO_WARNINGS
#undef _CRT_NONSTDC_NO_DEPRECATE
#include "third_party/stb/stb_image_write.h"

DEFINE_string(target_trace_file, "", "Specifies the trace file to load.");
DEFINE_string(trace_dump_path, "", "Output path for dumped files.");

namespace xe {
namespace gpu {

using namespace xe::gpu::xenos;

TraceDump::TraceDump() = default;

TraceDump::~TraceDump() = default;

int TraceDump::Main(const std::vector<std::wstring>& args) {
  // Grab path from the flag or unnamed argument.
  std::wstring path;
  std::wstring output_path;
  if (!FLAGS_target_trace_file.empty()) {
    // Passed as a named argument.
    // TODO(benvanik): find something better than gflags that supports
    // unicode.
    path = xe::to_wstring(FLAGS_target_trace_file);
  } else if (args.size() >= 2) {
    // Passed as an unnamed argument.
    path = args[1];

    if (args.size() >= 3) {
      output_path = args[2];
    }
  } else {
    // Open a file chooser and ask the user.
    auto file_picker = xe::ui::FilePicker::Create();
    file_picker->set_mode(ui::FilePicker::Mode::kOpen);
    file_picker->set_type(ui::FilePicker::Type::kFile);
    file_picker->set_multi_selection(false);
    file_picker->set_title(L"Select Trace File");
    file_picker->set_extensions({
        {L"Supported Files", L"*.xenia_gpu_trace"},
        {L"All Files (*.*)", L"*.*"},
    });
    if (file_picker->Show()) {
      auto selected_files = file_picker->selected_files();
      if (!selected_files.empty()) {
        path = selected_files[0];
      }
    }
  }

  if (path.empty()) {
    xe::FatalError("No trace file specified");
    return 1;
  }

  // Normalize the path and make absolute.
  auto abs_path = xe::to_absolute_path(path);
  XELOGI("Loading trace file %ls...", abs_path.c_str());

  if (!Setup()) {
    xe::FatalError("Unable to setup trace dump tool");
    return 1;
  }
  if (!Load(std::move(abs_path))) {
    xe::FatalError("Unable to load trace file; not found?");
    return 1;
  }

  // Root file name for outputs.
  if (output_path.empty()) {
    base_output_path_ =
        xe::fix_path_separators(xe::to_wstring(FLAGS_trace_dump_path));
    base_output_path_ =
        xe::join_paths(base_output_path_,
                       xe::find_name_from_path(xe::fix_path_separators(path)));
  } else {
    base_output_path_ = xe::fix_path_separators(output_path);
  }

  // Ensure output path exists.
  xe::filesystem::CreateParentFolder(base_output_path_);

  return Run();
}

bool TraceDump::Setup() {
  // Main display window.
  loop_ = ui::Loop::Create();
  window_ = xe::ui::Window::Create(loop_.get(), L"xenia-gpu-trace-dump");
  loop_->PostSynchronous([&]() {
    xe::threading::set_name("Win32 Loop");
    if (!window_->Initialize()) {
      xe::FatalError("Failed to initialize main window");
      return;
    }
  });
  window_->on_closed.AddListener([&](xe::ui::UIEvent* e) {
    loop_->Quit();
    XELOGI("User-initiated death!");
    exit(1);
  });
  loop_->on_quit.AddListener([&](xe::ui::UIEvent* e) { window_.reset(); });
  window_->Resize(1920, 1200);

  // Create the emulator but don't initialize so we can setup the window.
  emulator_ = std::make_unique<Emulator>(L"");
  X_STATUS result =
      emulator_->Setup(window_.get(), nullptr,
                       [this]() { return CreateGraphicsSystem(); }, nullptr);
  if (XFAILED(result)) {
    XELOGE("Failed to setup emulator: %.8X", result);
    return false;
  }
  memory_ = emulator_->memory();
  graphics_system_ = emulator_->graphics_system();

  window_->on_key_char.AddListener([&](xe::ui::KeyEvent* e) {
    if (e->key_code() == 0x74 /* VK_F5 */) {
      graphics_system_->ClearCaches();
      e->set_handled(true);
    }
  });

  player_ = std::make_unique<TracePlayer>(loop_.get(), graphics_system_);

  window_->on_painting.AddListener([&](xe::ui::UIEvent* e) {
    // Update titlebar status.
    auto file_name = xe::find_name_from_path(trace_file_path_);
    std::wstring title = std::wstring(L"Xenia GPU Trace Dump: ") + file_name;
    if (player_->is_playing_trace()) {
      int percent =
          static_cast<int>(player_->playback_percent() / 10000.0 * 100.0);
      title += xe::format_string(L" (%d%%)", percent);
    }
    window_->set_title(title);

    // Continuous paint.
    window_->Invalidate();
  });
  window_->Invalidate();

  return true;
}

bool TraceDump::Load(std::wstring trace_file_path) {
  trace_file_path_ = std::move(trace_file_path);

  if (!player_->Open(trace_file_path_)) {
    XELOGE("Could not load trace file");
    return false;
  }

  return true;
}

int TraceDump::Run() {
  loop_->Post([&]() {
    player_->SeekFrame(0);
    player_->SeekCommand(
        static_cast<int>(player_->current_frame()->commands.size() - 1));
  });
  player_->WaitOnPlayback();

  loop_->PostSynchronous([&]() {
    // Breathe.
  });
  loop_->PostSynchronous([&]() {
    // Breathe.
  });

  xe::threading::Fence capture_fence;
  int result = 0;
  loop_->PostDelayed(
      [&]() {
        // Capture.
        auto raw_image = graphics_system_->Capture();
        if (!raw_image) {
          // Failed to capture anything.
          result = -1;
          capture_fence.Signal();
          return;
        }

        // Save framebuffer png.
        std::string png_path = xe::to_string(base_output_path_ + L".png");
        stbi_write_png(png_path.c_str(), static_cast<int>(raw_image->width),
                       static_cast<int>(raw_image->height), 4,
                       raw_image->data.data(),
                       static_cast<int>(raw_image->stride));

        result = 0;
        capture_fence.Signal();
      },
      50);

  capture_fence.Wait();

  // Wait until we are exited.
  loop_->Quit();
  loop_->AwaitQuit();

  Profiler::Shutdown();
  window_.reset();
  loop_.reset();
  player_.reset();
  emulator_.reset();

  return result;
}

}  //  namespace gpu
}  //  namespace xe
<commit_msg>[GPU] Don't bother redrawing the trace dump window<commit_after>/**
 ******************************************************************************
 * Xenia : Xbox 360 Emulator Research Project                                 *
 ******************************************************************************
 * Copyright 2015 Ben Vanik. All rights reserved.                             *
 * Released under the BSD license - see LICENSE in the root for more details. *
 ******************************************************************************
 */

#include "xenia/gpu/trace_dump.h"

#include <gflags/gflags.h>

#include "third_party/stb/stb_image_write.h"
#include "xenia/base/logging.h"
#include "xenia/base/profiling.h"
#include "xenia/base/string.h"
#include "xenia/base/threading.h"
#include "xenia/gpu/command_processor.h"
#include "xenia/gpu/graphics_system.h"
#include "xenia/memory.h"
#include "xenia/ui/file_picker.h"
#include "xenia/ui/window.h"
#include "xenia/xbox.h"

#define STB_IMAGE_WRITE_IMPLEMENTATION
#undef _CRT_SECURE_NO_WARNINGS
#undef _CRT_NONSTDC_NO_DEPRECATE
#include "third_party/stb/stb_image_write.h"

DEFINE_string(target_trace_file, "", "Specifies the trace file to load.");
DEFINE_string(trace_dump_path, "", "Output path for dumped files.");

namespace xe {
namespace gpu {

using namespace xe::gpu::xenos;

TraceDump::TraceDump() = default;

TraceDump::~TraceDump() = default;

int TraceDump::Main(const std::vector<std::wstring>& args) {
  // Grab path from the flag or unnamed argument.
  std::wstring path;
  std::wstring output_path;
  if (!FLAGS_target_trace_file.empty()) {
    // Passed as a named argument.
    // TODO(benvanik): find something better than gflags that supports
    // unicode.
    path = xe::to_wstring(FLAGS_target_trace_file);
  } else if (args.size() >= 2) {
    // Passed as an unnamed argument.
    path = args[1];

    if (args.size() >= 3) {
      output_path = args[2];
    }
  } else {
    // Open a file chooser and ask the user.
    auto file_picker = xe::ui::FilePicker::Create();
    file_picker->set_mode(ui::FilePicker::Mode::kOpen);
    file_picker->set_type(ui::FilePicker::Type::kFile);
    file_picker->set_multi_selection(false);
    file_picker->set_title(L"Select Trace File");
    file_picker->set_extensions({
        {L"Supported Files", L"*.xenia_gpu_trace"},
        {L"All Files (*.*)", L"*.*"},
    });
    if (file_picker->Show()) {
      auto selected_files = file_picker->selected_files();
      if (!selected_files.empty()) {
        path = selected_files[0];
      }
    }
  }

  if (path.empty()) {
    xe::FatalError("No trace file specified");
    return 1;
  }

  // Normalize the path and make absolute.
  auto abs_path = xe::to_absolute_path(path);
  XELOGI("Loading trace file %ls...", abs_path.c_str());

  if (!Setup()) {
    xe::FatalError("Unable to setup trace dump tool");
    return 1;
  }
  if (!Load(std::move(abs_path))) {
    xe::FatalError("Unable to load trace file; not found?");
    return 1;
  }

  // Root file name for outputs.
  if (output_path.empty()) {
    base_output_path_ =
        xe::fix_path_separators(xe::to_wstring(FLAGS_trace_dump_path));
    base_output_path_ =
        xe::join_paths(base_output_path_,
                       xe::find_name_from_path(xe::fix_path_separators(path)));
  } else {
    base_output_path_ = xe::fix_path_separators(output_path);
  }

  // Ensure output path exists.
  xe::filesystem::CreateParentFolder(base_output_path_);

  return Run();
}

bool TraceDump::Setup() {
  // Main display window.
  loop_ = ui::Loop::Create();
  window_ = xe::ui::Window::Create(loop_.get(), L"xenia-gpu-trace-dump");
  loop_->PostSynchronous([&]() {
    xe::threading::set_name("Win32 Loop");
    if (!window_->Initialize()) {
      xe::FatalError("Failed to initialize main window");
      return;
    }
  });
  window_->on_closed.AddListener([&](xe::ui::UIEvent* e) {
    loop_->Quit();
    XELOGI("User-initiated death!");
    exit(1);
  });
  loop_->on_quit.AddListener([&](xe::ui::UIEvent* e) { window_.reset(); });
  window_->Resize(1280, 720);

  // Create the emulator but don't initialize so we can setup the window.
  emulator_ = std::make_unique<Emulator>(L"");
  X_STATUS result =
      emulator_->Setup(window_.get(), nullptr,
                       [this]() { return CreateGraphicsSystem(); }, nullptr);
  if (XFAILED(result)) {
    XELOGE("Failed to setup emulator: %.8X", result);
    return false;
  }
  memory_ = emulator_->memory();
  graphics_system_ = emulator_->graphics_system();

  window_->on_key_char.AddListener([&](xe::ui::KeyEvent* e) {
    if (e->key_code() == 0x74 /* VK_F5 */) {
      graphics_system_->ClearCaches();
      e->set_handled(true);
    }
  });

  player_ = std::make_unique<TracePlayer>(loop_.get(), graphics_system_);
  return true;
}

bool TraceDump::Load(std::wstring trace_file_path) {
  trace_file_path_ = std::move(trace_file_path);

  if (!player_->Open(trace_file_path_)) {
    XELOGE("Could not load trace file");
    return false;
  }

  return true;
}

int TraceDump::Run() {
  loop_->Post([&]() {
    player_->SeekFrame(0);
    player_->SeekCommand(
        static_cast<int>(player_->current_frame()->commands.size() - 1));
  });
  player_->WaitOnPlayback();

  loop_->PostSynchronous([&]() {
    // Breathe.
  });
  loop_->PostSynchronous([&]() {
    // Breathe.
  });

  int result = 0;
  auto capture_event = xe::threading::Event::CreateManualResetEvent(false);
  loop_->PostDelayed(
      [&]() {
        // Capture.
        auto raw_image = graphics_system_->Capture();
        if (!raw_image) {
          // Failed to capture anything.
          result = -1;
          capture_event->Set();
          return;
        }

        // Save framebuffer png.
        std::string png_path = xe::to_string(base_output_path_ + L".png");
        stbi_write_png(png_path.c_str(), static_cast<int>(raw_image->width),
                       static_cast<int>(raw_image->height), 4,
                       raw_image->data.data(),
                       static_cast<int>(raw_image->stride));

        result = 0;
        capture_event->Set();
      },
      50);

  while (xe::threading::Wait(capture_event.get(), false,
                             std::chrono::milliseconds(1)) ==
         xe::threading::WaitResult::kTimeout) {
    // Update titlebar status.
    auto file_name = xe::find_name_from_path(trace_file_path_);
    std::wstring title = std::wstring(L"Xenia GPU Trace Dump: ") + file_name;
    if (player_->is_playing_trace()) {
      int percent =
          static_cast<int>(player_->playback_percent() / 10000.0 * 100.0);
      title += xe::format_string(L" (%d%%)", percent);
    }
    window_->set_title(title);
  }

  // Profiler must exit before the window is closed.
  Profiler::Shutdown();

  // Wait until we are exited.
  loop_->Quit();
  loop_->AwaitQuit();

  window_.reset();
  loop_.reset();
  player_.reset();
  emulator_.reset();

  return result;
}

}  //  namespace gpu
}  //  namespace xe
<|endoftext|>


<commit_before>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you 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 <set>
#include <string>

#include <mesos/zookeeper/contender.hpp>
#include <mesos/zookeeper/detector.hpp>
#include <mesos/zookeeper/group.hpp>

#include <process/defer.hpp>
#include <process/dispatch.hpp>
#include <process/future.hpp>
#include <process/id.hpp>

#include <stout/check.hpp>
#include <stout/lambda.hpp>
#include <stout/option.hpp>
#include <stout/some.hpp>

using namespace process;

using std::set;
using std::string;

namespace zookeeper {

class LeaderContenderProcess : public Process<LeaderContenderProcess>
{
public:
  LeaderContenderProcess(
      Group* group,
      const string& data,
      const Option<string>& label);

  virtual ~LeaderContenderProcess();

  // LeaderContender implementation.
  Future<Future<Nothing> > contend();
  Future<bool> withdraw();

protected:
  virtual void finalize();

private:
  // Invoked when we have joined the group (or failed to do so).
  void joined();

  // Invoked when the group membership is cancelled.
  void cancelled(const Future<bool>& result);

  // Helper for cancelling the Group membership.
  void cancel();

  Group* group;
  const string data;
  const Option<string> label;

  // The contender's state transitions from contending -> watching ->
  // withdrawing or contending -> withdrawing. Each state is
  // identified by the corresponding Option<Promise> being assigned.
  // Note that these Option<Promise>s are never reset to None once it
  // is assigned.

  // Holds the promise for the future for contend().
  Option<Promise<Future<Nothing> >*> contending;

  // Holds the promise for the inner future enclosed by contend()'s
  // result which is satisfied when the contender's candidacy is
  // lost.
  Option<Promise<Nothing>*> watching;

  // Holds the promise for the future for withdraw().
  Option<Promise<bool>*> withdrawing;

  // Stores the result for joined().
  Future<Group::Membership> candidacy;
};

LeaderContenderProcess::LeaderContenderProcess(
    Group* _group,
    const string& _data,
    const Option<string>& _label)
  : ProcessBase(ID::generate("leader-contender")),
    group(_group),
    data(_data),
    label(_label) {}

LeaderContenderProcess::~LeaderContenderProcess()
{
  if (contending.isSome()) {
    contending.get()->discard();
    delete contending.get();
    contending = None();
  }

  if (watching.isSome()) {
    watching.get()->discard();
    delete watching.get();
    watching = None();
  }

  if (withdrawing.isSome()) {
    withdrawing.get()->discard();
    delete withdrawing.get();
    withdrawing = None();
  }
}

void LeaderContenderProcess::finalize()
{
  // We do not wait for the result here because the Group keeps
  // retrying (even after the contender is destroyed) until it
  // succeeds so the old membership is eventually going to be
  // cancelled.
  // There is a tricky situation where the contender terminates after
  // it has contended but before it is notified of the obtained
  // membership. In this case the membership is not cancelled during
  // contender destruction. The client thus should use withdraw() to
  // wait for the membership to be first obtained and then cancelled.
  cancel();
}

Future<Future<Nothing> > LeaderContenderProcess::contend()
{
  if (contending.isSome()) {
    return Failure("Cannot contend more than once");
  }

  LOG(INFO) << "Joining the ZK group";
  candidacy = group->join(data, label);
  candidacy
    .onAny(defer(self(), &Self::joined));

  // Okay, we wait and see what unfolds.
  contending = new Promise<Future<Nothing> >();
  return contending.get()->future();
}

Future<bool> LeaderContenderProcess::withdraw()
{
  if (contending.isNone()) {
    // Nothing to withdraw because the contender has not contended.
    return false;
  }

  if (withdrawing.isSome()) {
    // Repeated calls to withdraw get the same result.
    return withdrawing.get();
  }

  withdrawing = new Promise<bool>();

  CHECK(!candidacy.isDiscarded());

  if (candidacy.isPending()) {
    // If we have not obtained the candidacy yet, we withdraw after
    // it is obtained.
    LOG(INFO) << "Withdraw requested before the candidacy is obtained; will "
              << "withdraw after it happens";
    candidacy.onAny(defer(self(), &Self::cancel));
  } else if (candidacy.isReady()) {
    cancel();
  } else {
    // We have failed to obtain the candidacy so we do not need to
    // cancel it.
    return false;
  }

  return withdrawing.get()->future();
}

void LeaderContenderProcess::cancel()
{
  if (!candidacy.isReady()) {
    // Nothing to cancel.
    if (withdrawing.isSome()) {
      withdrawing.get()->set(false);
    }
    return;
  }

  LOG(INFO) << "Now cancelling the membership: " << candidacy.get().id();

  group->cancel(candidacy.get())
    .onAny(defer(self(), &Self::cancelled, lambda::_1));
}

void LeaderContenderProcess::cancelled(const Future<bool>& result)
{
  CHECK(candidacy.isReady());
  LOG(INFO) << "Membership cancelled: " << candidacy.get().id();

  // Can be called as a result of either withdraw() or server side
  // expiration.
  CHECK(withdrawing.isSome() || watching.isSome());

  CHECK(!result.isDiscarded());

  if (result.isFailed()) {
    if (withdrawing.isSome()) {
      withdrawing.get()->fail(result.failure());
    }

    if (watching.isSome()) {
      watching.get()->fail(result.failure());
    }
  } else {
    if (withdrawing.isSome()) {
      withdrawing.get()->set(result);
    }

    if (watching.isSome()) {
      watching.get()->set(Nothing());
    }
  }
}

void LeaderContenderProcess::joined()
{
  CHECK(!candidacy.isDiscarded());

  // Cannot be watching because the candidacy is not obtained yet.
  CHECK_NONE(watching);

  CHECK_SOME(contending);

  if (candidacy.isFailed()) {
    // The promise 'withdrawing' will be set to false in cancel().
    contending.get()->fail(candidacy.failure());
    return;
  }

  if (withdrawing.isSome()) {
    LOG(INFO) << "Joined group after the contender started withdrawing";

    // The promise 'withdrawing' will be set to 'false' in subsequent
    // 'cancel()' call.
    return;
  }

  LOG(INFO) << "New candidate (id='" << candidacy.get().id()
            << "') has entered the contest for leadership";

  // Transition to 'watching' state.
  watching = new Promise<Nothing>();

  // Notify the client.
  if (contending.get()->set(watching.get()->future())) {
    // Continue to watch that our membership is not removed (if the
    // client still cares about it).
    candidacy.get().cancelled()
      .onAny(defer(self(), &Self::cancelled, lambda::_1));
  }
}

LeaderContender::LeaderContender(
    Group* group,
    const string& data,
    const Option<string>& label)
{
  process = new LeaderContenderProcess(group, data, label);
  spawn(process);
}

LeaderContender::~LeaderContender()
{
  terminate(process);
  process::wait(process);
  delete process;
}

Future<Future<Nothing> > LeaderContender::contend()
{
  return dispatch(process, &LeaderContenderProcess::contend);
}

Future<bool> LeaderContender::withdraw()
{
  return dispatch(process, &LeaderContenderProcess::withdraw);
}

} // namespace zookeeper {
<commit_msg>Replaced CHECK with CHECK_READY.<commit_after>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you 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 <string>

#include <mesos/zookeeper/contender.hpp>
#include <mesos/zookeeper/detector.hpp>
#include <mesos/zookeeper/group.hpp>

#include <process/check.hpp>
#include <process/defer.hpp>
#include <process/dispatch.hpp>
#include <process/future.hpp>
#include <process/id.hpp>

#include <stout/lambda.hpp>
#include <stout/option.hpp>

using namespace process;

using std::string;

namespace zookeeper {

class LeaderContenderProcess : public Process<LeaderContenderProcess>
{
public:
  LeaderContenderProcess(
      Group* group,
      const string& data,
      const Option<string>& label);

  virtual ~LeaderContenderProcess();

  // LeaderContender implementation.
  Future<Future<Nothing> > contend();
  Future<bool> withdraw();

protected:
  virtual void finalize();

private:
  // Invoked when we have joined the group (or failed to do so).
  void joined();

  // Invoked when the group membership is cancelled.
  void cancelled(const Future<bool>& result);

  // Helper for cancelling the Group membership.
  void cancel();

  Group* group;
  const string data;
  const Option<string> label;

  // The contender's state transitions from contending -> watching ->
  // withdrawing or contending -> withdrawing. Each state is
  // identified by the corresponding Option<Promise> being assigned.
  // Note that these Option<Promise>s are never reset to None once it
  // is assigned.

  // Holds the promise for the future for contend().
  Option<Promise<Future<Nothing> >*> contending;

  // Holds the promise for the inner future enclosed by contend()'s
  // result which is satisfied when the contender's candidacy is
  // lost.
  Option<Promise<Nothing>*> watching;

  // Holds the promise for the future for withdraw().
  Option<Promise<bool>*> withdrawing;

  // Stores the result for joined().
  Future<Group::Membership> candidacy;
};

LeaderContenderProcess::LeaderContenderProcess(
    Group* _group,
    const string& _data,
    const Option<string>& _label)
  : ProcessBase(ID::generate("leader-contender")),
    group(_group),
    data(_data),
    label(_label) {}

LeaderContenderProcess::~LeaderContenderProcess()
{
  if (contending.isSome()) {
    contending.get()->discard();
    delete contending.get();
    contending = None();
  }

  if (watching.isSome()) {
    watching.get()->discard();
    delete watching.get();
    watching = None();
  }

  if (withdrawing.isSome()) {
    withdrawing.get()->discard();
    delete withdrawing.get();
    withdrawing = None();
  }
}

void LeaderContenderProcess::finalize()
{
  // We do not wait for the result here because the Group keeps
  // retrying (even after the contender is destroyed) until it
  // succeeds so the old membership is eventually going to be
  // cancelled.
  // There is a tricky situation where the contender terminates after
  // it has contended but before it is notified of the obtained
  // membership. In this case the membership is not cancelled during
  // contender destruction. The client thus should use withdraw() to
  // wait for the membership to be first obtained and then cancelled.
  cancel();
}

Future<Future<Nothing> > LeaderContenderProcess::contend()
{
  if (contending.isSome()) {
    return Failure("Cannot contend more than once");
  }

  LOG(INFO) << "Joining the ZK group";
  candidacy = group->join(data, label);
  candidacy
    .onAny(defer(self(), &Self::joined));

  // Okay, we wait and see what unfolds.
  contending = new Promise<Future<Nothing> >();
  return contending.get()->future();
}

Future<bool> LeaderContenderProcess::withdraw()
{
  if (contending.isNone()) {
    // Nothing to withdraw because the contender has not contended.
    return false;
  }

  if (withdrawing.isSome()) {
    // Repeated calls to withdraw get the same result.
    return withdrawing.get();
  }

  withdrawing = new Promise<bool>();

  CHECK(!candidacy.isDiscarded());

  if (candidacy.isPending()) {
    // If we have not obtained the candidacy yet, we withdraw after
    // it is obtained.
    LOG(INFO) << "Withdraw requested before the candidacy is obtained; will "
              << "withdraw after it happens";
    candidacy.onAny(defer(self(), &Self::cancel));
  } else if (candidacy.isReady()) {
    cancel();
  } else {
    // We have failed to obtain the candidacy so we do not need to
    // cancel it.
    return false;
  }

  return withdrawing.get()->future();
}

void LeaderContenderProcess::cancel()
{
  if (!candidacy.isReady()) {
    // Nothing to cancel.
    if (withdrawing.isSome()) {
      withdrawing.get()->set(false);
    }
    return;
  }

  LOG(INFO) << "Now cancelling the membership: " << candidacy.get().id();

  group->cancel(candidacy.get())
    .onAny(defer(self(), &Self::cancelled, lambda::_1));
}

void LeaderContenderProcess::cancelled(const Future<bool>& result)
{
  CHECK_READY(candidacy);
  LOG(INFO) << "Membership cancelled: " << candidacy.get().id();

  // Can be called as a result of either withdraw() or server side
  // expiration.
  CHECK(withdrawing.isSome() || watching.isSome());

  CHECK(!result.isDiscarded());

  if (result.isFailed()) {
    if (withdrawing.isSome()) {
      withdrawing.get()->fail(result.failure());
    }

    if (watching.isSome()) {
      watching.get()->fail(result.failure());
    }
  } else {
    if (withdrawing.isSome()) {
      withdrawing.get()->set(result);
    }

    if (watching.isSome()) {
      watching.get()->set(Nothing());
    }
  }
}

void LeaderContenderProcess::joined()
{
  CHECK(!candidacy.isDiscarded());

  // Cannot be watching because the candidacy is not obtained yet.
  CHECK_NONE(watching);

  CHECK_SOME(contending);

  if (candidacy.isFailed()) {
    // The promise 'withdrawing' will be set to false in cancel().
    contending.get()->fail(candidacy.failure());
    return;
  }

  if (withdrawing.isSome()) {
    LOG(INFO) << "Joined group after the contender started withdrawing";

    // The promise 'withdrawing' will be set to 'false' in subsequent
    // 'cancel()' call.
    return;
  }

  LOG(INFO) << "New candidate (id='" << candidacy.get().id()
            << "') has entered the contest for leadership";

  // Transition to 'watching' state.
  watching = new Promise<Nothing>();

  // Notify the client.
  if (contending.get()->set(watching.get()->future())) {
    // Continue to watch that our membership is not removed (if the
    // client still cares about it).
    candidacy.get().cancelled()
      .onAny(defer(self(), &Self::cancelled, lambda::_1));
  }
}

LeaderContender::LeaderContender(
    Group* group,
    const string& data,
    const Option<string>& label)
{
  process = new LeaderContenderProcess(group, data, label);
  spawn(process);
}

LeaderContender::~LeaderContender()
{
  terminate(process);
  process::wait(process);
  delete process;
}

Future<Future<Nothing> > LeaderContender::contend()
{
  return dispatch(process, &LeaderContenderProcess::contend);
}

Future<bool> LeaderContender::withdraw()
{
  return dispatch(process, &LeaderContenderProcess::withdraw);
}

} // namespace zookeeper {
<|endoftext|>


<commit_before>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2011.  All rights reserved
 */
#include	"../StroikaPreComp.h"

#include	"Sleep.h"

#include	"ThreadPool.h"

using	namespace	Stroika::Foundation;
using	namespace	Stroika::Foundation::Execution;

class	ThreadPool::MyRunnable_ : public IRunnable {
	public:
		MyRunnable_ (ThreadPool& threadPool)
			: fThreadPool_ (threadPool)
			, fCurTask_ ()
			, fNextTask_ ()
			, fCurTaskUpdateCritSection_ ()
			{
			}

	public:
		ThreadPool::TaskType	GetCurrentTask () const
			{
				AutoCriticalSection critSect (fCurTaskUpdateCritSection_);
				// THIS CODE IS TOO SUBTLE - BUT BECAUSE OF HOW THIS IS CALLED - fNextTask_ will NEVER be in the middle of being updated during this code - so this test is OK
				// Caller is never in the middle of doing a WaitForNextTask - and because we have this lock - we aren't updateing fCurTask_ or fNextTask_ either
				Assert (fCurTask_.IsNull () or fNextTask_.IsNull ());	// one or both must be null
				return fCurTask_.IsNull ()? fNextTask_ : fCurTask_;
			}

	public:
		virtual	void	Run () override
			{
				// For NOW - allow ThreadAbort to just kill this thread. In the future - we may want to implement some sort of restartability

				// Keep grabbing new tasks, and running them
				while (true) {
					{
						fThreadPool_.WaitForNextTask_ (&fNextTask_);			// This will block INDEFINITELY until ThreadAbort throws out or we have a new task to run
						AutoCriticalSection critSect (fCurTaskUpdateCritSection_);
						Assert (not fNextTask_.IsNull ());
						Assert (fCurTask_.IsNull ());
						fCurTask_ = fNextTask_;
						fNextTask_.clear ();
						Assert (not fCurTask_.IsNull ());
						Assert (fNextTask_.IsNull ());
					}
					try {
						fCurTask_->Run ();
						fCurTask_.clear ();
					}
					catch (const ThreadAbortException&) {
						AutoCriticalSection critSect (fCurTaskUpdateCritSection_);
						fCurTask_.clear ();
						throw;	// cancel this thread
					}
					catch (...) {
						AutoCriticalSection critSect (fCurTaskUpdateCritSection_);
						fCurTask_.clear ();
						// other exceptions WARNING WITH DEBUG MESSAGE - but otehrwise - EAT/IGNORE
					}
				}
			}

	private:
		ThreadPool&					fThreadPool_;
		mutable CriticalSection		fCurTaskUpdateCritSection_;
		ThreadPool::TaskType		fCurTask_;
		ThreadPool::TaskType		fNextTask_;

	public:
		DECLARE_USE_BLOCK_ALLOCATION(MyRunnable_);
};

/*
 ********************************************************************************
 ********************************* Execution::ThreadPool ************************
 ********************************************************************************
 */
ThreadPool::ThreadPool (unsigned int nThreads)
	: fCriticalSection_ ()
	, fAborted_ (false)
	, fThreads_ ()
	, fTasks_ ()
	, fTasksAdded_ ()
{
	SetPoolSize (nThreads);
}

unsigned int	ThreadPool::GetPoolSize () const
{
	AutoCriticalSection	critSection (fCriticalSection_);
	return fThreads_.size ();
}

void	ThreadPool::SetPoolSize (unsigned int poolSize)
{
	Debug::TraceContextBumper ctx (TSTR ("ThreadPool::SetPoolSize"));
	Require (not fAborted_);
	AutoCriticalSection	critSection (fCriticalSection_);
	fThreads_.reserve (poolSize);
	while (poolSize > fThreads_.size ()) {
		fThreads_.push_back (mkThread_ ());
	}

	if (poolSize < fThreads_.size ()) {
		AssertNotImplemented ();

		// MUST STOP THREADS and WAIT FOR THEM TO BE DONE (OR STORE THEM SOMEPLACE ELSE - NO - I THINK MUST ABORTANDWAIT().  Unsure.
		// For now - just assert!!!

		// TODO:
		//		(1)	HOIRRIBLE - NOW
		fThreads_.resize (poolSize);	// remove some off the end. OK if they are running?
	}

}

void	ThreadPool::AddTask (const TaskType& task)
{
	//Debug::TraceContextBumper ctx (TSTR ("ThreadPool::AddTask"));
	Require (not fAborted_);
	{
		AutoCriticalSection	critSection (fCriticalSection_);
		fTasks_.push_back (task);
	}

	// Notify any waiting threads to wakeup and claim the next task
	fTasksAdded_.Set ();
}

void	ThreadPool::AbortTask (const TaskType& task, Time::DurationSecondsType timeout)
{
	Debug::TraceContextBumper ctx (TSTR ("ThreadPool::AbortTask"));
	{
		// First see if its in the Q
		AutoCriticalSection	critSection (fCriticalSection_);
		for (list<TaskType>::iterator i = fTasks_.begin (); i != fTasks_.end (); ++i) {
			if (*i == task) {
				fTasks_.erase (i);
				return;
			}
		}
	}

	// If we got here - its NOT in the task Q, so maybe its already running.
	//
	//

	// TODO:
	//		We walk the list of existing threads and ask each one if its (indirected - running task) is the given one and abort that task.
	//		But that requires we can RESTART an ABORTED thread (or that we remove it from the list - maybe thats better). THat COULD be OK
	//		actually since it involves on API changes and makes sense. The only slight issue is a peformace one but probably for soemthing
	//		quite rare.
	//
	//		Anyhow SB OK for now to just not allow aborting a task which has already started....
	Thread	thread2Kill;
	{
		AutoCriticalSection	critSection (fCriticalSection_);
		for (vector<Thread>::iterator i = fThreads_.begin (); i != fThreads_.end (); ++i) {
			SharedPtr<IRunnable>	tr	=	i->GetRunnable ();
			Assert (dynamic_cast<MyRunnable_*> (tr.get ()) != nullptr);
			SharedPtr<IRunnable>	ct	=	dynamic_cast<MyRunnable_&> (*tr.get ()).GetCurrentTask ();
			if (task == ct) {
				thread2Kill	=	*i;
				*i = mkThread_ ();
				break;
			}
		}
	}
	if (not thread2Kill.GetStatus () != Thread::eNull) {
		thread2Kill.AbortAndWaitForDone (timeout);
	}
}

bool	ThreadPool::IsPresent (const TaskType& task) const
{
	{
		// First see if its in the Q
		AutoCriticalSection	critSection (fCriticalSection_);
		for (list<TaskType>::const_iterator i = fTasks_.begin (); i != fTasks_.end (); ++i) {
			if (*i == task) {
				return true;
			}
		}
	}
	return IsRunning (task);
}

bool	ThreadPool::IsRunning (const TaskType& task) const
{
	Require (not task.IsNull ());
	{
		AutoCriticalSection	critSection (fCriticalSection_);
		for (vector<Thread>::const_iterator i = fThreads_.begin (); i != fThreads_.end (); ++i) {
			SharedPtr<IRunnable>	tr	=	i->GetRunnable ();
			Assert (dynamic_cast<MyRunnable_*> (tr.get ()) != nullptr);
			SharedPtr<IRunnable>	rTask	=	dynamic_cast<MyRunnable_&> (*tr.get ()).GetCurrentTask ();
			if (task == rTask) {
				return true;
			}
		}
	}
	return false;
}

void	ThreadPool::WaitForTask (const TaskType& task, Time::DurationSecondsType timeout) const
{
	Debug::TraceContextBumper ctx (TSTR ("ThreadPool::WaitForTask"));
	// Inefficient / VERY SLOPPY impl
	Time::DurationSecondsType	endAt	=	timeout + Time::GetTickCount ();
	while (true) {
		if (not IsPresent (task)) {
			return;
		}
		Time::DurationSecondsType	remaining	=	timeout - Time::GetTickCount ();
		if (remaining <= 0.0) {
			DoThrow (WaitTimedOutException ());
		}
		Execution::Sleep (min (remaining, 1.0));
	}
}

vector<ThreadPool::TaskType>	ThreadPool::GetTasks () const
{
	vector<ThreadPool::TaskType>	result;
	{
		AutoCriticalSection	critSection (fCriticalSection_);
		result.reserve (fTasks_.size () + fThreads_.size ());
		result.insert (result.begin (), fTasks_.begin (), fTasks_.end ());			// copy pending tasks
		for (vector<Thread>::const_iterator i = fThreads_.begin (); i != fThreads_.end (); ++i) {
			SharedPtr<IRunnable>	tr	=	i->GetRunnable ();
			Assert (dynamic_cast<MyRunnable_*> (tr.get ()) != nullptr);
			SharedPtr<IRunnable>	task	=	dynamic_cast<MyRunnable_&> (*tr.get ()).GetCurrentTask ();
			if (not task.IsNull ()) {
				result.push_back (task);
			}
		}
	}
	return result;
}

vector<ThreadPool::TaskType>	ThreadPool::GetRunningTasks () const
{
	vector<ThreadPool::TaskType>	result;
	{
		AutoCriticalSection	critSection (fCriticalSection_);
		result.reserve (fThreads_.size ());
		for (vector<Thread>::const_iterator i = fThreads_.begin (); i != fThreads_.end (); ++i) {
			SharedPtr<IRunnable>	tr	=	i->GetRunnable ();
			Assert (dynamic_cast<MyRunnable_*> (tr.get ()) != nullptr);
			SharedPtr<IRunnable>	task	=	dynamic_cast<MyRunnable_&> (*tr.get ()).GetCurrentTask ();
			if (not task.IsNull ()) {
				result.push_back (task);
			}
		}
	}
	return result;
}

size_t	ThreadPool::GetTasksCount () const
{
	size_t	count	=	0;
	{
		// First see if its in the Q
		AutoCriticalSection	critSection (fCriticalSection_);
		count += fTasks_.size ();
		for (vector<Thread>::const_iterator i = fThreads_.begin (); i != fThreads_.end (); ++i) {
			SharedPtr<IRunnable>	tr	=	i->GetRunnable ();
			Assert (dynamic_cast<MyRunnable_*> (tr.get ()) != nullptr);
			SharedPtr<IRunnable>	task	=	dynamic_cast<MyRunnable_&> (*tr.get ()).GetCurrentTask ();
			if (not task.IsNull ()) {
				count++;
			}
		}
	}
	return count;
}

void	ThreadPool::WaitForDone (Time::DurationSecondsType timeout) const
{
	Debug::TraceContextBumper ctx (TSTR ("ThreadPool::WaitForDone"));
	Require (fAborted_);
	{
		Time::DurationSecondsType	endAt	=	timeout + Time::GetTickCount ();
		AutoCriticalSection	critSection (fCriticalSection_);
		for (vector<Thread>::const_iterator i = fThreads_.begin (); i != fThreads_.end (); ++i) {
			i->WaitForDone (endAt - Time::GetTickCount ());
		}
	}
}

void	ThreadPool::Abort ()
{
	Debug::TraceContextBumper ctx (TSTR ("ThreadPool::Abort"));
	fAborted_ = true;
	{
		// First see if its in the Q
		AutoCriticalSection	critSection (fCriticalSection_);
		fTasks_.clear ();
		for (vector<Thread>::iterator i = fThreads_.begin (); i != fThreads_.end (); ++i) {
			i->Abort ();
		}
	}
}

void	ThreadPool::AbortAndWaitForDone (Time::DurationSecondsType timeout)
{
	Abort ();
	WaitForDone (timeout);
}

// THIS is called NOT from 'this' - but from the context of an OWNED thread of the pool
void	ThreadPool::WaitForNextTask_ (TaskType* result)
{
	RequireNotNull (result);
	if (fAborted_) {
		Execution::DoThrow (ThreadAbortException ());
	}

	while (true) {
		{
			AutoCriticalSection	critSection (fCriticalSection_);
			if (not fTasks_.empty ()) {
				*result	=	fTasks_.front ();
				fTasks_.pop_front ();
				DbgTrace ("ThreadPool::WaitForNextTask_ () is starting a new task");
				return;
			}
		}

		// Prevent spinwaiting... This event is SET when any new item arrives
		//DbgTrace ("ThreadPool::WaitForNextTask_ () - about to wait for added tasks");
		fTasksAdded_.Wait ();
		//DbgTrace ("ThreadPool::WaitForNextTask_ () - completed wait for added tasks");
	}
}

Thread		ThreadPool::mkThread_ ()
{
	Thread	t	=	Thread (SharedPtr<IRunnable> (new ThreadPool::MyRunnable_ (*this)));		// ADD MY THREADOBJ
	t.SetThreadName (L"Thread Pool");
	t.Start ();
	return t;
}
<commit_msg>comments<commit_after>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2011.  All rights reserved
 */
#include	"../StroikaPreComp.h"

#include	"Sleep.h"

#include	"ThreadPool.h"

using	namespace	Stroika::Foundation;
using	namespace	Stroika::Foundation::Execution;

class	ThreadPool::MyRunnable_ : public IRunnable {
	public:
		MyRunnable_ (ThreadPool& threadPool)
			: fThreadPool_ (threadPool)
			, fCurTask_ ()
			, fNextTask_ ()
			, fCurTaskUpdateCritSection_ ()
			{
			}

	public:
		ThreadPool::TaskType	GetCurrentTask () const
			{
				AutoCriticalSection critSect (fCurTaskUpdateCritSection_);
				// THIS CODE IS TOO SUBTLE - BUT BECAUSE OF HOW THIS IS CALLED - fNextTask_ will NEVER be in the middle of being updated during this code - so this test is OK
				// Caller is never in the middle of doing a WaitForNextTask - and because we have this lock - we aren't updateing fCurTask_ or fNextTask_ either
				Assert (fCurTask_.IsNull () or fNextTask_.IsNull ());	// one or both must be null
				return fCurTask_.IsNull ()? fNextTask_ : fCurTask_;
			}

	public:
		virtual	void	Run () override
			{
				// For NOW - allow ThreadAbort to just kill this thread. In the future - we may want to implement some sort of restartability

				// Keep grabbing new tasks, and running them
				while (true) {
					{
						fThreadPool_.WaitForNextTask_ (&fNextTask_);			// This will block INDEFINITELY until ThreadAbort throws out or we have a new task to run
						AutoCriticalSection critSect (fCurTaskUpdateCritSection_);
						Assert (not fNextTask_.IsNull ());
						Assert (fCurTask_.IsNull ());
						fCurTask_ = fNextTask_;
						fNextTask_.clear ();
						Assert (not fCurTask_.IsNull ());
						Assert (fNextTask_.IsNull ());
					}
					try {
						fCurTask_->Run ();
						fCurTask_.clear ();
					}
					catch (const ThreadAbortException&) {
						AutoCriticalSection critSect (fCurTaskUpdateCritSection_);
						fCurTask_.clear ();
						throw;	// cancel this thread
					}
					catch (...) {
						AutoCriticalSection critSect (fCurTaskUpdateCritSection_);
						fCurTask_.clear ();
						// other exceptions WARNING WITH DEBUG MESSAGE - but otehrwise - EAT/IGNORE
					}
				}
			}

	private:
		ThreadPool&					fThreadPool_;
		mutable CriticalSection		fCurTaskUpdateCritSection_;
		ThreadPool::TaskType		fCurTask_;
		ThreadPool::TaskType		fNextTask_;

	public:
		DECLARE_USE_BLOCK_ALLOCATION(MyRunnable_);
};

/*
 ********************************************************************************
 ****************************** Execution::ThreadPool ***************************
 ********************************************************************************
 */
ThreadPool::ThreadPool (unsigned int nThreads)
	: fCriticalSection_ ()
	, fAborted_ (false)
	, fThreads_ ()
	, fTasks_ ()
	, fTasksAdded_ ()
{
	SetPoolSize (nThreads);
}

unsigned int	ThreadPool::GetPoolSize () const
{
	AutoCriticalSection	critSection (fCriticalSection_);
	return fThreads_.size ();
}

void	ThreadPool::SetPoolSize (unsigned int poolSize)
{
	Debug::TraceContextBumper ctx (TSTR ("ThreadPool::SetPoolSize"));
	Require (not fAborted_);
	AutoCriticalSection	critSection (fCriticalSection_);
	fThreads_.reserve (poolSize);
	while (poolSize > fThreads_.size ()) {
		fThreads_.push_back (mkThread_ ());
	}

	if (poolSize < fThreads_.size ()) {
		AssertNotImplemented ();

		// MUST STOP THREADS and WAIT FOR THEM TO BE DONE (OR STORE THEM SOMEPLACE ELSE - NO - I THINK MUST ABORTANDWAIT().  Unsure.
		// For now - just assert!!!

		// TODO:
		//		(1)	HOIRRIBLE - NOW
		fThreads_.resize (poolSize);	// remove some off the end. OK if they are running?
	}
}

void	ThreadPool::AddTask (const TaskType& task)
{
	//Debug::TraceContextBumper ctx (TSTR ("ThreadPool::AddTask"));
	Require (not fAborted_);
	{
		AutoCriticalSection	critSection (fCriticalSection_);
		fTasks_.push_back (task);
	}

	// Notify any waiting threads to wakeup and claim the next task
	fTasksAdded_.Set ();
}

void	ThreadPool::AbortTask (const TaskType& task, Time::DurationSecondsType timeout)
{
	Debug::TraceContextBumper ctx (TSTR ("ThreadPool::AbortTask"));
	{
		// First see if its in the Q
		AutoCriticalSection	critSection (fCriticalSection_);
		for (list<TaskType>::iterator i = fTasks_.begin (); i != fTasks_.end (); ++i) {
			if (*i == task) {
				fTasks_.erase (i);
				return;
			}
		}
	}

	// If we got here - its NOT in the task Q, so maybe its already running.
	//
	//

	// TODO:
	//		We walk the list of existing threads and ask each one if its (indirected - running task) is the given one and abort that task.
	//		But that requires we can RESTART an ABORTED thread (or that we remove it from the list - maybe thats better). THat COULD be OK
	//		actually since it involves on API changes and makes sense. The only slight issue is a peformace one but probably for soemthing
	//		quite rare.
	//
	//		Anyhow SB OK for now to just not allow aborting a task which has already started....
	Thread	thread2Kill;
	{
		AutoCriticalSection	critSection (fCriticalSection_);
		for (vector<Thread>::iterator i = fThreads_.begin (); i != fThreads_.end (); ++i) {
			SharedPtr<IRunnable>	tr	=	i->GetRunnable ();
			Assert (dynamic_cast<MyRunnable_*> (tr.get ()) != nullptr);
			SharedPtr<IRunnable>	ct	=	dynamic_cast<MyRunnable_&> (*tr.get ()).GetCurrentTask ();
			if (task == ct) {
				thread2Kill	=	*i;
				*i = mkThread_ ();
				break;
			}
		}
	}
	if (not thread2Kill.GetStatus () != Thread::eNull) {
		thread2Kill.AbortAndWaitForDone (timeout);
	}
}

bool	ThreadPool::IsPresent (const TaskType& task) const
{
	{
		// First see if its in the Q
		AutoCriticalSection	critSection (fCriticalSection_);
		for (list<TaskType>::const_iterator i = fTasks_.begin (); i != fTasks_.end (); ++i) {
			if (*i == task) {
				return true;
			}
		}
	}
	return IsRunning (task);
}

bool	ThreadPool::IsRunning (const TaskType& task) const
{
	Require (not task.IsNull ());
	{
		AutoCriticalSection	critSection (fCriticalSection_);
		for (vector<Thread>::const_iterator i = fThreads_.begin (); i != fThreads_.end (); ++i) {
			SharedPtr<IRunnable>	tr	=	i->GetRunnable ();
			Assert (dynamic_cast<MyRunnable_*> (tr.get ()) != nullptr);
			SharedPtr<IRunnable>	rTask	=	dynamic_cast<MyRunnable_&> (*tr.get ()).GetCurrentTask ();
			if (task == rTask) {
				return true;
			}
		}
	}
	return false;
}

void	ThreadPool::WaitForTask (const TaskType& task, Time::DurationSecondsType timeout) const
{
	Debug::TraceContextBumper ctx (TSTR ("ThreadPool::WaitForTask"));
	// Inefficient / VERY SLOPPY impl
	Time::DurationSecondsType	endAt	=	timeout + Time::GetTickCount ();
	while (true) {
		if (not IsPresent (task)) {
			return;
		}
		Time::DurationSecondsType	remaining	=	timeout - Time::GetTickCount ();
		if (remaining <= 0.0) {
			DoThrow (WaitTimedOutException ());
		}
		Execution::Sleep (min (remaining, 1.0));
	}
}

vector<ThreadPool::TaskType>	ThreadPool::GetTasks () const
{
	vector<ThreadPool::TaskType>	result;
	{
		AutoCriticalSection	critSection (fCriticalSection_);
		result.reserve (fTasks_.size () + fThreads_.size ());
		result.insert (result.begin (), fTasks_.begin (), fTasks_.end ());			// copy pending tasks
		for (vector<Thread>::const_iterator i = fThreads_.begin (); i != fThreads_.end (); ++i) {
			SharedPtr<IRunnable>	tr	=	i->GetRunnable ();
			Assert (dynamic_cast<MyRunnable_*> (tr.get ()) != nullptr);
			SharedPtr<IRunnable>	task	=	dynamic_cast<MyRunnable_&> (*tr.get ()).GetCurrentTask ();
			if (not task.IsNull ()) {
				result.push_back (task);
			}
		}
	}
	return result;
}

vector<ThreadPool::TaskType>	ThreadPool::GetRunningTasks () const
{
	vector<ThreadPool::TaskType>	result;
	{
		AutoCriticalSection	critSection (fCriticalSection_);
		result.reserve (fThreads_.size ());
		for (vector<Thread>::const_iterator i = fThreads_.begin (); i != fThreads_.end (); ++i) {
			SharedPtr<IRunnable>	tr	=	i->GetRunnable ();
			Assert (dynamic_cast<MyRunnable_*> (tr.get ()) != nullptr);
			SharedPtr<IRunnable>	task	=	dynamic_cast<MyRunnable_&> (*tr.get ()).GetCurrentTask ();
			if (not task.IsNull ()) {
				result.push_back (task);
			}
		}
	}
	return result;
}

size_t	ThreadPool::GetTasksCount () const
{
	size_t	count	=	0;
	{
		// First see if its in the Q
		AutoCriticalSection	critSection (fCriticalSection_);
		count += fTasks_.size ();
		for (vector<Thread>::const_iterator i = fThreads_.begin (); i != fThreads_.end (); ++i) {
			SharedPtr<IRunnable>	tr	=	i->GetRunnable ();
			Assert (dynamic_cast<MyRunnable_*> (tr.get ()) != nullptr);
			SharedPtr<IRunnable>	task	=	dynamic_cast<MyRunnable_&> (*tr.get ()).GetCurrentTask ();
			if (not task.IsNull ()) {
				count++;
			}
		}
	}
	return count;
}

void	ThreadPool::WaitForDone (Time::DurationSecondsType timeout) const
{
	Debug::TraceContextBumper ctx (TSTR ("ThreadPool::WaitForDone"));
	Require (fAborted_);
	{
		Time::DurationSecondsType	endAt	=	timeout + Time::GetTickCount ();
		AutoCriticalSection	critSection (fCriticalSection_);
		for (vector<Thread>::const_iterator i = fThreads_.begin (); i != fThreads_.end (); ++i) {
			i->WaitForDone (endAt - Time::GetTickCount ());
		}
	}
}

void	ThreadPool::Abort ()
{
	Debug::TraceContextBumper ctx (TSTR ("ThreadPool::Abort"));
	fAborted_ = true;
	{
		// First see if its in the Q
		AutoCriticalSection	critSection (fCriticalSection_);
		fTasks_.clear ();
		for (vector<Thread>::iterator i = fThreads_.begin (); i != fThreads_.end (); ++i) {
			i->Abort ();
		}
	}
}

void	ThreadPool::AbortAndWaitForDone (Time::DurationSecondsType timeout)
{
	Abort ();
	WaitForDone (timeout);
}

// THIS is called NOT from 'this' - but from the context of an OWNED thread of the pool
void	ThreadPool::WaitForNextTask_ (TaskType* result)
{
	RequireNotNull (result);
	if (fAborted_) {
		Execution::DoThrow (ThreadAbortException ());
	}

	while (true) {
		{
			AutoCriticalSection	critSection (fCriticalSection_);
			if (not fTasks_.empty ()) {
				*result	=	fTasks_.front ();
				fTasks_.pop_front ();
				DbgTrace ("ThreadPool::WaitForNextTask_ () is starting a new task");
				return;
			}
		}

		// Prevent spinwaiting... This event is SET when any new item arrives
		//DbgTrace ("ThreadPool::WaitForNextTask_ () - about to wait for added tasks");
		fTasksAdded_.Wait ();
		//DbgTrace ("ThreadPool::WaitForNextTask_ () - completed wait for added tasks");
	}
}

Thread		ThreadPool::mkThread_ ()
{
	Thread	t	=	Thread (SharedPtr<IRunnable> (new ThreadPool::MyRunnable_ (*this)));		// ADD MY THREADOBJ
	t.SetThreadName (L"Thread Pool");
	t.Start ();
	return t;
}
<|endoftext|>