f4958c1ee5b96a2981797c7dedcfcd45d82030fe
[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 <cassert>
23 #include <cstring>
24 #include <stdexcept>
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
31 namespace folly { namespace fibers {
32
33 namespace {
34 static const uint64_t kMagic8Bytes = 0xfaceb00cfaceb00c;
35
36 pid_t localThreadId() {
37   static thread_local pid_t threadId = syscall(SYS_gettid);
38   return threadId;
39 }
40
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());
44
45   std::fill(begin, end, kMagic8Bytes);
46 }
47
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());
52
53   auto firstNonMagic = std::find_if(
54     begin, end,
55     [](uint64_t val) {
56       return val != kMagic8Bytes;
57     }
58   );
59
60   return (end - firstNonMagic) * sizeof(uint64_t);
61 }
62
63 }  // anonymous namespace
64
65 void Fiber::setData(intptr_t data) {
66   assert(state_ == AWAITING);
67   data_ = data;
68   state_ = READY_TO_RUN;
69
70   if (LIKELY(threadId_ == localThreadId())) {
71     fiberManager_.readyFibers_.push_back(*this);
72     fiberManager_.ensureLoopScheduled();
73   } else {
74     fiberManager_.remoteReadyInsert(this);
75   }
76 }
77
78 Fiber::Fiber(FiberManager& fiberManager) :
79     fiberManager_(fiberManager) {
80
81   auto size = fiberManager_.options_.stackSize;
82   auto limit = fiberManager_.stackAllocator_.allocate(size);
83
84   fcontext_ = makeContext(limit, size, &Fiber::fiberFuncHelper);
85
86   if (UNLIKELY(fiberManager_.options_.debugRecordStackUsed)) {
87     fillMagic(fcontext_);
88   }
89 }
90
91 Fiber::~Fiber() {
92   fiberManager_.stackAllocator_.deallocate(
93     static_cast<unsigned char*>(fcontext_.stackLimit()),
94     fiberManager_.options_.stackSize);
95 }
96
97 void Fiber::recordStackPosition() {
98   int stackDummy;
99   fiberManager_.stackHighWatermark_ =
100     std::max(fiberManager_.stackHighWatermark_,
101              static_cast<size_t>(
102                static_cast<unsigned char*>(fcontext_.stackBase()) -
103                static_cast<unsigned char*>(
104                  static_cast<void*>(&stackDummy))));
105 }
106
107 void Fiber::fiberFuncHelper(intptr_t fiber) {
108   reinterpret_cast<Fiber*>(fiber)->fiberFunc();
109 }
110
111 /*
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.
115  */
116 #ifdef FOLLY_SANITIZE_ADDRESS
117 volatile bool loopForever = true;
118 #else
119 static constexpr bool loopForever = true;
120 #endif
121
122 void Fiber::fiberFunc() {
123   while (loopForever) {
124     assert(state_ == NOT_STARTED);
125
126     threadId_ = localThreadId();
127     state_ = RUNNING;
128
129     try {
130       if (resultFunc_) {
131         assert(finallyFunc_);
132         assert(!func_);
133
134         resultFunc_();
135       } else {
136         assert(func_);
137         func_();
138       }
139     } catch (...) {
140       fiberManager_.exceptionCallback_(std::current_exception(),
141                                        "running Fiber func_/resultFunc_");
142     }
143
144     if (UNLIKELY(fiberManager_.options_.debugRecordStackUsed)) {
145       fiberManager_.stackHighWatermark_ =
146         std::max(fiberManager_.stackHighWatermark_,
147                  nonMagicInBytes(fcontext_));
148     }
149
150     state_ = INVALID;
151
152     fiberManager_.activeFiber_ = nullptr;
153
154     auto fiber = reinterpret_cast<Fiber*>(
155       jumpContext(&fcontext_, &fiberManager_.mainContext_, 0));
156     assert(fiber == this);
157   }
158 }
159
160 intptr_t Fiber::preempt(State state) {
161   assert(fiberManager_.activeFiber_ == this);
162   assert(state_ == RUNNING);
163   assert(state != RUNNING);
164
165   fiberManager_.activeFiber_ = nullptr;
166   state_ = state;
167
168   recordStackPosition();
169
170   auto ret = jumpContext(&fcontext_, &fiberManager_.mainContext_, 0);
171
172   assert(fiberManager_.activeFiber_ == this);
173   assert(state_ == READY_TO_RUN);
174   state_ = RUNNING;
175
176   return ret;
177 }
178
179 Fiber::LocalData::LocalData(const LocalData& other) : data_(nullptr) {
180   *this = other;
181 }
182
183 Fiber::LocalData& Fiber::LocalData::operator=(const LocalData& other) {
184   reset();
185   if (!other.data_) {
186     return *this;
187   }
188
189   dataSize_ = other.dataSize_;
190   dataType_ = other.dataType_;
191   dataDestructor_ = other.dataDestructor_;
192   dataCopyConstructor_ = other.dataCopyConstructor_;
193
194   if (dataSize_ <= kBufferSize) {
195     data_ = &buffer_;
196   } else {
197     data_ = allocateHeapBuffer(dataSize_);
198   }
199
200   dataCopyConstructor_(data_, other.data_);
201
202   return *this;
203 }
204
205 void Fiber::LocalData::reset() {
206   if (!data_) {
207     return;
208   }
209
210   dataDestructor_(data_);
211   data_ = nullptr;
212 }
213
214 void* Fiber::LocalData::allocateHeapBuffer(size_t size) {
215   return new char[size];
216 }
217
218 void Fiber::LocalData::freeHeapBuffer(void* buffer) {
219   delete[] reinterpret_cast<char*>(buffer);
220 }
221
222 }}