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 #include <err.h>
18 #include <errno.h>
19 #include <pthread.h>
20 #include <stdint.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <sys/mman.h>
25 #include <unistd.h>
26 
27 #include <new>
28 
29 #include "Action.h"
30 #include "Thread.h"
31 #include "Threads.h"
32 
ThreadRunner(void * data)33 void* ThreadRunner(void* data) {
34   Thread* thread = reinterpret_cast<Thread*>(data);
35   while (true) {
36     thread->WaitForPending();
37     Action* action = thread->GetAction();
38     thread->AddTimeNsecs(action->Execute(thread->pointers()));
39     bool end_thread = action->EndThread();
40     thread->ClearPending();
41     if (end_thread) {
42       break;
43     }
44   }
45   return nullptr;
46 }
47 
Threads(Pointers * pointers,size_t max_threads)48 Threads::Threads(Pointers* pointers, size_t max_threads)
49     : pointers_(pointers), max_threads_(max_threads) {
50   size_t pagesize = getpagesize();
51   data_size_ = (max_threads_ * sizeof(Thread) + pagesize - 1) & ~(pagesize - 1);
52   max_threads_ = data_size_ / sizeof(Thread);
53 
54   void* memory = mmap(nullptr, data_size_, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
55   if (memory == MAP_FAILED) {
56     err(1, "Failed to map in memory for Threads: map size %zu, max threads %zu\n",
57         data_size_, max_threads_);
58   }
59 
60   if (Thread::ACTION_SIZE < Action::MaxActionSize()) {
61     err(1, "Thread action size is too small: ACTION_SIZE %zu, max size %zu\n",
62         Thread::ACTION_SIZE, Action::MaxActionSize());
63   }
64 
65   threads_ = new (memory) Thread[max_threads_];
66 }
67 
~Threads()68 Threads::~Threads() {
69   if (threads_) {
70     munmap(threads_, data_size_);
71     threads_ = nullptr;
72     data_size_ = 0;
73   }
74 }
75 
CreateThread(pid_t tid)76 Thread* Threads::CreateThread(pid_t tid) {
77   if (num_threads_ == max_threads_) {
78     err(1, "Too many threads created, current max %zu.\n", num_threads_);
79   }
80   Thread* thread = FindEmptyEntry(tid);
81   if (thread == nullptr) {
82     err(1, "No empty entries found, current max %zu, num threads %zu\n",
83           max_threads_, num_threads_);
84   }
85   thread->tid_ = tid;
86   thread->pointers_ = pointers_;
87   thread->total_time_nsecs_ = 0;
88   if (pthread_create(&thread->thread_id_, nullptr, ThreadRunner, thread) == -1) {
89     err(1, "Failed to create thread %d: %s\n", tid, strerror(errno));
90   }
91 
92   num_threads_++;
93   return thread;
94 }
95 
FindThread(pid_t tid)96 Thread* Threads::FindThread(pid_t tid) {
97   size_t index = GetHashEntry(tid);
98   for (size_t entries = num_threads_; entries != 0; ) {
99     pid_t cur_tid = threads_[index].tid_;
100     if (cur_tid == tid) {
101       return threads_ + index;
102     }
103     if (cur_tid != 0) {
104       entries--;
105     }
106     if (++index == max_threads_) {
107       index = 0;
108     }
109   }
110   return nullptr;
111 }
112 
WaitForAllToQuiesce()113 void Threads::WaitForAllToQuiesce() {
114   for (size_t i = 0, threads = 0; threads < num_threads_; i++) {
115     pid_t cur_tid = threads_[i].tid_;
116     if (cur_tid != 0) {
117       threads++;
118       threads_[i].WaitForReady();
119     }
120   }
121 }
122 
GetHashEntry(pid_t tid)123 size_t Threads::GetHashEntry(pid_t tid) {
124   return tid % max_threads_;
125 }
126 
FindEmptyEntry(pid_t tid)127 Thread* Threads::FindEmptyEntry(pid_t tid) {
128   size_t index = GetHashEntry(tid);
129   for (size_t entries = 0; entries < max_threads_; entries++) {
130     if (threads_[index].tid_ == 0) {
131       return threads_ + index;
132     }
133     if (++index == max_threads_) {
134       index = 0;
135     }
136   }
137   return nullptr;
138 }
139 
Finish(Thread * thread)140 void Threads::Finish(Thread* thread) {
141   int ret = pthread_join(thread->thread_id_, nullptr);
142   if (ret != 0) {
143     fprintf(stderr, "pthread_join failed: %s\n", strerror(ret));
144     exit(1);
145   }
146   total_time_nsecs_ += thread->total_time_nsecs_;
147   thread->tid_ = 0;
148   num_threads_--;
149 }
150 
FinishAll()151 void Threads::FinishAll() {
152   for (size_t i = 0; i < max_threads_; i++) {
153     if (threads_[i].tid_ != 0) {
154       threads_[i].CreateAction(0, "thread_done", nullptr);
155       threads_[i].SetPending();
156       Finish(threads_ + i);
157     }
158   }
159 }
160