Simplify activateFiber()/deactivateFiber()
[folly.git] / folly / fibers / Fiber.cpp
1 /*
2  * Copyright 2016 Facebook, Inc.
3  *
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
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 #include "Fiber.h"
17
18 #include <glog/logging.h>
19 #include <algorithm>
20 #include <cstring>
21 #include <stdexcept>
22
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>
29
30 namespace folly {
31 namespace fibers {
32
33 namespace {
34 static const uint64_t kMagic8Bytes = 0xfaceb00cfaceb00c;
35
36 std::thread::id localThreadId() {
37   return std::this_thread::get_id();
38 }
39
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());
44
45   auto firstNonMagic = std::find_if(
46       begin, end, [](uint64_t val) { return val != kMagic8Bytes; });
47
48   return (end - firstNonMagic) * sizeof(uint64_t);
49 }
50
51 } // anonymous namespace
52
53 void Fiber::resume() {
54   DCHECK_EQ(state_, AWAITING);
55   state_ = READY_TO_RUN;
56
57   if (fiberManager_.observer_) {
58     fiberManager_.observer_->runnable(reinterpret_cast<uintptr_t>(this));
59   }
60
61   if (LIKELY(threadId_ == localThreadId())) {
62     fiberManager_.readyFibers_.push_back(*this);
63     fiberManager_.ensureLoopScheduled();
64   } else {
65     fiberManager_.remoteReadyInsert(this);
66   }
67 }
68
69 Fiber::Fiber(FiberManager& fiberManager) : fiberManager_(fiberManager) {
70   auto size = fiberManager_.options_.stackSize;
71   auto limit = fiberManager_.stackAllocator_.allocate(size);
72
73   fcontext_ = makeContext(limit, size, &Fiber::fiberFuncHelper);
74
75   fiberManager_.allFibers_.push_back(*this);
76 }
77
78 void Fiber::init(bool recordStackUsed) {
79 // It is necessary to disable the logic for ASAN because we change
80 // the fiber's stack.
81 #ifndef FOLLY_SANITIZE_ADDRESS
82   recordStackUsed_ = recordStackUsed;
83   if (UNLIKELY(recordStackUsed_ && !stackFilledWithMagic_)) {
84     auto limit = fcontext_.stackLimit();
85     auto base = fcontext_.stackBase();
86
87     std::fill(
88         static_cast<uint64_t*>(limit),
89         static_cast<uint64_t*>(base),
90         kMagic8Bytes);
91
92     // newer versions of boost allocate context on fiber stack,
93     // need to create a new one
94     auto size = fiberManager_.options_.stackSize;
95     fcontext_ = makeContext(limit, size, &Fiber::fiberFuncHelper);
96
97     stackFilledWithMagic_ = true;
98   }
99 #else
100   (void)recordStackUsed;
101 #endif
102 }
103
104 Fiber::~Fiber() {
105 #ifdef FOLLY_SANITIZE_ADDRESS
106   fiberManager_.unpoisonFiberStack(this);
107 #endif
108   fiberManager_.stackAllocator_.deallocate(
109       static_cast<unsigned char*>(fcontext_.stackLimit()),
110       fiberManager_.options_.stackSize);
111 }
112
113 void Fiber::recordStackPosition() {
114   int stackDummy;
115   auto currentPosition = static_cast<size_t>(
116       static_cast<unsigned char*>(fcontext_.stackBase()) -
117       static_cast<unsigned char*>(static_cast<void*>(&stackDummy)));
118   fiberManager_.stackHighWatermark_ =
119       std::max(fiberManager_.stackHighWatermark_, currentPosition);
120   VLOG(4) << "Stack usage: " << currentPosition;
121 }
122
123 void Fiber::fiberFuncHelper(intptr_t fiber) {
124   reinterpret_cast<Fiber*>(fiber)->fiberFunc();
125 }
126
127 void Fiber::fiberFunc() {
128 #ifdef FOLLY_SANITIZE_ADDRESS
129   fiberManager_.registerFinishSwitchStackWithAsan(
130       nullptr, &asanMainStackBase_, &asanMainStackSize_);
131 #endif
132
133   while (true) {
134     DCHECK_EQ(state_, NOT_STARTED);
135
136     threadId_ = localThreadId();
137     state_ = RUNNING;
138
139     try {
140       if (resultFunc_) {
141         DCHECK(finallyFunc_);
142         DCHECK(!func_);
143
144         resultFunc_();
145       } else {
146         DCHECK(func_);
147         func_();
148       }
149     } catch (...) {
150       fiberManager_.exceptionCallback_(
151           std::current_exception(), "running Fiber func_/resultFunc_");
152     }
153
154     if (UNLIKELY(recordStackUsed_)) {
155       fiberManager_.stackHighWatermark_ = std::max(
156           fiberManager_.stackHighWatermark_, nonMagicInBytes(fcontext_));
157       VLOG(3) << "Max stack usage: " << fiberManager_.stackHighWatermark_;
158       CHECK(
159           fiberManager_.stackHighWatermark_ <
160           fiberManager_.options_.stackSize - 64)
161           << "Fiber stack overflow";
162     }
163
164     state_ = INVALID;
165
166     fiberManager_.deactivateFiber(this);
167   }
168 }
169
170 void Fiber::preempt(State state) {
171   auto preemptImpl = [&]() mutable {
172     DCHECK_EQ(fiberManager_.activeFiber_, this);
173     DCHECK_EQ(state_, RUNNING);
174     DCHECK_NE(state, RUNNING);
175
176     state_ = state;
177
178     recordStackPosition();
179
180     fiberManager_.deactivateFiber(this);
181
182     DCHECK_EQ(fiberManager_.activeFiber_, this);
183     DCHECK_EQ(state_, READY_TO_RUN);
184     state_ = RUNNING;
185   };
186
187   if (fiberManager_.preemptRunner_) {
188     fiberManager_.preemptRunner_->run(std::ref(preemptImpl));
189   } else {
190     preemptImpl();
191   }
192 }
193
194 Fiber::LocalData::LocalData(const LocalData& other) : data_(nullptr) {
195   *this = other;
196 }
197
198 Fiber::LocalData& Fiber::LocalData::operator=(const LocalData& other) {
199   reset();
200   if (!other.data_) {
201     return *this;
202   }
203
204   dataSize_ = other.dataSize_;
205   dataType_ = other.dataType_;
206   dataDestructor_ = other.dataDestructor_;
207   dataCopyConstructor_ = other.dataCopyConstructor_;
208
209   if (dataSize_ <= kBufferSize) {
210     data_ = &buffer_;
211   } else {
212     data_ = allocateHeapBuffer(dataSize_);
213   }
214
215   dataCopyConstructor_(data_, other.data_);
216
217   return *this;
218 }
219
220 void Fiber::LocalData::reset() {
221   if (!data_) {
222     return;
223   }
224
225   dataDestructor_(data_);
226   data_ = nullptr;
227 }
228
229 void* Fiber::LocalData::allocateHeapBuffer(size_t size) {
230   return new char[size];
231 }
232
233 void Fiber::LocalData::freeHeapBuffer(void* buffer) {
234   delete[] reinterpret_cast<char*>(buffer);
235 }
236 }
237 }