2 * Copyright 2017 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/FiberManagerInternal.h>
26 #include <folly/portability/SysSyscall.h>
27 #include <folly/portability/Unistd.h>
33 static const uint64_t kMagic8Bytes = 0xfaceb00cfaceb00c;
35 std::thread::id localThreadId() {
36 return std::this_thread::get_id();
39 /* Size of the region from p + nBytes down to the last non-magic value */
40 static size_t nonMagicInBytes(unsigned char* stackLimit, size_t stackSize) {
41 CHECK_EQ(reinterpret_cast<intptr_t>(stackLimit) % sizeof(uint64_t), 0u);
42 CHECK_EQ(stackSize % sizeof(uint64_t), 0u);
43 uint64_t* begin = reinterpret_cast<uint64_t*>(stackLimit);
44 uint64_t* end = reinterpret_cast<uint64_t*>(stackLimit + stackSize);
46 auto firstNonMagic = std::find_if(
47 begin, end, [](uint64_t val) { return val != kMagic8Bytes; });
49 return (end - firstNonMagic) * sizeof(uint64_t);
54 void Fiber::resume() {
55 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)
71 : fiberManager_(fiberManager),
72 fiberStackSize_(fiberManager_.options_.stackSize),
73 fiberStackLimit_(fiberManager_.stackAllocator_.allocate(fiberStackSize_)),
74 fiberImpl_([this] { fiberFunc(); }, fiberStackLimit_, fiberStackSize_) {
75 fiberManager_.allFibers_.push_back(*this);
78 void Fiber::init(bool recordStackUsed) {
79 // It is necessary to disable the logic for ASAN because we change
81 #ifndef FOLLY_SANITIZE_ADDRESS
82 recordStackUsed_ = recordStackUsed;
83 if (UNLIKELY(recordStackUsed_ && !stackFilledWithMagic_)) {
85 reinterpret_cast<intptr_t>(fiberStackLimit_) % sizeof(uint64_t), 0u);
86 CHECK_EQ(fiberStackSize_ % sizeof(uint64_t), 0u);
88 reinterpret_cast<uint64_t*>(fiberStackLimit_),
89 reinterpret_cast<uint64_t*>(fiberStackLimit_ + fiberStackSize_),
92 stackFilledWithMagic_ = true;
94 // newer versions of boost allocate context on fiber stack,
95 // need to create a new one
97 FiberImpl([this] { fiberFunc(); }, fiberStackLimit_, fiberStackSize_);
100 (void)recordStackUsed;
105 #ifdef FOLLY_SANITIZE_ADDRESS
106 if (asanFakeStack_ != nullptr) {
107 fiberManager_.freeFakeStack(asanFakeStack_);
109 fiberManager_.unpoisonFiberStack(this);
111 fiberManager_.stackAllocator_.deallocate(fiberStackLimit_, fiberStackSize_);
114 void Fiber::recordStackPosition() {
116 auto currentPosition = static_cast<size_t>(
117 fiberStackLimit_ + fiberStackSize_ -
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 [[noreturn]] void Fiber::fiberFunc() {
125 #ifdef FOLLY_SANITIZE_ADDRESS
126 fiberManager_.registerFinishSwitchStackWithAsan(
127 nullptr, &asanMainStackBase_, &asanMainStackSize_);
131 DCHECK_EQ(state_, NOT_STARTED);
133 threadId_ = localThreadId();
138 DCHECK(finallyFunc_);
147 fiberManager_.exceptionCallback_(
148 std::current_exception(), "running Fiber func_/resultFunc_");
151 if (UNLIKELY(recordStackUsed_)) {
152 fiberManager_.stackHighWatermark_ = std::max(
153 fiberManager_.stackHighWatermark_,
154 nonMagicInBytes(fiberStackLimit_, fiberStackSize_));
155 VLOG(3) << "Max stack usage: " << fiberManager_.stackHighWatermark_;
157 fiberManager_.stackHighWatermark_ <
158 fiberManager_.options_.stackSize - 64)
159 << "Fiber stack overflow";
164 fiberManager_.deactivateFiber(this);
168 void Fiber::preempt(State state) {
169 auto preemptImpl = [&]() mutable {
170 DCHECK_EQ(fiberManager_.activeFiber_, this);
171 DCHECK_EQ(state_, RUNNING);
172 DCHECK_NE(state, RUNNING);
176 recordStackPosition();
178 fiberManager_.deactivateFiber(this);
180 DCHECK_EQ(fiberManager_.activeFiber_, this);
181 DCHECK_EQ(state_, READY_TO_RUN);
185 if (fiberManager_.preemptRunner_) {
186 fiberManager_.preemptRunner_->run(std::ref(preemptImpl));
192 Fiber::LocalData::~LocalData() {
196 Fiber::LocalData::LocalData(const LocalData& other) : data_(nullptr) {
200 Fiber::LocalData& Fiber::LocalData::operator=(const LocalData& other) {
206 dataSize_ = other.dataSize_;
207 dataType_ = other.dataType_;
208 dataDestructor_ = other.dataDestructor_;
209 dataCopyConstructor_ = other.dataCopyConstructor_;
211 if (dataSize_ <= kBufferSize) {
214 data_ = allocateHeapBuffer(dataSize_);
217 dataCopyConstructor_(data_, other.data_);
222 void Fiber::LocalData::reset() {
227 dataDestructor_(data_);
231 void* Fiber::LocalData::allocateHeapBuffer(size_t size) {
232 return new char[size];
235 void Fiber::LocalData::freeHeapBuffer(void* buffer) {
236 delete[] reinterpret_cast<char*>(buffer);