summaryrefslogtreecommitdiffstats
path: root/xpcom/threads/SharedThreadPool.h
blob: f2c7068051252710c82f3c128f0ab0ab11bb0f33 (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
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef SharedThreadPool_h_
#define SharedThreadPool_h_

#include <utility>
#include <type_traits>
#include "mozilla/AlreadyAddRefed.h"
#include "mozilla/RefCountType.h"
#include "nsCOMPtr.h"
#include "nsID.h"
#include "nsIThreadPool.h"
#include "nsString.h"
#include "nscore.h"

class nsIRunnable;

namespace mozilla {

// Wrapper that makes an nsIThreadPool a singleton, and provides a
// consistent threadsafe interface to get instances. Callers simply get a
// SharedThreadPool by the name of its nsIThreadPool. All get requests of
// the same name get the same SharedThreadPool. Users must store a reference
// to the pool, and when the last reference to a SharedThreadPool is dropped
// the pool is shutdown and deleted. Users aren't required to manually
// shutdown the pool, and can release references on any thread. This can make
// it significantly easier to use thread pools, because the caller doesn't need
// to worry about joining and tearing it down.
//
// On Windows all threads in the pool have MSCOM initialized with
// COINIT_MULTITHREADED. Note that not all users of MSCOM use this mode see [1],
// and mixing MSCOM objects between the two is terrible for performance, and can
// cause some functions to fail. So be careful when using Win32 APIs on a
// SharedThreadPool, and avoid sharing objects if at all possible.
//
// [1]
// https://searchfox.org/mozilla-central/search?q=coinitialize&redirect=false
class SharedThreadPool : public nsIThreadPool {
 public:
  // Gets (possibly creating) the shared thread pool singleton instance with
  // thread pool named aName.
  static already_AddRefed<SharedThreadPool> Get(const nsCString& aName,
                                                uint32_t aThreadLimit = 4);

  // We implement custom threadsafe AddRef/Release pair, that destroys the
  // the shared pool singleton when the refcount drops to 0. The addref/release
  // are implemented using locking, so it's not recommended that you use them
  // in a tight loop.
  NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) override;
  NS_IMETHOD_(MozExternalRefCountType) AddRef(void) override;
  NS_IMETHOD_(MozExternalRefCountType) Release(void) override;
  using HasThreadSafeRefCnt = std::true_type;

  // Forward behaviour to wrapped thread pool implementation.
  NS_FORWARD_SAFE_NSITHREADPOOL(mPool);

  // Call this when dispatching from an event on the same
  // threadpool that is about to complete. We should not create a new thread
  // in that case since a thread is about to become idle.
  nsresult DispatchFromEndOfTaskInThisPool(nsIRunnable* event) {
    return Dispatch(event, NS_DISPATCH_AT_END);
  }

  NS_IMETHOD DispatchFromScript(nsIRunnable* event, uint32_t flags) override {
    return Dispatch(event, flags);
  }

  NS_IMETHOD Dispatch(already_AddRefed<nsIRunnable> event,
                      uint32_t flags = NS_DISPATCH_NORMAL) override {
    return !mPool ? NS_ERROR_NULL_POINTER
                  : mPool->Dispatch(std::move(event), flags);
  }

  NS_IMETHOD DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t) override {
    return NS_ERROR_NOT_IMPLEMENTED;
  }

  using nsIEventTarget::Dispatch;

  NS_IMETHOD RegisterShutdownTask(nsITargetShutdownTask* task) override {
    return NS_ERROR_NOT_IMPLEMENTED;
  }

  NS_IMETHOD UnregisterShutdownTask(nsITargetShutdownTask* task) override {
    return NS_ERROR_NOT_IMPLEMENTED;
  }

  NS_IMETHOD IsOnCurrentThread(bool* _retval) override {
    return !mPool ? NS_ERROR_NULL_POINTER : mPool->IsOnCurrentThread(_retval);
  }

  NS_IMETHOD_(bool) IsOnCurrentThreadInfallible() override {
    return mPool && mPool->IsOnCurrentThread();
  }

  // Creates necessary statics. Called once at startup.
  static void InitStatics();

  // Spins the event loop until all thread pools are shutdown.
  // *Must* be called on the main thread.
  static void SpinUntilEmpty();

 private:
  // Returns whether there are no pools in existence at the moment.
  static bool IsEmpty();

  // Creates a singleton SharedThreadPool wrapper around aPool.
  // aName is the name of the aPool, and is used to lookup the
  // SharedThreadPool in the hash table of all created pools.
  SharedThreadPool(const nsCString& aName, nsIThreadPool* aPool);
  virtual ~SharedThreadPool();

  nsresult EnsureThreadLimitIsAtLeast(uint32_t aThreadLimit);

  // Name of mPool.
  const nsCString mName;

  // Thread pool being wrapped.
  nsCOMPtr<nsIThreadPool> mPool;

  // Refcount. We implement custom ref counting so that the thread pool is
  // shutdown in a threadsafe manner and singletonness is preserved.
  nsrefcnt mRefCnt;
};

}  // namespace mozilla

#endif  // SharedThreadPool_h_