js/src/vm/ThreadPool.h

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
michael@0 2 * vim: set ts=8 sts=4 et sw=4 tw=99:
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #ifndef vm_ThreadPool_h
michael@0 8 #define vm_ThreadPool_h
michael@0 9
michael@0 10 #include "mozilla/Atomics.h"
michael@0 11
michael@0 12 #include "jsalloc.h"
michael@0 13 #include "jslock.h"
michael@0 14 #include "jsmath.h"
michael@0 15 #include "jspubtd.h"
michael@0 16
michael@0 17 #include "js/Vector.h"
michael@0 18 #include "vm/Monitor.h"
michael@0 19
michael@0 20 struct JSRuntime;
michael@0 21 struct JSCompartment;
michael@0 22
michael@0 23 namespace js {
michael@0 24
michael@0 25 class ThreadPool;
michael@0 26
michael@0 27 /////////////////////////////////////////////////////////////////////////////
michael@0 28 // ThreadPoolWorker
michael@0 29 //
michael@0 30 // Class for worker threads in the pool. All threads (i.e. helpers and main
michael@0 31 // thread) have a worker associted with them. By convention, the worker id of
michael@0 32 // the main thread is 0.
michael@0 33
michael@0 34 class ThreadPoolWorker
michael@0 35 {
michael@0 36 const uint32_t workerId_;
michael@0 37 ThreadPool *pool_;
michael@0 38
michael@0 39 // Slices this thread is responsible for.
michael@0 40 //
michael@0 41 // This a uint32 composed of two uint16s (the lower and upper bounds) so
michael@0 42 // that we may do a single CAS. See {Compose,Decompose}SliceBounds
michael@0 43 // functions below.
michael@0 44 mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> sliceBounds_;
michael@0 45
michael@0 46 // Current point in the worker's lifecycle.
michael@0 47 volatile enum WorkerState {
michael@0 48 CREATED, ACTIVE, TERMINATED
michael@0 49 } state_;
michael@0 50
michael@0 51 // Per-worker scheduler RNG state used for picking a random worker during
michael@0 52 // work stealing.
michael@0 53 uint32_t schedulerRNGState_;
michael@0 54
michael@0 55 // The thread's main function.
michael@0 56 static void HelperThreadMain(void *arg);
michael@0 57 void helperLoop();
michael@0 58
michael@0 59 bool hasWork() const;
michael@0 60 bool popSliceFront(uint16_t *sliceId);
michael@0 61 bool popSliceBack(uint16_t *sliceId);
michael@0 62 bool stealFrom(ThreadPoolWorker *victim, uint16_t *sliceId);
michael@0 63
michael@0 64 // Get a worker at random from the pool using our own thread-local RNG
michael@0 65 // state. This is a weak, but very fast, random function [1]. We choose
michael@0 66 // [a,b,c] = 11,21,13.
michael@0 67 //
michael@0 68 // [1] http://www.jstatsoft.org/v08/i14/paper
michael@0 69 public:
michael@0 70 static const uint32_t XORSHIFT_A = 11;
michael@0 71 static const uint32_t XORSHIFT_B = 21;
michael@0 72 static const uint32_t XORSHIFT_C = 13;
michael@0 73
michael@0 74 private:
michael@0 75 ThreadPoolWorker *randomWorker();
michael@0 76
michael@0 77 public:
michael@0 78 ThreadPoolWorker(uint32_t workerId, uint32_t rngSeed, ThreadPool *pool);
michael@0 79
michael@0 80 uint32_t id() const { return workerId_; }
michael@0 81 bool isMainThread() const { return id() == 0; }
michael@0 82
michael@0 83 // Submits a new set of slices. Assumes !hasWork().
michael@0 84 void submitSlices(uint16_t sliceStart, uint16_t sliceEnd);
michael@0 85
michael@0 86 // Get the next slice; work stealing happens here if work stealing is
michael@0 87 // on. Returns false if there are no more slices to hand out.
michael@0 88 bool getSlice(ForkJoinContext *cx, uint16_t *sliceId);
michael@0 89
michael@0 90 // Discard remaining slices. Used for aborting jobs.
michael@0 91 void discardSlices();
michael@0 92
michael@0 93 // Invoked from the main thread; signals worker to start.
michael@0 94 bool start();
michael@0 95
michael@0 96 // Invoked from the main thread; signals the worker loop to return.
michael@0 97 void terminate(AutoLockMonitor &lock);
michael@0 98
michael@0 99 static size_t offsetOfSliceBounds() {
michael@0 100 return offsetof(ThreadPoolWorker, sliceBounds_);
michael@0 101 }
michael@0 102
michael@0 103 static size_t offsetOfSchedulerRNGState() {
michael@0 104 return offsetof(ThreadPoolWorker, schedulerRNGState_);
michael@0 105 }
michael@0 106 };
michael@0 107
michael@0 108 /////////////////////////////////////////////////////////////////////////////
michael@0 109 // A ParallelJob is the main runnable abstraction in the ThreadPool.
michael@0 110 //
michael@0 111 // The unit of work here is in terms of threads, *not* slices. The
michael@0 112 // user-provided function has the responsibility of getting slices of work via
michael@0 113 // the |ForkJoinGetSlice| intrinsic.
michael@0 114
michael@0 115 class ParallelJob
michael@0 116 {
michael@0 117 public:
michael@0 118 virtual bool executeFromWorker(ThreadPoolWorker *worker, uintptr_t stackLimit) = 0;
michael@0 119 virtual bool executeFromMainThread(ThreadPoolWorker *mainWorker) = 0;
michael@0 120 };
michael@0 121
michael@0 122 /////////////////////////////////////////////////////////////////////////////
michael@0 123 // ThreadPool used for parallel JavaScript execution. Unless you are building
michael@0 124 // a new kind of parallel service, it is very likely that you do not wish to
michael@0 125 // interact with the threadpool directly. In particular, if you wish to
michael@0 126 // execute JavaScript in parallel, you probably want to look at |js::ForkJoin|
michael@0 127 // in |forkjoin.cpp|.
michael@0 128 //
michael@0 129 // The ThreadPool always maintains a fixed pool of worker threads. You can
michael@0 130 // query the number of worker threads via the method |numWorkers()|. Note
michael@0 131 // that this number may be zero (generally if threads are disabled, or when
michael@0 132 // manually specified for benchmarking purposes).
michael@0 133 //
michael@0 134 // The way to submit a job is using |executeJob()|---in this case, the job
michael@0 135 // will be executed by all worker threads, including the main thread. This
michael@0 136 // does not fail if there are no worker threads, it simply runs all the work
michael@0 137 // using the main thread only.
michael@0 138 //
michael@0 139 // Of course, each thread may have any number of previously submitted things
michael@0 140 // that they are already working on, and so they will finish those before they
michael@0 141 // get to this job. Therefore it is possible to have some worker threads pick
michael@0 142 // up (and even finish) their piece of the job before others have even
michael@0 143 // started. The main thread is also used by the pool as a worker thread.
michael@0 144 //
michael@0 145 // The ThreadPool supports work stealing. Every time a worker completes all
michael@0 146 // the slices in its local queue, it tries to acquire some work from other
michael@0 147 // workers (including the main thread). Execution terminates when there is no
michael@0 148 // work left to be done, i.e., when all the workers have an empty queue. The
michael@0 149 // stealing algorithm operates in 2 phases: (1) workers process all the slices
michael@0 150 // in their local queue, and then (2) workers try to steal from other peers.
michael@0 151 // Since workers start to steal only *after* they have completed all the
michael@0 152 // slices in their queue, the design is particularly convenient in the context
michael@0 153 // of Fork/Join-like parallelism, where workers receive a bunch of slices to
michael@0 154 // be done at the very beginning of the job, and have to wait until all the
michael@0 155 // threads have joined back. During phase (1) there is no synchronization
michael@0 156 // overhead between workers introduced by the stealing algorithm, and
michael@0 157 // therefore the execution overhead introduced is almost zero with balanced
michael@0 158 // workloads. The way a |ParallelJob| is divided into multiple slices has to
michael@0 159 // be specified by the instance implementing the job (e.g., |ForkJoinShared|
michael@0 160 // in |ForkJoin.cpp|).
michael@0 161
michael@0 162 class ThreadPool : public Monitor
michael@0 163 {
michael@0 164 private:
michael@0 165 friend class ThreadPoolWorker;
michael@0 166
michael@0 167 // Initialized lazily.
michael@0 168 js::Vector<ThreadPoolWorker *, 8, SystemAllocPolicy> workers_;
michael@0 169
michael@0 170 // The number of active workers. Should only access under lock.
michael@0 171 uint32_t activeWorkers_;
michael@0 172 PRCondVar *joinBarrier_;
michael@0 173
michael@0 174 // The current job.
michael@0 175 ParallelJob *job_;
michael@0 176
michael@0 177 #ifdef DEBUG
michael@0 178 // Initialized at startup only.
michael@0 179 JSRuntime *const runtime_;
michael@0 180
michael@0 181 // Number of stolen slices in the last parallel job.
michael@0 182 mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> stolenSlices_;
michael@0 183 #endif
michael@0 184
michael@0 185 // Number of pending slices in the current job.
michael@0 186 mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> pendingSlices_;
michael@0 187
michael@0 188 // Whether the main thread is currently processing slices.
michael@0 189 bool isMainThreadActive_;
michael@0 190
michael@0 191 bool lazyStartWorkers(JSContext *cx);
michael@0 192 void terminateWorkers();
michael@0 193 void terminateWorkersAndReportOOM(JSContext *cx);
michael@0 194 void join(AutoLockMonitor &lock);
michael@0 195 void waitForWorkers(AutoLockMonitor &lock);
michael@0 196 ThreadPoolWorker *mainThreadWorker() { return workers_[0]; }
michael@0 197
michael@0 198 public:
michael@0 199 #ifdef DEBUG
michael@0 200 static size_t offsetOfStolenSlices() {
michael@0 201 return offsetof(ThreadPool, stolenSlices_);
michael@0 202 }
michael@0 203 #endif
michael@0 204 static size_t offsetOfPendingSlices() {
michael@0 205 return offsetof(ThreadPool, pendingSlices_);
michael@0 206 }
michael@0 207 static size_t offsetOfWorkers() {
michael@0 208 return offsetof(ThreadPool, workers_);
michael@0 209 }
michael@0 210
michael@0 211 static const uint16_t MAX_SLICE_ID = UINT16_MAX;
michael@0 212
michael@0 213 ThreadPool(JSRuntime *rt);
michael@0 214 ~ThreadPool();
michael@0 215
michael@0 216 bool init();
michael@0 217
michael@0 218 // Return number of worker threads in the pool, counting the main thread.
michael@0 219 uint32_t numWorkers() const;
michael@0 220
michael@0 221 // Returns whether we have any pending slices.
michael@0 222 bool hasWork() const { return pendingSlices_ != 0; }
michael@0 223
michael@0 224 // Returns the current job. Must have one.
michael@0 225 ParallelJob *job() const {
michael@0 226 MOZ_ASSERT(job_);
michael@0 227 return job_;
michael@0 228 }
michael@0 229
michael@0 230 // Returns whether or not the scheduler should perform work stealing.
michael@0 231 bool workStealing() const;
michael@0 232
michael@0 233 // Returns whether or not the main thread is working.
michael@0 234 bool isMainThreadActive() const { return isMainThreadActive_; }
michael@0 235
michael@0 236 #ifdef DEBUG
michael@0 237 // Return the number of stolen slices in the last parallel job.
michael@0 238 uint16_t stolenSlices() { return stolenSlices_; }
michael@0 239 #endif
michael@0 240
michael@0 241 // Wait until all worker threads have finished their current set
michael@0 242 // of slices and then return. You must not submit new jobs after
michael@0 243 // invoking |terminate()|.
michael@0 244 void terminate();
michael@0 245
michael@0 246 // Execute the given ParallelJob using the main thread and any available worker.
michael@0 247 // Blocks until the main thread has completed execution.
michael@0 248 ParallelResult executeJob(JSContext *cx, ParallelJob *job, uint16_t sliceStart,
michael@0 249 uint16_t numSlices);
michael@0 250
michael@0 251 // Abort the current job.
michael@0 252 void abortJob();
michael@0 253 };
michael@0 254
michael@0 255 } // namespace js
michael@0 256
michael@0 257 #endif /* vm_ThreadPool_h */

mercurial