blob: 27c5dfa249ec9d78f5fcf414e1bf0ee55aa61c57 [file] [log] [blame]
Mark Slee9f0c6512007-02-28 23:58:26 +00001// Copyright (c) 2006- Facebook
2// Distributed under the Thrift Software License
3//
4// See accompanying file LICENSE or visit the Thrift site at:
5// http://developers.facebook.com/thrift/
6
Marc Slemko66949872006-07-15 01:52:39 +00007#include "PosixThreadFactory.h"
Marc Slemko3a3b53b2007-05-22 23:59:54 +00008#include "Exception.h"
Marc Slemko66949872006-07-15 01:52:39 +00009
10#include <assert.h>
11#include <pthread.h>
12
Marc Slemko6f038a72006-08-03 18:58:09 +000013#include <iostream>
14
15#include <boost/weak_ptr.hpp>
16
Marc Slemko66949872006-07-15 01:52:39 +000017namespace facebook { namespace thrift { namespace concurrency {
18
David Reissd4a269c2007-08-23 02:37:19 +000019using boost::shared_ptr;
20using boost::weak_ptr;
Marc Slemko6f038a72006-08-03 18:58:09 +000021
Mark Sleef5f2be42006-09-05 21:05:31 +000022/**
Marc Slemko3a3b53b2007-05-22 23:59:54 +000023 * The POSIX thread class.
Mark Sleef5f2be42006-09-05 21:05:31 +000024 *
25 * @author marc
26 * @version $Id:$
27 */
Marc Slemko66949872006-07-15 01:52:39 +000028class PthreadThread: public Thread {
Mark Sleef5f2be42006-09-05 21:05:31 +000029 public:
Marc Slemko66949872006-07-15 01:52:39 +000030
Mark Sleef5f2be42006-09-05 21:05:31 +000031 enum STATE {
32 uninitialized,
33 starting,
34 started,
35 stopping,
36 stopped
Marc Slemko66949872006-07-15 01:52:39 +000037 };
38
39 static const int MB = 1024 * 1024;
40
Marc Slemko8a40a762006-07-19 17:46:50 +000041 static void* threadMain(void* arg);
42
Mark Sleef5f2be42006-09-05 21:05:31 +000043 private:
Mark Slee2f6404d2006-10-10 01:37:40 +000044 pthread_t pthread_;
45 STATE state_;
46 int policy_;
47 int priority_;
48 int stackSize_;
49 weak_ptr<PthreadThread> self_;
Marc Slemko67606e52007-06-04 21:01:19 +000050 bool detached_;
Marc Slemko6f038a72006-08-03 18:58:09 +000051
Mark Sleef5f2be42006-09-05 21:05:31 +000052 public:
Marc Slemko3a3b53b2007-05-22 23:59:54 +000053
Marc Slemko67606e52007-06-04 21:01:19 +000054 PthreadThread(int policy, int priority, int stackSize, bool detached, shared_ptr<Runnable> runnable) :
Mark Slee2f6404d2006-10-10 01:37:40 +000055 pthread_(0),
Marc Slemko3a3b53b2007-05-22 23:59:54 +000056 state_(uninitialized),
Mark Slee2f6404d2006-10-10 01:37:40 +000057 policy_(policy),
58 priority_(priority),
Marc Slemko67606e52007-06-04 21:01:19 +000059 stackSize_(stackSize),
60 detached_(detached) {
Marc Slemkofe5ba12e2006-07-20 21:16:27 +000061
62 this->Thread::runnable(runnable);
63 }
Marc Slemko66949872006-07-15 01:52:39 +000064
Marc Slemko67606e52007-06-04 21:01:19 +000065 ~PthreadThread() {
66 /* Nothing references this thread, if is is not detached, do a join
Marc Slemkoa6479032007-06-05 22:20:14 +000067 now, otherwise the thread-id and, possibly, other resources will
Marc Slemko67606e52007-06-04 21:01:19 +000068 be leaked. */
69 if(!detached_) {
70 try {
71 join();
72 } catch(...) {
Marc Slemkoa6479032007-06-05 22:20:14 +000073 // We're really hosed.
Marc Slemko67606e52007-06-04 21:01:19 +000074 }
75 }
76 }
Marc Slemko6f038a72006-08-03 18:58:09 +000077
Marc Slemko66949872006-07-15 01:52:39 +000078 void start() {
Mark Slee2f6404d2006-10-10 01:37:40 +000079 if (state_ != uninitialized) {
Marc Slemko66949872006-07-15 01:52:39 +000080 return;
81 }
82
Marc Slemko66949872006-07-15 01:52:39 +000083 pthread_attr_t thread_attr;
Mark Slee2782d6d2007-05-23 04:55:30 +000084 if (pthread_attr_init(&thread_attr) != 0) {
Marc Slemko3a3b53b2007-05-22 23:59:54 +000085 throw SystemResourceException("pthread_attr_init failed");
86 }
Aditya Agarwal9dc57402007-03-31 17:45:12 +000087
Marc Slemkoa6479032007-06-05 22:20:14 +000088 if(pthread_attr_setdetachstate(&thread_attr,
89 detached_ ?
90 PTHREAD_CREATE_DETACHED :
Marc Slemko67606e52007-06-04 21:01:19 +000091 PTHREAD_CREATE_JOINABLE) != 0) {
92 throw SystemResourceException("pthread_attr_setdetachstate failed");
Marc Slemko3a3b53b2007-05-22 23:59:54 +000093 }
Marc Slemko66949872006-07-15 01:52:39 +000094
95 // Set thread stack size
Mark Slee2782d6d2007-05-23 04:55:30 +000096 if (pthread_attr_setstacksize(&thread_attr, MB * stackSize_) != 0) {
97 throw SystemResourceException("pthread_attr_setstacksize failed");
Marc Slemko3a3b53b2007-05-22 23:59:54 +000098 }
Marc Slemko66949872006-07-15 01:52:39 +000099
100 // Set thread policy
Mark Slee2782d6d2007-05-23 04:55:30 +0000101 if (pthread_attr_setschedpolicy(&thread_attr, policy_) != 0) {
102 throw SystemResourceException("pthread_attr_setschedpolicy failed");
Marc Slemko3a3b53b2007-05-22 23:59:54 +0000103 }
Marc Slemko66949872006-07-15 01:52:39 +0000104
105 struct sched_param sched_param;
Mark Slee2f6404d2006-10-10 01:37:40 +0000106 sched_param.sched_priority = priority_;
Marc Slemko66949872006-07-15 01:52:39 +0000107
108 // Set thread priority
Mark Slee2782d6d2007-05-23 04:55:30 +0000109 if (pthread_attr_setschedparam(&thread_attr, &sched_param) != 0) {
110 throw SystemResourceException("pthread_attr_setschedparam failed");
Marc Slemko3a3b53b2007-05-22 23:59:54 +0000111 }
Marc Slemko66949872006-07-15 01:52:39 +0000112
Mark Sleef5f2be42006-09-05 21:05:31 +0000113 // Create reference
Marc Slemko6f038a72006-08-03 18:58:09 +0000114 shared_ptr<PthreadThread>* selfRef = new shared_ptr<PthreadThread>();
Mark Slee2f6404d2006-10-10 01:37:40 +0000115 *selfRef = self_.lock();
Marc Slemko3a3b53b2007-05-22 23:59:54 +0000116
Marc Slemko67606e52007-06-04 21:01:19 +0000117 state_ = starting;
118
Mark Slee2782d6d2007-05-23 04:55:30 +0000119 if (pthread_create(&pthread_, &thread_attr, threadMain, (void*)selfRef) != 0) {
120 throw SystemResourceException("pthread_create failed");
Marc Slemko3a3b53b2007-05-22 23:59:54 +0000121 }
Marc Slemko66949872006-07-15 01:52:39 +0000122 }
123
124 void join() {
Marc Slemko67606e52007-06-04 21:01:19 +0000125 if (!detached_ && state_ != uninitialized) {
Marc Slemko66949872006-07-15 01:52:39 +0000126 void* ignore;
Marc Slemkoa6479032007-06-05 22:20:14 +0000127 /* XXX
128 If join fails it is most likely due to the fact
129 that the last reference was the thread itself and cannot
130 join. This results in leaked threads and will eventually
131 cause the process to run out of thread resources.
132 We're beyond the point of throwing an exception. Not clear how
133 best to handle this. */
134 detached_ = pthread_join(pthread_, &ignore) == 0;
Marc Slemko66949872006-07-15 01:52:39 +0000135 }
136 }
137
Marc Slemkoa6479032007-06-05 22:20:14 +0000138 id_t getId() {
Mark Slee98439152007-08-21 02:39:40 +0000139 // TODO(dreiss): Stop using C-style casts.
140 return (id_t)pthread_;
Marc Slemko3a3b53b2007-05-22 23:59:54 +0000141 }
142
Mark Sleef5f2be42006-09-05 21:05:31 +0000143 shared_ptr<Runnable> runnable() const { return Thread::runnable(); }
Marc Slemkofe5ba12e2006-07-20 21:16:27 +0000144
Mark Sleef5f2be42006-09-05 21:05:31 +0000145 void runnable(shared_ptr<Runnable> value) { Thread::runnable(value); }
Marc Slemko66949872006-07-15 01:52:39 +0000146
Marc Slemko6f038a72006-08-03 18:58:09 +0000147 void weakRef(shared_ptr<PthreadThread> self) {
Aditya Agarwal3f234da2007-04-01 01:19:57 +0000148 assert(self.get() == this);
Mark Slee2f6404d2006-10-10 01:37:40 +0000149 self_ = weak_ptr<PthreadThread>(self);
Marc Slemko6f038a72006-08-03 18:58:09 +0000150 }
Marc Slemko66949872006-07-15 01:52:39 +0000151};
152
Marc Slemko8a40a762006-07-19 17:46:50 +0000153void* PthreadThread::threadMain(void* arg) {
Marc Slemko6f038a72006-08-03 18:58:09 +0000154 shared_ptr<PthreadThread> thread = *(shared_ptr<PthreadThread>*)arg;
Marc Slemko6f038a72006-08-03 18:58:09 +0000155 delete reinterpret_cast<shared_ptr<PthreadThread>*>(arg);
156
Mark Sleef5f2be42006-09-05 21:05:31 +0000157 if (thread == NULL) {
Marc Slemko6f038a72006-08-03 18:58:09 +0000158 return (void*)0;
159 }
160
Mark Slee2f6404d2006-10-10 01:37:40 +0000161 if (thread->state_ != starting) {
Marc Slemko8a40a762006-07-19 17:46:50 +0000162 return (void*)0;
163 }
164
Mark Slee2f6404d2006-10-10 01:37:40 +0000165 thread->state_ = starting;
Marc Slemkofe5ba12e2006-07-20 21:16:27 +0000166 thread->runnable()->run();
Mark Slee2f6404d2006-10-10 01:37:40 +0000167 if (thread->state_ != stopping && thread->state_ != stopped) {
168 thread->state_ = stopping;
Marc Slemko8a40a762006-07-19 17:46:50 +0000169 }
Marc Slemko3a3b53b2007-05-22 23:59:54 +0000170
Marc Slemko8a40a762006-07-19 17:46:50 +0000171 return (void*)0;
172}
173
Mark Sleef5f2be42006-09-05 21:05:31 +0000174/**
175 * POSIX Thread factory implementation
176 */
Marc Slemko66949872006-07-15 01:52:39 +0000177class PosixThreadFactory::Impl {
178
Mark Sleef5f2be42006-09-05 21:05:31 +0000179 private:
Mark Slee2f6404d2006-10-10 01:37:40 +0000180 POLICY policy_;
181 PRIORITY priority_;
182 int stackSize_;
183 bool detached_;
Marc Slemko66949872006-07-15 01:52:39 +0000184
Mark Sleef5f2be42006-09-05 21:05:31 +0000185 /**
186 * Converts generic posix thread schedule policy enums into pthread
187 * API values.
188 */
Marc Slemko66949872006-07-15 01:52:39 +0000189 static int toPthreadPolicy(POLICY policy) {
Mark Sleeb9ff32a2006-11-16 01:00:24 +0000190 switch (policy) {
191 case OTHER:
192 return SCHED_OTHER;
193 case FIFO:
194 return SCHED_FIFO;
195 case ROUND_ROBIN:
196 return SCHED_RR;
Marc Slemko66949872006-07-15 01:52:39 +0000197 }
Mark Sleeb9ff32a2006-11-16 01:00:24 +0000198 return SCHED_OTHER;
Marc Slemko66949872006-07-15 01:52:39 +0000199 }
200
Mark Sleef5f2be42006-09-05 21:05:31 +0000201 /**
202 * Converts relative thread priorities to absolute value based on posix
203 * thread scheduler policy
204 *
205 * The idea is simply to divide up the priority range for the given policy
206 * into the correpsonding relative priority level (lowest..highest) and
207 * then pro-rate accordingly.
208 */
Marc Slemko66949872006-07-15 01:52:39 +0000209 static int toPthreadPriority(POLICY policy, PRIORITY priority) {
Marc Slemko66949872006-07-15 01:52:39 +0000210 int pthread_policy = toPthreadPolicy(policy);
Marc Slemko66949872006-07-15 01:52:39 +0000211 int min_priority = sched_get_priority_min(pthread_policy);
Marc Slemko66949872006-07-15 01:52:39 +0000212 int max_priority = sched_get_priority_max(pthread_policy);
Marc Slemko66949872006-07-15 01:52:39 +0000213 int quanta = (HIGHEST - LOWEST) + 1;
Marc Slemko66949872006-07-15 01:52:39 +0000214 float stepsperquanta = (max_priority - min_priority) / quanta;
215
Mark Slee29050782006-09-29 00:12:30 +0000216 if (priority <= HIGHEST) {
Marc Slemko66949872006-07-15 01:52:39 +0000217 return (int)(min_priority + stepsperquanta * priority);
218 } else {
Marc Slemko66949872006-07-15 01:52:39 +0000219 // should never get here for priority increments.
Marc Slemko66949872006-07-15 01:52:39 +0000220 assert(false);
Marc Slemko66949872006-07-15 01:52:39 +0000221 return (int)(min_priority + stepsperquanta * NORMAL);
222 }
223 }
224
Mark Sleef5f2be42006-09-05 21:05:31 +0000225 public:
Marc Slemko66949872006-07-15 01:52:39 +0000226
Marc Slemko3a3b53b2007-05-22 23:59:54 +0000227 Impl(POLICY policy, PRIORITY priority, int stackSize, bool detached) :
Mark Slee2f6404d2006-10-10 01:37:40 +0000228 policy_(policy),
229 priority_(priority),
230 stackSize_(stackSize),
231 detached_(detached) {}
Marc Slemko66949872006-07-15 01:52:39 +0000232
Mark Sleef5f2be42006-09-05 21:05:31 +0000233 /**
Marc Slemko3a3b53b2007-05-22 23:59:54 +0000234 * Creates a new POSIX thread to run the runnable object
Mark Sleef5f2be42006-09-05 21:05:31 +0000235 *
236 * @param runnable A runnable object
237 */
Marc Slemko6f038a72006-08-03 18:58:09 +0000238 shared_ptr<Thread> newThread(shared_ptr<Runnable> runnable) const {
Marc Slemko67606e52007-06-04 21:01:19 +0000239 shared_ptr<PthreadThread> result = shared_ptr<PthreadThread>(new PthreadThread(toPthreadPolicy(policy_), toPthreadPriority(policy_, priority_), stackSize_, detached_, runnable));
Marc Slemko6f038a72006-08-03 18:58:09 +0000240 result->weakRef(result);
241 runnable->thread(result);
242 return result;
Marc Slemko66949872006-07-15 01:52:39 +0000243 }
244
Marc Slemkoa6479032007-06-05 22:20:14 +0000245 int getStackSize() const { return stackSize_; }
Marc Slemko66949872006-07-15 01:52:39 +0000246
Marc Slemkoa6479032007-06-05 22:20:14 +0000247 void setStackSize(int value) { stackSize_ = value; }
Marc Slemko66949872006-07-15 01:52:39 +0000248
Marc Slemkoa6479032007-06-05 22:20:14 +0000249 PRIORITY getPriority() const { return priority_; }
Marc Slemko3a3b53b2007-05-22 23:59:54 +0000250
Mark Sleef5f2be42006-09-05 21:05:31 +0000251 /**
252 * Sets priority.
253 *
254 * XXX
255 * Need to handle incremental priorities properly.
256 */
Marc Slemkoa6479032007-06-05 22:20:14 +0000257 void setPriority(PRIORITY value) { priority_ = value; }
258
259 bool isDetached() const { return detached_; }
260
261 void setDetached(bool value) { detached_ = value; }
262
Mark Slee98439152007-08-21 02:39:40 +0000263 Thread::id_t getCurrentThreadId() const {
264 // TODO(dreiss): Stop using C-style casts.
265 return (id_t)pthread_self();
266 }
Marc Slemkoa6479032007-06-05 22:20:14 +0000267
Marc Slemko66949872006-07-15 01:52:39 +0000268};
269
Marc Slemko3a3b53b2007-05-22 23:59:54 +0000270PosixThreadFactory::PosixThreadFactory(POLICY policy, PRIORITY priority, int stackSize, bool detached) :
Mark Slee2f6404d2006-10-10 01:37:40 +0000271 impl_(new PosixThreadFactory::Impl(policy, priority, stackSize, detached)) {}
Marc Slemko66949872006-07-15 01:52:39 +0000272
Mark Slee2f6404d2006-10-10 01:37:40 +0000273shared_ptr<Thread> PosixThreadFactory::newThread(shared_ptr<Runnable> runnable) const { return impl_->newThread(runnable); }
Marc Slemko66949872006-07-15 01:52:39 +0000274
Marc Slemkoa6479032007-06-05 22:20:14 +0000275int PosixThreadFactory::getStackSize() const { return impl_->getStackSize(); }
Marc Slemko66949872006-07-15 01:52:39 +0000276
Marc Slemkoa6479032007-06-05 22:20:14 +0000277void PosixThreadFactory::setStackSize(int value) { impl_->setStackSize(value); }
Marc Slemko66949872006-07-15 01:52:39 +0000278
Marc Slemkoa6479032007-06-05 22:20:14 +0000279PosixThreadFactory::PRIORITY PosixThreadFactory::getPriority() const { return impl_->getPriority(); }
Marc Slemko66949872006-07-15 01:52:39 +0000280
Marc Slemkoa6479032007-06-05 22:20:14 +0000281void PosixThreadFactory::setPriority(PosixThreadFactory::PRIORITY value) { impl_->setPriority(value); }
Marc Slemko66949872006-07-15 01:52:39 +0000282
Marc Slemkoa6479032007-06-05 22:20:14 +0000283bool PosixThreadFactory::isDetached() const { return impl_->isDetached(); }
284
285void PosixThreadFactory::setDetached(bool value) { impl_->setDetached(value); }
286
287Thread::id_t PosixThreadFactory::getCurrentThreadId() const { return impl_->getCurrentThreadId(); }
Marc Slemko3a3b53b2007-05-22 23:59:54 +0000288
Marc Slemko66949872006-07-15 01:52:39 +0000289}}} // facebook::thrift::concurrency