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