1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "hwc-drm-worker"
18 
19 #include "worker.h"
20 
21 #include <errno.h>
22 #include <pthread.h>
23 #include <stdlib.h>
24 #include <sys/resource.h>
25 #include <sys/signal.h>
26 
27 #include <cutils/log.h>
28 
29 namespace android {
30 
Worker(const char * name,int priority)31 Worker::Worker(const char *name, int priority)
32     : name_(name), priority_(priority), exit_(false), initialized_(false) {
33 }
34 
~Worker()35 Worker::~Worker() {
36   if (!initialized_)
37     return;
38 
39   pthread_kill(thread_, SIGTERM);
40   pthread_cond_destroy(&cond_);
41   pthread_mutex_destroy(&lock_);
42 }
43 
InitWorker()44 int Worker::InitWorker() {
45   int ret = pthread_cond_init(&cond_, NULL);
46   if (ret) {
47     ALOGE("Failed to int thread %s condition %d", name_.c_str(), ret);
48     return ret;
49   }
50 
51   ret = pthread_mutex_init(&lock_, NULL);
52   if (ret) {
53     ALOGE("Failed to init thread %s lock %d", name_.c_str(), ret);
54     pthread_cond_destroy(&cond_);
55     return ret;
56   }
57 
58   ret = pthread_create(&thread_, NULL, InternalRoutine, this);
59   if (ret) {
60     ALOGE("Could not create thread %s %d", name_.c_str(), ret);
61     pthread_mutex_destroy(&lock_);
62     pthread_cond_destroy(&cond_);
63     return ret;
64   }
65   initialized_ = true;
66   return 0;
67 }
68 
initialized() const69 bool Worker::initialized() const {
70   return initialized_;
71 }
72 
Lock()73 int Worker::Lock() {
74   return pthread_mutex_lock(&lock_);
75 }
76 
Unlock()77 int Worker::Unlock() {
78   return pthread_mutex_unlock(&lock_);
79 }
80 
SignalLocked()81 int Worker::SignalLocked() {
82   return SignalThreadLocked(false);
83 }
84 
ExitLocked()85 int Worker::ExitLocked() {
86   int signal_ret = SignalThreadLocked(true);
87   if (signal_ret)
88     ALOGE("Failed to signal thread %s with exit %d", name_.c_str(), signal_ret);
89 
90   int join_ret = pthread_join(thread_, NULL);
91   if (join_ret && join_ret != ESRCH)
92     ALOGE("Failed to join thread %s in exit %d", name_.c_str(), join_ret);
93 
94   return signal_ret | join_ret;
95 }
96 
Signal()97 int Worker::Signal() {
98   int ret = Lock();
99   if (ret) {
100     ALOGE("Failed to acquire lock in Signal() %d\n", ret);
101     return ret;
102   }
103 
104   int signal_ret = SignalLocked();
105 
106   ret = Unlock();
107   if (ret) {
108     ALOGE("Failed to release lock in Signal() %d\n", ret);
109     return ret;
110   }
111   return signal_ret;
112 }
113 
Exit()114 int Worker::Exit() {
115   int ret = Lock();
116   if (ret) {
117     ALOGE("Failed to acquire lock in Exit() %d\n", ret);
118     return ret;
119   }
120 
121   int exit_ret = ExitLocked();
122 
123   ret = Unlock();
124   if (ret) {
125     ALOGE("Failed to release lock in Exit() %d\n", ret);
126     return ret;
127   }
128   return exit_ret;
129 }
130 
WaitForSignalOrExitLocked()131 int Worker::WaitForSignalOrExitLocked() {
132   if (exit_)
133     return -EINTR;
134 
135   int ret = pthread_cond_wait(&cond_, &lock_);
136 
137   if (exit_)
138     return -EINTR;
139 
140   return ret;
141 }
142 
143 // static
InternalRoutine(void * arg)144 void *Worker::InternalRoutine(void *arg) {
145   Worker *worker = (Worker *)arg;
146 
147   setpriority(PRIO_PROCESS, 0, worker->priority_);
148 
149   while (true) {
150     int ret = worker->Lock();
151     if (ret) {
152       ALOGE("Failed to lock %s thread %d", worker->name_.c_str(), ret);
153       continue;
154     }
155 
156     bool exit = worker->exit_;
157 
158     ret = worker->Unlock();
159     if (ret) {
160       ALOGE("Failed to unlock %s thread %d", worker->name_.c_str(), ret);
161       break;
162     }
163     if (exit)
164       break;
165 
166     worker->Routine();
167   }
168   return NULL;
169 }
170 
SignalThreadLocked(bool exit)171 int Worker::SignalThreadLocked(bool exit) {
172   if (exit)
173     exit_ = exit;
174 
175   int ret = pthread_cond_signal(&cond_);
176   if (ret) {
177     ALOGE("Failed to signal condition on %s thread %d", name_.c_str(), ret);
178     return ret;
179   }
180 
181   return 0;
182 }
183 }
184