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