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)));
}
Beispiel #23
0
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();
 }