MMikaël CapelleApply clang-format.
d13f6bb8创建于 2023年7月9日历史提交
#ifndef ENV_ENVFS_H
#define ENV_ENVFS_H

#include "thread_utils.h"
#include <thread>

namespace env
{

struct File
{
  std::wstring name;
  std::wstring lcname;
  FILETIME lastModified;
  uint64_t size;

  File(std::wstring_view name, FILETIME ft, uint64_t size);
};

struct Directory
{
  std::wstring name;
  std::wstring lcname;

  std::vector<Directory> dirs;
  std::vector<File> files;

  Directory();
  Directory(std::wstring_view name);
};

template <class T>
class ThreadPool
{
public:
  ThreadPool(std::size_t max = 1) { setMax(max); }

  ~ThreadPool() { stopAndJoin(); }

  void setMax(std::size_t n) { m_threads.resize(n); }

  void stopAndJoin()
  {
    for (auto& ti : m_threads) {
      ti.stop = true;
      ti.wakeup();
    }

    for (auto& ti : m_threads) {
      if (ti.thread.joinable()) {
        ti.thread.join();
      }
    }
  }

  void waitForAll()
  {
    for (;;) {
      bool done = true;

      for (auto& ti : m_threads) {
        if (ti.busy) {
          done = false;
          break;
        }
      }

      if (done) {
        break;
      }

      std::this_thread::sleep_for(std::chrono::milliseconds(1));
    }
  }

  T& request()
  {
    if (m_threads.empty()) {
      std::terminate();
    }

    for (;;) {
      for (auto& ti : m_threads) {
        bool expected = false;

        if (ti.busy.compare_exchange_strong(expected, true)) {
          ti.wakeup();
          return ti.o;
        }
      }

      std::this_thread::sleep_for(std::chrono::milliseconds(1));
    }
  }

  template <class F>
  void forEach(F&& f)
  {
    for (auto& ti : m_threads) {
      f(ti.o);
    }
  }

private:
  struct ThreadInfo
  {
    std::thread thread;
    std::atomic<bool> busy;
    T o;

    std::condition_variable cv;
    std::mutex mutex;
    bool ready;

    std::atomic<bool> stop;

    ThreadInfo() : busy(true), ready(false), stop(false)
    {
      thread = MOShared::startSafeThread([&] {
        run();
      });
    }

    ~ThreadInfo()
    {
      if (thread.joinable()) {
        stop = true;
        wakeup();
        thread.join();
      }
    }

    void wakeup()
    {
      {
        std::scoped_lock lock(mutex);
        ready = true;
      }

      cv.notify_one();
    }

    void run()
    {
      busy = false;

      while (!stop) {
        std::unique_lock lock(mutex);
        cv.wait(lock, [&] {
          return ready;
        });

        if (stop) {
          break;
        }

        o.run();

        ready = false;
        busy  = false;
      }
    }
  };

  std::list<ThreadInfo> m_threads;
};

using DirStartF = void(void*, std::wstring_view);
using DirEndF   = void(void*, std::wstring_view);
using FileF     = void(void*, std::wstring_view, FILETIME, uint64_t);

void setHandleCloserThreadCount(std::size_t n);

class DirectoryWalker
{
public:
  void forEachEntry(const std::wstring& path, void* cx, DirStartF* dirStartF,
                    DirEndF* dirEndF, FileF* fileF);

private:
  std::vector<std::unique_ptr<unsigned char[]>> m_buffers;
};

void forEachEntry(const std::wstring& path, void* cx, DirStartF* dirStartF,
                  DirEndF* dirEndF, FileF* fileF);

Directory getFilesAndDirs(const std::wstring& path);
Directory getFilesAndDirsWithFind(const std::wstring& path);

}  // namespace env

#endif  // ENV_ENVFS_H