]> git.saurik.com Git - apple/javascriptcore.git/blob - runtime/GCActivityCallbackCF.cpp
JavaScriptCore-1097.3.3.tar.gz
[apple/javascriptcore.git] / runtime / GCActivityCallbackCF.cpp
1 /*
2 * Copyright (C) 2010 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 *
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14 * its contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include "config.h"
30 #include "GCActivityCallback.h"
31
32 #include "APIShims.h"
33 #include "Heap.h"
34 #include "JSGlobalData.h"
35 #include "JSLock.h"
36 #include "JSObject.h"
37 #include "ScopeChain.h"
38 #include <wtf/RetainPtr.h>
39 #include <wtf/WTFThreadData.h>
40
41 #if !USE(CF)
42 #error "This file should only be used on CF platforms."
43 #endif
44
45 namespace JSC {
46
47 bool GCActivityCallback::s_shouldCreateGCTimer = true;
48
49 struct DefaultGCActivityCallbackPlatformData {
50 static void timerDidFire(CFRunLoopTimerRef, void *info);
51
52 RetainPtr<CFRunLoopTimerRef> timer;
53 RetainPtr<CFRunLoopRef> runLoop;
54 CFRunLoopTimerContext context;
55 double delay;
56 DefaultGCActivityCallback* thisObject;
57 JSGlobalData* globalData;
58 Mutex shutdownMutex;
59 };
60
61 const double gcTimeSlicePerMB = 0.01; // Percentage of CPU time we will spend to reclaim 1 MB
62 const double maxGCTimeSlice = 0.05; // The maximum amount of CPU time we want to use for opportunistic timer-triggered collections.
63 const double timerSlop = 2.0; // Fudge factor to avoid performance cost of resetting timer.
64 const double pagingTimeOut = 0.1; // Time in seconds to allow opportunistic timer to iterate over all blocks to see if the Heap is paged out.
65 const CFTimeInterval decade = 60 * 60 * 24 * 365 * 10;
66 const CFTimeInterval hour = 60 * 60;
67
68 void DefaultGCActivityCallbackPlatformData::timerDidFire(CFRunLoopTimerRef, void *info)
69 {
70 DefaultGCActivityCallbackPlatformData* d = static_cast<DefaultGCActivityCallbackPlatformData*>(info);
71 d->shutdownMutex.lock();
72 if (!d->globalData) {
73 d->shutdownMutex.unlock();
74 delete d->thisObject;
75 return;
76 }
77 {
78 // We don't ref here to prevent us from resurrecting the ref count of a "dead" JSGlobalData.
79 APIEntryShim shim(d->globalData, APIEntryShimWithoutLock::DontRefGlobalData);
80
81 Heap* heap = &d->globalData->heap;
82 heap->collectAllGarbage();
83 }
84 d->shutdownMutex.unlock();
85 }
86
87 DefaultGCActivityCallback::DefaultGCActivityCallback(Heap* heap)
88 {
89 commonConstructor(heap, CFRunLoopGetCurrent());
90 }
91
92 DefaultGCActivityCallback::DefaultGCActivityCallback(Heap* heap, CFRunLoopRef runLoop)
93 {
94 commonConstructor(heap, runLoop);
95 }
96
97 DefaultGCActivityCallback::~DefaultGCActivityCallback()
98 {
99 CFRunLoopRemoveTimer(d->runLoop.get(), d->timer.get(), kCFRunLoopCommonModes);
100 CFRunLoopTimerInvalidate(d->timer.get());
101 }
102
103 void DefaultGCActivityCallback::commonConstructor(Heap* heap, CFRunLoopRef runLoop)
104 {
105 d = adoptPtr(new DefaultGCActivityCallbackPlatformData);
106
107 memset(&d->context, 0, sizeof(CFRunLoopTimerContext));
108 d->context.info = d.get();
109 d->thisObject = this;
110 d->globalData = heap->globalData();
111 d->runLoop = runLoop;
112 d->timer.adoptCF(CFRunLoopTimerCreate(0, decade, decade, 0, 0, DefaultGCActivityCallbackPlatformData::timerDidFire, &d->context));
113 d->delay = decade;
114 CFRunLoopAddTimer(d->runLoop.get(), d->timer.get(), kCFRunLoopCommonModes);
115 }
116
117 void DefaultGCActivityCallback::invalidate()
118 {
119 d->globalData = 0;
120 // We set the next fire date in the distant past to cause the timer to immediately fire so that
121 // the timer on the remote thread realizes that the VM is shutting down.
122 CFRunLoopTimerSetNextFireDate(d->timer.get(), CFAbsoluteTimeGetCurrent() - decade);
123 }
124
125 void DefaultGCActivityCallback::didStartVMShutdown()
126 {
127 if (CFRunLoopGetCurrent() == d->runLoop.get()) {
128 invalidate();
129 delete this;
130 return;
131 }
132 ASSERT(!d->globalData->apiLock().currentThreadIsHoldingLock());
133 MutexLocker locker(d->shutdownMutex);
134 invalidate();
135 }
136
137 static void scheduleTimer(DefaultGCActivityCallbackPlatformData* d, double newDelay)
138 {
139 if (newDelay * timerSlop > d->delay)
140 return;
141 double delta = d->delay - newDelay;
142 d->delay = newDelay;
143 CFRunLoopTimerSetNextFireDate(d->timer.get(), CFRunLoopTimerGetNextFireDate(d->timer.get()) - delta);
144 }
145
146 static void cancelTimer(DefaultGCActivityCallbackPlatformData* d)
147 {
148 d->delay = decade;
149 CFRunLoopTimerSetNextFireDate(d->timer.get(), CFAbsoluteTimeGetCurrent() + decade);
150 }
151
152 void DefaultGCActivityCallback::didAllocate(size_t bytes)
153 {
154 // The first byte allocated in an allocation cycle will report 0 bytes to didAllocate.
155 // We pretend it's one byte so that we don't ignore this allocation entirely.
156 if (!bytes)
157 bytes = 1;
158 Heap* heap = &static_cast<DefaultGCActivityCallbackPlatformData*>(d->context.info)->globalData->heap;
159 double gcTimeSlice = std::min((static_cast<double>(bytes) / MB) * gcTimeSlicePerMB, maxGCTimeSlice);
160 double newDelay = heap->lastGCLength() / gcTimeSlice;
161 scheduleTimer(d.get(), newDelay);
162 }
163
164 void DefaultGCActivityCallback::willCollect()
165 {
166 cancelTimer(d.get());
167 }
168
169 void DefaultGCActivityCallback::synchronize()
170 {
171 if (CFRunLoopGetCurrent() == d->runLoop.get())
172 return;
173 CFRunLoopRemoveTimer(d->runLoop.get(), d->timer.get(), kCFRunLoopCommonModes);
174 d->runLoop = CFRunLoopGetCurrent();
175 CFRunLoopAddTimer(d->runLoop.get(), d->timer.get(), kCFRunLoopCommonModes);
176 }
177
178 void DefaultGCActivityCallback::cancel()
179 {
180 cancelTimer(d.get());
181 }
182
183 }