foundationdb/flow/ThreadHelper.actor.h

621 lines
17 KiB
C
Raw Normal View History

2017-05-26 04:48:44 +08:00
/*
* ThreadHelper.actor.h
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
2017-05-26 04:48:44 +08:00
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
2017-05-26 04:48:44 +08:00
* http://www.apache.org/licenses/LICENSE-2.0
*
2017-05-26 04:48:44 +08:00
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
// When actually compiled (NO_INTELLISENSE), include the generated
// version of this file. In intellisense use the source version.
#if defined(NO_INTELLISENSE) && !defined(FLOW_THREADHELPER_ACTOR_G_H)
#define FLOW_THREADHELPER_ACTOR_G_H
#include "flow/ThreadHelper.actor.g.h"
2017-05-26 04:48:44 +08:00
#elif !defined(FLOW_THREADHELPER_ACTOR_H)
#define FLOW_THREADHELPER_ACTOR_H
2017-05-26 04:48:44 +08:00
2020-07-16 07:33:01 +08:00
#include <utility>
2017-05-26 04:48:44 +08:00
#include "flow/flow.h"
#include "flow/actorcompiler.h" // This must be the last #include.
2017-05-26 04:48:44 +08:00
// template <class F>
// void onMainThreadVoid( F f ) {
// Promise<Void> signal;
// doOnMainThreadVoid( signal.getFuture(), f );
// g_network->onMainThread( std::move(signal), TaskPriority::DefaultOnMainThread );
2017-05-26 04:48:44 +08:00
// }
template <class F>
void onMainThreadVoid(F f, Error* err, TaskPriority taskID = TaskPriority::DefaultOnMainThread) {
2017-05-26 04:48:44 +08:00
Promise<Void> signal;
doOnMainThreadVoid(signal.getFuture(), f, err);
g_network->onMainThread(std::move(signal), taskID);
2017-05-26 04:48:44 +08:00
}
struct ThreadCallback {
virtual bool canFire(int notMadeActive) const = 0;
virtual void fire(const Void& unused, int& userParam) = 0;
2017-05-26 04:48:44 +08:00
virtual void error(const Error&, int& userParam) = 0;
virtual ThreadCallback* addCallback(ThreadCallback* cb);
2017-05-26 04:48:44 +08:00
virtual bool contains(ThreadCallback* cb) const { return false; }
2017-05-26 04:48:44 +08:00
virtual void clearCallback(ThreadCallback* cb) {
2017-05-26 04:48:44 +08:00
// If this is the only registered callback this will be called with (possibly) arbitrary pointers
}
virtual void destroy() { UNSTOPPABLE_ASSERT(false); }
virtual bool isMultiCallback() const { return false; }
2017-05-26 04:48:44 +08:00
};
class ThreadMultiCallback final : public ThreadCallback, public FastAllocated<ThreadMultiCallback> {
2017-05-26 04:48:44 +08:00
public:
ThreadMultiCallback() {}
2017-05-26 04:48:44 +08:00
ThreadCallback* addCallback(ThreadCallback* callback) override {
UNSTOPPABLE_ASSERT(callbackMap.count(callback) ==
0); // May be triggered by a waitForAll on a vector with the same future in it more than once
2017-05-26 04:48:44 +08:00
callbackMap[callback] = callbacks.size();
callbacks.push_back(callback);
return (ThreadCallback*)this;
}
bool contains(ThreadCallback* cb) const override { return callbackMap.count(cb) != 0; }
2017-05-26 04:48:44 +08:00
void clearCallback(ThreadCallback* callback) override {
2017-05-26 04:48:44 +08:00
auto it = callbackMap.find(callback);
if (it == callbackMap.end())
return;
UNSTOPPABLE_ASSERT(it->second < callbacks.size() && it->second >= 0);
if (it->second != callbacks.size() - 1) {
callbacks[it->second] = callbacks.back();
callbackMap[callbacks[it->second]] = it->second;
}
callbacks.pop_back();
callbackMap.erase(it);
}
bool canFire(int notMadeActive) const override { return true; }
2017-05-26 04:48:44 +08:00
void fire(const Void& value, int& loopDepth) override {
2017-05-26 04:48:44 +08:00
if (callbacks.size() > 10000)
TraceEvent(SevWarn, "LargeMultiCallback").detail("CallbacksSize", callbacks.size());
UNSTOPPABLE_ASSERT(loopDepth == 0);
while (callbacks.size()) {
auto cb = callbacks.back();
callbacks.pop_back();
callbackMap.erase(cb);
if (cb->canFire(0)) {
int ld = 0;
cb->fire(value, ld);
}
}
}
void error(const Error& err, int& loopDepth) override {
2017-05-26 04:48:44 +08:00
if (callbacks.size() > 10000)
TraceEvent(SevWarn, "LargeMultiCallback").detail("CallbacksSize", callbacks.size());
UNSTOPPABLE_ASSERT(loopDepth == 0);
while (callbacks.size()) {
auto cb = callbacks.back();
callbacks.pop_back();
callbackMap.erase(cb);
if (cb->canFire(0)) {
int ld = 0;
cb->error(err, ld);
}
}
}
void destroy() override {
2017-05-26 04:48:44 +08:00
UNSTOPPABLE_ASSERT(callbacks.empty());
delete this;
}
bool isMultiCallback() const override { return true; }
2017-05-26 04:48:44 +08:00
private:
std::vector<ThreadCallback*> callbacks;
std::unordered_map<ThreadCallback*, int> callbackMap;
};
struct SetCallbackResult {
enum Result { FIRED, CANNOT_FIRE, CALLBACK_SET };
};
class ThreadSingleAssignmentVarBase {
public:
enum Status { Unset, NeverSet, Set, ErrorSet }; // order is important
2017-05-26 04:48:44 +08:00
// volatile long referenceCount;
ThreadSpinLock mutex;
Status status;
Error error;
ThreadCallback* callback;
2017-05-26 04:48:44 +08:00
2020-07-16 07:33:01 +08:00
bool isReady() {
2017-05-26 04:48:44 +08:00
ThreadSpinLockHolder holder(mutex);
return isReadyUnsafe();
}
2020-07-16 07:33:01 +08:00
bool isError() {
2017-05-26 04:48:44 +08:00
ThreadSpinLockHolder holder(mutex);
return isErrorUnsafe();
}
int getErrorCode() {
ThreadSpinLockHolder holder(mutex);
if (!isReadyUnsafe())
return error_code_future_not_set;
if (!isErrorUnsafe())
return error_code_success;
2017-05-26 04:48:44 +08:00
return error.code();
}
2020-07-16 07:33:01 +08:00
bool canBeSet() {
2017-05-26 04:48:44 +08:00
ThreadSpinLockHolder holder(mutex);
return canBeSetUnsafe();
}
class BlockCallback : public ThreadCallback {
public:
Event ev;
BlockCallback(ThreadSingleAssignmentVarBase& sav) {
int ignore = 0;
sav.callOrSetAsCallback(this, ignore, 0);
ev.block();
}
2017-05-26 04:48:44 +08:00
bool canFire(int notMadeActive) const override { return true; }
void fire(const Void& unused, int& userParam) override { ev.set(); }
void error(const Error&, int& userParam) override { ev.set(); }
2017-05-26 04:48:44 +08:00
};
void blockUntilReady() {
2020-04-24 10:50:13 +08:00
if (!isReady()) {
BlockCallback cb(*this);
2017-05-26 04:48:44 +08:00
}
}
2020-09-18 04:26:28 +08:00
void blockUntilReadyCheckOnMainThread() {
if (!isReady()) {
2020-09-17 05:34:13 +08:00
if (g_network->isOnMainThread()) {
throw blocked_from_network_thread();
}
BlockCallback cb(*this);
2017-05-26 04:48:44 +08:00
}
}
ThreadSingleAssignmentVarBase() : status(Unset), callback(NULL), valueReferenceCount(0) {} //, referenceCount(1) {}
2020-07-16 07:33:01 +08:00
~ThreadSingleAssignmentVarBase() {
this->mutex.assertNotEntered();
2017-05-26 04:48:44 +08:00
if (callback)
2017-05-26 04:48:44 +08:00
callback->destroy();
}
virtual void addref() = 0;
virtual void delref() = 0;
2017-05-26 04:48:44 +08:00
void send(Never) {
if (TRACE_SAMPLE())
TraceEvent(SevSample, "Promise_sendNever");
2017-05-26 04:48:44 +08:00
ThreadSpinLockHolder holder(mutex);
if (!canBeSetUnsafe())
ASSERT(false); // Promise fulfilled twice
2017-05-26 04:48:44 +08:00
this->status = NeverSet;
}
void sendError(const Error& err) {
if (TRACE_SAMPLE())
TraceEvent(SevSample, "Promise_sendError").detail("ErrorCode", err.code());
2017-05-26 04:48:44 +08:00
this->mutex.enter();
if (!canBeSetUnsafe()) {
this->mutex.leave();
ASSERT(false); // Promise fulfilled twice
2017-05-26 04:48:44 +08:00
}
error = err;
2020-07-16 07:33:01 +08:00
status = ErrorSet;
2017-05-26 04:48:44 +08:00
if (!callback) {
this->mutex.leave();
return;
}
auto func = callback;
if (!callback->isMultiCallback())
2020-08-28 06:31:24 +08:00
callback = nullptr;
2017-05-26 04:48:44 +08:00
if (!func->canFire(0)) {
this->mutex.leave();
} else {
this->mutex.leave();
// Thread safe because status is now ErrorSet and callback is nullptr, meaning than callback cannot change
2017-05-26 04:48:44 +08:00
int userParam = 0;
func->error(err, userParam);
}
}
SetCallbackResult::Result callOrSetAsCallback(ThreadCallback* callback, int& userParam1, int notMadeActive) {
2017-05-26 04:48:44 +08:00
this->mutex.enter();
if (isReadyUnsafe()) {
if (callback->canFire(notMadeActive)) {
this->mutex.leave();
// Thread safe because the Future is ready, meaning that status and this->error will not change
2017-05-26 04:48:44 +08:00
if (status == ErrorSet) {
auto error = this->error; // Since callback might free this
callback->error(error, userParam1);
2017-05-26 04:48:44 +08:00
} else {
callback->fire(Void(), userParam1);
2017-05-26 04:48:44 +08:00
}
return SetCallbackResult::FIRED;
} else {
this->mutex.leave();
return SetCallbackResult::CANNOT_FIRE;
}
} else {
if (this->callback)
this->callback = this->callback->addCallback(callback);
2017-05-26 04:48:44 +08:00
else
this->callback = callback;
this->mutex.leave();
return SetCallbackResult::CALLBACK_SET;
}
}
// If this function returns false, then this SAV has already been set and the callback has been or will be called.
// If this function returns true, then the callback has not and will not be called by this SAV (unless it is set
// later). This doesn't clear callbacks that are nested multiple levels inside of multi-callbacks
bool clearCallback(ThreadCallback* cb) {
2017-05-26 04:48:44 +08:00
this->mutex.enter();
// If another thread is calling fire in send/sendError, it would be unsafe to clear the callback
2017-05-26 04:48:44 +08:00
if (isReadyUnsafe()) {
this->mutex.leave();
return false;
}
// Only clear the callback if it belongs to the caller, because
// another actor could be waiting on it now!
if (callback == cb)
2020-08-28 06:31:24 +08:00
callback = nullptr;
else if (callback != nullptr)
callback->clearCallback(cb);
2017-05-26 04:48:44 +08:00
this->mutex.leave();
return true;
}
void setCancel(Future<Void>&& cf) { cancelFuture = std::move(cf); }
2017-05-26 04:48:44 +08:00
virtual void cancel() {
2020-09-06 04:58:22 +08:00
onMainThreadVoid(
[this]() {
this->cancelFuture.cancel();
this->delref();
},
nullptr);
2017-05-26 04:48:44 +08:00
}
void releaseMemory() {
ThreadSpinLockHolder holder(mutex);
if (--valueReferenceCount == 0)
cleanupUnsafe();
}
private:
Future<Void> cancelFuture;
int32_t valueReferenceCount;
protected:
2020-09-10 23:32:52 +08:00
// The caller of any of these *Unsafe functions should be holding |mutex|
2017-05-26 04:48:44 +08:00
bool isReadyUnsafe() const { return status >= Set; }
bool isErrorUnsafe() const { return status == ErrorSet; }
bool canBeSetUnsafe() const { return status == Unset; }
void addValueReferenceUnsafe() { ++valueReferenceCount; }
2017-05-26 04:48:44 +08:00
virtual void cleanupUnsafe() {
if (status != ErrorSet) {
2017-05-26 04:48:44 +08:00
error = future_released();
status = ErrorSet;
}
valueReferenceCount = 0;
this->addref();
cancel();
}
};
template <class T>
class ThreadSingleAssignmentVar
: public ThreadSingleAssignmentVarBase,
/* public FastAllocated<ThreadSingleAssignmentVar<T>>,*/ public ThreadSafeReferenceCounted<
ThreadSingleAssignmentVar<T>> {
2017-05-26 04:48:44 +08:00
public:
virtual ~ThreadSingleAssignmentVar() {}
T value;
T get() {
ThreadSpinLockHolder holder(mutex);
if (!isReadyUnsafe())
2017-05-26 04:48:44 +08:00
throw future_not_set();
if (isErrorUnsafe())
2017-05-26 04:48:44 +08:00
throw error;
addValueReferenceUnsafe();
return value;
}
void addref() override { ThreadSafeReferenceCounted<ThreadSingleAssignmentVar<T>>::addref(); }
2017-05-26 04:48:44 +08:00
void delref() override { ThreadSafeReferenceCounted<ThreadSingleAssignmentVar<T>>::delref(); }
2017-05-26 04:48:44 +08:00
2020-10-09 00:31:19 +08:00
void send(const T& value) {
if (TRACE_SAMPLE())
TraceEvent(SevSample, "Promise_send");
2017-05-26 04:48:44 +08:00
this->mutex.enter();
if (!canBeSetUnsafe()) {
this->mutex.leave();
ASSERT(false); // Promise fulfilled twice
2017-05-26 04:48:44 +08:00
}
this->value = value; //< Danger: polymorphic operation inside lock
2017-05-26 04:48:44 +08:00
this->status = Set;
if (!callback) {
this->mutex.leave();
return;
}
auto func = callback;
if (!callback->isMultiCallback())
2020-08-28 06:31:24 +08:00
callback = nullptr;
2017-05-26 04:48:44 +08:00
if (!func->canFire(0)) {
this->mutex.leave();
} else {
this->mutex.leave();
// Thread safe because status is now Set and callback is nullptr, meaning than callback cannot change
2017-05-26 04:48:44 +08:00
int userParam = 0;
func->fire(Void(), userParam);
}
}
void cleanupUnsafe() override {
2017-05-26 04:48:44 +08:00
value = T();
ThreadSingleAssignmentVarBase::cleanupUnsafe();
}
};
template <class T>
class ThreadFuture {
2017-05-26 04:48:44 +08:00
public:
T get() { return sav->get(); }
T getBlocking() {
sav->blockUntilReady();
return sav->get();
}
void blockUntilReady() { sav->blockUntilReady(); }
2017-05-26 04:48:44 +08:00
2020-09-18 04:26:28 +08:00
void blockUntilReadyCheckOnMainThread() { sav->blockUntilReadyCheckOnMainThread(); }
bool isValid() const { return sav != 0; }
bool isReady() { return sav->isReady(); }
bool isError() { return sav->isError(); }
2017-05-26 04:48:44 +08:00
Error& getError() {
if (!isError())
2017-05-26 04:48:44 +08:00
throw future_not_error();
return sav->error;
}
SetCallbackResult::Result callOrSetAsCallback(ThreadCallback* callback, int& userParam1, int notMadeActive) {
2017-05-26 04:48:44 +08:00
return sav->callOrSetAsCallback(callback, userParam1, notMadeActive);
}
bool clearCallback(ThreadCallback* cb) { return sav->clearCallback(cb); }
2017-05-26 04:48:44 +08:00
void cancel() { extractPtr()->cancel(); }
2017-05-26 04:48:44 +08:00
ThreadFuture() : sav(0) {}
explicit ThreadFuture(ThreadSingleAssignmentVar<T>* sav) : sav(sav) {
2017-05-26 04:48:44 +08:00
// sav->addref();
}
ThreadFuture(const ThreadFuture<T>& rhs) : sav(rhs.sav) {
if (sav)
sav->addref();
2017-05-26 04:48:44 +08:00
}
2020-06-10 08:33:41 +08:00
ThreadFuture(ThreadFuture<T>&& rhs) noexcept : sav(rhs.sav) { rhs.sav = 0; }
ThreadFuture(const T& presentValue) : sav(new ThreadSingleAssignmentVar<T>()) { sav->send(presentValue); }
ThreadFuture(Never) : sav(new ThreadSingleAssignmentVar<T>()) {}
ThreadFuture(const Error& error) : sav(new ThreadSingleAssignmentVar<T>()) { sav->sendError(error); }
2017-05-26 04:48:44 +08:00
~ThreadFuture() {
if (sav)
sav->delref();
2017-05-26 04:48:44 +08:00
}
void operator=(const ThreadFuture<T>& rhs) {
if (rhs.sav)
rhs.sav->addref();
if (sav)
sav->delref();
2017-05-26 04:48:44 +08:00
sav = rhs.sav;
}
2020-06-10 08:33:41 +08:00
void operator=(ThreadFuture<T>&& rhs) noexcept {
2017-05-26 04:48:44 +08:00
if (sav != rhs.sav) {
if (sav)
sav->delref();
2017-05-26 04:48:44 +08:00
sav = rhs.sav;
rhs.sav = 0;
}
}
bool operator==(const ThreadFuture& rhs) { return rhs.sav == sav; }
bool operator!=(const ThreadFuture& rhs) { return rhs.sav != sav; }
2017-05-26 04:48:44 +08:00
ThreadSingleAssignmentVarBase* getPtr() const { return sav; }
ThreadSingleAssignmentVarBase* extractPtr() {
auto* p = sav;
sav = nullptr;
return p;
}
2017-05-26 04:48:44 +08:00
private:
ThreadSingleAssignmentVar<T>* sav;
};
// A callback class used to convert a ThreadFuture into a Future
template <class T>
2017-05-26 04:48:44 +08:00
struct CompletionCallback : public ThreadCallback, ReferenceCounted<CompletionCallback<T>> {
// The thread future being waited on
2017-05-26 04:48:44 +08:00
ThreadFuture<T> threadFuture;
// The promise whose future we are triggering when this callback gets called
2017-05-26 04:48:44 +08:00
Promise<T> promise;
// Unused
2017-05-26 04:48:44 +08:00
int userParam;
// Holds own reference to prevent deletion until callback is fired
2017-05-26 04:48:44 +08:00
Reference<CompletionCallback<T>> self;
CompletionCallback(ThreadFuture<T> threadFuture) { this->threadFuture = threadFuture; }
2017-05-26 04:48:44 +08:00
bool canFire(int notMadeActive) const override { return true; }
2017-05-26 04:48:44 +08:00
// Trigger the promise
void fire(const Void& unused, int& userParam) override {
2017-05-26 04:48:44 +08:00
promise.send(threadFuture.get());
self.clear();
}
// Send the error through the promise
void error(const Error& e, int& userParam) override {
2017-05-26 04:48:44 +08:00
promise.sendError(e);
self.clear();
}
};
// Converts a ThreadFuture into a Future
// WARNING: This is not actually thread safe! It can only be safely used from the main thread, on futures which are
// being set on the main thread
// FIXME: does not support cancellation
template <class T>
2017-05-26 04:48:44 +08:00
Future<T> unsafeThreadFutureToFuture(ThreadFuture<T> threadFuture) {
2020-11-07 15:50:55 +08:00
auto callback = makeReference<CompletionCallback<T>>(threadFuture);
2017-05-26 04:48:44 +08:00
callback->self = callback;
threadFuture.callOrSetAsCallback(callback.getPtr(), callback->userParam, 0);
return callback->promise.getFuture();
}
ACTOR template <class R, class F>
Future<Void> doOnMainThread(Future<Void> signal, F f, ThreadSingleAssignmentVar<R>* result) {
2017-05-26 04:48:44 +08:00
try {
wait(signal);
R r = wait(f());
2017-05-26 04:48:44 +08:00
result->send(r);
} catch (Error& e) {
if (!result->canBeSet()) {
TraceEvent(SevError, "OnMainThreadSetTwice").error(e, true);
2017-05-26 04:48:44 +08:00
}
result->sendError(e);
}
ThreadFuture<R> destroyResultAfterReturning(
result); // Call result->delref(), but only after our return promise is no longer referenced on this thread
2017-05-26 04:48:44 +08:00
return Void();
}
ACTOR template <class F>
void doOnMainThreadVoid(Future<Void> signal, F f, Error* err) {
wait(signal);
2017-05-26 04:48:44 +08:00
if (err && err->code() != invalid_error_code)
return;
try {
f();
} catch (Error& e) {
if (err)
*err = e;
}
}
2020-07-16 07:33:01 +08:00
template <class F>
ThreadFuture<decltype(std::declval<F>()().getValue())> onMainThread(F f) {
2017-05-26 04:48:44 +08:00
Promise<Void> signal;
2020-07-16 07:33:01 +08:00
auto returnValue = new ThreadSingleAssignmentVar<decltype(std::declval<F>()().getValue())>();
2017-05-26 04:48:44 +08:00
returnValue->addref(); // For the ThreadFuture we return
2020-07-16 07:33:01 +08:00
Future<Void> cancelFuture =
doOnMainThread<decltype(std::declval<F>()().getValue()), F>(signal.getFuture(), f, returnValue);
returnValue->setCancel(std::move(cancelFuture));
g_network->onMainThread(std::move(signal), TaskPriority::DefaultOnMainThread);
2020-07-16 07:33:01 +08:00
return ThreadFuture<decltype(std::declval<F>()().getValue())>(returnValue);
2017-05-26 04:48:44 +08:00
}
template <class V>
class ThreadSafeAsyncVar : NonCopyable, public ThreadSafeReferenceCounted<ThreadSafeAsyncVar<V>> {
public:
struct State {
State(V value, ThreadFuture<Void> onChange) : value(value), onChange(onChange) {}
V value;
ThreadFuture<Void> onChange;
};
ThreadSafeAsyncVar() : value(), nextChange(new ThreadSingleAssignmentVar<Void>()) {}
ThreadSafeAsyncVar(V const& v) : value(v), nextChange(new ThreadSingleAssignmentVar<Void>()) {}
State get() {
ThreadSpinLockHolder holder(lock);
nextChange->addref();
return State(value, ThreadFuture<Void>(nextChange.getPtr()));
}
void set(V const& v, bool triggerIfSame = false) {
Reference<ThreadSingleAssignmentVar<Void>> trigger(new ThreadSingleAssignmentVar<Void>());
lock.enter();
bool changed = this->value != v;
if (changed || triggerIfSame) {
2017-05-26 04:48:44 +08:00
std::swap(this->nextChange, trigger);
this->value = v;
}
lock.leave();
if (changed || triggerIfSame) {
2017-05-26 04:48:44 +08:00
trigger->send(Void());
}
}
private:
V value;
Reference<ThreadSingleAssignmentVar<Void>> nextChange;
ThreadSpinLock lock;
};
#include "flow/unactorcompiler.h"
2017-05-26 04:48:44 +08:00
#endif