]>
Commit | Line | Data |
---|---|---|
14957cd0 A |
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 | ||
6fe7ccc8 A |
47 | bool GCActivityCallback::s_shouldCreateGCTimer = true; |
48 | ||
14957cd0 | 49 | struct DefaultGCActivityCallbackPlatformData { |
6fe7ccc8 | 50 | static void timerDidFire(CFRunLoopTimerRef, void *info); |
14957cd0 A |
51 | |
52 | RetainPtr<CFRunLoopTimerRef> timer; | |
53 | RetainPtr<CFRunLoopRef> runLoop; | |
54 | CFRunLoopTimerContext context; | |
6fe7ccc8 A |
55 | double delay; |
56 | DefaultGCActivityCallback* thisObject; | |
57 | JSGlobalData* globalData; | |
58 | Mutex shutdownMutex; | |
14957cd0 A |
59 | }; |
60 | ||
6fe7ccc8 A |
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. | |
14957cd0 | 65 | const CFTimeInterval decade = 60 * 60 * 24 * 365 * 10; |
6fe7ccc8 | 66 | const CFTimeInterval hour = 60 * 60; |
14957cd0 | 67 | |
6fe7ccc8 | 68 | void DefaultGCActivityCallbackPlatformData::timerDidFire(CFRunLoopTimerRef, void *info) |
14957cd0 | 69 | { |
6fe7ccc8 A |
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(); | |
14957cd0 A |
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()); | |
14957cd0 A |
101 | } |
102 | ||
103 | void DefaultGCActivityCallback::commonConstructor(Heap* heap, CFRunLoopRef runLoop) | |
104 | { | |
105 | d = adoptPtr(new DefaultGCActivityCallbackPlatformData); | |
106 | ||
107 | memset(&d->context, 0, sizeof(CFRunLoopTimerContext)); | |
6fe7ccc8 A |
108 | d->context.info = d.get(); |
109 | d->thisObject = this; | |
110 | d->globalData = heap->globalData(); | |
14957cd0 | 111 | d->runLoop = runLoop; |
6fe7ccc8 A |
112 | d->timer.adoptCF(CFRunLoopTimerCreate(0, decade, decade, 0, 0, DefaultGCActivityCallbackPlatformData::timerDidFire, &d->context)); |
113 | d->delay = decade; | |
14957cd0 A |
114 | CFRunLoopAddTimer(d->runLoop.get(), d->timer.get(), kCFRunLoopCommonModes); |
115 | } | |
116 | ||
6fe7ccc8 A |
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) | |
14957cd0 | 153 | { |
6fe7ccc8 A |
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()); | |
14957cd0 A |
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 | ||
6fe7ccc8 A |
178 | void DefaultGCActivityCallback::cancel() |
179 | { | |
180 | cancelTimer(d.get()); | |
181 | } | |
182 | ||
14957cd0 | 183 | } |