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 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 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 68 Threads::~Threads() { 69 if (threads_) { 70 munmap(threads_, data_size_); 71 threads_ = nullptr; 72 data_size_ = 0; 73 } 74 } 75 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 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 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 123 size_t Threads::GetHashEntry(pid_t tid) { 124 return tid % max_threads_; 125 } 126 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 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 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