2 * Copyright (C) 2004 Apple Computer, Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, 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 COMPUTER, 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.
27 #include "runtime_root.h"
29 #include "JSGlobalObject.h"
32 #include "runtime_object.h"
33 #include <wtf/HashCountedSet.h>
34 #include <wtf/HashSet.h>
36 namespace KJS
{ namespace Bindings
{
38 // This code attempts to solve two problems: (1) plug-ins leaking references to
39 // JS and the DOM; (2) plug-ins holding stale references to JS and the DOM. Previous
40 // comments in this file claimed that problem #1 was an issue in Java, in particular,
41 // because Java, allegedly, didn't always call finalize when collecting an object.
43 typedef HashSet
<RootObject
*> RootObjectSet
;
45 static RootObjectSet
* rootObjectSet()
47 static RootObjectSet staticRootObjectSet
;
48 return &staticRootObjectSet
;
51 // FIXME: These two functions are a potential performance problem. We could
52 // fix them by adding a JSObject to RootObject dictionary.
54 RootObject
* findProtectingRootObject(JSObject
* jsObject
)
56 RootObjectSet::const_iterator end
= rootObjectSet()->end();
57 for (RootObjectSet::const_iterator it
= rootObjectSet()->begin(); it
!= end
; ++it
) {
58 if ((*it
)->gcIsProtected(jsObject
))
64 RootObject
* findRootObject(JSGlobalObject
* globalObject
)
66 RootObjectSet::const_iterator end
= rootObjectSet()->end();
67 for (RootObjectSet::const_iterator it
= rootObjectSet()->begin(); it
!= end
; ++it
) {
68 if ((*it
)->globalObject() == globalObject
)
74 // May only be set by dispatchToJavaScriptThread().
75 #if ENABLE(JAVA_BINDINGS)
76 static CFRunLoopSourceRef completionSource
;
78 static void completedJavaScriptAccess (void *i
)
80 assert (CFRunLoopGetCurrent() != RootObject::runLoop());
82 JSObjectCallContext
*callContext
= (JSObjectCallContext
*)i
;
83 CFRunLoopRef runLoop
= (CFRunLoopRef
)callContext
->originatingLoop
;
85 assert (CFRunLoopGetCurrent() == runLoop
);
87 CFRunLoopStop(runLoop
);
90 static pthread_once_t javaScriptAccessLockOnce
= PTHREAD_ONCE_INIT
;
91 static pthread_mutex_t javaScriptAccessLock
;
92 static int javaScriptAccessLockCount
= 0;
94 static void initializeJavaScriptAccessLock()
96 pthread_mutexattr_t attr
;
98 pthread_mutexattr_init(&attr
);
99 pthread_mutexattr_settype (&attr
, PTHREAD_MUTEX_RECURSIVE
);
101 pthread_mutex_init(&javaScriptAccessLock
, &attr
);
104 static inline void lockJavaScriptAccess()
106 // Perhaps add deadlock detection?
107 pthread_once(&javaScriptAccessLockOnce
, initializeJavaScriptAccessLock
);
108 pthread_mutex_lock(&javaScriptAccessLock
);
109 javaScriptAccessLockCount
++;
112 static inline void unlockJavaScriptAccess()
114 javaScriptAccessLockCount
--;
115 pthread_mutex_unlock(&javaScriptAccessLock
);
119 void RootObject::dispatchToJavaScriptThread(JSObjectCallContext
*context
)
121 // This lock guarantees that only one thread can invoke
122 // at a time, and also guarantees that completionSource;
123 // won't get clobbered.
124 lockJavaScriptAccess();
126 CFRunLoopRef currentRunLoop
= CFRunLoopGetCurrent();
128 assert (currentRunLoop
!= RootObject::runLoop());
130 // Setup a source to signal once the invocation of the JavaScript
133 // FIXME: This could be a potential performance issue. Creating and
134 // adding run loop sources is expensive. We could create one source
135 // per thread, as needed, instead.
136 context
->originatingLoop
= currentRunLoop
;
137 CFRunLoopSourceContext sourceContext
= {0, context
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, completedJavaScriptAccess
};
138 completionSource
= CFRunLoopSourceCreate(NULL
, 0, &sourceContext
);
139 CFRunLoopAddSource(currentRunLoop
, completionSource
, kCFRunLoopDefaultMode
);
141 // Wakeup JavaScript access thread and make it do it's work.
142 CFRunLoopSourceSignal(RootObject::performJavaScriptSource());
143 if (CFRunLoopIsWaiting(RootObject::runLoop())) {
144 CFRunLoopWakeUp(RootObject::runLoop());
147 // Wait until the JavaScript access thread is done.
150 CFRunLoopRemoveSource(currentRunLoop
, completionSource
, kCFRunLoopDefaultMode
);
151 CFRelease (completionSource
);
153 unlockJavaScriptAccess();
156 static void performJavaScriptAccess(void*)
158 assert (CFRunLoopGetCurrent() == RootObject::runLoop());
160 // Dispatch JavaScript calls here.
161 CFRunLoopSourceContext sourceContext
;
162 CFRunLoopSourceGetContext (completionSource
, &sourceContext
);
163 JSObjectCallContext
*callContext
= (JSObjectCallContext
*)sourceContext
.info
;
164 CFRunLoopRef originatingLoop
= callContext
->originatingLoop
;
166 JavaJSObject::invoke (callContext
);
168 // Signal the originating thread that we're done.
169 CFRunLoopSourceSignal (completionSource
);
170 if (CFRunLoopIsWaiting(originatingLoop
)) {
171 CFRunLoopWakeUp(originatingLoop
);
174 #endif // ENABLE(JAVA_BINDINGS)
176 CreateRootObjectFunction
RootObject::_createRootObject
= 0;
177 CFRunLoopRef
RootObject::_runLoop
= 0;
178 CFRunLoopSourceRef
RootObject::_performJavaScriptSource
= 0;
180 // Must be called from the thread that will be used to access JavaScript.
181 void RootObject::setCreateRootObject(CreateRootObjectFunction createRootObject
) {
182 // Should only be called once.
183 ASSERT(!_createRootObject
);
185 _createRootObject
= createRootObject
;
187 // Assume that we can retain this run loop forever. It'll most
188 // likely (always?) be the main loop.
189 _runLoop
= (CFRunLoopRef
)CFRetain (CFRunLoopGetCurrent ());
191 // Setup a source the other threads can use to signal the _runLoop
192 // thread that a JavaScript call needs to be invoked.
194 #if ENABLE(JAVA_BINDINGS)
195 CFRunLoopSourceContext sourceContext
= {0, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, performJavaScriptAccess
};
196 RootObject::_performJavaScriptSource
= CFRunLoopSourceCreate(NULL
, 0, &sourceContext
);
197 CFRunLoopAddSource(RootObject::_runLoop
, RootObject::_performJavaScriptSource
, kCFRunLoopDefaultMode
);
198 #endif // ENABLE(JAVA_BINDINGS)
202 PassRefPtr
<RootObject
> RootObject::create(const void* nativeHandle
, JSGlobalObject
* globalObject
)
204 return new RootObject(nativeHandle
, globalObject
);
207 RootObject::RootObject(const void* nativeHandle
, JSGlobalObject
* globalObject
)
209 , m_nativeHandle(nativeHandle
)
210 , m_globalObject(globalObject
)
212 ASSERT(globalObject
);
213 rootObjectSet()->add(this);
216 RootObject::~RootObject()
222 void RootObject::invalidate()
228 HashSet
<RuntimeObjectImp
*>::iterator end
= m_runtimeObjects
.end();
229 for (HashSet
<RuntimeObjectImp
*>::iterator it
= m_runtimeObjects
.begin(); it
!= end
; ++it
)
232 m_runtimeObjects
.clear();
240 ProtectCountSet::iterator end
= m_protectCountSet
.end();
241 for (ProtectCountSet::iterator it
= m_protectCountSet
.begin(); it
!= end
; ++it
) {
243 KJS::gcUnprotect(it
->first
);
245 m_protectCountSet
.clear();
247 rootObjectSet()->remove(this);
250 void RootObject::gcProtect(JSObject
* jsObject
)
254 if (!m_protectCountSet
.contains(jsObject
)) {
256 KJS::gcProtect(jsObject
);
258 m_protectCountSet
.add(jsObject
);
261 void RootObject::gcUnprotect(JSObject
* jsObject
)
268 if (m_protectCountSet
.count(jsObject
) == 1) {
270 KJS::gcUnprotect(jsObject
);
272 m_protectCountSet
.remove(jsObject
);
275 bool RootObject::gcIsProtected(JSObject
* jsObject
)
278 return m_protectCountSet
.contains(jsObject
);
281 const void* RootObject::nativeHandle() const
284 return m_nativeHandle
;
287 JSGlobalObject
* RootObject::globalObject() const
290 return m_globalObject
;
293 void RootObject::addRuntimeObject(RuntimeObjectImp
* object
)
296 ASSERT(!m_runtimeObjects
.contains(object
));
298 m_runtimeObjects
.add(object
);
301 void RootObject::removeRuntimeObject(RuntimeObjectImp
* object
)
304 ASSERT(m_runtimeObjects
.contains(object
));
306 m_runtimeObjects
.remove(object
);
309 } } // namespace KJS::Bindings