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 <glog/logging.h>
23 #include <folly/Likely.h>
24 #include <folly/Portability.h>
25 #include <folly/fibers/BoostContextCompatibility.h>
26 #include <folly/fibers/FiberManagerInternal.h>
27 #include <folly/portability/SysSyscall.h>
28 #include <folly/portability/Unistd.h>
34 static const uint64_t kMagic8Bytes = 0xfaceb00cfaceb00c;
36 std::thread::id localThreadId() {
37 return std::this_thread::get_id();
40 /* Size of the region from p + nBytes down to the last non-magic value */
41 static size_t nonMagicInBytes(const FContext& context) {
42 uint64_t* begin = static_cast<uint64_t*>(context.stackLimit());
43 uint64_t* end = static_cast<uint64_t*>(context.stackBase());
45 auto firstNonMagic = std::find_if(
46 begin, end, [](uint64_t val) { return val != kMagic8Bytes; });
48 return (end - firstNonMagic) * sizeof(uint64_t);
51 } // anonymous namespace
53 void Fiber::setData(FContext::FiberData data) {
54 DCHECK_EQ(state_, AWAITING);
56 state_ = READY_TO_RUN;
58 if (fiberManager_.observer_) {
59 fiberManager_.observer_->runnable(reinterpret_cast<uintptr_t>(this));
62 if (LIKELY(threadId_ == localThreadId())) {
63 fiberManager_.readyFibers_.push_back(*this);
64 fiberManager_.ensureLoopScheduled();
66 fiberManager_.remoteReadyInsert(this);
70 Fiber::Fiber(FiberManager& fiberManager) : fiberManager_(fiberManager) {
71 auto size = fiberManager_.options_.stackSize;
72 auto limit = fiberManager_.stackAllocator_.allocate(size);
74 fcontext_ = makeContext(limit, size, &Fiber::fiberFuncHelper);
76 fiberManager_.allFibers_.push_back(*this);
79 void Fiber::init(bool recordStackUsed) {
80 // It is necessary to disable the logic for ASAN because we change
82 #ifndef FOLLY_SANITIZE_ADDRESS
83 recordStackUsed_ = recordStackUsed;
84 if (UNLIKELY(recordStackUsed_ && !stackFilledWithMagic_)) {
85 auto limit = fcontext_.stackLimit();
86 auto base = fcontext_.stackBase();
89 static_cast<uint64_t*>(limit),
90 static_cast<uint64_t*>(base),
93 // newer versions of boost allocate context on fiber stack,
94 // need to create a new one
95 auto size = fiberManager_.options_.stackSize;
96 fcontext_ = makeContext(limit, size, &Fiber::fiberFuncHelper);
98 stackFilledWithMagic_ = true;
101 (void)recordStackUsed;
106 #ifdef FOLLY_SANITIZE_ADDRESS
107 fiberManager_.unpoisonFiberStack(this);
109 fiberManager_.stackAllocator_.deallocate(
110 static_cast<unsigned char*>(fcontext_.stackLimit()),
111 fiberManager_.options_.stackSize);
114 void Fiber::recordStackPosition() {
116 auto currentPosition = static_cast<size_t>(
117 static_cast<unsigned char*>(fcontext_.stackBase()) -
118 static_cast<unsigned char*>(static_cast<void*>(&stackDummy)));
119 fiberManager_.stackHighWatermark_ =
120 std::max(fiberManager_.stackHighWatermark_, currentPosition);
121 VLOG(4) << "Stack usage: " << currentPosition;
124 void Fiber::fiberFuncHelper(FContext::FiberArg fiber) {
125 reinterpret_cast<Fiber*>(FContext::getFiber(fiber))->fiberFunc();
128 void Fiber::fiberFunc() {
129 #ifdef FOLLY_SANITIZE_ADDRESS
130 fiberManager_.registerFinishSwitchStackWithAsan(
131 nullptr, &asanMainStackBase_, &asanMainStackSize_);
135 DCHECK_EQ(state_, NOT_STARTED);
137 threadId_ = localThreadId();
142 DCHECK(finallyFunc_);
151 fiberManager_.exceptionCallback_(
152 std::current_exception(), "running Fiber func_/resultFunc_");
155 if (UNLIKELY(recordStackUsed_)) {
156 fiberManager_.stackHighWatermark_ = std::max(
157 fiberManager_.stackHighWatermark_, nonMagicInBytes(fcontext_));
158 VLOG(3) << "Max stack usage: " << fiberManager_.stackHighWatermark_;
160 fiberManager_.stackHighWatermark_ <
161 fiberManager_.options_.stackSize - 64)
162 << "Fiber stack overflow";
167 auto context = fiberManager_.deactivateFiber(this);
169 DCHECK_EQ(reinterpret_cast<Fiber*>(context), this);
173 FContext::FiberData Fiber::preempt(State state) {
174 FContext::FiberData ret;
176 auto preemptImpl = [&]() mutable {
177 DCHECK_EQ(fiberManager_.activeFiber_, this);
178 DCHECK_EQ(state_, RUNNING);
179 DCHECK_NE(state, RUNNING);
183 recordStackPosition();
185 ret = fiberManager_.deactivateFiber(this);
187 DCHECK_EQ(fiberManager_.activeFiber_, this);
188 DCHECK_EQ(state_, READY_TO_RUN);
192 if (fiberManager_.preemptRunner_) {
193 fiberManager_.preemptRunner_->run(std::ref(preemptImpl));
201 Fiber::LocalData::LocalData(const LocalData& other) : data_(nullptr) {
205 Fiber::LocalData& Fiber::LocalData::operator=(const LocalData& other) {
211 dataSize_ = other.dataSize_;
212 dataType_ = other.dataType_;
213 dataDestructor_ = other.dataDestructor_;
214 dataCopyConstructor_ = other.dataCopyConstructor_;
216 if (dataSize_ <= kBufferSize) {
219 data_ = allocateHeapBuffer(dataSize_);
222 dataCopyConstructor_(data_, other.data_);
227 void Fiber::LocalData::reset() {
232 dataDestructor_(data_);
236 void* Fiber::LocalData::allocateHeapBuffer(size_t size) {
237 return new char[size];
240 void Fiber::LocalData::freeHeapBuffer(void* buffer) {
241 delete[] reinterpret_cast<char*>(buffer);