xref: /aosp_15_r20/external/libgav1/src/utils/threadpool.cc (revision 095378508e87ed692bf8dfeb34008b65b3735891)
1*09537850SAkhilesh Sanikop // Copyright 2019 The libgav1 Authors
2*09537850SAkhilesh Sanikop //
3*09537850SAkhilesh Sanikop // Licensed under the Apache License, Version 2.0 (the "License");
4*09537850SAkhilesh Sanikop // you may not use this file except in compliance with the License.
5*09537850SAkhilesh Sanikop // You may obtain a copy of the License at
6*09537850SAkhilesh Sanikop //
7*09537850SAkhilesh Sanikop //      http://www.apache.org/licenses/LICENSE-2.0
8*09537850SAkhilesh Sanikop //
9*09537850SAkhilesh Sanikop // Unless required by applicable law or agreed to in writing, software
10*09537850SAkhilesh Sanikop // distributed under the License is distributed on an "AS IS" BASIS,
11*09537850SAkhilesh Sanikop // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*09537850SAkhilesh Sanikop // See the License for the specific language governing permissions and
13*09537850SAkhilesh Sanikop // limitations under the License.
14*09537850SAkhilesh Sanikop 
15*09537850SAkhilesh Sanikop #include "src/utils/threadpool.h"
16*09537850SAkhilesh Sanikop 
17*09537850SAkhilesh Sanikop #if defined(_MSC_VER)
18*09537850SAkhilesh Sanikop #include <process.h>
19*09537850SAkhilesh Sanikop #include <windows.h>
20*09537850SAkhilesh Sanikop #else  // defined(_MSC_VER)
21*09537850SAkhilesh Sanikop #include <pthread.h>
22*09537850SAkhilesh Sanikop #endif  // defined(_MSC_VER)
23*09537850SAkhilesh Sanikop #if defined(__ANDROID__) || defined(__GLIBC__)
24*09537850SAkhilesh Sanikop #include <sys/types.h>
25*09537850SAkhilesh Sanikop #include <unistd.h>
26*09537850SAkhilesh Sanikop #endif
27*09537850SAkhilesh Sanikop #include <algorithm>
28*09537850SAkhilesh Sanikop #include <cassert>
29*09537850SAkhilesh Sanikop #include <cinttypes>
30*09537850SAkhilesh Sanikop #include <cstddef>
31*09537850SAkhilesh Sanikop #include <cstdint>
32*09537850SAkhilesh Sanikop #include <cstdio>
33*09537850SAkhilesh Sanikop #include <cstring>
34*09537850SAkhilesh Sanikop #include <new>
35*09537850SAkhilesh Sanikop #include <utility>
36*09537850SAkhilesh Sanikop 
37*09537850SAkhilesh Sanikop #if defined(__ANDROID__)
38*09537850SAkhilesh Sanikop #include <chrono>  // NOLINT (unapproved c++11 header)
39*09537850SAkhilesh Sanikop #endif
40*09537850SAkhilesh Sanikop 
41*09537850SAkhilesh Sanikop // Define the GetTid() function, a wrapper for the gettid() system call in
42*09537850SAkhilesh Sanikop // Linux.
43*09537850SAkhilesh Sanikop #if defined(__ANDROID__)
GetTid()44*09537850SAkhilesh Sanikop static pid_t GetTid() { return gettid(); }
45*09537850SAkhilesh Sanikop #elif defined(__GLIBC__)
46*09537850SAkhilesh Sanikop // The glibc wrapper for the gettid() system call was added in glibc 2.30.
47*09537850SAkhilesh Sanikop // Emulate it for older versions of glibc.
48*09537850SAkhilesh Sanikop #if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 30)
GetTid()49*09537850SAkhilesh Sanikop static pid_t GetTid() { return gettid(); }
50*09537850SAkhilesh Sanikop #else  // Older than glibc 2.30
51*09537850SAkhilesh Sanikop #include <sys/syscall.h>
52*09537850SAkhilesh Sanikop 
GetTid()53*09537850SAkhilesh Sanikop static pid_t GetTid() { return static_cast<pid_t>(syscall(SYS_gettid)); }
54*09537850SAkhilesh Sanikop #endif  // glibc 2.30 or later.
55*09537850SAkhilesh Sanikop #endif  // defined(__GLIBC__)
56*09537850SAkhilesh Sanikop 
57*09537850SAkhilesh Sanikop namespace libgav1 {
58*09537850SAkhilesh Sanikop 
59*09537850SAkhilesh Sanikop #if defined(__ANDROID__)
60*09537850SAkhilesh Sanikop namespace {
61*09537850SAkhilesh Sanikop 
62*09537850SAkhilesh Sanikop using Clock = std::chrono::steady_clock;
63*09537850SAkhilesh Sanikop using Duration = Clock::duration;
64*09537850SAkhilesh Sanikop constexpr Duration kBusyWaitDuration =
65*09537850SAkhilesh Sanikop     std::chrono::duration_cast<Duration>(std::chrono::duration<double>(2e-3));
66*09537850SAkhilesh Sanikop 
67*09537850SAkhilesh Sanikop }  // namespace
68*09537850SAkhilesh Sanikop #endif  // defined(__ANDROID__)
69*09537850SAkhilesh Sanikop 
70*09537850SAkhilesh Sanikop // static
Create(int num_threads)71*09537850SAkhilesh Sanikop std::unique_ptr<ThreadPool> ThreadPool::Create(int num_threads) {
72*09537850SAkhilesh Sanikop   return Create(/*name_prefix=*/"", num_threads);
73*09537850SAkhilesh Sanikop }
74*09537850SAkhilesh Sanikop 
75*09537850SAkhilesh Sanikop // static
Create(const char name_prefix[],int num_threads)76*09537850SAkhilesh Sanikop std::unique_ptr<ThreadPool> ThreadPool::Create(const char name_prefix[],
77*09537850SAkhilesh Sanikop                                                int num_threads) {
78*09537850SAkhilesh Sanikop   if (name_prefix == nullptr || num_threads <= 0) return nullptr;
79*09537850SAkhilesh Sanikop   std::unique_ptr<WorkerThread*[]> threads(new (std::nothrow)
80*09537850SAkhilesh Sanikop                                                WorkerThread*[num_threads]);
81*09537850SAkhilesh Sanikop   if (threads == nullptr) return nullptr;
82*09537850SAkhilesh Sanikop   std::unique_ptr<ThreadPool> pool(new (std::nothrow) ThreadPool(
83*09537850SAkhilesh Sanikop       name_prefix, std::move(threads), num_threads));
84*09537850SAkhilesh Sanikop   if (pool != nullptr && !pool->StartWorkers()) {
85*09537850SAkhilesh Sanikop     pool = nullptr;
86*09537850SAkhilesh Sanikop   }
87*09537850SAkhilesh Sanikop   return pool;
88*09537850SAkhilesh Sanikop }
89*09537850SAkhilesh Sanikop 
ThreadPool(const char name_prefix[],std::unique_ptr<WorkerThread * []> threads,int num_threads)90*09537850SAkhilesh Sanikop ThreadPool::ThreadPool(const char name_prefix[],
91*09537850SAkhilesh Sanikop                        std::unique_ptr<WorkerThread*[]> threads,
92*09537850SAkhilesh Sanikop                        int num_threads)
93*09537850SAkhilesh Sanikop     : threads_(std::move(threads)), num_threads_(num_threads) {
94*09537850SAkhilesh Sanikop   threads_[0] = nullptr;
95*09537850SAkhilesh Sanikop   assert(name_prefix != nullptr);
96*09537850SAkhilesh Sanikop   const size_t name_prefix_len =
97*09537850SAkhilesh Sanikop       std::min(strlen(name_prefix), sizeof(name_prefix_) - 1);
98*09537850SAkhilesh Sanikop   memcpy(name_prefix_, name_prefix, name_prefix_len);
99*09537850SAkhilesh Sanikop   name_prefix_[name_prefix_len] = '\0';
100*09537850SAkhilesh Sanikop }
101*09537850SAkhilesh Sanikop 
~ThreadPool()102*09537850SAkhilesh Sanikop ThreadPool::~ThreadPool() { Shutdown(); }
103*09537850SAkhilesh Sanikop 
Schedule(std::function<void ()> closure)104*09537850SAkhilesh Sanikop void ThreadPool::Schedule(std::function<void()> closure) {
105*09537850SAkhilesh Sanikop   LockMutex();
106*09537850SAkhilesh Sanikop   if (!queue_.GrowIfNeeded()) {
107*09537850SAkhilesh Sanikop     // queue_ is full and we can't grow it. Run |closure| directly.
108*09537850SAkhilesh Sanikop     UnlockMutex();
109*09537850SAkhilesh Sanikop     closure();
110*09537850SAkhilesh Sanikop     return;
111*09537850SAkhilesh Sanikop   }
112*09537850SAkhilesh Sanikop   queue_.Push(std::move(closure));
113*09537850SAkhilesh Sanikop   UnlockMutex();
114*09537850SAkhilesh Sanikop   SignalOne();
115*09537850SAkhilesh Sanikop }
116*09537850SAkhilesh Sanikop 
num_threads() const117*09537850SAkhilesh Sanikop int ThreadPool::num_threads() const { return num_threads_; }
118*09537850SAkhilesh Sanikop 
119*09537850SAkhilesh Sanikop // A simple implementation that mirrors the non-portable Thread.  We may
120*09537850SAkhilesh Sanikop // choose to expand this in the future as a portable implementation of
121*09537850SAkhilesh Sanikop // Thread, or replace it at such a time as one is implemented.
122*09537850SAkhilesh Sanikop class ThreadPool::WorkerThread : public Allocable {
123*09537850SAkhilesh Sanikop  public:
124*09537850SAkhilesh Sanikop   // Creates and starts a thread that runs pool->WorkerFunction().
125*09537850SAkhilesh Sanikop   explicit WorkerThread(ThreadPool* pool);
126*09537850SAkhilesh Sanikop 
127*09537850SAkhilesh Sanikop   // Not copyable or movable.
128*09537850SAkhilesh Sanikop   WorkerThread(const WorkerThread&) = delete;
129*09537850SAkhilesh Sanikop   WorkerThread& operator=(const WorkerThread&) = delete;
130*09537850SAkhilesh Sanikop 
131*09537850SAkhilesh Sanikop   // REQUIRES: Join() must have been called if Start() was called and
132*09537850SAkhilesh Sanikop   // succeeded.
133*09537850SAkhilesh Sanikop   ~WorkerThread() = default;
134*09537850SAkhilesh Sanikop 
135*09537850SAkhilesh Sanikop   LIBGAV1_MUST_USE_RESULT bool Start();
136*09537850SAkhilesh Sanikop 
137*09537850SAkhilesh Sanikop   // Joins with the running thread.
138*09537850SAkhilesh Sanikop   void Join();
139*09537850SAkhilesh Sanikop 
140*09537850SAkhilesh Sanikop  private:
141*09537850SAkhilesh Sanikop #if defined(_MSC_VER)
142*09537850SAkhilesh Sanikop   static unsigned int __stdcall ThreadBody(void* arg);
143*09537850SAkhilesh Sanikop #else
144*09537850SAkhilesh Sanikop   static void* ThreadBody(void* arg);
145*09537850SAkhilesh Sanikop #endif
146*09537850SAkhilesh Sanikop 
147*09537850SAkhilesh Sanikop   void SetupName();
148*09537850SAkhilesh Sanikop   void Run();
149*09537850SAkhilesh Sanikop 
150*09537850SAkhilesh Sanikop   ThreadPool* pool_;
151*09537850SAkhilesh Sanikop #if defined(_MSC_VER)
152*09537850SAkhilesh Sanikop   HANDLE handle_;
153*09537850SAkhilesh Sanikop #else
154*09537850SAkhilesh Sanikop   pthread_t thread_;
155*09537850SAkhilesh Sanikop #endif
156*09537850SAkhilesh Sanikop };
157*09537850SAkhilesh Sanikop 
WorkerThread(ThreadPool * pool)158*09537850SAkhilesh Sanikop ThreadPool::WorkerThread::WorkerThread(ThreadPool* pool) : pool_(pool) {}
159*09537850SAkhilesh Sanikop 
160*09537850SAkhilesh Sanikop #if defined(_MSC_VER)
161*09537850SAkhilesh Sanikop 
Start()162*09537850SAkhilesh Sanikop bool ThreadPool::WorkerThread::Start() {
163*09537850SAkhilesh Sanikop   // Since our code calls the C run-time library (CRT), use _beginthreadex
164*09537850SAkhilesh Sanikop   // rather than CreateThread. Microsoft documentation says "If a thread
165*09537850SAkhilesh Sanikop   // created using CreateThread calls the CRT, the CRT may terminate the
166*09537850SAkhilesh Sanikop   // process in low-memory conditions."
167*09537850SAkhilesh Sanikop   uintptr_t handle = _beginthreadex(
168*09537850SAkhilesh Sanikop       /*security=*/nullptr, /*stack_size=*/0, ThreadBody, this,
169*09537850SAkhilesh Sanikop       /*initflag=*/CREATE_SUSPENDED, /*thrdaddr=*/nullptr);
170*09537850SAkhilesh Sanikop   if (handle == 0) return false;
171*09537850SAkhilesh Sanikop   handle_ = reinterpret_cast<HANDLE>(handle);
172*09537850SAkhilesh Sanikop   ResumeThread(handle_);
173*09537850SAkhilesh Sanikop   return true;
174*09537850SAkhilesh Sanikop }
175*09537850SAkhilesh Sanikop 
Join()176*09537850SAkhilesh Sanikop void ThreadPool::WorkerThread::Join() {
177*09537850SAkhilesh Sanikop   WaitForSingleObject(handle_, INFINITE);
178*09537850SAkhilesh Sanikop   CloseHandle(handle_);
179*09537850SAkhilesh Sanikop }
180*09537850SAkhilesh Sanikop 
ThreadBody(void * arg)181*09537850SAkhilesh Sanikop unsigned int ThreadPool::WorkerThread::ThreadBody(void* arg) {
182*09537850SAkhilesh Sanikop   auto* thread = static_cast<WorkerThread*>(arg);
183*09537850SAkhilesh Sanikop   thread->Run();
184*09537850SAkhilesh Sanikop   return 0;
185*09537850SAkhilesh Sanikop }
186*09537850SAkhilesh Sanikop 
SetupName()187*09537850SAkhilesh Sanikop void ThreadPool::WorkerThread::SetupName() {
188*09537850SAkhilesh Sanikop   // Not currently supported on Windows.
189*09537850SAkhilesh Sanikop }
190*09537850SAkhilesh Sanikop 
191*09537850SAkhilesh Sanikop #else  // defined(_MSC_VER)
192*09537850SAkhilesh Sanikop 
Start()193*09537850SAkhilesh Sanikop bool ThreadPool::WorkerThread::Start() {
194*09537850SAkhilesh Sanikop   return pthread_create(&thread_, nullptr, ThreadBody, this) == 0;
195*09537850SAkhilesh Sanikop }
196*09537850SAkhilesh Sanikop 
Join()197*09537850SAkhilesh Sanikop void ThreadPool::WorkerThread::Join() { pthread_join(thread_, nullptr); }
198*09537850SAkhilesh Sanikop 
ThreadBody(void * arg)199*09537850SAkhilesh Sanikop void* ThreadPool::WorkerThread::ThreadBody(void* arg) {
200*09537850SAkhilesh Sanikop   auto* thread = static_cast<WorkerThread*>(arg);
201*09537850SAkhilesh Sanikop   thread->Run();
202*09537850SAkhilesh Sanikop   return nullptr;
203*09537850SAkhilesh Sanikop }
204*09537850SAkhilesh Sanikop 
SetupName()205*09537850SAkhilesh Sanikop void ThreadPool::WorkerThread::SetupName() {
206*09537850SAkhilesh Sanikop   if (pool_->name_prefix_[0] != '\0') {
207*09537850SAkhilesh Sanikop #if defined(__APPLE__)
208*09537850SAkhilesh Sanikop     // Apple's version of pthread_setname_np takes one argument and operates on
209*09537850SAkhilesh Sanikop     // the current thread only. Also, pthread_mach_thread_np is Apple-specific.
210*09537850SAkhilesh Sanikop     // The maximum size of the |name| buffer was noted in the Chromium source
211*09537850SAkhilesh Sanikop     // code and was confirmed by experiments.
212*09537850SAkhilesh Sanikop     char name[64];
213*09537850SAkhilesh Sanikop     mach_port_t id = pthread_mach_thread_np(pthread_self());
214*09537850SAkhilesh Sanikop     int rv = snprintf(name, sizeof(name), "%s/%" PRId64, pool_->name_prefix_,
215*09537850SAkhilesh Sanikop                       static_cast<int64_t>(id));
216*09537850SAkhilesh Sanikop     assert(rv >= 0);
217*09537850SAkhilesh Sanikop     rv = pthread_setname_np(name);
218*09537850SAkhilesh Sanikop     assert(rv == 0);
219*09537850SAkhilesh Sanikop     static_cast<void>(rv);
220*09537850SAkhilesh Sanikop #elif defined(__ANDROID__) || (defined(__GLIBC__) && !defined(__GNU__))
221*09537850SAkhilesh Sanikop     // If the |name| buffer is longer than 16 bytes, pthread_setname_np fails
222*09537850SAkhilesh Sanikop     // with error 34 (ERANGE) on Android.
223*09537850SAkhilesh Sanikop     char name[16];
224*09537850SAkhilesh Sanikop     pid_t id = GetTid();
225*09537850SAkhilesh Sanikop     int rv = snprintf(name, sizeof(name), "%s/%" PRId64, pool_->name_prefix_,
226*09537850SAkhilesh Sanikop                       static_cast<int64_t>(id));
227*09537850SAkhilesh Sanikop     assert(rv >= 0);
228*09537850SAkhilesh Sanikop     rv = pthread_setname_np(pthread_self(), name);
229*09537850SAkhilesh Sanikop     assert(rv == 0);
230*09537850SAkhilesh Sanikop     static_cast<void>(rv);
231*09537850SAkhilesh Sanikop #endif
232*09537850SAkhilesh Sanikop   }
233*09537850SAkhilesh Sanikop }
234*09537850SAkhilesh Sanikop 
235*09537850SAkhilesh Sanikop #endif  // defined(_MSC_VER)
236*09537850SAkhilesh Sanikop 
Run()237*09537850SAkhilesh Sanikop void ThreadPool::WorkerThread::Run() {
238*09537850SAkhilesh Sanikop   SetupName();
239*09537850SAkhilesh Sanikop   pool_->WorkerFunction();
240*09537850SAkhilesh Sanikop }
241*09537850SAkhilesh Sanikop 
StartWorkers()242*09537850SAkhilesh Sanikop bool ThreadPool::StartWorkers() {
243*09537850SAkhilesh Sanikop   if (!queue_.Init()) return false;
244*09537850SAkhilesh Sanikop   for (int i = 0; i < num_threads_; ++i) {
245*09537850SAkhilesh Sanikop     threads_[i] = new (std::nothrow) WorkerThread(this);
246*09537850SAkhilesh Sanikop     if (threads_[i] == nullptr) return false;
247*09537850SAkhilesh Sanikop     if (!threads_[i]->Start()) {
248*09537850SAkhilesh Sanikop       delete threads_[i];
249*09537850SAkhilesh Sanikop       threads_[i] = nullptr;
250*09537850SAkhilesh Sanikop       return false;
251*09537850SAkhilesh Sanikop     }
252*09537850SAkhilesh Sanikop   }
253*09537850SAkhilesh Sanikop   return true;
254*09537850SAkhilesh Sanikop }
255*09537850SAkhilesh Sanikop 
WorkerFunction()256*09537850SAkhilesh Sanikop void ThreadPool::WorkerFunction() {
257*09537850SAkhilesh Sanikop   LockMutex();
258*09537850SAkhilesh Sanikop   while (true) {
259*09537850SAkhilesh Sanikop     if (queue_.Empty()) {
260*09537850SAkhilesh Sanikop       if (exit_threads_) {
261*09537850SAkhilesh Sanikop         break;  // Queue is empty and exit was requested.
262*09537850SAkhilesh Sanikop       }
263*09537850SAkhilesh Sanikop #if defined(__ANDROID__)
264*09537850SAkhilesh Sanikop       // On android, if we go to a conditional wait right away, the CPU governor
265*09537850SAkhilesh Sanikop       // kicks in and starts shutting the cores down. So we do a very small busy
266*09537850SAkhilesh Sanikop       // wait to see if we get our next job within that period. This
267*09537850SAkhilesh Sanikop       // significantly improves the performance of common cases of tile parallel
268*09537850SAkhilesh Sanikop       // decoding. If we don't receive a job in the busy wait time, we then go
269*09537850SAkhilesh Sanikop       // to an actual conditional wait as usual.
270*09537850SAkhilesh Sanikop       UnlockMutex();
271*09537850SAkhilesh Sanikop       bool found_job = false;
272*09537850SAkhilesh Sanikop       const auto wait_start = Clock::now();
273*09537850SAkhilesh Sanikop       while (Clock::now() - wait_start < kBusyWaitDuration) {
274*09537850SAkhilesh Sanikop         LockMutex();
275*09537850SAkhilesh Sanikop         if (!queue_.Empty()) {
276*09537850SAkhilesh Sanikop           found_job = true;
277*09537850SAkhilesh Sanikop           break;
278*09537850SAkhilesh Sanikop         }
279*09537850SAkhilesh Sanikop         UnlockMutex();
280*09537850SAkhilesh Sanikop       }
281*09537850SAkhilesh Sanikop       // If |found_job| is true, we simply continue since we already hold the
282*09537850SAkhilesh Sanikop       // mutex and we know for sure that the |queue_| is not empty.
283*09537850SAkhilesh Sanikop       if (found_job) continue;
284*09537850SAkhilesh Sanikop       // Since |found_job_| was false, the mutex is not being held at this
285*09537850SAkhilesh Sanikop       // point.
286*09537850SAkhilesh Sanikop       LockMutex();
287*09537850SAkhilesh Sanikop       // Ensure that the queue is still empty.
288*09537850SAkhilesh Sanikop       if (!queue_.Empty()) continue;
289*09537850SAkhilesh Sanikop       if (exit_threads_) {
290*09537850SAkhilesh Sanikop         break;  // Queue is empty and exit was requested.
291*09537850SAkhilesh Sanikop       }
292*09537850SAkhilesh Sanikop #endif  // defined(__ANDROID__)
293*09537850SAkhilesh Sanikop       // Queue is still empty, wait for signal or broadcast.
294*09537850SAkhilesh Sanikop       Wait();
295*09537850SAkhilesh Sanikop     } else {
296*09537850SAkhilesh Sanikop       // Take a job from the queue.
297*09537850SAkhilesh Sanikop       std::function<void()> job = std::move(queue_.Front());
298*09537850SAkhilesh Sanikop       queue_.Pop();
299*09537850SAkhilesh Sanikop 
300*09537850SAkhilesh Sanikop       UnlockMutex();
301*09537850SAkhilesh Sanikop       // Note that it is good practice to surround this with a try/catch so
302*09537850SAkhilesh Sanikop       // the thread pool doesn't go to hell if the job throws an exception.
303*09537850SAkhilesh Sanikop       // This is omitted here because Google3 doesn't like exceptions.
304*09537850SAkhilesh Sanikop       std::move(job)();
305*09537850SAkhilesh Sanikop       job = nullptr;
306*09537850SAkhilesh Sanikop 
307*09537850SAkhilesh Sanikop       LockMutex();
308*09537850SAkhilesh Sanikop     }
309*09537850SAkhilesh Sanikop   }
310*09537850SAkhilesh Sanikop   UnlockMutex();
311*09537850SAkhilesh Sanikop }
312*09537850SAkhilesh Sanikop 
Shutdown()313*09537850SAkhilesh Sanikop void ThreadPool::Shutdown() {
314*09537850SAkhilesh Sanikop   // Tell worker threads how to exit.
315*09537850SAkhilesh Sanikop   LockMutex();
316*09537850SAkhilesh Sanikop   exit_threads_ = true;
317*09537850SAkhilesh Sanikop   UnlockMutex();
318*09537850SAkhilesh Sanikop   SignalAll();
319*09537850SAkhilesh Sanikop 
320*09537850SAkhilesh Sanikop   // Join all workers. This will block.
321*09537850SAkhilesh Sanikop   for (int i = 0; i < num_threads_; ++i) {
322*09537850SAkhilesh Sanikop     if (threads_[i] == nullptr) break;
323*09537850SAkhilesh Sanikop     threads_[i]->Join();
324*09537850SAkhilesh Sanikop     delete threads_[i];
325*09537850SAkhilesh Sanikop   }
326*09537850SAkhilesh Sanikop }
327*09537850SAkhilesh Sanikop 
328*09537850SAkhilesh Sanikop }  // namespace libgav1
329