051641d9aba75d9f830ad9f1b739cdf36942e004
[WebKit-https.git] / Source / JavaScriptCore / dfg / DFGWorklist.cpp
1 /*
2  * Copyright (C) 2013 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "DFGWorklist.h"
28
29 #if ENABLE(DFG_JIT)
30
31 #include "CodeBlock.h"
32 #include "DeferGC.h"
33 #include "DFGLongLivedState.h"
34
35 namespace JSC { namespace DFG {
36
37 Worklist::Worklist()
38     : m_numberOfActiveThreads(0)
39 {
40 }
41
42 Worklist::~Worklist()
43 {
44     {
45         MutexLocker locker(m_lock);
46         for (unsigned i = m_threads.size(); i--;)
47             m_queue.append(RefPtr<Plan>(0)); // Use null plan to indicate that we want the thread to terminate.
48         m_planEnqueued.broadcast();
49     }
50     for (unsigned i = m_threads.size(); i--;)
51         waitForThreadCompletion(m_threads[i]);
52     ASSERT(!m_numberOfActiveThreads);
53 }
54
55 void Worklist::finishCreation(unsigned numberOfThreads)
56 {
57     RELEASE_ASSERT(numberOfThreads);
58     for (unsigned i = numberOfThreads; i--;)
59         m_threads.append(createThread(threadFunction, this, "JSC Compilation Thread"));
60 }
61
62 PassRefPtr<Worklist> Worklist::create(unsigned numberOfThreads)
63 {
64     RefPtr<Worklist> result = adoptRef(new Worklist());
65     result->finishCreation(numberOfThreads);
66     return result;
67 }
68
69 void Worklist::enqueue(PassRefPtr<Plan> passedPlan)
70 {
71     RefPtr<Plan> plan = passedPlan;
72     MutexLocker locker(m_lock);
73     if (Options::verboseCompilationQueue()) {
74         dump(locker, WTF::dataFile());
75         dataLog(": Enqueueing plan to optimize ", *plan->key(), "\n");
76     }
77     ASSERT(m_plans.find(plan->key()) == m_plans.end());
78     m_plans.add(plan->key(), plan);
79     m_queue.append(plan);
80     m_planEnqueued.signal();
81 }
82
83 Worklist::State Worklist::compilationState(CodeBlock* profiledBlock)
84 {
85     MutexLocker locker(m_lock);
86     PlanMap::iterator iter = m_plans.find(profiledBlock);
87     if (iter == m_plans.end())
88         return NotKnown;
89     return iter->value->isCompiled ? Compiled : Compiling;
90 }
91
92 void Worklist::waitUntilAllPlansForVMAreReady(VM& vm)
93 {
94     DeferGC deferGC(vm.heap);
95     // Wait for all of the plans for the given VM to complete. The idea here
96     // is that we want all of the caller VM's plans to be done. We don't care
97     // about any other VM's plans, and we won't attempt to wait on those.
98     // After we release this lock, we know that although other VMs may still
99     // be adding plans, our VM will not be.
100     
101     MutexLocker locker(m_lock);
102     
103     if (Options::verboseCompilationQueue()) {
104         dump(locker, WTF::dataFile());
105         dataLog(": Waiting for all in VM to complete.\n");
106     }
107     
108     for (;;) {
109         bool allAreCompiled = true;
110         PlanMap::iterator end = m_plans.end();
111         for (PlanMap::iterator iter = m_plans.begin(); iter != end; ++iter) {
112             if (&iter->value->vm != &vm)
113                 continue;
114             if (!iter->value->isCompiled) {
115                 allAreCompiled = false;
116                 break;
117             }
118         }
119         
120         if (allAreCompiled)
121             break;
122         
123         m_planCompiled.wait(m_lock);
124     }
125 }
126
127 void Worklist::removeAllReadyPlansForVM(VM& vm, Vector<RefPtr<Plan>, 8>& myReadyPlans)
128 {
129     DeferGC deferGC(vm.heap);
130     MutexLocker locker(m_lock);
131     for (size_t i = 0; i < m_readyPlans.size(); ++i) {
132         RefPtr<Plan> plan = m_readyPlans[i];
133         if (&plan->vm != &vm)
134             continue;
135         if (!plan->isCompiled)
136             continue;
137         myReadyPlans.append(plan);
138         m_readyPlans[i--] = m_readyPlans.last();
139         m_readyPlans.removeLast();
140         m_plans.remove(plan->key());
141     }
142 }
143
144 void Worklist::removeAllReadyPlansForVM(VM& vm)
145 {
146     Vector<RefPtr<Plan>, 8> myReadyPlans;
147     removeAllReadyPlansForVM(vm, myReadyPlans);
148 }
149
150 Worklist::State Worklist::completeAllReadyPlansForVM(VM& vm, CodeBlock* requestedProfiledBlock)
151 {
152     DeferGC deferGC(vm.heap);
153     Vector<RefPtr<Plan>, 8> myReadyPlans;
154     
155     removeAllReadyPlansForVM(vm, myReadyPlans);
156     
157     State resultingState = NotKnown;
158
159     while (!myReadyPlans.isEmpty()) {
160         RefPtr<Plan> plan = myReadyPlans.takeLast();
161         CodeBlock* profiledBlock = plan->key();
162         
163         if (Options::verboseCompilationQueue())
164             dataLog(*this, ": Completing ", *profiledBlock, "\n");
165         
166         RELEASE_ASSERT(plan->isCompiled);
167         
168         plan->finalizeAndNotifyCallback();
169         
170         if (profiledBlock == requestedProfiledBlock)
171             resultingState = Compiled;
172     }
173     
174     if (requestedProfiledBlock && resultingState == NotKnown) {
175         MutexLocker locker(m_lock);
176         if (m_plans.contains(requestedProfiledBlock))
177             resultingState = Compiling;
178     }
179     
180     return resultingState;
181 }
182
183 void Worklist::completeAllPlansForVM(VM& vm)
184 {
185     DeferGC deferGC(vm.heap);
186     waitUntilAllPlansForVMAreReady(vm);
187     completeAllReadyPlansForVM(vm);
188 }
189
190 size_t Worklist::queueLength()
191 {
192     MutexLocker locker(m_lock);
193     return m_queue.size();
194 }
195
196 void Worklist::dump(PrintStream& out) const
197 {
198     MutexLocker locker(m_lock);
199     dump(locker, out);
200 }
201
202 void Worklist::dump(const MutexLocker&, PrintStream& out) const
203 {
204     out.print(
205         "Worklist(", RawPointer(this), ")[Queue Length = ", m_queue.size(),
206         ", Map Size = ", m_plans.size(), ", Num Ready = ", m_readyPlans.size(),
207         ", Num Active Threads = ", m_numberOfActiveThreads, "/", m_threads.size(), "]");
208 }
209
210 void Worklist::runThread()
211 {
212     CompilationScope compilationScope;
213     
214     if (Options::verboseCompilationQueue())
215         dataLog(*this, ": Thread started\n");
216     
217     LongLivedState longLivedState;
218     
219     for (;;) {
220         RefPtr<Plan> plan;
221         {
222             MutexLocker locker(m_lock);
223             while (m_queue.isEmpty())
224                 m_planEnqueued.wait(m_lock);
225             plan = m_queue.takeFirst();
226             if (plan)
227                 m_numberOfActiveThreads++;
228         }
229         
230         if (!plan) {
231             if (Options::verboseCompilationQueue())
232                 dataLog(*this, ": Thread shutting down\n");
233             return;
234         }
235         
236         if (Options::verboseCompilationQueue())
237             dataLog(*this, ": Compiling ", *plan->key(), " asynchronously\n");
238         
239         plan->compileInThread(longLivedState);
240         
241         {
242             MutexLocker locker(m_lock);
243             plan->notifyReady();
244             
245             if (Options::verboseCompilationQueue()) {
246                 dump(locker, WTF::dataFile());
247                 dataLog(": Compiled ", *plan->key(), " asynchronously\n");
248             }
249             
250             m_readyPlans.append(plan);
251             
252             m_planCompiled.broadcast();
253             m_numberOfActiveThreads--;
254         }
255     }
256 }
257
258 void Worklist::threadFunction(void* argument)
259 {
260     static_cast<Worklist*>(argument)->runThread();
261 }
262
263 static pthread_once_t initializeGlobalWorklistKeyOnce = PTHREAD_ONCE_INIT;
264 static Worklist* theGlobalWorklist;
265
266 static void initializeGlobalWorklistOnce()
267 {
268     unsigned numberOfThreads;
269     
270     if (Options::useExperimentalFTL())
271         numberOfThreads = 1; // We don't yet use LLVM in a thread-safe way.
272     else
273         numberOfThreads = Options::numberOfCompilerThreads();
274     
275     theGlobalWorklist = Worklist::create(numberOfThreads).leakRef();
276 }
277
278 Worklist* globalWorklist()
279 {
280     pthread_once(&initializeGlobalWorklistKeyOnce, initializeGlobalWorklistOnce);
281     return theGlobalWorklist;
282 }
283
284 } } // namespace JSC::DFG
285
286 #endif // ENABLE(DFG_JIT)
287