1 /*
2  * Copyright 2017 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "SkTaskGroup2D.h"
9 
start()10 void SkTaskGroup2D::start() {
11     fThreadsGroup->batch(fThreadCnt, [this](int threadId){
12         this->work(threadId);
13     });
14 }
15 
addColumn()16 void SkTaskGroup2D::addColumn() {
17     SkASSERT(!fIsFinishing); // we're not supposed to add more work after the calling of finish
18     fWidth++;
19 }
20 
finish()21 void SkTaskGroup2D::finish() {
22     fIsFinishing.store(true, std::memory_order_relaxed);
23     fThreadsGroup->wait();
24 }
25 
work(int threadId)26 void SkSpinningTaskGroup2D::work(int threadId) {
27     int workCol = 0;
28     int initCol = 0;
29 
30     while (true) {
31         SkASSERT(workCol <= fWidth);
32         if (this->isFinishing() && workCol >= fWidth) {
33             return;
34         }
35 
36         // Note that row = threadId
37         if (workCol < fWidth && fKernel->work2D(threadId, workCol, threadId)) {
38             workCol++;
39         } else {
40             // Initialize something if we can't work
41             this->initAnUninitializedColumn(initCol, threadId);
42         }
43     }
44 }
45 
work(int threadId)46 void SkFlexibleTaskGroup2D::work(int threadId) {
47     int row = threadId;
48     int initCol = 0;
49     int numRowsCompleted = 0;
50     std::vector<bool> completedRows(fHeight, false);
51 
52     // Only keep fHeight - numRowsCompleted number of threads looping. When rows are about to
53     // complete, this strategy keeps the contention low.
54     while (threadId < fHeight - numRowsCompleted) {
55         RowData& rowData = fRowData[row];
56 
57         // The Android roller somehow gets a false-positive compile warning/error about the try-lock
58         // and unlock process. Hence we disable -Wthread-safety-analysis to bypass it.
59 #ifdef __clang__
60 #pragma clang diagnostic push
61 #pragma clang diagnostic ignored "-Wthread-safety-analysis"
62 #endif
63         if (rowData.fMutex.try_acquire()) {
64             while (rowData.fNextColumn < fWidth &&
65                     fKernel->work2D(row, rowData.fNextColumn, threadId)) {
66                 rowData.fNextColumn++;
67             }
68             // isFinishing can never go from true to false. Once it's true, we count how many rows
69             // are completed (out of work). If that count reaches fHeight, then we're out of work
70             // for the whole group and we can stop.
71             if (rowData.fNextColumn == fWidth && this->isFinishing()) {
72                 numRowsCompleted += (completedRows[row] == false);
73                 completedRows[row] = true; // so we won't count this row twice
74             }
75             rowData.fMutex.release();
76         }
77 #ifdef __clang__
78 #pragma clang diagnostic pop
79 #endif
80 
81         // By reaching here, we're either unable to acquire the row, or out of work, or blocked by
82         // initialization
83         row = (row + 1) % fHeight; // Move to the next row
84         this->initAnUninitializedColumn(initCol, threadId); // Initialize something
85     }
86 }
87