From f675aa6c39fc2ba32f35f12d9c4694b12247e2a7 Mon Sep 17 00:00:00 2001 From: Martin Stransky Date: Thu, 6 Oct 2011 11:52:17 +0200 Subject: [PATCH 01/24] Bug 627699 - Port GTK2 to GTK3, mozcontainer patch, r=karlt --- widget/src/gtk2/gtk2compat.h | 64 ++++++++++++++++++++++++- widget/src/gtk2/mozcontainer.c | 86 ++++++++++++++++++++-------------- 2 files changed, 114 insertions(+), 36 deletions(-) diff --git a/widget/src/gtk2/gtk2compat.h b/widget/src/gtk2/gtk2compat.h index 46680cc69618..d867f767d00a 100644 --- a/widget/src/gtk2/gtk2compat.h +++ b/widget/src/gtk2/gtk2compat.h @@ -109,11 +109,70 @@ gtk_widget_get_allocation(GtkWidget *widget, GtkAllocation *allocation) *allocation = widget->allocation; } +static inline void +gtk_widget_set_allocation(GtkWidget *widget, const GtkAllocation *allocation); +{ + widget->allocation = *allocation; +} + static inline gboolean gdk_window_is_destroyed(GdkWindow *window) { return GDK_WINDOW_OBJECT(window)->destroyed; } + +static inline void +gtk_widget_set_can_focus(GtkWidget *widget, gboolean can_focus) +{ + if (can_focus) + GTK_WIDGET_SET_FLAGS (container, GTK_CAN_FOCUS); + else + GTK_WIDGET_UNSET_FLAGS (container, GTK_CAN_FOCUS); +} + +static inline gboolean +gtk_widget_get_visible(GtkWidget *widget) +{ + return GTK_WIDGET_VISIBLE (widget); +} + +static inline void +gtk_widget_set_window(GtkWidget *widget, GdkWindow *window) +{ + widget->window = window; +} +#endif + +#if !GTK_CHECK_VERSION(2, 20, 0) +static inline void +gtk_widget_set_mapped(GtkWidget *widget, gboolean mapped) +{ + if (mapped) + GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED); + else + GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED); +} + +static inline gboolean +gtk_widget_get_mapped(GtkWidget *widget) +{ + return GTK_WIDGET_MAPPED (widget); +} + +static inline void +gtk_widget_set_realized(GtkWidget *widget, gboolean realized) +{ + if (realized) + GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED); + else + GTK_WIDGET_UNSET_FLAGS(widget, GTK_REALIZED); +} + +static inline gboolean +gtk_widget_get_realized(GtkWidget *widget) +{ + return GTK_WIDGET_REALIZED (widget); +} #endif #if !GTK_CHECK_VERSION(2, 22, 0) @@ -131,12 +190,13 @@ gdk_drag_context_get_actions(GdkDragContext *context) #endif #if !GTK_CHECK_VERSION(2, 24, 0) +#ifdef GDK_WINDOW_XDISPLAY static inline GdkWindow * gdk_x11_window_lookup_for_display(GdkDisplay *display, Window window) { return gdk_window_lookup_for_display(display, window); } - +#endif static inline GdkDisplay * gdk_window_get_display(GdkWindow *window) { @@ -150,11 +210,13 @@ gdk_window_get_screen (GdkWindow *window) } #endif +#ifdef GDK_WINDOW_XWINDOW static inline Window gdk_x11_window_get_xid(GdkWindow *window) { return(GDK_WINDOW_XWINDOW(window)); } +#endif #ifdef __cplusplus } diff --git a/widget/src/gtk2/mozcontainer.c b/widget/src/gtk2/mozcontainer.c index 68e4ebfdb683..8bec19913c54 100644 --- a/widget/src/gtk2/mozcontainer.c +++ b/widget/src/gtk2/mozcontainer.c @@ -45,6 +45,10 @@ #include "maiRedundantObjectFactory.h" #endif +#if defined(MOZ_WIDGET_GTK2) +#include "gtk2compat.h" +#endif + /* init methods */ static void moz_container_class_init (MozContainerClass *klass); static void moz_container_init (MozContainer *container); @@ -199,15 +203,16 @@ moz_container_class_init (MozContainerClass *klass) void moz_container_init (MozContainer *container) { - GTK_WIDGET_SET_FLAGS(container, GTK_CAN_FOCUS); - container->container.resize_mode = GTK_RESIZE_IMMEDIATE; - gtk_widget_set_redraw_on_allocate(GTK_WIDGET(container), - FALSE); + gtk_widget_set_can_focus(GTK_WIDGET(container), TRUE); + gtk_container_set_resize_mode(GTK_CONTAINER(container), GTK_RESIZE_IMMEDIATE); + gtk_widget_set_redraw_on_allocate(GTK_WIDGET(container), FALSE); +#if defined(MOZ_WIDGET_GTK2) /* Mozilla uses the the gdbrgb colormap and visual throughout the backend so for widgets we just use that colormap instead of the default one. */ gtk_widget_set_colormap(GTK_WIDGET(container), gdk_rgb_get_colormap()); +#endif } void @@ -220,30 +225,30 @@ moz_container_map (GtkWidget *widget) g_return_if_fail (IS_MOZ_CONTAINER(widget)); container = MOZ_CONTAINER (widget); - GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED); + gtk_widget_set_mapped(widget, TRUE); tmp_list = container->children; while (tmp_list) { tmp_child = ((MozContainerChild *)tmp_list->data)->widget; - if (GTK_WIDGET_VISIBLE(tmp_child)) { - if (!GTK_WIDGET_MAPPED(tmp_child)) + if (gtk_widget_get_visible(tmp_child)) { + if (!gtk_widget_get_mapped(tmp_child)) gtk_widget_map(tmp_child); } tmp_list = tmp_list->next; } - gdk_window_show (widget->window); + gdk_window_show (gtk_widget_get_window(widget)); } void moz_container_unmap (GtkWidget *widget) { g_return_if_fail (IS_MOZ_CONTAINER (widget)); - - GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED); - gdk_window_hide (widget->window); + gtk_widget_set_mapped(widget, FALSE); + + gdk_window_hide (gtk_widget_get_window(widget)); } void @@ -252,12 +257,13 @@ moz_container_realize (GtkWidget *widget) GdkWindowAttr attributes; gint attributes_mask = 0; MozContainer *container; + GtkAllocation allocation; g_return_if_fail(IS_MOZ_CONTAINER(widget)); container = MOZ_CONTAINER(widget); - GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED); + gtk_widget_set_realized(widget, TRUE); /* create the shell window */ @@ -270,28 +276,38 @@ moz_container_realize (GtkWidget *widget) GDK_POINTER_MOTION_HINT_MASK | #endif GDK_POINTER_MOTION_MASK); - attributes.x = widget->allocation.x; - attributes.y = widget->allocation.y; - attributes.width = widget->allocation.width; - attributes.height = widget->allocation.height; + gtk_widget_get_allocation(widget, &allocation); + attributes.x = allocation.x; + attributes.y = allocation.y; + attributes.width = allocation.width; + attributes.height = allocation.height; attributes.wclass = GDK_INPUT_OUTPUT; attributes.visual = gtk_widget_get_visual (widget); +#if defined(MOZ_WIDGET_GTK2) attributes.colormap = gtk_widget_get_colormap (widget); +#endif attributes.window_type = GDK_WINDOW_CHILD; - attributes_mask |= GDK_WA_VISUAL | GDK_WA_COLORMAP | - GDK_WA_X | GDK_WA_Y; + attributes_mask |= GDK_WA_VISUAL | GDK_WA_X | GDK_WA_Y; +#if defined(MOZ_WIDGET_GTK2) + attributes_mask |= GDK_WA_COLORMAP; +#endif - widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), - &attributes, attributes_mask); + gtk_widget_set_window(widget, gdk_window_new (gtk_widget_get_parent_window (widget), + &attributes, attributes_mask)); /* printf("widget->window is %p\n", (void *)widget->window); */ - gdk_window_set_user_data (widget->window, container); + gdk_window_set_user_data (gtk_widget_get_window(widget), container); +#if defined(MOZ_WIDGET_GTK2) widget->style = gtk_style_attach (widget->style, widget->window); +#endif +/* TODO GTK3? */ +#if defined(MOZ_WIDGET_GTK2) /* set the back pixmap to None so that you don't end up with the gtk default which is BlackPixel */ gdk_window_set_back_pixmap (widget->window, NULL, FALSE); +#endif } void @@ -315,15 +331,16 @@ moz_container_size_allocate (GtkWidget *widget, /* short circuit if you can */ container = MOZ_CONTAINER (widget); + gtk_widget_get_allocation(widget, &tmp_allocation); if (!container->children && - widget->allocation.x == allocation->x && - widget->allocation.y == allocation->y && - widget->allocation.width == allocation->width && - widget->allocation.height == allocation->height) { + tmp_allocation.x == allocation->x && + tmp_allocation.y == allocation->y && + tmp_allocation.width == allocation->width && + tmp_allocation.height == allocation->height) { return; } - widget->allocation = *allocation; + gtk_widget_set_allocation(widget, allocation); tmp_list = container->children; @@ -335,12 +352,12 @@ moz_container_size_allocate (GtkWidget *widget, tmp_list = tmp_list->next; } - if (GTK_WIDGET_REALIZED (widget)) { - gdk_window_move_resize(widget->window, - widget->allocation.x, - widget->allocation.y, - widget->allocation.width, - widget->allocation.height); + if (gtk_widget_get_realized(widget)) { + gdk_window_move_resize(gtk_widget_get_window(widget), + allocation->x, + allocation->y, + allocation->width, + allocation->height); } } @@ -383,7 +400,7 @@ moz_container_remove (GtkContainer *container, GtkWidget *child_widget) * the parent_window if the child_widget is placed in another * container. */ - if (parent_window != GTK_WIDGET(container)->window) + if (parent_window != gtk_widget_get_window(GTK_WIDGET(container))) gtk_widget_set_parent_window(child_widget, parent_window); g_object_unref(parent_window); @@ -421,12 +438,11 @@ moz_container_allocate_child (MozContainer *container, GtkAllocation allocation; GtkRequisition requisition; + gtk_widget_get_allocation (child->widget, &allocation); allocation.x = child->x; allocation.y = child->y; /* gtk_widget_get_child_requisition (child->widget, &requisition); */ /* gtk_widget_size_request (child->widget, &requisition); */ - allocation.width = child->widget->allocation.width; - allocation.height = child->widget->allocation.height; gtk_widget_size_allocate (child->widget, &allocation); } From 0896b66a52a2df7e1a02a0d4713483a0e0084ed5 Mon Sep 17 00:00:00 2001 From: Nick Hurley Date: Thu, 6 Oct 2011 12:02:23 +0200 Subject: [PATCH 02/24] bug 687085 - telemetry for hit/reval/miss by device type. r=mcmanus --- netwerk/protocol/http/nsHttpChannel.cpp | 54 ++++++++++++++++--- .../telemetry/TelemetryHistograms.h | 3 ++ 2 files changed, 50 insertions(+), 7 deletions(-) diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp index 92da4ca43abb..425280d2c4d4 100644 --- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -69,6 +69,11 @@ #include "mozilla/Telemetry.h" #include "nsDOMError.h" +// Device IDs for various cache types +const char kDiskDeviceID[] = "disk"; +const char kMemoryDeviceID[] = "memory"; +const char kOfflineDeviceID[] = "offline"; + // True if the local cache should be bypassed when processing a request. #define BYPASS_LOCAL_CACHE(loadFlags) \ (loadFlags & (nsIRequest::LOAD_BYPASS_CACHE | \ @@ -272,6 +277,23 @@ nsHttpChannel::Connect(bool firstTime) } mozilla::Telemetry::Accumulate( mozilla::Telemetry::HTTP_CACHE_DISPOSITION, kCacheHit); + + char* cacheDeviceID = nsnull; + mCacheEntry->GetDeviceID(&cacheDeviceID); + if (cacheDeviceID) { + if (!strcmp(cacheDeviceID, kDiskDeviceID)) + mozilla::Telemetry::Accumulate( + mozilla::Telemetry::HTTP_DISK_CACHE_DISPOSITION, + kCacheHit); + else if (!strcmp(cacheDeviceID, kMemoryDeviceID)) + mozilla::Telemetry::Accumulate( + mozilla::Telemetry::HTTP_MEMORY_CACHE_DISPOSITION, + kCacheHit); + else if (!strcmp(cacheDeviceID, kOfflineDeviceID)) + mozilla::Telemetry::Accumulate( + mozilla::Telemetry::HTTP_OFFLINE_CACHE_DISPOSITION, + kCacheHit); + } return rv; } else if (mLoadFlags & LOAD_ONLY_FROM_CACHE) { @@ -1070,16 +1092,34 @@ nsHttpChannel::ProcessResponse() break; } + int cacheDisposition; if (!mDidReval) - mozilla::Telemetry::Accumulate( - mozilla::Telemetry::HTTP_CACHE_DISPOSITION, kCacheMissed); + cacheDisposition = kCacheMissed; else if (successfulReval) - mozilla::Telemetry::Accumulate( - mozilla::Telemetry::HTTP_CACHE_DISPOSITION, kCacheHitViaReval); + cacheDisposition = kCacheHitViaReval; else - mozilla::Telemetry::Accumulate( - mozilla::Telemetry::HTTP_CACHE_DISPOSITION, - kCacheMissedViaReval); + cacheDisposition = kCacheMissedViaReval; + + mozilla::Telemetry::Accumulate(mozilla::Telemetry::HTTP_CACHE_DISPOSITION, + cacheDisposition); + if (mCacheEntry) { + char* cacheDeviceID = nsnull; + mCacheEntry->GetDeviceID(&cacheDeviceID); + if (cacheDeviceID) { + if (!strcmp(cacheDeviceID, kDiskDeviceID)) + mozilla::Telemetry::Accumulate( + mozilla::Telemetry::HTTP_DISK_CACHE_DISPOSITION, + cacheDisposition); + else if (!strcmp(cacheDeviceID, kMemoryDeviceID)) + mozilla::Telemetry::Accumulate( + mozilla::Telemetry::HTTP_MEMORY_CACHE_DISPOSITION, + cacheDisposition); + else if (!strcmp(cacheDeviceID, kOfflineDeviceID)) + mozilla::Telemetry::Accumulate( + mozilla::Telemetry::HTTP_OFFLINE_CACHE_DISPOSITION, + cacheDisposition); + } + } return rv; } diff --git a/toolkit/components/telemetry/TelemetryHistograms.h b/toolkit/components/telemetry/TelemetryHistograms.h index 3829dcef4cd0..375794c48799 100644 --- a/toolkit/components/telemetry/TelemetryHistograms.h +++ b/toolkit/components/telemetry/TelemetryHistograms.h @@ -142,6 +142,9 @@ HTTP_HISTOGRAMS(SUB, "subitem: ") #undef HTTP_HISTOGRAMS HISTOGRAM(HTTP_CACHE_DISPOSITION, 1, 5, 5, LINEAR, "HTTP Cache Hit, Reval, Failed-Reval, Miss") +HISTOGRAM(HTTP_DISK_CACHE_DISPOSITION, 1, 5, 5, LINEAR, "HTTP Disk Cache Hit, Reval, Failed-Reval, Miss") +HISTOGRAM(HTTP_MEMORY_CACHE_DISPOSITION, 1, 5, 5, LINEAR, "HTTP Memory Cache Hit, Reval, Failed-Reval, Miss") +HISTOGRAM(HTTP_OFFLINE_CACHE_DISPOSITION, 1, 5, 5, LINEAR, "HTTP Offline Cache Hit, Reval, Failed-Reval, Miss") HISTOGRAM(FIND_PLUGINS, 1, 3000, 10, EXPONENTIAL, "Time spent scanning filesystem for plugins (ms)") HISTOGRAM(CHECK_JAVA_ENABLED, 1, 3000, 10, EXPONENTIAL, "Time spent checking if Java is enabled (ms)") From 910a95477ef00c293604c3b8af3b4273a7e78279 Mon Sep 17 00:00:00 2001 From: Igor Bukanov Date: Fri, 23 Sep 2011 16:14:26 +0200 Subject: [PATCH 03/24] bug 688641 - allocating GC chunks using helper thread. r=wmccloskey --- js/src/jsapi.cpp | 8 +- js/src/jsapi.h | 2 +- js/src/jscntxt.cpp | 25 ++- js/src/jscntxt.h | 9 +- js/src/jsgc.cpp | 455 +++++++++++++++++++++++++++++---------------- js/src/jsgc.h | 128 ++++++++++--- js/src/jslock.cpp | 34 +++- js/src/jslock.h | 9 + 8 files changed, 455 insertions(+), 215 deletions(-) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 4561f18fbcc3..41a112af0984 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -648,8 +648,6 @@ JSRuntime::JSRuntime() protoHazardShape(0), gcSystemAvailableChunkListHead(NULL), gcUserAvailableChunkListHead(NULL), - gcEmptyChunkListHead(NULL), - gcEmptyChunkCount(0), gcKeepAtoms(0), gcBytes(0), gcTriggerBytes(0), @@ -2792,9 +2790,9 @@ JS_GetGCParameter(JSRuntime *rt, JSGCParamKey key) case JSGC_MODE: return uint32(rt->gcMode); case JSGC_UNUSED_CHUNKS: - return uint32(rt->gcEmptyChunkCount); + return uint32(rt->gcChunkPool.getEmptyCount()); case JSGC_TOTAL_CHUNKS: - return uint32(rt->gcChunkSet.count() + rt->gcEmptyChunkCount); + return uint32(rt->gcChunkSet.count() + rt->gcChunkPool.getEmptyCount()); default: JS_ASSERT(key == JSGC_NUMBER); return rt->gcNumber; @@ -4850,7 +4848,7 @@ CompileUCFunctionForPrincipalsCommon(JSContext *cx, JSObject *obj, chars, length, filename, lineno, version)) { return NULL; } - + if (obj && funAtom && !obj->defineProperty(cx, ATOM_TO_JSID(funAtom), ObjectValue(*fun), NULL, NULL, JSPROP_ENUMERATE)) { diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 242aee13e621..cec22285de5a 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -2855,7 +2855,7 @@ typedef enum JSGCParamKey { /* Select GC mode. */ JSGC_MODE = 6, - /* Number of GC chunks waiting to expire. */ + /* Number of cached empty GC chunks. */ JSGC_UNUSED_CHUNKS = 7, /* Total number of allocated GC chunks. */ diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index 5ec00ea30130..4f6980a3c92e 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -476,10 +476,10 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode) ) { JS_ASSERT(!rt->gcRunning); - JS_UNLOCK_GC(rt); #ifdef JS_THREADSAFE - rt->gcHelperThread.waitBackgroundSweepEnd(rt); + rt->gcHelperThread.waitBackgroundSweepEnd(); #endif + JS_UNLOCK_GC(rt); if (last) { #ifdef JS_THREADSAFE @@ -559,10 +559,10 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode) js_ClearContextThread(cx); JS_ASSERT_IF(JS_CLIST_IS_EMPTY(&t->contextList), !t->data.requestDepth); #endif - JS_UNLOCK_GC(rt); #ifdef JS_THREADSAFE - rt->gcHelperThread.waitBackgroundSweepEnd(rt); + rt->gcHelperThread.waitBackgroundSweepEnd(); #endif + JS_UNLOCK_GC(rt); Foreground::delete_(cx); } @@ -1177,7 +1177,10 @@ js_InvokeOperationCallback(JSContext *cx) * We have to wait until the background thread is done in order * to get a correct answer. */ - rt->gcHelperThread.waitBackgroundSweepEnd(rt); + { + AutoLockGC lock(rt); + rt->gcHelperThread.waitBackgroundSweepEnd(); + } if (checkOutOfMemory(rt)) { js_ReportOutOfMemory(cx); return false; @@ -1518,8 +1521,17 @@ JSRuntime::onTooMuchMalloc() JS_FRIEND_API(void *) JSRuntime::onOutOfMemory(void *p, size_t nbytes, JSContext *cx) { + /* + * Retry when we are done with the background sweeping and have stopped + * all the allocations and released the empty GC chunks. + */ + { #ifdef JS_THREADSAFE - gcHelperThread.waitBackgroundSweepEnd(this); + AutoLockGC lock(this); + gcHelperThread.waitBackgroundSweepOrAllocEnd(); +#endif + gcChunkPool.expire(this, true); + } if (!p) p = OffTheBooks::malloc_(nbytes); else if (p == reinterpret_cast(1)) @@ -1528,7 +1540,6 @@ JSRuntime::onOutOfMemory(void *p, size_t nbytes, JSContext *cx) p = OffTheBooks::realloc_(p, nbytes); if (p) return p; -#endif if (cx) js_ReportOutOfMemory(cx); return NULL; diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 0d1c301dbc1a..c66d2183b647 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -411,14 +411,7 @@ struct JSRuntime { */ js::gc::Chunk *gcSystemAvailableChunkListHead; js::gc::Chunk *gcUserAvailableChunkListHead; - - /* - * Singly-linked list of empty chunks and its length. We use the list not - * to release empty chunks immediately so they can be used for future - * allocations. This avoids very high overhead of chunk release/allocation. - */ - js::gc::Chunk *gcEmptyChunkListHead; - size_t gcEmptyChunkCount; + js::gc::ChunkPool gcChunkPool; js::RootedValueMap gcRootsHash; js::GCLocks gcLocksHash; diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index a56293a43643..941160593e4b 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -414,8 +414,138 @@ FinalizeArenas(JSContext *cx, ArenaLists::ArenaList *al, AllocKind thingKind) } } -} /* namespace gc */ -} /* namespace js */ +#ifdef JS_THREADSAFE +inline bool +ChunkPool::wantBackgroundAllocation(JSRuntime *rt) const +{ + /* + * To minimize memory waste we do not want to run the background chunk + * allocation if we have empty chunks or when the runtime needs just few + * of them. + */ + return rt->gcHelperThread.canBackgroundAllocate() && + emptyCount == 0 && + rt->gcChunkSet.count() >= 4; +} +#endif + +/* Must be called with the GC lock taken. */ +inline Chunk * +ChunkPool::get(JSRuntime *rt) +{ + JS_ASSERT(this == &rt->gcChunkPool); + + Chunk *chunk = emptyChunkListHead; + if (chunk) { + JS_ASSERT(emptyCount); + emptyChunkListHead = chunk->info.next; + --emptyCount; + } else { + JS_ASSERT(!emptyCount); + chunk = Chunk::allocate(); + if (!chunk) + return NULL; + } + JS_ASSERT(chunk->unused()); + JS_ASSERT(!rt->gcChunkSet.has(chunk)); + +#ifdef JS_THREADSAFE + if (wantBackgroundAllocation(rt)) + rt->gcHelperThread.startBackgroundAllocationIfIdle(); +#endif + + return chunk; +} + +/* Must be called either during the GC or with the GC lock taken. */ +inline void +ChunkPool::put(JSRuntime *rt, Chunk *chunk) +{ + JS_ASSERT(this == &rt->gcChunkPool); + + size_t initialAge = 0; +#ifdef JS_THREADSAFE + /* + * When we have not yet started the background finalization, we must keep + * empty chunks until we are done with all the sweeping and finalization + * that cannot be done in the background even if shouldShrink() is true. + * This way we can safely call IsAboutToBeFinalized and Cell::isMarked for + * finalized GC things in empty chunks. So we only release the chunk if we + * are called from the background thread. + */ + if (rt->gcHelperThread.sweeping()) { + if (rt->gcHelperThread.shouldShrink()) { + Chunk::release(chunk); + return; + } + + /* + * Set the age to one as we expire chunks early during the background + * sweep so this chunk already survived one GC cycle. + */ + initialAge = 1; + } +#endif + + chunk->info.age = initialAge; + chunk->info.next = emptyChunkListHead; + emptyChunkListHead = chunk; + emptyCount++; +} + +/* Must be called either during the GC or with the GC lock taken. */ +void +ChunkPool::expire(JSRuntime *rt, bool releaseAll) +{ + JS_ASSERT(this == &rt->gcChunkPool); + + /* + * Return old empty chunks to the system while preserving the order of + * other chunks in the list. This way, if the GC runs several times + * without emptying the list, the older chunks will stay at the tail + * and are more likely to reach the max age. + */ + for (Chunk **chunkp = &emptyChunkListHead; *chunkp; ) { + JS_ASSERT(emptyCount); + Chunk *chunk = *chunkp; + JS_ASSERT(chunk->unused()); + JS_ASSERT(!rt->gcChunkSet.has(chunk)); + JS_ASSERT(chunk->info.age <= MAX_EMPTY_CHUNK_AGE); + if (releaseAll || chunk->info.age == MAX_EMPTY_CHUNK_AGE) { + *chunkp = chunk->info.next; + --emptyCount; + Chunk::release(chunk); + } else { + /* Keep the chunk but increase its age. */ + ++chunk->info.age; + chunkp = &chunk->info.next; + } + } + JS_ASSERT_IF(releaseAll, !emptyCount); +} + +/* static */ Chunk * +Chunk::allocate() +{ + Chunk *chunk = static_cast(AllocGCChunk()); + if (!chunk) + return NULL; + chunk->init(); +#ifdef MOZ_GCTIMER + JS_ATOMIC_INCREMENT(&newChunkCount); +#endif + return chunk; +} + +/* static */ inline void +Chunk::release(Chunk *chunk) +{ + JS_ASSERT(chunk); +#ifdef MOZ_GCTIMER + JS_ATOMIC_INCREMENT(&destroyChunkCount); +#endif + FreeGCChunk(chunk); +} void Chunk::init() @@ -511,7 +641,7 @@ Chunk::releaseArena(ArenaHeader *aheader) JSRuntime *rt = comp->rt; #ifdef JS_THREADSAFE AutoLockGC maybeLock; - if (rt->gcHelperThread.sweeping) + if (rt->gcHelperThread.sweeping()) maybeLock.lock(rt); #endif @@ -519,7 +649,7 @@ Chunk::releaseArena(ArenaHeader *aheader) JS_ASSERT(size_t(rt->gcBytes) >= ArenaSize); JS_ASSERT(size_t(comp->gcBytes) >= ArenaSize); #ifdef JS_THREADSAFE - if (rt->gcHelperThread.sweeping) { + if (rt->gcHelperThread.sweeping()) { rt->reduceGCTriggerBytes(GC_HEAP_GROWTH_FACTOR * ArenaSize); comp->reduceGCTriggerBytes(GC_HEAP_GROWTH_FACTOR * ArenaSize); } @@ -540,40 +670,12 @@ Chunk::releaseArena(ArenaHeader *aheader) } else { rt->gcChunkSet.remove(this); removeFromAvailableList(); - - /* - * We keep empty chunks until we are done with finalization to allow - * calling IsAboutToBeFinalized/Cell::isMarked for finalized GC things - * in empty chunks. So we add the chunk to the empty set even during - * GC_SHRINK. - */ - info.age = 0; - info.next = rt->gcEmptyChunkListHead; - rt->gcEmptyChunkListHead = this; - rt->gcEmptyChunkCount++; + rt->gcChunkPool.put(rt, this); } } -inline Chunk * -AllocateGCChunk(JSRuntime *rt) -{ - Chunk *p = static_cast(AllocGCChunk()); -#ifdef MOZ_GCTIMER - if (p) - JS_ATOMIC_INCREMENT(&newChunkCount); -#endif - return p; -} - -inline void -ReleaseGCChunk(JSRuntime *rt, Chunk *p) -{ - JS_ASSERT(p); -#ifdef MOZ_GCTIMER - JS_ATOMIC_INCREMENT(&destroyChunkCount); -#endif - FreeGCChunk(p); -} +} /* namespace gc */ +} /* namespace js */ /* The caller must hold the GC lock. */ static Chunk * @@ -585,25 +687,11 @@ PickChunk(JSCompartment *comp) if (chunk) return chunk; - /* - * We do not have available chunks, either get one from the empty set or - * allocate one. - */ - chunk = rt->gcEmptyChunkListHead; - if (chunk) { - JS_ASSERT(chunk->unused()); - JS_ASSERT(!rt->gcChunkSet.has(chunk)); - JS_ASSERT(rt->gcEmptyChunkCount >= 1); - rt->gcEmptyChunkListHead = chunk->info.next; - rt->gcEmptyChunkCount--; - } else { - chunk = AllocateGCChunk(rt); - if (!chunk) - return NULL; + chunk = rt->gcChunkPool.get(rt); + if (!chunk) + return NULL; - chunk->init(); - rt->gcChunkAllocationSinceLastGC = true; - } + rt->gcChunkAllocationSinceLastGC = true; /* * FIXME bug 583732 - chunk is newly allocated and cannot be present in @@ -612,7 +700,7 @@ PickChunk(JSCompartment *comp) GCChunkSet::AddPtr p = rt->gcChunkSet.lookupForAdd(chunk); JS_ASSERT(!p); if (!rt->gcChunkSet.add(p, chunk)) { - ReleaseGCChunk(rt, chunk); + Chunk::release(chunk); return NULL; } @@ -623,30 +711,6 @@ PickChunk(JSCompartment *comp) return chunk; } -static void -ExpireGCChunks(JSRuntime *rt, JSGCInvocationKind gckind) -{ - AutoLockGC lock(rt); - - /* Return old empty chunks to the system. */ - for (Chunk **chunkp = &rt->gcEmptyChunkListHead; *chunkp; ) { - JS_ASSERT(rt->gcEmptyChunkCount); - Chunk *chunk = *chunkp; - JS_ASSERT(chunk->unused()); - JS_ASSERT(!rt->gcChunkSet.has(chunk)); - JS_ASSERT(chunk->info.age <= MAX_EMPTY_CHUNK_AGE); - if (gckind == GC_SHRINK || chunk->info.age == MAX_EMPTY_CHUNK_AGE) { - *chunkp = chunk->info.next; - --rt->gcEmptyChunkCount; - ReleaseGCChunk(rt, chunk); - } else { - /* Keep the chunk but increase its age. */ - ++chunk->info.age; - chunkp = &chunk->info.next; - } - } -} - JS_FRIEND_API(bool) IsAboutToBeFinalized(JSContext *cx, const void *thing) { @@ -698,7 +762,7 @@ js_InitGC(JSRuntime *rt, uint32 maxbytes) rt->requestDone = JS_NEW_CONDVAR(rt->gcLock); if (!rt->requestDone) return false; - if (!rt->gcHelperThread.init(rt)) + if (!rt->gcHelperThread.init()) return false; #endif @@ -995,20 +1059,19 @@ js_FinishGC(JSRuntime *rt) rt->gcSystemAvailableChunkListHead = NULL; rt->gcUserAvailableChunkListHead = NULL; for (GCChunkSet::Range r(rt->gcChunkSet.all()); !r.empty(); r.popFront()) - ReleaseGCChunk(rt, r.front()); + Chunk::release(r.front()); rt->gcChunkSet.clear(); - for (Chunk *chunk = rt->gcEmptyChunkListHead; chunk; ) { - Chunk *next = chunk->info.next; - ReleaseGCChunk(rt, chunk); - chunk = next; - } - rt->gcEmptyChunkListHead = NULL; - rt->gcEmptyChunkCount = 0; #ifdef JS_THREADSAFE - rt->gcHelperThread.finish(rt); + rt->gcHelperThread.finish(); #endif + /* + * Finish the pool after the background thread stops in case it was doing + * the background sweeping. + */ + rt->gcChunkPool.expire(rt, true); + #ifdef DEBUG if (!rt->gcRootsHash.empty()) CheckLeakedRoots(rt); @@ -1237,7 +1300,7 @@ ArenaLists::allocateFromArena(JSCompartment *comp, AllocKind thingKind) * added new empty arenas. */ JS_ASSERT(*bfs == BFS_RUN); - comp->rt->gcHelperThread.waitBackgroundSweepEnd(comp->rt, false); + comp->rt->gcHelperThread.waitBackgroundSweepEnd(); JS_ASSERT(*bfs == BFS_JUST_FINISHED || *bfs == BFS_DONE); } } @@ -1320,7 +1383,7 @@ ArenaLists::finalizeLater(JSContext *cx, AllocKind thingKind) thingKind == FINALIZE_STRING); #ifdef JS_THREADSAFE - JS_ASSERT(!cx->runtime->gcHelperThread.sweeping); + JS_ASSERT(!cx->runtime->gcHelperThread.sweeping()); ArenaList *al = &arenaLists[thingKind]; if (!al->head) { @@ -1468,8 +1531,10 @@ RunLastDitchGC(JSContext *cx) js_GC(cx, rt->gcTriggerCompartment, GC_NORMAL); #ifdef JS_THREADSAFE - if (rt->gcBytes >= rt->gcMaxBytes) - cx->runtime->gcHelperThread.waitBackgroundSweepEnd(cx->runtime); + if (rt->gcBytes >= rt->gcMaxBytes) { + AutoLockGC lock(rt); + cx->runtime->gcHelperThread.waitBackgroundSweepEnd(); + } #endif } @@ -1999,7 +2064,7 @@ MaybeGC(JSContext *cx) */ int64 now = PRMJ_Now(); if (rt->gcNextFullGCTime && rt->gcNextFullGCTime <= now) { - if (rt->gcChunkAllocationSinceLastGC || rt->gcEmptyChunkListHead) { + if (rt->gcChunkAllocationSinceLastGC) { GCREASON(MAYBEGC); js_GC(cx, NULL, GC_SHRINK); } else { @@ -2015,28 +2080,40 @@ MaybeGC(JSContext *cx) namespace js { bool -GCHelperThread::init(JSRuntime *rt) +GCHelperThread::init() { - if (!(wakeup = PR_NewCondVar(rt->gcLock))) + if (!(wakeup = PR_NewCondVar(runtime()->gcLock))) return false; - if (!(sweepingDone = PR_NewCondVar(rt->gcLock))) + if (!(done = PR_NewCondVar(runtime()->gcLock))) return false; - thread = PR_CreateThread(PR_USER_THREAD, threadMain, rt, PR_PRIORITY_NORMAL, + thread = PR_CreateThread(PR_USER_THREAD, threadMain, this, PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); - return !!thread; + if (!thread) + return false; + backgroundAllocation = (js_GetCPUCount() >= 2); + return true; +} + +inline JSRuntime * +GCHelperThread::runtime() +{ + return reinterpret_cast(reinterpret_cast(this) - offsetof(JSRuntime, gcHelperThread)); } void -GCHelperThread::finish(JSRuntime *rt) +GCHelperThread::finish() { PRThread *join = NULL; { - AutoLockGC lock(rt); - if (thread && !shutdown) { - shutdown = true; - PR_NotifyCondVar(wakeup); + AutoLockGC lock(runtime()); + if (thread && state != SHUTDOWN) { + /* The allocation should have been stopped during the last GC. */ + JS_ASSERT(state == IDLE || state == SWEEPING); + if (state == IDLE) + PR_NotifyCondVar(wakeup); + state = SHUTDOWN; join = thread; } } @@ -2046,66 +2123,114 @@ GCHelperThread::finish(JSRuntime *rt) } if (wakeup) PR_DestroyCondVar(wakeup); - if (sweepingDone) - PR_DestroyCondVar(sweepingDone); + if (done) + PR_DestroyCondVar(done); } /* static */ void GCHelperThread::threadMain(void *arg) { - JSRuntime *rt = static_cast(arg); - rt->gcHelperThread.threadLoop(rt); + static_cast(arg)->threadLoop(); } void -GCHelperThread::threadLoop(JSRuntime *rt) +GCHelperThread::threadLoop() { + JSRuntime *rt = runtime(); AutoLockGC lock(rt); - while (!shutdown) { - /* - * Sweeping can be true here on the first iteration if a GC and the - * corresponding startBackgroundSweep call happen before this thread - * has a chance to run. - */ - if (!sweeping) + + /* + * Even on the first iteration the state can be SHUTDOWN or SWEEPING if + * the stop request or the GC and the corresponding startBackgroundSweep call + * happen before this thread has a chance to run. + */ + for (;;) { + switch (state) { + case SHUTDOWN: + return; + case IDLE: PR_WaitCondVar(wakeup, PR_INTERVAL_NO_TIMEOUT); - if (sweeping) { - AutoUnlockGC unlock(rt); + break; + case SWEEPING: doSweep(); + if (state == SWEEPING) + state = IDLE; + PR_NotifyAllCondVar(done); + break; + case ALLOCATING: + do { + Chunk *chunk; + { + AutoUnlockGC unlock(rt); + chunk = Chunk::allocate(); + } + + /* OOM stops the background allocation. */ + if (!chunk) + break; + rt->gcChunkPool.put(rt, chunk); + } while (state == ALLOCATING && rt->gcChunkPool.wantBackgroundAllocation(rt)); + if (state == ALLOCATING) + state = IDLE; + break; + case CANCEL_ALLOCATION: + state = IDLE; + PR_NotifyAllCondVar(done); + break; } - sweeping = false; - PR_NotifyAllCondVar(sweepingDone); } } bool -GCHelperThread::prepareForBackgroundSweep(JSContext *context) { - size_t maxArenaLists = MAX_BACKGROUND_FINALIZE_KINDS * context->runtime->compartments.length(); +GCHelperThread::prepareForBackgroundSweep(JSContext *cx) +{ + JS_ASSERT(cx->runtime == runtime()); + JS_ASSERT(state == IDLE); + size_t maxArenaLists = MAX_BACKGROUND_FINALIZE_KINDS * runtime()->compartments.length(); if (!finalizeVector.reserve(maxArenaLists)) return false; - cx = context; + context = cx; return true; } -void -GCHelperThread::startBackgroundSweep(JSRuntime *rt, JSGCInvocationKind gckind) +/* Must be called with the GC lock taken. */ +inline void +GCHelperThread::startBackgroundSweep(bool shouldShrink) { /* The caller takes the GC lock. */ - JS_ASSERT(!sweeping); - lastGCKind = gckind; - sweeping = true; + JS_ASSERT(state == IDLE); + shrinkFlag = shouldShrink; + state = SWEEPING; PR_NotifyCondVar(wakeup); } +/* Must be called with the GC lock taken. */ void -GCHelperThread::waitBackgroundSweepEnd(JSRuntime *rt, bool gcUnlocked) +GCHelperThread::waitBackgroundSweepEnd() { - AutoLockGC maybeLock; - if (gcUnlocked) - maybeLock.lock(rt); - while (sweeping) - PR_WaitCondVar(sweepingDone, PR_INTERVAL_NO_TIMEOUT); + while (state == SWEEPING) + PR_WaitCondVar(done, PR_INTERVAL_NO_TIMEOUT); +} + +/* Must be called with the GC lock taken. */ +void +GCHelperThread::waitBackgroundSweepOrAllocEnd() +{ + if (state == ALLOCATING) + state = CANCEL_ALLOCATION; + while (state == SWEEPING || state == CANCEL_ALLOCATION) + PR_WaitCondVar(done, PR_INTERVAL_NO_TIMEOUT); +} + +/* Must be called with the GC lock taken. */ +inline void +GCHelperThread::startBackgroundAllocationIfIdle() +{ + if (state == IDLE) { + state = ALLOCATING; + PR_NotifyCondVar(wakeup); + } } JS_FRIEND_API(void) @@ -2127,20 +2252,30 @@ GCHelperThread::replenishAndFreeLater(void *ptr) Foreground::free_(ptr); } +/* Must be called with the GC lock taken. */ void GCHelperThread::doSweep() { - JS_ASSERT(cx); + JS_ASSERT(context); + + /* + * Expire the chunks released during the GC so they will be available to + * the rest of the system immediately. + */ + JSRuntime *rt = runtime(); + rt->gcChunkPool.expire(rt, shouldShrink()); + + AutoUnlockGC unlock(rt); /* * We must finalize in the insert order, see comments in * finalizeObjects. */ for (ArenaHeader **i = finalizeVector.begin(); i != finalizeVector.end(); ++i) - ArenaLists::backgroundFinalize(cx, *i); + ArenaLists::backgroundFinalize(context, *i); finalizeVector.resize(0); - ExpireGCChunks(cx->runtime, lastGCKind); - cx = NULL; + + context = NULL; if (freeCursor) { void **array = freeCursorEnd - FREE_ARRAY_LENGTH; @@ -2384,7 +2519,7 @@ SweepPhase(JSContext *cx, GCMarker *gcmarker, JSGCInvocationKind gckind GCTIMER_ * use IsAboutToBeFinalized(). * This is done on the GCHelperThread if JS_THREADSAFE is defined. */ - ExpireGCChunks(rt, gckind); + rt->gcChunkPool.expire(rt, gckind == GC_SHRINK); #endif GCTIMESTAMP(sweepDestroyEnd); @@ -2397,8 +2532,7 @@ SweepPhase(JSContext *cx, GCMarker *gcmarker, JSGCInvocationKind gckind GCTIMER_ * * In a JS_THREADSAFE build, the calling thread must be rt->gcThread and each * other thread must be either outside all requests or blocked waiting for GC - * to finish. Note that the caller does not hold rt->gcLock. - * If comp is set, we perform a single-compartment GC. + * to finish. The caller must hold rt->gcLock. */ static void MarkAndSweep(JSContext *cx, JSGCInvocationKind gckind GCTIMER_PARAM) @@ -2413,6 +2547,8 @@ MarkAndSweep(JSContext *cx, JSGCInvocationKind gckind GCTIMER_PARAM) /* Reset malloc counter. */ rt->resetGCMallocBytes(); + AutoUnlockGC unlock(rt); + GCMarker gcmarker(cx); JS_ASSERT(IS_GC_MARKING_TRACER(&gcmarker)); JS_ASSERT(gcmarker.getMarkColor() == BLACK); @@ -2649,29 +2785,29 @@ GCCycle(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIMER_P rt->gcCurrentCompartment = comp; rt->gcMarkAndSweep = true; - { - AutoUnlockGC unlock(rt); #ifdef JS_THREADSAFE - /* - * As we about to purge caches and clear the mark bits we must wait - * for any background finalization to finish. - */ - JS_ASSERT(!cx->gcBackgroundFree); - rt->gcHelperThread.waitBackgroundSweepEnd(rt); - if (gckind != GC_LAST_CONTEXT && rt->state != JSRTS_LANDING) { - if (rt->gcHelperThread.prepareForBackgroundSweep(cx)) - cx->gcBackgroundFree = &rt->gcHelperThread; - } -#endif - MarkAndSweep(cx, gckind GCTIMER_ARG); + /* + * As we about to purge caches and clear the mark bits we must wait for + * any background finalization to finish. We must also wait for the + * background allocation to finish so we can avoid taking the GC lock + * when manipulating the chunks during the GC. + */ + JS_ASSERT(!cx->gcBackgroundFree); + rt->gcHelperThread.waitBackgroundSweepOrAllocEnd(); + if (gckind != GC_LAST_CONTEXT && rt->state != JSRTS_LANDING) { + if (rt->gcHelperThread.prepareForBackgroundSweep(cx)) + cx->gcBackgroundFree = &rt->gcHelperThread; } +#endif + + MarkAndSweep(cx, gckind GCTIMER_ARG); #ifdef JS_THREADSAFE if (gckind != GC_LAST_CONTEXT && rt->state != JSRTS_LANDING) { JS_ASSERT(cx->gcBackgroundFree == &rt->gcHelperThread); cx->gcBackgroundFree = NULL; - rt->gcHelperThread.startBackgroundSweep(rt, gckind); + rt->gcHelperThread.startBackgroundSweep(gckind == GC_SHRINK); } else { JS_ASSERT(!cx->gcBackgroundFree); } @@ -2744,9 +2880,6 @@ js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind) } { -#ifdef JS_THREADSAFE - rt->gcHelperThread.waitBackgroundSweepEnd(rt); -#endif /* Lock out other GC allocator and collector invocations. */ AutoLockGC lock(rt); rt->gcPoke = false; @@ -2803,7 +2936,7 @@ TraceRuntime(JSTracer *trc) AutoLockGC lock(rt); AutoGCSession gcsession(cx); - rt->gcHelperThread.waitBackgroundSweepEnd(rt, false); + rt->gcHelperThread.waitBackgroundSweepEnd(); AutoUnlockGC unlock(rt); AutoCopyFreeListToArenas copy(rt); @@ -2867,7 +3000,7 @@ IterateCompartmentsArenasCells(JSContext *cx, void *data, AutoLockGC lock(rt); AutoGCSession gcsession(cx); #ifdef JS_THREADSAFE - rt->gcHelperThread.waitBackgroundSweepEnd(rt, false); + rt->gcHelperThread.waitBackgroundSweepEnd(); #endif AutoUnlockGC unlock(rt); @@ -2901,7 +3034,7 @@ IterateCells(JSContext *cx, JSCompartment *compartment, AllocKind thingKind, AutoLockGC lock(rt); AutoGCSession gcsession(cx); #ifdef JS_THREADSAFE - rt->gcHelperThread.waitBackgroundSweepEnd(rt, false); + rt->gcHelperThread.waitBackgroundSweepEnd(); #endif AutoUnlockGC unlock(rt); diff --git a/js/src/jsgc.h b/js/src/jsgc.h index 7cd570267b2d..4c46af3c3472 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -641,8 +641,6 @@ struct Chunk { return addr; } - void init(); - bool unused() const { return info.numFree == ArenasPerChunk; } @@ -657,11 +655,42 @@ struct Chunk { ArenaHeader *allocateArena(JSCompartment *comp, AllocKind kind); void releaseArena(ArenaHeader *aheader); + + static Chunk *allocate(); + static inline void release(Chunk *chunk); + + private: + inline void init(); }; JS_STATIC_ASSERT(sizeof(Chunk) <= GC_CHUNK_SIZE); JS_STATIC_ASSERT(sizeof(Chunk) + BytesPerArena > GC_CHUNK_SIZE); +class ChunkPool { + Chunk *emptyChunkListHead; + size_t emptyCount; + + public: + ChunkPool() + : emptyChunkListHead(NULL), + emptyCount(0) { } + + size_t getEmptyCount() const { + return emptyCount; + } + + inline bool wantBackgroundAllocation(JSRuntime *rt) const; + + /* Must be called with the GC lock taken. */ + inline Chunk *get(JSRuntime *rt); + + /* Must be called either during the GC or with the GC lock taken. */ + inline void put(JSRuntime *rt, Chunk *chunk); + + /* Must be called either during the GC or with the GC lock taken. */ + void expire(JSRuntime *rt, bool releaseAll); +}; + inline uintptr_t Cell::address() const { @@ -1288,26 +1317,35 @@ namespace js { #ifdef JS_THREADSAFE -/* - * During the finalization we do not free immediately. Rather we add the - * corresponding pointers to a buffer which we later release on a separated - * thread. - * - * The buffer is implemented as a vector of 64K arrays of pointers, not as a - * simple vector, to avoid realloc calls during the vector growth and to not - * bloat the binary size of the inlined freeLater method. Any OOM during - * buffer growth results in the pointer being freed immediately. - */ class GCHelperThread { + enum State { + IDLE, + SWEEPING, + ALLOCATING, + CANCEL_ALLOCATION, + SHUTDOWN + }; + + /* + * During the finalization we do not free immediately. Rather we add the + * corresponding pointers to a buffer which we later release on a + * separated thread. + * + * The buffer is implemented as a vector of 64K arrays of pointers, not as + * a simple vector, to avoid realloc calls during the vector growth and to + * not bloat the binary size of the inlined freeLater method. Any OOM + * during buffer growth results in the pointer being freed immediately. + */ static const size_t FREE_ARRAY_SIZE = size_t(1) << 16; static const size_t FREE_ARRAY_LENGTH = FREE_ARRAY_SIZE / sizeof(void *); - JSContext *cx; PRThread* thread; PRCondVar* wakeup; - PRCondVar* sweepingDone; - bool shutdown; - JSGCInvocationKind lastGCKind; + PRCondVar* done; + volatile State state; + + JSContext *context; + bool shrinkFlag; Vector freeVector; void **freeCursor; @@ -1315,6 +1353,8 @@ class GCHelperThread { Vector finalizeVector; + bool backgroundAllocation; + friend struct js::gc::ArenaLists; JS_FRIEND_API(void) @@ -1328,38 +1368,70 @@ class GCHelperThread { } static void threadMain(void* arg); + void threadLoop(); - void threadLoop(JSRuntime *rt); + /* Must be called with the GC lock taken. */ void doSweep(); public: GCHelperThread() : thread(NULL), wakeup(NULL), - sweepingDone(NULL), - shutdown(false), + done(NULL), + state(IDLE), freeCursor(NULL), freeCursorEnd(NULL), - sweeping(false) { } + backgroundAllocation(true) + { } - volatile bool sweeping; - bool init(JSRuntime *rt); - void finish(JSRuntime *rt); + inline JSRuntime *runtime(); - /* Must be called with GC lock taken. */ - void startBackgroundSweep(JSRuntime *rt, JSGCInvocationKind gckind); + bool init(); + void finish(); - void waitBackgroundSweepEnd(JSRuntime *rt, bool gcUnlocked = true); + /* Must be called with the GC lock taken. */ + inline void startBackgroundSweep(bool shouldShrink); + + /* Must be called with the GC lock taken. */ + void waitBackgroundSweepEnd(); + + /* Must be called with the GC lock taken. */ + void waitBackgroundSweepOrAllocEnd(); + + /* Must be called with the GC lock taken. */ + inline void startBackgroundAllocationIfIdle(); + + bool canBackgroundAllocate() const { + return backgroundAllocation; + } + + void disableBackgroundAllocation() { + backgroundAllocation = false; + } + + /* + * Outside the GC lock may give true answer when in fact the sweeping has + * been done. + */ + bool sweeping() const { + return state == SWEEPING; + } + + bool shouldShrink() const { + JS_ASSERT(sweeping()); + return shrinkFlag; + } void freeLater(void *ptr) { - JS_ASSERT(!sweeping); + JS_ASSERT(!sweeping()); if (freeCursor != freeCursorEnd) *freeCursor++ = ptr; else replenishAndFreeLater(ptr); } - bool prepareForBackgroundSweep(JSContext *context); + /* Must be called with the GC lock taken. */ + bool prepareForBackgroundSweep(JSContext *cx); }; #endif /* JS_THREADSAFE */ diff --git a/js/src/jslock.cpp b/js/src/jslock.cpp index a9bb5c2301c1..831423656f78 100644 --- a/js/src/jslock.cpp +++ b/js/src/jslock.cpp @@ -44,6 +44,13 @@ */ #include #include + +#ifdef XP_WIN +# include "jswin.h" +#else +# include +#endif + #include "jspubtd.h" #include "jsutil.h" #include "jstypes.h" @@ -73,7 +80,6 @@ extern long __cdecl _InterlockedCompareExchange(long *volatile dest, long exchange, long comp); JS_END_EXTERN_C #pragma intrinsic(_InterlockedCompareExchange) - JS_STATIC_ASSERT(sizeof(jsword) == sizeof(long)); static JS_ALWAYS_INLINE int @@ -92,11 +98,12 @@ NativeCompareAndSwap(volatile jsword *w, jsword ov, jsword nv) } #elif defined(_MSC_VER) && (defined(_M_AMD64) || defined(_M_X64)) -JS_BEGIN_EXTERN_C -extern long long __cdecl -_InterlockedCompareExchange64(long long *volatile dest, long long exchange, long long comp); -JS_END_EXTERN_C +/* + * Compared with the _InterlockedCompareExchange in the 32 bit case above MSVC + * declares _InterlockedCompareExchange64 through . + */ #pragma intrinsic(_InterlockedCompareExchange64) +JS_STATIC_ASSERT(sizeof(jsword) == sizeof(long long)); static JS_ALWAYS_INLINE int NativeCompareAndSwap(volatile jsword *w, jsword ov, jsword nv) @@ -304,6 +311,23 @@ js_AtomicClearMask(volatile jsword *w, jsword mask) } while (!js_CompareAndSwap(w, ov, nv)); } +unsigned +js_GetCPUCount() +{ + static unsigned ncpus = 0; + if (ncpus == 0) { +# ifdef XP_WIN + SYSTEM_INFO sysinfo; + GetSystemInfo(&sysinfo); + ncpus = unsigned(sysinfo.dwNumberOfProcessors); +# else + long n = sysconf(_SC_NPROCESSORS_ONLN); + ncpus = (n > 0) ? unsigned(n) : 1; +# endif + } + return ncpus; +} + #ifndef NSPR_LOCK struct JSFatLock { diff --git a/js/src/jslock.h b/js/src/jslock.h index d8246b505acc..add2651fe6f6 100644 --- a/js/src/jslock.h +++ b/js/src/jslock.h @@ -198,6 +198,9 @@ js_AtomicClearMask(volatile jsword *w, jsword mask); #define JS_ATOMIC_SET_MASK(w, mask) js_AtomicSetMask(w, mask) #define JS_ATOMIC_CLEAR_MASK(w, mask) js_AtomicClearMask(w, mask) +extern unsigned +js_GetCPUCount(); + #else static inline JSBool @@ -209,6 +212,12 @@ js_CompareAndSwap(jsword *w, jsword ov, jsword nv) #define JS_ATOMIC_SET_MASK(w, mask) (*(w) |= (mask)) #define JS_ATOMIC_CLEAR_MASK(w, mask) (*(w) &= ~(mask)) +static inline unsigned +js_GetCPUCount() +{ + return 1; +} + #endif #ifdef __cplusplus From 1bdc76e889853124c611fd850f362febd2505de6 Mon Sep 17 00:00:00 2001 From: Ed Morley Date: Thu, 6 Oct 2011 11:25:25 +0100 Subject: [PATCH 04/24] Backout f09c204296f8 (bug 627699) for bustage --- widget/src/gtk2/gtk2compat.h | 64 +------------------------ widget/src/gtk2/mozcontainer.c | 86 ++++++++++++++-------------------- 2 files changed, 36 insertions(+), 114 deletions(-) diff --git a/widget/src/gtk2/gtk2compat.h b/widget/src/gtk2/gtk2compat.h index d867f767d00a..46680cc69618 100644 --- a/widget/src/gtk2/gtk2compat.h +++ b/widget/src/gtk2/gtk2compat.h @@ -109,70 +109,11 @@ gtk_widget_get_allocation(GtkWidget *widget, GtkAllocation *allocation) *allocation = widget->allocation; } -static inline void -gtk_widget_set_allocation(GtkWidget *widget, const GtkAllocation *allocation); -{ - widget->allocation = *allocation; -} - static inline gboolean gdk_window_is_destroyed(GdkWindow *window) { return GDK_WINDOW_OBJECT(window)->destroyed; } - -static inline void -gtk_widget_set_can_focus(GtkWidget *widget, gboolean can_focus) -{ - if (can_focus) - GTK_WIDGET_SET_FLAGS (container, GTK_CAN_FOCUS); - else - GTK_WIDGET_UNSET_FLAGS (container, GTK_CAN_FOCUS); -} - -static inline gboolean -gtk_widget_get_visible(GtkWidget *widget) -{ - return GTK_WIDGET_VISIBLE (widget); -} - -static inline void -gtk_widget_set_window(GtkWidget *widget, GdkWindow *window) -{ - widget->window = window; -} -#endif - -#if !GTK_CHECK_VERSION(2, 20, 0) -static inline void -gtk_widget_set_mapped(GtkWidget *widget, gboolean mapped) -{ - if (mapped) - GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED); - else - GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED); -} - -static inline gboolean -gtk_widget_get_mapped(GtkWidget *widget) -{ - return GTK_WIDGET_MAPPED (widget); -} - -static inline void -gtk_widget_set_realized(GtkWidget *widget, gboolean realized) -{ - if (realized) - GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED); - else - GTK_WIDGET_UNSET_FLAGS(widget, GTK_REALIZED); -} - -static inline gboolean -gtk_widget_get_realized(GtkWidget *widget) -{ - return GTK_WIDGET_REALIZED (widget); -} #endif #if !GTK_CHECK_VERSION(2, 22, 0) @@ -190,13 +131,12 @@ gdk_drag_context_get_actions(GdkDragContext *context) #endif #if !GTK_CHECK_VERSION(2, 24, 0) -#ifdef GDK_WINDOW_XDISPLAY static inline GdkWindow * gdk_x11_window_lookup_for_display(GdkDisplay *display, Window window) { return gdk_window_lookup_for_display(display, window); } -#endif + static inline GdkDisplay * gdk_window_get_display(GdkWindow *window) { @@ -210,13 +150,11 @@ gdk_window_get_screen (GdkWindow *window) } #endif -#ifdef GDK_WINDOW_XWINDOW static inline Window gdk_x11_window_get_xid(GdkWindow *window) { return(GDK_WINDOW_XWINDOW(window)); } -#endif #ifdef __cplusplus } diff --git a/widget/src/gtk2/mozcontainer.c b/widget/src/gtk2/mozcontainer.c index 8bec19913c54..68e4ebfdb683 100644 --- a/widget/src/gtk2/mozcontainer.c +++ b/widget/src/gtk2/mozcontainer.c @@ -45,10 +45,6 @@ #include "maiRedundantObjectFactory.h" #endif -#if defined(MOZ_WIDGET_GTK2) -#include "gtk2compat.h" -#endif - /* init methods */ static void moz_container_class_init (MozContainerClass *klass); static void moz_container_init (MozContainer *container); @@ -203,16 +199,15 @@ moz_container_class_init (MozContainerClass *klass) void moz_container_init (MozContainer *container) { - gtk_widget_set_can_focus(GTK_WIDGET(container), TRUE); - gtk_container_set_resize_mode(GTK_CONTAINER(container), GTK_RESIZE_IMMEDIATE); - gtk_widget_set_redraw_on_allocate(GTK_WIDGET(container), FALSE); + GTK_WIDGET_SET_FLAGS(container, GTK_CAN_FOCUS); + container->container.resize_mode = GTK_RESIZE_IMMEDIATE; + gtk_widget_set_redraw_on_allocate(GTK_WIDGET(container), + FALSE); -#if defined(MOZ_WIDGET_GTK2) /* Mozilla uses the the gdbrgb colormap and visual throughout the backend so for widgets we just use that colormap instead of the default one. */ gtk_widget_set_colormap(GTK_WIDGET(container), gdk_rgb_get_colormap()); -#endif } void @@ -225,30 +220,30 @@ moz_container_map (GtkWidget *widget) g_return_if_fail (IS_MOZ_CONTAINER(widget)); container = MOZ_CONTAINER (widget); - gtk_widget_set_mapped(widget, TRUE); + GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED); tmp_list = container->children; while (tmp_list) { tmp_child = ((MozContainerChild *)tmp_list->data)->widget; - if (gtk_widget_get_visible(tmp_child)) { - if (!gtk_widget_get_mapped(tmp_child)) + if (GTK_WIDGET_VISIBLE(tmp_child)) { + if (!GTK_WIDGET_MAPPED(tmp_child)) gtk_widget_map(tmp_child); } tmp_list = tmp_list->next; } - gdk_window_show (gtk_widget_get_window(widget)); + gdk_window_show (widget->window); } void moz_container_unmap (GtkWidget *widget) { g_return_if_fail (IS_MOZ_CONTAINER (widget)); + + GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED); - gtk_widget_set_mapped(widget, FALSE); - - gdk_window_hide (gtk_widget_get_window(widget)); + gdk_window_hide (widget->window); } void @@ -257,13 +252,12 @@ moz_container_realize (GtkWidget *widget) GdkWindowAttr attributes; gint attributes_mask = 0; MozContainer *container; - GtkAllocation allocation; g_return_if_fail(IS_MOZ_CONTAINER(widget)); container = MOZ_CONTAINER(widget); - gtk_widget_set_realized(widget, TRUE); + GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED); /* create the shell window */ @@ -276,38 +270,28 @@ moz_container_realize (GtkWidget *widget) GDK_POINTER_MOTION_HINT_MASK | #endif GDK_POINTER_MOTION_MASK); - gtk_widget_get_allocation(widget, &allocation); - attributes.x = allocation.x; - attributes.y = allocation.y; - attributes.width = allocation.width; - attributes.height = allocation.height; + attributes.x = widget->allocation.x; + attributes.y = widget->allocation.y; + attributes.width = widget->allocation.width; + attributes.height = widget->allocation.height; attributes.wclass = GDK_INPUT_OUTPUT; attributes.visual = gtk_widget_get_visual (widget); -#if defined(MOZ_WIDGET_GTK2) attributes.colormap = gtk_widget_get_colormap (widget); -#endif attributes.window_type = GDK_WINDOW_CHILD; - attributes_mask |= GDK_WA_VISUAL | GDK_WA_X | GDK_WA_Y; -#if defined(MOZ_WIDGET_GTK2) - attributes_mask |= GDK_WA_COLORMAP; -#endif + attributes_mask |= GDK_WA_VISUAL | GDK_WA_COLORMAP | + GDK_WA_X | GDK_WA_Y; - gtk_widget_set_window(widget, gdk_window_new (gtk_widget_get_parent_window (widget), - &attributes, attributes_mask)); + widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), + &attributes, attributes_mask); /* printf("widget->window is %p\n", (void *)widget->window); */ - gdk_window_set_user_data (gtk_widget_get_window(widget), container); + gdk_window_set_user_data (widget->window, container); -#if defined(MOZ_WIDGET_GTK2) widget->style = gtk_style_attach (widget->style, widget->window); -#endif -/* TODO GTK3? */ -#if defined(MOZ_WIDGET_GTK2) /* set the back pixmap to None so that you don't end up with the gtk default which is BlackPixel */ gdk_window_set_back_pixmap (widget->window, NULL, FALSE); -#endif } void @@ -331,16 +315,15 @@ moz_container_size_allocate (GtkWidget *widget, /* short circuit if you can */ container = MOZ_CONTAINER (widget); - gtk_widget_get_allocation(widget, &tmp_allocation); if (!container->children && - tmp_allocation.x == allocation->x && - tmp_allocation.y == allocation->y && - tmp_allocation.width == allocation->width && - tmp_allocation.height == allocation->height) { + widget->allocation.x == allocation->x && + widget->allocation.y == allocation->y && + widget->allocation.width == allocation->width && + widget->allocation.height == allocation->height) { return; } - gtk_widget_set_allocation(widget, allocation); + widget->allocation = *allocation; tmp_list = container->children; @@ -352,12 +335,12 @@ moz_container_size_allocate (GtkWidget *widget, tmp_list = tmp_list->next; } - if (gtk_widget_get_realized(widget)) { - gdk_window_move_resize(gtk_widget_get_window(widget), - allocation->x, - allocation->y, - allocation->width, - allocation->height); + if (GTK_WIDGET_REALIZED (widget)) { + gdk_window_move_resize(widget->window, + widget->allocation.x, + widget->allocation.y, + widget->allocation.width, + widget->allocation.height); } } @@ -400,7 +383,7 @@ moz_container_remove (GtkContainer *container, GtkWidget *child_widget) * the parent_window if the child_widget is placed in another * container. */ - if (parent_window != gtk_widget_get_window(GTK_WIDGET(container))) + if (parent_window != GTK_WIDGET(container)->window) gtk_widget_set_parent_window(child_widget, parent_window); g_object_unref(parent_window); @@ -438,11 +421,12 @@ moz_container_allocate_child (MozContainer *container, GtkAllocation allocation; GtkRequisition requisition; - gtk_widget_get_allocation (child->widget, &allocation); allocation.x = child->x; allocation.y = child->y; /* gtk_widget_get_child_requisition (child->widget, &requisition); */ /* gtk_widget_size_request (child->widget, &requisition); */ + allocation.width = child->widget->allocation.width; + allocation.height = child->widget->allocation.height; gtk_widget_size_allocate (child->widget, &allocation); } From fa5f69859ad02504f4968e313ed9ac935197f1f7 Mon Sep 17 00:00:00 2001 From: Lucas Rocha Date: Thu, 6 Oct 2011 15:48:31 +0100 Subject: [PATCH 05/24] Bug 692071 - Keep awesome panel open when context menu is dismissed (r=mfinkle) --- mobile/chrome/content/AwesomePanel.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/mobile/chrome/content/AwesomePanel.js b/mobile/chrome/content/AwesomePanel.js index f5a0b95e3dfd..cc231f431d59 100644 --- a/mobile/chrome/content/AwesomePanel.js +++ b/mobile/chrome/content/AwesomePanel.js @@ -28,9 +28,19 @@ var AwesomeScreen = { ] }, + _popupShowing: false, handleEvent: function(aEvent) { switch (aEvent.type) { + case "PopupChanged" : + this._popupShowing = (aEvent.detail != null); + break; case "TapDown" : + // If a popup has been shown on top of the active + // awesome panel (e.g. context menu), the panel should + // not be dismissed on TapDown. + if (this._popupShowing) + break; + let target = aEvent.target; while (target && this._targets.indexOf(target) == -1) target = target.parentNode; @@ -51,6 +61,7 @@ var AwesomeScreen = { BrowserUI._editURI(); this.container.hidden = this.headers.hidden = false; window.addEventListener("TapDown", this, false); + window.addEventListener("PopupChanged", this, false); } if (aPanel) { @@ -67,6 +78,7 @@ var AwesomeScreen = { BrowserUI._edit.detachController(); BrowserUI.popDialog(); window.removeEventListener("TapDown", this, false); + window.removeEventListener("PopupChanged", this, false); } if (this._activePanel) From 7f938c05b89aa9d9bb7d2333f4dd4ec857821046 Mon Sep 17 00:00:00 2001 From: Lucas Rocha Date: Thu, 6 Oct 2011 15:48:31 +0100 Subject: [PATCH 06/24] Bug 692071 - Ensure awesome panel is closed before each awesome screen test (r=mfinkle) --- mobile/chrome/tests/browser_awesomescreen.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mobile/chrome/tests/browser_awesomescreen.js b/mobile/chrome/tests/browser_awesomescreen.js index 4fe3b5d03b7d..132a2316db8a 100644 --- a/mobile/chrome/tests/browser_awesomescreen.js +++ b/mobile/chrome/tests/browser_awesomescreen.js @@ -25,6 +25,10 @@ function runNextTest() { if (gTests.length > 0) { gCurrentTest = gTests.shift(); info(gCurrentTest.desc); + + // Ensure all tests start with hidden awesome screen + AwesomeScreen.activePanel = null; + gCurrentTest.run(); } else { @@ -187,7 +191,6 @@ gTests.push({ }); setTimeout(function() { - AwesomeScreen.activePanel = null; runNextTest(); }, 0); } @@ -282,8 +285,6 @@ gTests.push({ edit.clickSelectsAll = oldClickSelectsAll; - AwesomeScreen.activePanel = null; - // Ensure the tab is well closed before doing the rest of the code, otherwise // this cause some bugs with the composition events let tabCount = Browser.tabs.length; @@ -348,7 +349,6 @@ gTests.push({ self.onPopupReady(); }, 500); } else { - AwesomeScreen.activePanel = null; runNextTest(); } } From 36d6328e9e050b7b22ece612ba5d795617c5e8bf Mon Sep 17 00:00:00 2001 From: Lucas Rocha Date: Thu, 6 Oct 2011 15:48:31 +0100 Subject: [PATCH 07/24] Bug 692071 - Add browser test for popup being dismissed on top of awesome panel (r=mfinkle) --- mobile/chrome/tests/browser_awesomescreen.js | 33 ++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/mobile/chrome/tests/browser_awesomescreen.js b/mobile/chrome/tests/browser_awesomescreen.js index 132a2316db8a..6283881b1dfc 100644 --- a/mobile/chrome/tests/browser_awesomescreen.js +++ b/mobile/chrome/tests/browser_awesomescreen.js @@ -435,3 +435,36 @@ gTests.push({ } }); +// Case: Test context popup dismiss on top of awesome panel +gTests.push({ + desc: "Case: Test context popup dismiss on top of awesome panel", + + run: function() { + waitForNavigationPanel(gCurrentTest.onPopupReady); + AllPagesList.doCommand(); + }, + + onPopupReady: function() { + EventUtils.synthesizeMouse(AllPagesList.panel, AllPagesList.panel.width / 2, + AllPagesList.panel.height / 2, { type: "mousedown" }); + + // Simulate a long tap + setTimeout(function(self) { + EventUtils.synthesizeMouse(AllPagesList.panel, AllPagesList.panel.width / 2, + AllPagesList.panel.height / 2, { type: "mouseup" }); + + let contextContainer = document.getElementById("context-container"); + + ok(!AllPagesList.panel.hidden, "The context popup is still visible after long tap"); + ok(!contextContainer.hidden, "The context popup is visible after long tap"); + + EventUtils.synthesizeMouse(AllPagesList.panel, 0, 0, {}); + + ok(contextContainer.hidden, "The context popup is not visible after tap"); + ok(!AllPagesList.panel.hidden, "The awesome panel is still visible after popup is dismissed"); + + AwesomeScreen.activePanel = null; + runNextTest(); + }, 500, this); + } +}); From ac86b93435db385d466bb99e14ae8e432763953d Mon Sep 17 00:00:00 2001 From: Joel Maher Date: Thu, 6 Oct 2011 10:51:03 -0400 Subject: [PATCH 08/24] Bug 691425 - allow for reftest/mochitest android crashes to be dumped in the logs just like desktop crashes. r=ctalbert --- build/mobile/remoteautomation.py | 12 ++++++++++++ testing/mochitest/runtestsremote.py | 1 + 2 files changed, 13 insertions(+) diff --git a/build/mobile/remoteautomation.py b/build/mobile/remoteautomation.py index de38525ecff7..d1d321aa7283 100644 --- a/build/mobile/remoteautomation.py +++ b/build/mobile/remoteautomation.py @@ -40,6 +40,9 @@ import time import sys import os import socket +import automationutils +import tempfile +import shutil from automation import Automation from devicemanager import DeviceManager, NetworkTools @@ -105,6 +108,15 @@ class RemoteAutomation(Automation): return status + def checkForCrashes(self, directory, symbolsPath): + dumpDir = tempfile.mkdtemp() + self._devicemanager.getDirectory(self._remoteProfile + '/minidumps/', dumpDir) + automationutils.checkForCrashes(dumpDir, symbolsPath, self.lastTestSeen) + try: + shutil.rmtree(dumpDir) + except: + print "WARNING: unable to remove directory: %s" % (dumpDir) + def buildCommandLine(self, app, debuggerInfo, profileDir, testURL, extraArgs): # If remote profile is specified, use that instead if (self._remoteProfile): diff --git a/testing/mochitest/runtestsremote.py b/testing/mochitest/runtestsremote.py index 62b59580c14d..1a41f4cebcff 100644 --- a/testing/mochitest/runtestsremote.py +++ b/testing/mochitest/runtestsremote.py @@ -192,6 +192,7 @@ class MochiRemote(Mochitest): self._dm = devmgr self.runSSLTunnel = False self.remoteProfile = options.remoteTestRoot + "/profile" + self._automation.setRemoteProfile(self.remoteProfile) self.remoteLog = options.remoteLogFile def cleanup(self, manifest, options): From b5c9f7bcb5c733de415a39ec1bfaae4b20ef4f22 Mon Sep 17 00:00:00 2001 From: William Lachance Date: Thu, 6 Oct 2011 10:51:03 -0400 Subject: [PATCH 09/24] Bug 691815 - devicemanagerSUT should say what file it failed to pull when pull fails. r=jmaher --- build/mobile/devicemanagerSUT.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/mobile/devicemanagerSUT.py b/build/mobile/devicemanagerSUT.py index c8340655a05a..fffe65abec3f 100644 --- a/build/mobile/devicemanagerSUT.py +++ b/build/mobile/devicemanagerSUT.py @@ -684,7 +684,7 @@ class DeviceManagerSUT(DeviceManager): return None # prompt should follow read_exact(len(prompt), buffer, 'could not find prompt') - print 'DeviceManager: error pulling file: %s' % error_str + print "DeviceManager: error pulling file '%s': %s" % (remoteFile, error_str) return None # read file data From 30e9deddd9967a4a760453b8c11b1f25663f601a Mon Sep 17 00:00:00 2001 From: Joel Maher Date: Thu, 6 Oct 2011 10:51:03 -0400 Subject: [PATCH 10/24] Bug 676274 - refactor specialpowers so we can use the api in mochitest-chrome without specialpowers. r=ted, a=test-only. --- content/events/test/test_bug648573.html | 8 +- content/events/test/test_bug662678.html | 10 +- docshell/test/chrome/bug293235_window.xul | 10 +- docshell/test/chrome/bug298622_window.xul | 6 + docshell/test/chrome/bug301397_window.xul | 6 + docshell/test/chrome/bug321671_window.xul | 6 + docshell/test/chrome/bug396649_window.xul | 6 + docshell/test/chrome/bug582176_window.xul | 6 + docshell/test/chrome/bug89419_window.xul | 6 + ...rror_events_abort_transactions_iframe.html | 2 +- .../test/event_propagation_iframe.html | 2 +- layout/generic/test/file_bug514732_window.xul | 6 + layout/style/test/test_pointer-events.html | 7 +- testing/mochitest/Makefile.in | 1 - testing/mochitest/browser-test-overlay.xul | 1 + testing/mochitest/browser-test.js | 35 +- testing/mochitest/harness-overlay.xul | 6 + testing/mochitest/jar.mn | 4 +- testing/mochitest/redirect.html | 13 +- testing/mochitest/redirect.js | 45 -- testing/mochitest/runtests.py | 1 - .../components/SpecialPowersObserver.js | 220 +++------ .../specialpowers/content/specialpowers.js | 436 +++--------------- testing/mochitest/specialpowers/jar.mn | 2 + .../tests/SimpleTest/ChromePowers.js | 120 +++++ .../mochitest/tests/SimpleTest/Makefile.in | 2 + .../mochitest/tests/SimpleTest/SimpleTest.js | 5 + .../SimpleTest/SpecialPowersObserverAPI.js | 208 +++++++++ .../mochitest/tests/SimpleTest/TestRunner.js | 2 + .../tests/SimpleTest/specialpowersAPI.js | 391 ++++++++++++++++ 30 files changed, 961 insertions(+), 612 deletions(-) delete mode 100644 testing/mochitest/redirect.js create mode 100644 testing/mochitest/tests/SimpleTest/ChromePowers.js create mode 100644 testing/mochitest/tests/SimpleTest/SpecialPowersObserverAPI.js create mode 100644 testing/mochitest/tests/SimpleTest/specialpowersAPI.js diff --git a/content/events/test/test_bug648573.html b/content/events/test/test_bug648573.html index 1b3f4a9943a8..85043b34a272 100644 --- a/content/events/test/test_bug648573.html +++ b/content/events/test/test_bug648573.html @@ -1,7 +1,7 @@ Test for Bug 648573 @@ -19,7 +19,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=648573 /** Test for Bug 648573 **/ -ok(!SpecialPowers.DOMWindowUtils.mayHaveTouchEventListeners, +var utils = SpecialPowers.getDOMWindowUtils(window); + +ok(!utils.mayHaveTouchEventListeners, "There shouldn't be any touch event listeners yet."); ok("createTouch" in document, "Should have createTouch function"); @@ -101,7 +103,7 @@ for (var i = 0; i < events.length; ++i) { runEventTest(events[i]); } -ok(SpecialPowers.DOMWindowUtils.mayHaveTouchEventListeners, +ok(utils.mayHaveTouchEventListeners, "There should be touch event listeners."); diff --git a/content/events/test/test_bug662678.html b/content/events/test/test_bug662678.html index 334baf9bf631..dfcf5dcf8452 100644 --- a/content/events/test/test_bug662678.html +++ b/content/events/test/test_bug662678.html @@ -18,8 +18,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=662678 diff --git a/docshell/test/chrome/bug293235_window.xul b/docshell/test/chrome/bug293235_window.xul index fa003f3f70af..88e73d62d43b 100644 --- a/docshell/test/chrome/bug293235_window.xul +++ b/docshell/test/chrome/bug293235_window.xul @@ -8,13 +8,19 @@ onload="setTimeout(nextTest,0);" title="bug 293235 test"> + diff --git a/dom/indexedDB/test/error_events_abort_transactions_iframe.html b/dom/indexedDB/test/error_events_abort_transactions_iframe.html index a14821b1f2e4..2d385682e80b 100644 --- a/dom/indexedDB/test/error_events_abort_transactions_iframe.html +++ b/dom/indexedDB/test/error_events_abort_transactions_iframe.html @@ -50,7 +50,7 @@ function testSteps() { netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); - let uri = SpecialPowers.getDocumentURIObject(window.document); + let uri = window.parent.SpecialPowers.getDocumentURIObject(window.document); Components.classes["@mozilla.org/permissionmanager;1"] .getService(Components.interfaces.nsIPermissionManager) .add(uri, "indexedDB", diff --git a/dom/indexedDB/test/event_propagation_iframe.html b/dom/indexedDB/test/event_propagation_iframe.html index d90a79d4bf13..7fd1942d5603 100644 --- a/dom/indexedDB/test/event_propagation_iframe.html +++ b/dom/indexedDB/test/event_propagation_iframe.html @@ -89,7 +89,7 @@ function testSteps() { netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); - let uri = SpecialPowers.getDocumentURIObject(window.document); + let uri = window.parent.SpecialPowers.getDocumentURIObject(window.document); Components.classes["@mozilla.org/permissionmanager;1"] .getService(Components.interfaces.nsIPermissionManager) .add(uri, "indexedDB", diff --git a/layout/generic/test/file_bug514732_window.xul b/layout/generic/test/file_bug514732_window.xul index 37d5e464b982..d8cad98de3fc 100755 --- a/layout/generic/test/file_bug514732_window.xul +++ b/layout/generic/test/file_bug514732_window.xul @@ -8,6 +8,12 @@ onload="setTimeout(nextTest,0);" title="bug 514732 test"> + diff --git a/layout/style/test/test_pointer-events.html b/layout/style/test/test_pointer-events.html index a90e3c3a8ec9..c274270553a5 100644 --- a/layout/style/test/test_pointer-events.html +++ b/layout/style/test/test_pointer-events.html @@ -4,7 +4,7 @@ Test for pointer-events in HTML - +