2 * Copyright 2016 Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 #include <sys/syscall.h>
24 #include <glog/logging.h>
26 #include <folly/Likely.h>
27 #include <folly/Portability.h>
28 #include <folly/experimental/fibers/BoostContextCompatibility.h>
29 #include <folly/experimental/fibers/FiberManager.h>
30 #include <folly/portability/Syscall.h>
32 namespace folly { namespace fibers {
35 static const uint64_t kMagic8Bytes = 0xfaceb00cfaceb00c;
37 pid_t localThreadId() {
38 // __thread doesn't allow non-const initialization.
39 // OSX doesn't support thread_local.
40 static FOLLY_TLS pid_t threadId = 0;
41 if (UNLIKELY(threadId == 0)) {
42 threadId = syscall(FOLLY_SYS_gettid);
47 /* Size of the region from p + nBytes down to the last non-magic value */
48 static size_t nonMagicInBytes(const FContext& context) {
49 uint64_t* begin = static_cast<uint64_t*>(context.stackLimit());
50 uint64_t* end = static_cast<uint64_t*>(context.stackBase());
52 auto firstNonMagic = std::find_if(
55 return val != kMagic8Bytes;
59 return (end - firstNonMagic) * sizeof(uint64_t);
62 } // anonymous namespace
64 void Fiber::setData(intptr_t data) {
65 DCHECK_EQ(state_, AWAITING);
67 state_ = READY_TO_RUN;
69 if (fiberManager_.observer_) {
70 fiberManager_.observer_->runnable(reinterpret_cast<uintptr_t>(this));
73 if (LIKELY(threadId_ == localThreadId())) {
74 fiberManager_.readyFibers_.push_back(*this);
75 fiberManager_.ensureLoopScheduled();
77 fiberManager_.remoteReadyInsert(this);
81 Fiber::Fiber(FiberManager& fiberManager) :
82 fiberManager_(fiberManager) {
84 auto size = fiberManager_.options_.stackSize;
85 auto limit = fiberManager_.stackAllocator_.allocate(size);
87 fcontext_ = makeContext(limit, size, &Fiber::fiberFuncHelper);
90 void Fiber::init(bool recordStackUsed) {
91 // It is necessary to disable the logic for ASAN because we change
93 #ifndef FOLLY_SANITIZE_ADDRESS
94 recordStackUsed_ = recordStackUsed;
95 if (UNLIKELY(recordStackUsed_ && !stackFilledWithMagic_)) {
96 auto limit = fcontext_.stackLimit();
97 auto base = fcontext_.stackBase();
99 std::fill(static_cast<uint64_t*>(limit),
100 static_cast<uint64_t*>(base),
103 // newer versions of boost allocate context on fiber stack,
104 // need to create a new one
105 auto size = fiberManager_.options_.stackSize;
106 fcontext_ = makeContext(limit, size, &Fiber::fiberFuncHelper);
108 stackFilledWithMagic_ = true;
111 (void)recordStackUsed;
116 fiberManager_.stackAllocator_.deallocate(
117 static_cast<unsigned char*>(fcontext_.stackLimit()),
118 fiberManager_.options_.stackSize);
121 void Fiber::recordStackPosition() {
123 auto currentPosition = static_cast<size_t>(
124 static_cast<unsigned char*>(fcontext_.stackBase()) -
125 static_cast<unsigned char*>(static_cast<void*>(&stackDummy)));
126 fiberManager_.stackHighWatermark_ =
127 std::max(fiberManager_.stackHighWatermark_, currentPosition);
128 VLOG(4) << "Stack usage: " << currentPosition;
131 void Fiber::fiberFuncHelper(intptr_t fiber) {
132 reinterpret_cast<Fiber*>(fiber)->fiberFunc();
136 * Some weird bug in ASAN causes fiberFunc to allocate boundless amounts of
137 * memory inside __asan_handle_no_return. Work around this in ASAN builds by
138 * tricking the compiler into thinking it may, someday, return.
140 #ifdef FOLLY_SANITIZE_ADDRESS
141 volatile bool loopForever = true;
143 static constexpr bool loopForever = true;
146 void Fiber::fiberFunc() {
147 while (loopForever) {
148 DCHECK_EQ(state_, NOT_STARTED);
150 threadId_ = localThreadId();
155 DCHECK(finallyFunc_);
164 fiberManager_.exceptionCallback_(std::current_exception(),
165 "running Fiber func_/resultFunc_");
168 if (UNLIKELY(recordStackUsed_)) {
169 fiberManager_.stackHighWatermark_ =
170 std::max(fiberManager_.stackHighWatermark_,
171 nonMagicInBytes(fcontext_));
172 VLOG(3) << "Max stack usage: " << fiberManager_.stackHighWatermark_;
173 CHECK(fiberManager_.stackHighWatermark_ <
174 fiberManager_.options_.stackSize - 64) << "Fiber stack overflow";
179 fiberManager_.activeFiber_ = nullptr;
181 auto context = jumpContext(&fcontext_, &fiberManager_.mainContext_, 0);
182 DCHECK_EQ(reinterpret_cast<Fiber*>(context), this);
186 intptr_t Fiber::preempt(State state) {
189 auto preemptImpl = [&]() mutable {
190 DCHECK_EQ(fiberManager_.activeFiber_, this);
191 DCHECK_EQ(state_, RUNNING);
192 DCHECK_NE(state, RUNNING);
194 fiberManager_.activeFiber_ = nullptr;
197 recordStackPosition();
199 ret = jumpContext(&fcontext_, &fiberManager_.mainContext_, 0);
201 DCHECK_EQ(fiberManager_.activeFiber_, this);
202 DCHECK_EQ(state_, READY_TO_RUN);
206 if (fiberManager_.preemptRunner_) {
207 fiberManager_.preemptRunner_->run(std::ref(preemptImpl));
215 Fiber::LocalData::LocalData(const LocalData& other) : data_(nullptr) {
219 Fiber::LocalData& Fiber::LocalData::operator=(const LocalData& other) {
225 dataSize_ = other.dataSize_;
226 dataType_ = other.dataType_;
227 dataDestructor_ = other.dataDestructor_;
228 dataCopyConstructor_ = other.dataCopyConstructor_;
230 if (dataSize_ <= kBufferSize) {
233 data_ = allocateHeapBuffer(dataSize_);
236 dataCopyConstructor_(data_, other.data_);
241 void Fiber::LocalData::reset() {
246 dataDestructor_(data_);
250 void* Fiber::LocalData::allocateHeapBuffer(size_t size) {
251 return new char[size];
254 void Fiber::LocalData::freeHeapBuffer(void* buffer) {
255 delete[] reinterpret_cast<char*>(buffer);