TEST_F(ImageFrameGeneratorTest, incompleteDecodeBecomesCompleteMultiThreaded) { setFrameStatus(ImageFrame::FramePartial); char buffer[100 * 100 * 4]; m_generator->decodeAndScale(0, imageInfo(), buffer, 100 * 4); EXPECT_EQ(1, m_decodeRequestCount); EXPECT_EQ(0, m_decodersDestroyed); SkData* data = m_generator->refEncodedData(); EXPECT_EQ(nullptr, data); // LocalFrame can now be decoded completely. setFrameStatus(ImageFrame::FrameComplete); addNewData(); // addNewData is calling m_generator->setData with allDataReceived == false, which means that // refEncodedData should return null. data = m_generator->refEncodedData(); EXPECT_EQ(nullptr, data); OwnPtr<WebThread> thread = adoptPtr(Platform::current()->createThread("DecodeThread")); thread->taskRunner()->postTask(BLINK_FROM_HERE, new Task(threadSafeBind(&decodeThreadMain, AllowCrossThreadAccess(m_generator.get())))); thread.clear(); EXPECT_EQ(2, m_decodeRequestCount); EXPECT_EQ(1, m_decodersDestroyed); // Decoder created again. m_generator->decodeAndScale(0, imageInfo(), buffer, 100 * 4); EXPECT_EQ(3, m_decodeRequestCount); addNewData(true); data = m_generator->refEncodedData(); ASSERT_TRUE(data); // To prevent data writting, SkData::unique() should be false. ASSERT_TRUE(!data->unique()); // Thread will also ref and unref the data. thread = adoptPtr(Platform::current()->createThread("RefEncodedDataThread")); thread->taskRunner()->postTask(BLINK_FROM_HERE, new Task(threadSafeBind(&decodeThreadWithRefEncodedMain, AllowCrossThreadAccess(m_generator.get())))); thread.clear(); EXPECT_EQ(4, m_decodeRequestCount); data->unref(); // m_generator is holding the only reference to SkData now. ASSERT_TRUE(data->unique()); data = m_generator->refEncodedData(); ASSERT_TRUE(data && !data->unique()); // Delete generator, and SkData should have the only reference. m_generator = nullptr; ASSERT_TRUE(data->unique()); data->unref(); }
TEST(SpinLockTest, Torture) { char sharedBuffer[bufferSize]; OwnPtr<WebThread> thread1 = adoptPtr(Platform::current()->createThread("thread1")); OwnPtr<WebThread> thread2 = adoptPtr(Platform::current()->createThread("thread2")); thread1->taskRunner()->postTask(BLINK_FROM_HERE, new Task(threadSafeBind(&threadMain, AllowCrossThreadAccess(static_cast<char*>(sharedBuffer))))); thread2->taskRunner()->postTask(BLINK_FROM_HERE, new Task(threadSafeBind(&threadMain, AllowCrossThreadAccess(static_cast<char*>(sharedBuffer))))); thread1.clear(); thread2.clear(); }
void DataConsumerHandleTestUtil::ReplayingHandle::Context::notify() { if (!m_client) return; ASSERT(m_readerThread); m_readerThread->taskRunner()->postTask(BLINK_FROM_HERE, new Task(threadSafeBind(&Context::notifyInternal, this))); }
TEST_F(DeferredImageDecoderTest, decodeOnOtherThread) { m_lazyDecoder->setData(*m_data, true); RefPtr<SkImage> image = m_lazyDecoder->createFrameAtIndex(0); ASSERT_TRUE(image); EXPECT_EQ(1, image->width()); EXPECT_EQ(1, image->height()); SkPictureRecorder recorder; SkCanvas* tempCanvas = recorder.beginRecording(100, 100, 0, 0); tempCanvas->drawImage(image.get(), 0, 0); RefPtr<SkPicture> picture = adoptRef(recorder.endRecording()); EXPECT_EQ(0, m_decodeRequestCount); // Create a thread to rasterize SkPicture. OwnPtr<WebThread> thread = adoptPtr(Platform::current()->createThread("RasterThread")); thread->taskRunner()->postTask(BLINK_FROM_HERE, new Task(threadSafeBind(&rasterizeMain, AllowCrossThreadAccess(m_surface->getCanvas()), AllowCrossThreadAccess(picture.get())))); thread.clear(); EXPECT_EQ(0, m_decodeRequestCount); SkBitmap canvasBitmap; canvasBitmap.allocN32Pixels(100, 100); ASSERT_TRUE(m_surface->getCanvas()->readPixels(&canvasBitmap, 0, 0)); SkAutoLockPixels autoLock(canvasBitmap); EXPECT_EQ(SkColorSetARGB(255, 255, 255, 255), canvasBitmap.getColor(0, 0)); }
void WorkerInspectorProxy::disconnectFromInspector() { m_pageInspector = nullptr; if (!m_workerThread) return; addDebuggerTaskForWorker(FROM_HERE, adoptPtr(new Task(threadSafeBind(disconnectFromWorkerGlobalScopeInspectorTask, AllowCrossThreadAccess(m_workerThread))))); }
// Attempts to run some simple script for |worker|. void checkWorkerCanExecuteScript(WorkerThread* worker) { OwnPtr<WebWaitableEvent> waitEvent = adoptPtr(Platform::current()->createWaitableEvent()); worker->backingThread().platformThread().taskRunner()->postTask(FROM_HERE, threadSafeBind(&CompositorWorkerManagerTest::executeScriptInWorker, AllowCrossThreadAccess(this), AllowCrossThreadAccess(worker), AllowCrossThreadAccess(waitEvent.get()))); waitEvent->wait(); }
// Tests that a new WebThread is created if all existing workers are terminated before a new worker is created. TEST_F(CompositorWorkerThreadTest, TerminateFirstAndCreateSecond) { // Create the first worker, wait until it is initialized, and terminate it. OwnPtr<WaitableEvent> creationEvent = adoptPtr(new WaitableEvent()); OwnPtr<CompositorWorkerThread> compositorWorker = createCompositorWorker(creationEvent.get()); WebThreadSupportingGC* firstThread = CompositorWorkerThread::sharedBackingThread(); waitForWaitableEventAfterIteratingCurrentLoop(creationEvent.get()); ASSERT(compositorWorker->isolate()); compositorWorker->terminateAndWait(); // Create the second worker. Verify that the second worker lives in a different WebThread since the first // thread will have been destroyed after destroying the first worker. creationEvent = adoptPtr(new WaitableEvent()); compositorWorker = createCompositorWorker(creationEvent.get()); WebThreadSupportingGC* secondThread = CompositorWorkerThread::sharedBackingThread(); EXPECT_NE(firstThread, secondThread); waitForWaitableEventAfterIteratingCurrentLoop(creationEvent.get()); // Jump over to the worker's thread to verify that the Isolate is set up correctly and execute script. OwnPtr<WaitableEvent> checkEvent = adoptPtr(new WaitableEvent()); secondThread->platformThread().getWebTaskRunner()->postTask(BLINK_FROM_HERE, threadSafeBind(&checkCurrentIsolate, AllowCrossThreadAccess(compositorWorker->isolate()), AllowCrossThreadAccess(checkEvent.get()))); waitForWaitableEventAfterIteratingCurrentLoop(checkEvent.get()); checkWorkerCanExecuteScript(compositorWorker.get()); compositorWorker->terminateAndWait(); }
void WorkerInspectorProxy::sendMessageToInspector(const String& message) { if (!m_workerThread) return; addDebuggerTaskForWorker(FROM_HERE, adoptPtr(new Task(threadSafeBind(dispatchOnInspectorBackendTask, message, AllowCrossThreadAccess(m_workerThread))))); m_workerThread->interruptAndDispatchInspectorCommands(); }
DataConsumerHandleTestUtil::Thread::Thread(const char* name, InitializationPolicy initializationPolicy) : m_thread(WebThreadSupportingGC::create(name)) , m_initializationPolicy(initializationPolicy) , m_waitableEvent(adoptPtr(Platform::current()->createWaitableEvent())) { m_thread->postTask(BLINK_FROM_HERE, new Task(threadSafeBind(&Thread::initialize, AllowCrossThreadAccess(this)))); m_waitableEvent->wait(); }
void WorkerInspectorProxy::connectToInspector(WorkerInspectorProxy::PageInspector* pageInspector) { if (!m_workerThread) return; ASSERT(!m_pageInspector); m_pageInspector = pageInspector; addDebuggerTaskForWorker(FROM_HERE, adoptPtr(new Task(threadSafeBind(connectToWorkerGlobalScopeInspectorTask, AllowCrossThreadAccess(m_workerThread))))); }
void AsyncAudioDecoder::decode(DOMArrayBuffer* audioData, float sampleRate, AudioBufferCallback* successCallback, AudioBufferCallback* errorCallback, ScriptPromiseResolver* resolver, AbstractAudioContext* context) { RefPtr<AudioBus> bus = createBusFromInMemoryAudioFile(audioData->data(), audioData->byteLength(), false, sampleRate); // Decoding is finished, but we need to do the callbacks on the main thread. // The leaked reference to audioBuffer is picked up in notifyComplete. Platform::current()->mainThread()->taskRunner()->postTask(BLINK_FROM_HERE, threadSafeBind(&AsyncAudioDecoder::notifyComplete, AllowCrossThreadAccess(audioData), successCallback, errorCallback, bus.release().leakRef(), resolver, context)); }
void CompositorProxy::disconnect() { m_connected = false; if (isMainThread()) decrementCountForElement(m_elementId); else Platform::current()->mainThread()->taskRunner()->postTask(FROM_HERE, threadSafeBind(&decrementCountForElement, m_elementId)); }
CompositorProxy::CompositorProxy(uint64_t elementId, uint32_t attributeFlags) : m_elementId(elementId) , m_bitfieldsSupported(attributeFlags) { ASSERT(isControlThread()); ASSERT(sanityCheckAttributeFlags(m_bitfieldsSupported)); Platform::current()->mainThread()->taskRunner()->postTask(FROM_HERE, threadSafeBind(&incrementProxyCountForElement, m_elementId)); }
void DeferredTaskHandler::requestToDeleteHandlersOnMainThread() { ASSERT(isGraphOwner()); ASSERT(isAudioThread()); if (m_renderingOrphanHandlers.isEmpty()) return; m_deletableOrphanHandlers.appendVector(m_renderingOrphanHandlers); m_renderingOrphanHandlers.clear(); Platform::current()->mainThread()->taskRunner()->postTask(BLINK_FROM_HERE, threadSafeBind(&DeferredTaskHandler::deleteHandlersOnMainThread, PassRefPtr<DeferredTaskHandler>(this))); }
void HTMLParserThread::postTask(PassOwnPtr<CrossThreadClosure> closure) { ASSERT(isMainThread()); if (!m_thread) { m_thread = WebThreadSupportingGC::create("HTMLParserThread"); postTask(threadSafeBind(&HTMLParserThread::setupHTMLParserThread, AllowCrossThreadAccess(this))); } m_thread->postTask(BLINK_FROM_HERE, closure); }
void HRTFDatabaseLoader::waitForLoaderThreadCompletion() { if (!m_thread) return; TaskSynchronizer sync; // TODO(alexclarke): Should this be posted as a loading task? m_thread->taskRunner()->postTask(BLINK_FROM_HERE, new Task(threadSafeBind(&HRTFDatabaseLoader::cleanupTask, AllowCrossThreadAccess(this), AllowCrossThreadAccess(&sync)))); sync.waitForTaskCompletion(); m_thread.clear(); }
void CanvasAsyncBlobCreator::initiateJpegEncoding(const double& quality) { m_jpegEncoderState = JPEGImageEncoderState::create(m_size, quality, m_encodedImage.get()); if (!m_jpegEncoderState) { Platform::current()->mainThread()->getWebTaskRunner()->postTask(BLINK_FROM_HERE, bind(&BlobCallback::handleEvent, m_callback, nullptr)); m_selfRef.clear(); return; } BackgroundTaskRunner::TaskSize taskSize = (m_size.height() * m_size.width() >= LongTaskImageSizeThreshold) ? BackgroundTaskRunner::TaskSizeLongRunningTask : BackgroundTaskRunner::TaskSizeShortRunningTask; BackgroundTaskRunner::postOnBackgroundThread(BLINK_FROM_HERE, threadSafeBind(&CanvasAsyncBlobCreator::encodeImageOnEncoderThread, AllowCrossThreadAccess(this), quality), taskSize); }
void WebCLProgram::callbackProxy(cl_program program, void* userData) { OwnPtr<WebCLProgramHolder> holder = adoptPtr(static_cast<WebCLProgramHolder*>(userData)); holder->program2 = program; if (isMainThread()) { callbackProxyOnMainThread(holder.release()); return; } Platform::current()->mainThread()->postTask(FROM_HERE, threadSafeBind(&WebCLProgram::callbackProxyOnMainThread, holder.release())); }
void HRTFDatabaseLoader::loadAsynchronously() { ASSERT(isMainThread()); MutexLocker locker(m_lock); if (!m_hrtfDatabase && !m_thread) { // Start the asynchronous database loading process. m_thread = adoptPtr(Platform::current()->createThread("HRTF database loader")); // TODO(alexclarke): Should this be posted as a loading task? m_thread->taskRunner()->postTask(BLINK_FROM_HERE, new Task(threadSafeBind(&HRTFDatabaseLoader::loadTask, AllowCrossThreadAccess(this)))); } }
void CompositorWorkerManager::shutdownBackingThread() { MutexLocker lock(m_mutex); ASSERT(m_thread->isCurrentThread()); ASSERT(m_workerCount > 0); --m_workerCount; if (m_workerCount == 0) { m_thread->shutdown(); Platform::current()->mainThread()->postTask(FROM_HERE, threadSafeBind(destroyThread, AllowCrossThreadAccess(m_thread.leakPtr()))); m_thread = nullptr; } }
void OfflineAudioDestinationHandler::startRendering() { ASSERT(isMainThread()); ASSERT(m_renderThread); ASSERT(m_renderTarget); if (!m_renderTarget) return; // Rendering was not started. Starting now. if (!m_isRenderingStarted) { m_isRenderingStarted = true; m_renderThread->taskRunner()->postTask(BLINK_FROM_HERE, threadSafeBind(&OfflineAudioDestinationHandler::startOfflineRendering, this)); return; } // Rendering is already started, which implicitly means we resume the // rendering by calling |doOfflineRendering| on the render thread. m_renderThread->taskRunner()->postTask(BLINK_FROM_HERE, threadSafeBind(&OfflineAudioDestinationHandler::doOfflineRendering, this)); }
void AsyncAudioDecoder::decodeAsync(DOMArrayBuffer* audioData, float sampleRate, AudioBufferCallback* successCallback, AudioBufferCallback* errorCallback, ScriptPromiseResolver* resolver, AbstractAudioContext* context) { ASSERT(isMainThread()); ASSERT(audioData); if (!audioData) return; // Add a ref to keep audioData alive until completion of decoding. RefPtr<DOMArrayBuffer> audioDataRef(audioData); // The leak references to successCallback and errorCallback are picked up on notifyComplete. m_thread->taskRunner()->postTask(BLINK_FROM_HERE, new Task(threadSafeBind(&AsyncAudioDecoder::decode, AllowCrossThreadAccess(audioDataRef.release().leakRef()), sampleRate, successCallback, errorCallback, resolver, context))); }
void WebCL::callbackProxy(cl_event event, cl_int type, void* userData) { OwnPtr<WebCLHolder> holder = adoptPtr(static_cast<WebCLHolder*>(userData)); holder->event = event; holder->type = type; if (!isMainThread()) { Platform::current()->mainThread()->taskRunner()->postTask(BLINK_FROM_HERE, threadSafeBind(&WebCL::callbackProxyOnMainThread, holder.release())); return; } callbackProxyOnMainThread(holder.release()); }
void HTMLParserThread::shutdown() { ASSERT(isMainThread()); ASSERT(s_sharedThread); // currentThread will always be non-null in production, but can be null in Chromium unit tests. if (Platform::current()->currentThread() && s_sharedThread->m_thread) { WaitableEvent waitableEvent; s_sharedThread->postTask(threadSafeBind(&HTMLParserThread::cleanupHTMLParserThread, AllowCrossThreadAccess(s_sharedThread), AllowCrossThreadAccess(&waitableEvent))); SafePointScope scope(BlinkGC::HeapPointersOnStack); waitableEvent.wait(); } delete s_sharedThread; s_sharedThread = nullptr; }
void AbstractAudioContext::resolvePromisesForResume() { // This runs inside the AbstractAudioContext's lock when handling pre-render tasks. ASSERT(isAudioThread()); ASSERT(isGraphOwner()); // Resolve any pending promises created by resume(). Only do this if we haven't already started // resolving these promises. This gets called very often and it takes some time to resolve the // promises in the main thread. if (!m_isResolvingResumePromises && m_resumeResolvers.size() > 0) { m_isResolvingResumePromises = true; Platform::current()->mainThread()->postTask(FROM_HERE, threadSafeBind(&AbstractAudioContext::resolvePromisesForResumeOnMainThread, this)); } }
void CanvasAsyncBlobCreator::scheduleAsyncBlobCreation(bool canUseIdlePeriodScheduling, double quality) { // TODO: async blob creation should be supported in worker_pool threads as well. but right now blink does not have that ASSERT(isMainThread()); // Make self-reference to keep this object alive until the final task completes m_selfRef = this; // At the time being, progressive encoding is only applicable to png image format, // and thus idle tasks scheduling can only be applied to png image format. // TODO(xlai): Progressive encoding on jpeg and webp image formats (crbug.com/571398, crbug.com/571399) if (canUseIdlePeriodScheduling) { ASSERT(m_mimeType == "image/png"); Platform::current()->mainThread()->scheduler()->postIdleTask(BLINK_FROM_HERE, bind<double>(&CanvasAsyncBlobCreator::initiatePngEncoding, this)); } else if (m_mimeType == "image/jpeg") { Platform::current()->mainThread()->getWebTaskRunner()->postTask(BLINK_FROM_HERE, bind(&CanvasAsyncBlobCreator::initiateJpegEncoding, this, quality)); } else { BackgroundTaskRunner::TaskSize taskSize = (m_size.height() * m_size.width() >= LongTaskImageSizeThreshold) ? BackgroundTaskRunner::TaskSizeLongRunningTask : BackgroundTaskRunner::TaskSizeShortRunningTask; BackgroundTaskRunner::postOnBackgroundThread(BLINK_FROM_HERE, threadSafeBind(&CanvasAsyncBlobCreator::encodeImageOnEncoderThread, AllowCrossThreadAccess(this), quality), taskSize); } }
TEST_F(ImageFrameGeneratorTest, incompleteDecodeBecomesCompleteMultiThreaded) { setFrameStatus(ImageFrame::FramePartial); char buffer[100 * 100 * 4]; m_generator->decodeAndScale(imageInfo(), 0, buffer, 100 * 4); EXPECT_EQ(1, m_decodeRequestCount); EXPECT_EQ(0, m_decodersDestroyed); // LocalFrame can now be decoded completely. setFrameStatus(ImageFrame::FrameComplete); addNewData(); OwnPtr<WebThread> thread = adoptPtr(Platform::current()->createThread("DecodeThread")); thread->postTask(FROM_HERE, new Task(threadSafeBind(&decodeThreadMain, AllowCrossThreadAccess(m_generator.get())))); thread.clear(); EXPECT_EQ(2, m_decodeRequestCount); EXPECT_EQ(1, m_decodersDestroyed); // Decoder created again. m_generator->decodeAndScale(imageInfo(), 0, buffer, 100 * 4); EXPECT_EQ(3, m_decodeRequestCount); }
void updateReaderNoLock(Token token) { if (token != m_token) { // This request is not fresh. Ignore it. return; } ASSERT(m_readerThread); ASSERT(m_reader); if (m_readerThread->isCurrentThread()) { if (m_isInTwoPhaseRead) { // We are waiting for the two-phase read completion. m_isUpdateWaitingForEndRead = true; return; } // Unregister the old one, then register the new one. m_reader = nullptr; m_reader = m_handle->obtainReader(m_client); return; } ++m_token; m_readerThread->taskRunner()->postTask(BLINK_FROM_HERE, threadSafeBind(&Context::updateReader, this, m_token)); }
void WebSharedWorkerImpl::workerThreadTerminated() { Platform::current()->mainThread()->taskRunner()->postTask(BLINK_FROM_HERE, threadSafeBind(&WebSharedWorkerImpl::workerThreadTerminatedOnMainThread, AllowCrossThreadAccess(this))); }
// Attempts to run some simple script for |worker|. void checkWorkerCanExecuteScript(WorkerThread* worker) { OwnPtr<WaitableEvent> waitEvent = adoptPtr(new WaitableEvent()); worker->backingThread().platformThread().getWebTaskRunner()->postTask(BLINK_FROM_HERE, threadSafeBind(&CompositorWorkerThreadTest::executeScriptInWorker, AllowCrossThreadAccess(this), AllowCrossThreadAccess(worker), AllowCrossThreadAccess(waitEvent.get()))); waitEvent->wait(); }