summaryrefslogtreecommitdiff
path: root/indra/llcommon/threadpool.h
blob: 7ced7fbf9fc676a46fffc10d50031b42fe1ab5f1 (plain)
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
/**
 * @file   threadpool.h
 * @author Nat Goodspeed
 * @date   2021-10-21
 * @brief  ThreadPool configures a WorkQueue along with a pool of threads to
 *         service it.
 * 
 * $LicenseInfo:firstyear=2021&license=viewerlgpl$
 * Copyright (c) 2021, Linden Research, Inc.
 * $/LicenseInfo$
 */

#if ! defined(LL_THREADPOOL_H)
#define LL_THREADPOOL_H

#include "llcoros.h"
#include "threadpool_fwd.h"
#include "workqueue.h"
#include <memory>                   // std::unique_ptr
#include <string>
#include <thread>
#include <utility>                  // std::pair
#include <vector>

namespace LL
{

    class ThreadPoolBase: public LLInstanceTracker<ThreadPoolBase, std::string>
    {
    private:
        using super = LLInstanceTracker<ThreadPoolBase, std::string>;

    public:
        /**
         * Pass ThreadPoolBase a string name. This can be used to look up the
         * relevant WorkQueue.
         *
         * The number of threads you pass sets the compile-time default. But
         * if the user has overridden the LLSD map in the "ThreadPoolSizes"
         * setting with a key matching this ThreadPool name, that setting
         * overrides this parameter.
         */
        ThreadPoolBase(const std::string& name, size_t threads,
                       WorkQueueBase* queue, bool auto_shutdown = true);
        virtual ~ThreadPoolBase();

        /**
         * Launch the ThreadPool. Until this call, a constructed ThreadPool
         * launches no threads. That permits coders to derive from ThreadPool,
         * or store it as a member of some other class, but refrain from
         * launching it until all other construction is complete.
         */
        void start();

        /**
         * ThreadPool listens for application shutdown events. Call close() to
         * shut down this ThreadPool early.
         */
        virtual void close();

        std::string getName() const { return mName; }
        size_t getWidth() const { return mThreads.size(); }

        /**
         * Override run() if you need special processing. The default run()
         * implementation simply calls WorkQueue::runUntilClose().
         */
        virtual void run();

        /**
         * getConfiguredWidth() returns the setting, if any, for the specified
         * ThreadPool name. Returns dft if the "ThreadPoolSizes" map does not
         * contain the specified name.
         */
        static
        size_t getConfiguredWidth(const std::string& name, size_t dft=0);

        /**
         * This getWidth() returns the width of the instantiated ThreadPool
         * with the specified name, if any. If no instance exists, returns its
         * getConfiguredWidth() if any. If there's no instance and no relevant
         * override, return dft. Presumably dft should match the threads
         * parameter passed to the ThreadPool constructor call that will
         * eventually instantiate the ThreadPool with that name.
         */
        static
        size_t getWidth(const std::string& name, size_t dft);

    protected:
        std::unique_ptr<WorkQueueBase> mQueue;
        std::vector<std::pair<std::string, std::thread>> mThreads;
        bool mAutomaticShutdown;

    private:
        void run(const std::string& name);

        std::string mName;
        size_t mThreadCount;
        LLTempBoundListener mStopListener;
    };

    /**
     * Specialize with WorkQueue or, for timestamped tasks, WorkSchedule
     */
    template <class QUEUE>
    struct ThreadPoolUsing: public ThreadPoolBase
    {
        using queue_t = QUEUE;

        /**
         * Pass ThreadPoolUsing a string name. This can be used to look up the
         * relevant WorkQueue.
         *
         * The number of threads you pass sets the compile-time default. But
         * if the user has overridden the LLSD map in the "ThreadPoolSizes"
         * setting with a key matching this ThreadPool name, that setting
         * overrides this parameter.
         *
         * Pass an explicit capacity to limit the size of the queue.
         * Constraining the queue can cause a submitter to block. Do not
         * constrain any ThreadPool accepting work from the main thread.
         */
        ThreadPoolUsing(const std::string& name,
                        size_t threads=1,
                        size_t capacity=1024*1024,
                        bool auto_shutdown = true):
            ThreadPoolBase(name, threads, new queue_t(name, capacity), auto_shutdown)
        {}
        ~ThreadPoolUsing() override {}

        /**
         * obtain a non-const reference to the specific WorkQueue subclass to
         * post work to it
         */
        queue_t& getQueue() { return static_cast<queue_t&>(*mQueue); }
    };

    /// ThreadPool is shorthand for using the simpler WorkQueue
    using ThreadPool = ThreadPoolUsing<WorkQueue>;

} // namespace LL

#endif /* ! defined(LL_THREADPOOL_H) */