File: vtkSMPThreadPool.h

package info (click to toggle)
vtk9 9.5.2%2Bdfsg3-6
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 205,984 kB
  • sloc: cpp: 2,336,570; ansic: 327,116; python: 111,200; yacc: 4,104; java: 3,977; sh: 3,032; xml: 2,771; perl: 2,189; lex: 1,787; makefile: 181; javascript: 165; objc: 153; tcl: 59
file content (181 lines) | stat: -rw-r--r-- 5,858 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-License-Identifier: BSD-3-Clause
// .NAME vtkSMPThreadPool - A thread pool implementation using std::thread
//
// .SECTION Description
// vtkSMPThreadPool class creates a thread pool of std::thread, the number
// of thread must be specified at the initialization of the class.
// The DoJob() method is used attributes the job to a free thread, if all
// threads are working, the job is kept in a queue. Note that vtkSMPThreadPool
// destructor joins threads and finish the jobs in the queue.

#ifndef vtkSMPThreadPool_h
#define vtkSMPThreadPool_h

#include "vtkCommonCoreModule.h" // For export macro
#include "vtkSystemIncludes.h"

#include <atomic>     // For std::atomic
#include <functional> // For std::function
#include <mutex>      // For std::unique_lock
#include <thread>     // For std::thread
#include <vector>     // For std::vector

namespace vtk
{
namespace detail
{
namespace smp
{
VTK_ABI_NAMESPACE_BEGIN

/**
 * @brief Internal thread pool implementation used in SMP functions
 *
 * This class is designed to be a Singleton thread pool, but local pool can be allocated too.
 * This thread pool use a Proxy system that is used to allocate a certain amount of threads from
 * the pool, which enable support for SMP local scopes.
 * You need to have a Proxy to submit job to the pool.
 */
class VTKCOMMONCORE_EXPORT vtkSMPThreadPool
{
  // Internal data structures
  struct ThreadJob;
  struct ThreadData;
  struct ProxyThreadData;
  struct ProxyData;

public:
  /**
   * @brief Proxy class used to submit work to the thread pool.
   *
   * A proxy act like a single thread pool, but it submits work to its parent thread pool.
   * Using a proxy from multiple threads at the same time is undefined behaviour.
   *
   * Note: Even if nothing prevent a proxy to be moved around threads, it should either be used in
   * the creating thread or in a thread that does not belong to the pool, otherwise it may create a
   * deadlock when joining.
   */
  class VTKCOMMONCORE_EXPORT Proxy final
  {
  public:
    /**
     * @brief Destructor
     *
     * Join must have been called since the last DoJob before destroying the proxy.
     */
    ~Proxy();
    Proxy(const Proxy&) = delete;
    Proxy& operator=(const Proxy&) = delete;
    Proxy(Proxy&&) noexcept;
    Proxy& operator=(Proxy&&) noexcept;

    /**
     * @brief Blocks calling thread until all jobs are done.
     *
     * Note: nested proxies may execute jobs on calling thread during this function to maximize
     * parallelism.
     */
    void Join();

    /**
     * @brief Add a job to the thread pool queue
     */
    void DoJob(std::function<void()> job);

    /**
     * @brief Get a reference on all system threads used by this proxy
     */
    std::vector<std::reference_wrapper<std::thread>> GetThreads() const;

    /**
     * @brief Return true is this proxy is allocated from a thread that does not belong to the pool
     */
    bool IsTopLevel() const noexcept;

  private:
    friend class vtkSMPThreadPool; // Only the thread pool can construct this object

    Proxy(std::unique_ptr<ProxyData>&& data);

    std::unique_ptr<ProxyData> Data;
  };

  vtkSMPThreadPool();
  ~vtkSMPThreadPool();
  vtkSMPThreadPool(const vtkSMPThreadPool&) = delete;
  vtkSMPThreadPool& operator=(const vtkSMPThreadPool&) = delete;

  /**
   * @brief Create a proxy
   *
   * Create a proxy that will use at most threadCount thread of the thread pool.
   * Proxy act as a thread pool on its own, but will in practice submit its work to this pool,
   * this prevent threads to be created every time a SMP function is called.
   *
   * If the current thread not in the pool, it will create a "top-level" proxy, otherwise it will
   * create a nested proxy. A nested proxy will never use a thread that is already in use by its
   * "parent" proxies to prevent deadlocks. It means that nested paralism may have a more limited
   * amount of threads.
   *
   * @param threadCount max amount of thread to use. If 0, uses the number of thread of the pool.
   * If greater than the number of thread of the pool, uses the number of thread of the pool.
   * @return A proxy.
   */
  Proxy AllocateThreads(std::size_t threadCount = 0);

  /**
   * Value returned by `GetThreadID` when called by a thread that does not belong to the pool.
   */
  static constexpr std::size_t ExternalThreadID = 1;

  /**
   * @brief Get caller proxy thread virtual ID
   *
   * This function must be called from a proxy thread.
   * If this function is called from non proxy thread, returns `ExternalThreadID`.
   * Valid proxy thread virtual ID are always >= 2
   */
  std::size_t GetThreadId() const noexcept;

  /**
   * @brief Returns true when called from a proxy thread, false otherwise.
   */
  bool IsParallelScope() const noexcept;

  /**
   * @brief Returns true for a single proxy thread, false for the others.
   */
  bool GetSingleThread() const;

  /**
   * @brief Returns number of system thread used by the thread pool.
   */
  std::size_t ThreadCount() const noexcept;

private:
  // static because also used by proxy
  static void RunJob(ThreadData& data, std::size_t jobIndex, std::unique_lock<std::mutex>& lock);

  ThreadData* GetCallerThreadData() const noexcept;

  std::thread MakeThread();
  void FillThreadsForNestedProxy(ProxyData* proxy, std::size_t maxCount);
  std::size_t GetNextThreadId() noexcept;

  std::atomic<bool> Initialized{};
  std::atomic<bool> Joining{};
  std::vector<std::unique_ptr<ThreadData>> Threads; // Thread pool, fixed size
  std::atomic<std::size_t> NextProxyThreadId{ 1 };

public:
  static vtkSMPThreadPool& GetInstance();
};

VTK_ABI_NAMESPACE_END
} // namespace smp
} // namespace detail
} // namespace vtk

#endif
/* VTK-HeaderTest-Exclude: vtkSMPThreadPool.h */