2 * Copyright 2015 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>
26 #include <folly/Likely.h>
27 #include <folly/Portability.h>
28 #include <folly/experimental/fibers/BoostContextCompatibility.h>
29 #include <folly/experimental/fibers/FiberManager.h>
31 namespace folly { namespace fibers {
34 static const uint64_t kMagic8Bytes = 0xfaceb00cfaceb00c;
36 pid_t localThreadId() {
37 static thread_local pid_t threadId = syscall(SYS_gettid);
41 static void fillMagic(const FContext& context) {
42 uint64_t* begin = static_cast<uint64_t*>(context.stackLimit());
43 uint64_t* end = static_cast<uint64_t*>(context.stackBase());
45 std::fill(begin, end, kMagic8Bytes);
48 /* Size of the region from p + nBytes down to the last non-magic value */
49 static size_t nonMagicInBytes(const FContext& context) {
50 uint64_t* begin = static_cast<uint64_t*>(context.stackLimit());
51 uint64_t* end = static_cast<uint64_t*>(context.stackBase());
53 auto firstNonMagic = std::find_if(
56 return val != kMagic8Bytes;
60 return (end - firstNonMagic) * sizeof(uint64_t);
63 } // anonymous namespace
65 void Fiber::setData(intptr_t data) {
66 assert(state_ == AWAITING);
68 state_ = READY_TO_RUN;
70 if (LIKELY(threadId_ == localThreadId())) {
71 fiberManager_.readyFibers_.push_back(*this);
72 fiberManager_.ensureLoopScheduled();
74 fiberManager_.remoteReadyInsert(this);
78 Fiber::Fiber(FiberManager& fiberManager) :
79 fiberManager_(fiberManager) {
81 auto size = fiberManager_.options_.stackSize;
82 auto limit = fiberManager_.stackAllocator_.allocate(size);
84 fcontext_ = makeContext(limit, size, &Fiber::fiberFuncHelper);
86 if (UNLIKELY(fiberManager_.options_.debugRecordStackUsed)) {
92 fiberManager_.stackAllocator_.deallocate(
93 static_cast<unsigned char*>(fcontext_.stackLimit()),
94 fiberManager_.options_.stackSize);
97 void Fiber::recordStackPosition() {
99 fiberManager_.stackHighWatermark_ =
100 std::max(fiberManager_.stackHighWatermark_,
102 static_cast<unsigned char*>(fcontext_.stackBase()) -
103 static_cast<unsigned char*>(
104 static_cast<void*>(&stackDummy))));
107 void Fiber::fiberFuncHelper(intptr_t fiber) {
108 reinterpret_cast<Fiber*>(fiber)->fiberFunc();
112 * Some weird bug in ASAN causes fiberFunc to allocate boundless amounts of
113 * memory inside __asan_handle_no_return. Work around this in ASAN builds by
114 * tricking the compiler into thinking it may, someday, return.
116 #ifdef FOLLY_SANITIZE_ADDRESS
117 volatile bool loopForever = true;
119 static constexpr bool loopForever = true;
122 void Fiber::fiberFunc() {
123 while (loopForever) {
124 assert(state_ == NOT_STARTED);
126 threadId_ = localThreadId();
131 assert(finallyFunc_);
140 fiberManager_.exceptionCallback_(std::current_exception(),
141 "running Fiber func_/resultFunc_");
144 if (UNLIKELY(fiberManager_.options_.debugRecordStackUsed)) {
145 fiberManager_.stackHighWatermark_ =
146 std::max(fiberManager_.stackHighWatermark_,
147 nonMagicInBytes(fcontext_));
152 fiberManager_.activeFiber_ = nullptr;
154 auto fiber = reinterpret_cast<Fiber*>(
155 jumpContext(&fcontext_, &fiberManager_.mainContext_, 0));
156 assert(fiber == this);
160 intptr_t Fiber::preempt(State state) {
161 assert(fiberManager_.activeFiber_ == this);
162 assert(state_ == RUNNING);
163 assert(state != RUNNING);
165 fiberManager_.activeFiber_ = nullptr;
168 recordStackPosition();
170 auto ret = jumpContext(&fcontext_, &fiberManager_.mainContext_, 0);
172 assert(fiberManager_.activeFiber_ == this);
173 assert(state_ == READY_TO_RUN);
179 Fiber::LocalData::LocalData(const LocalData& other) : data_(nullptr) {
183 Fiber::LocalData& Fiber::LocalData::operator=(const LocalData& other) {
189 dataSize_ = other.dataSize_;
190 dataType_ = other.dataType_;
191 dataDestructor_ = other.dataDestructor_;
192 dataCopyConstructor_ = other.dataCopyConstructor_;
194 if (dataSize_ <= kBufferSize) {
197 data_ = allocateHeapBuffer(dataSize_);
200 dataCopyConstructor_(data_, other.data_);
205 void Fiber::LocalData::reset() {
210 dataDestructor_(data_);
214 void* Fiber::LocalData::allocateHeapBuffer(size_t size) {
215 return new char[size];
218 void Fiber::LocalData::freeHeapBuffer(void* buffer) {
219 delete[] reinterpret_cast<char*>(buffer);