/usr/include/thunderbird/mozilla/MozPromise.h is in thunderbird-dev 1:52.8.0-1~deb8u1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#if !defined(MozPromise_h_)
#define MozPromise_h_
#include "mozilla/AbstractThread.h"
#include "mozilla/IndexSequence.h"
#include "mozilla/Logging.h"
#include "mozilla/Maybe.h"
#include "mozilla/Mutex.h"
#include "mozilla/Monitor.h"
#include "mozilla/Tuple.h"
#include "mozilla/TypeTraits.h"
#include "nsTArray.h"
#include "nsThreadUtils.h"
#if defined(DEBUG) || !defined(RELEASE_OR_BETA)
#define PROMISE_DEBUG
#endif
#ifdef PROMISE_DEBUG
#define PROMISE_ASSERT MOZ_RELEASE_ASSERT
#else
#define PROMISE_ASSERT(...) do { } while (0)
#endif
namespace mozilla {
extern LazyLogModule gMozPromiseLog;
#define PROMISE_LOG(x, ...) \
MOZ_LOG(gMozPromiseLog, mozilla::LogLevel::Debug, (x, ##__VA_ARGS__))
namespace detail {
template<typename ThisType, typename Ret, typename ArgType>
static TrueType TakesArgumentHelper(Ret (ThisType::*)(ArgType));
template<typename ThisType, typename Ret, typename ArgType>
static TrueType TakesArgumentHelper(Ret (ThisType::*)(ArgType) const);
template<typename ThisType, typename Ret>
static FalseType TakesArgumentHelper(Ret (ThisType::*)());
template<typename ThisType, typename Ret>
static FalseType TakesArgumentHelper(Ret (ThisType::*)() const);
template<typename ThisType, typename Ret, typename ArgType>
static Ret ReturnTypeHelper(Ret (ThisType::*)(ArgType));
template<typename ThisType, typename Ret, typename ArgType>
static Ret ReturnTypeHelper(Ret (ThisType::*)(ArgType) const);
template<typename ThisType, typename Ret>
static Ret ReturnTypeHelper(Ret (ThisType::*)());
template<typename ThisType, typename Ret>
static Ret ReturnTypeHelper(Ret (ThisType::*)() const);
template<typename MethodType>
struct ReturnType {
typedef decltype(detail::ReturnTypeHelper(DeclVal<MethodType>())) Type;
};
} // namespace detail
template<typename MethodType>
struct TakesArgument {
static const bool value = decltype(detail::TakesArgumentHelper(DeclVal<MethodType>()))::value;
};
template<typename MethodType, typename TargetType>
struct ReturnTypeIs {
static const bool value = IsConvertible<typename detail::ReturnType<MethodType>::Type, TargetType>::value;
};
/*
* A promise manages an asynchronous request that may or may not be able to be
* fulfilled immediately. When an API returns a promise, the consumer may attach
* callbacks to be invoked (asynchronously, on a specified thread) when the
* request is either completed (resolved) or cannot be completed (rejected).
* Whereas JS promise callbacks are dispatched from Microtask checkpoints,
* MozPromises resolution/rejection make a normal round-trip through the event
* loop, which simplifies their ordering semantics relative to other native code.
*
* MozPromises attempt to mirror the spirit of JS Promises to the extent that
* is possible (and desirable) in C++. While the intent is that MozPromises
* feel familiar to programmers who are accustomed to their JS-implemented cousin,
* we don't shy away from imposing restrictions and adding features that make
* sense for the use cases we encounter.
*
* A MozPromise is ThreadSafe, and may be ->Then()ed on any thread. The Then()
* call accepts resolve and reject callbacks, and returns a MozPromise::Request.
* The Request object serves several purposes for the consumer.
*
* (1) It allows the caller to cancel the delivery of the resolve/reject value
* if it has not already occurred, via Disconnect() (this must be done on
* the target thread to avoid racing).
*
* (2) It provides access to a "Completion Promise", which is roughly analagous
* to the Promise returned directly by ->then() calls on JS promises. If
* the resolve/reject callback returns a new MozPromise, that promise is
* chained to the completion promise, such that its resolve/reject value
* will be forwarded along when it arrives. If the resolve/reject callback
* returns void, the completion promise is resolved/rejected with the same
* value that was passed to the callback.
*
* The MozPromise APIs skirt traditional XPCOM convention by returning nsRefPtrs
* (rather than already_AddRefed) from various methods. This is done to allow elegant
* chaining of calls without cluttering up the code with intermediate variables, and
* without introducing separate API variants for callers that want a return value
* (from, say, ->Then()) from those that don't.
*
* When IsExclusive is true, the MozPromise does a release-mode assertion that
* there is at most one call to either Then(...) or ChainTo(...).
*/
class MozPromiseRefcountable
{
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MozPromiseRefcountable)
protected:
virtual ~MozPromiseRefcountable() {}
};
template<typename T> class MozPromiseHolder;
template<typename ResolveValueT, typename RejectValueT, bool IsExclusive>
class MozPromise : public MozPromiseRefcountable
{
static const uint32_t sMagic = 0xcecace11;
public:
typedef ResolveValueT ResolveValueType;
typedef RejectValueT RejectValueType;
class ResolveOrRejectValue
{
public:
template<typename ResolveValueType_>
void SetResolve(ResolveValueType_&& aResolveValue)
{
MOZ_ASSERT(IsNothing());
mResolveValue.emplace(Forward<ResolveValueType_>(aResolveValue));
}
template<typename RejectValueType_>
void SetReject(RejectValueType_&& aRejectValue)
{
MOZ_ASSERT(IsNothing());
mRejectValue.emplace(Forward<RejectValueType_>(aRejectValue));
}
template<typename ResolveValueType_>
static ResolveOrRejectValue MakeResolve(ResolveValueType_&& aResolveValue)
{
ResolveOrRejectValue val;
val.SetResolve(Forward<ResolveValueType_>(aResolveValue));
return val;
}
template<typename RejectValueType_>
static ResolveOrRejectValue MakeReject(RejectValueType_&& aRejectValue)
{
ResolveOrRejectValue val;
val.SetReject(Forward<RejectValueType_>(aRejectValue));
return val;
}
bool IsResolve() const { return mResolveValue.isSome(); }
bool IsReject() const { return mRejectValue.isSome(); }
bool IsNothing() const { return mResolveValue.isNothing() && mRejectValue.isNothing(); }
const ResolveValueType& ResolveValue() const { return mResolveValue.ref(); }
const RejectValueType& RejectValue() const { return mRejectValue.ref(); }
private:
Maybe<ResolveValueType> mResolveValue;
Maybe<RejectValueType> mRejectValue;
};
protected:
// MozPromise is the public type, and never constructed directly. Construct
// a MozPromise::Private, defined below.
MozPromise(const char* aCreationSite, bool aIsCompletionPromise)
: mCreationSite(aCreationSite)
, mMutex("MozPromise Mutex")
, mHaveRequest(false)
, mIsCompletionPromise(aIsCompletionPromise)
#ifdef PROMISE_DEBUG
, mMagic4(mMutex.mLock)
#endif
{
PROMISE_LOG("%s creating MozPromise (%p)", mCreationSite, this);
}
public:
// MozPromise::Private allows us to separate the public interface (upon which
// consumers of the promise may invoke methods like Then()) from the private
// interface (upon which the creator of the promise may invoke Resolve() or
// Reject()). APIs should create and store a MozPromise::Private (usually
// via a MozPromiseHolder), and return a MozPromise to consumers.
//
// NB: We can include the definition of this class inline once B2G ICS is gone.
class Private;
template<typename ResolveValueType_>
static RefPtr<MozPromise>
CreateAndResolve(ResolveValueType_&& aResolveValue, const char* aResolveSite)
{
RefPtr<typename MozPromise::Private> p = new MozPromise::Private(aResolveSite);
p->Resolve(Forward<ResolveValueType_>(aResolveValue), aResolveSite);
return p.forget();
}
template<typename RejectValueType_>
static RefPtr<MozPromise>
CreateAndReject(RejectValueType_&& aRejectValue, const char* aRejectSite)
{
RefPtr<typename MozPromise::Private> p = new MozPromise::Private(aRejectSite);
p->Reject(Forward<RejectValueType_>(aRejectValue), aRejectSite);
return p.forget();
}
typedef MozPromise<nsTArray<ResolveValueType>, RejectValueType, IsExclusive> AllPromiseType;
private:
class AllPromiseHolder : public MozPromiseRefcountable
{
public:
explicit AllPromiseHolder(size_t aDependentPromises)
: mPromise(new typename AllPromiseType::Private(__func__))
, mOutstandingPromises(aDependentPromises)
{
mResolveValues.SetLength(aDependentPromises);
}
void Resolve(size_t aIndex, const ResolveValueType& aResolveValue)
{
if (!mPromise) {
// Already rejected.
return;
}
mResolveValues[aIndex].emplace(aResolveValue);
if (--mOutstandingPromises == 0) {
nsTArray<ResolveValueType> resolveValues;
resolveValues.SetCapacity(mResolveValues.Length());
for (size_t i = 0; i < mResolveValues.Length(); ++i) {
resolveValues.AppendElement(mResolveValues[i].ref());
}
mPromise->Resolve(resolveValues, __func__);
mPromise = nullptr;
mResolveValues.Clear();
}
}
void Reject(const RejectValueType& aRejectValue)
{
if (!mPromise) {
// Already rejected.
return;
}
mPromise->Reject(aRejectValue, __func__);
mPromise = nullptr;
mResolveValues.Clear();
}
AllPromiseType* Promise() { return mPromise; }
private:
nsTArray<Maybe<ResolveValueType>> mResolveValues;
RefPtr<typename AllPromiseType::Private> mPromise;
size_t mOutstandingPromises;
};
public:
static RefPtr<AllPromiseType> All(AbstractThread* aProcessingThread, nsTArray<RefPtr<MozPromise>>& aPromises)
{
RefPtr<AllPromiseHolder> holder = new AllPromiseHolder(aPromises.Length());
for (size_t i = 0; i < aPromises.Length(); ++i) {
aPromises[i]->Then(aProcessingThread, __func__,
[holder, i] (ResolveValueType aResolveValue) -> void { holder->Resolve(i, aResolveValue); },
[holder] (RejectValueType aRejectValue) -> void { holder->Reject(aRejectValue); }
);
}
return holder->Promise();
}
class Request : public MozPromiseRefcountable
{
public:
virtual void Disconnect() = 0;
virtual MozPromise* CompletionPromise() = 0;
virtual void AssertIsDead() = 0;
protected:
Request() : mComplete(false), mDisconnected(false) {}
virtual ~Request() {}
bool mComplete;
bool mDisconnected;
};
protected:
/*
* A ThenValue tracks a single consumer waiting on the promise. When a consumer
* invokes promise->Then(...), a ThenValue is created. Once the Promise is
* resolved or rejected, a {Resolve,Reject}Runnable is dispatched, which
* invokes the resolve/reject method and then deletes the ThenValue.
*/
class ThenValueBase : public Request
{
static const uint32_t sMagic = 0xfadece11;
public:
class ResolveOrRejectRunnable : public Runnable
{
public:
ResolveOrRejectRunnable(ThenValueBase* aThenValue, MozPromise* aPromise)
: mThenValue(aThenValue)
, mPromise(aPromise)
{
MOZ_DIAGNOSTIC_ASSERT(!mPromise->IsPending());
}
~ResolveOrRejectRunnable()
{
if (mThenValue) {
mThenValue->AssertIsDead();
}
}
NS_IMETHOD Run() override
{
PROMISE_LOG("ResolveOrRejectRunnable::Run() [this=%p]", this);
mThenValue->DoResolveOrReject(mPromise->Value());
mThenValue = nullptr;
mPromise = nullptr;
return NS_OK;
}
private:
RefPtr<ThenValueBase> mThenValue;
RefPtr<MozPromise> mPromise;
};
explicit ThenValueBase(AbstractThread* aResponseTarget, const char* aCallSite)
: mResponseTarget(aResponseTarget), mCallSite(aCallSite) {}
#ifdef PROMISE_DEBUG
~ThenValueBase()
{
mMagic1 = 0;
mMagic2 = 0;
}
#endif
MozPromise* CompletionPromise() override
{
MOZ_DIAGNOSTIC_ASSERT(mResponseTarget->IsCurrentThreadIn());
MOZ_DIAGNOSTIC_ASSERT(!Request::mComplete);
if (!mCompletionPromise) {
mCompletionPromise = new MozPromise::Private(
"<completion promise>", true /* aIsCompletionPromise */);
}
return mCompletionPromise;
}
void AssertIsDead() override
{
PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic);
// We want to assert that this ThenValues is dead - that is to say, that
// there are no consumers waiting for the result. In the case of a normal
// ThenValue, we check that it has been disconnected, which is the way
// that the consumer signals that it no longer wishes to hear about the
// result. If this ThenValue has a completion promise (which is mutually
// exclusive with being disconnectable), we recursively assert that every
// ThenValue associated with the completion promise is dead.
if (mCompletionPromise) {
mCompletionPromise->AssertIsDead();
} else {
MOZ_DIAGNOSTIC_ASSERT(Request::mDisconnected);
}
}
void Dispatch(MozPromise *aPromise)
{
PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic);
aPromise->mMutex.AssertCurrentThreadOwns();
MOZ_ASSERT(!aPromise->IsPending());
RefPtr<Runnable> runnable =
static_cast<Runnable*>(new (typename ThenValueBase::ResolveOrRejectRunnable)(this, aPromise));
PROMISE_LOG("%s Then() call made from %s [Runnable=%p, Promise=%p, ThenValue=%p]",
aPromise->mValue.IsResolve() ? "Resolving" : "Rejecting", ThenValueBase::mCallSite,
runnable.get(), aPromise, this);
// Promise consumers are allowed to disconnect the Request object and
// then shut down the thread or task queue that the promise result would
// be dispatched on. So we unfortunately can't assert that promise
// dispatch succeeds. :-(
mResponseTarget->Dispatch(runnable.forget(), AbstractThread::DontAssertDispatchSuccess);
}
virtual void Disconnect() override
{
MOZ_DIAGNOSTIC_ASSERT(ThenValueBase::mResponseTarget->IsCurrentThreadIn());
MOZ_DIAGNOSTIC_ASSERT(!Request::mComplete);
Request::mDisconnected = true;
// We could support rejecting the completion promise on disconnection, but
// then we'd need to have some sort of default reject value. The use cases
// of disconnection and completion promise chaining seem pretty orthogonal,
// so let's use assert against it.
MOZ_DIAGNOSTIC_ASSERT(!mCompletionPromise);
}
protected:
virtual already_AddRefed<MozPromise> DoResolveOrRejectInternal(const ResolveOrRejectValue& aValue) = 0;
void DoResolveOrReject(const ResolveOrRejectValue& aValue)
{
PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic);
MOZ_DIAGNOSTIC_ASSERT(mResponseTarget->IsCurrentThreadIn());
Request::mComplete = true;
if (Request::mDisconnected) {
PROMISE_LOG("ThenValue::DoResolveOrReject disconnected - bailing out [this=%p]", this);
return;
}
// Invoke the resolve or reject method.
RefPtr<MozPromise> p = DoResolveOrRejectInternal(aValue);
// If there's a completion promise, resolve it appropriately with the
// result of the method.
//
// We jump through some hoops to cast to MozPromise::Private here. This
// can go away when we can just declare mCompletionPromise as
// MozPromise::Private. See the declaration below.
RefPtr<MozPromise::Private> completionPromise =
dont_AddRef(static_cast<MozPromise::Private*>(mCompletionPromise.forget().take()));
if (completionPromise) {
if (p) {
p->ChainTo(completionPromise.forget(), "<chained completion promise>");
} else {
completionPromise->ResolveOrReject(aValue, "<completion of non-promise-returning method>");
}
}
}
RefPtr<AbstractThread> mResponseTarget; // May be released on any thread.
#ifdef PROMISE_DEBUG
uint32_t mMagic1 = sMagic;
#endif
// Declaring RefPtr<MozPromise::Private> here causes build failures
// on MSVC because MozPromise::Private is only forward-declared at this
// point. This hack can go away when we inline-declare MozPromise::Private,
// which is blocked on the B2G ICS compiler being too old.
RefPtr<MozPromise> mCompletionPromise;
#ifdef PROMISE_DEBUG
uint32_t mMagic2 = sMagic;
#endif
const char* mCallSite;
};
/*
* We create two overloads for invoking Resolve/Reject Methods so as to
* make the resolve/reject value argument "optional".
*/
template<typename ThisType, typename MethodType, typename ValueType>
static typename EnableIf<ReturnTypeIs<MethodType, RefPtr<MozPromise>>::value &&
TakesArgument<MethodType>::value,
already_AddRefed<MozPromise>>::Type
InvokeCallbackMethod(ThisType* aThisVal, MethodType aMethod, ValueType&& aValue)
{
return ((*aThisVal).*aMethod)(Forward<ValueType>(aValue)).forget();
}
template<typename ThisType, typename MethodType, typename ValueType>
static typename EnableIf<ReturnTypeIs<MethodType, void>::value &&
TakesArgument<MethodType>::value,
already_AddRefed<MozPromise>>::Type
InvokeCallbackMethod(ThisType* aThisVal, MethodType aMethod, ValueType&& aValue)
{
((*aThisVal).*aMethod)(Forward<ValueType>(aValue));
return nullptr;
}
template<typename ThisType, typename MethodType, typename ValueType>
static typename EnableIf<ReturnTypeIs<MethodType, RefPtr<MozPromise>>::value &&
!TakesArgument<MethodType>::value,
already_AddRefed<MozPromise>>::Type
InvokeCallbackMethod(ThisType* aThisVal, MethodType aMethod, ValueType&& aValue)
{
return ((*aThisVal).*aMethod)().forget();
}
template<typename ThisType, typename MethodType, typename ValueType>
static typename EnableIf<ReturnTypeIs<MethodType, void>::value &&
!TakesArgument<MethodType>::value,
already_AddRefed<MozPromise>>::Type
InvokeCallbackMethod(ThisType* aThisVal, MethodType aMethod, ValueType&& aValue)
{
((*aThisVal).*aMethod)();
return nullptr;
}
template<typename ThisType, typename ResolveMethodType, typename RejectMethodType>
class MethodThenValue : public ThenValueBase
{
public:
MethodThenValue(AbstractThread* aResponseTarget, ThisType* aThisVal,
ResolveMethodType aResolveMethod, RejectMethodType aRejectMethod,
const char* aCallSite)
: ThenValueBase(aResponseTarget, aCallSite)
, mThisVal(aThisVal)
, mResolveMethod(aResolveMethod)
, mRejectMethod(aRejectMethod) {}
virtual void Disconnect() override
{
ThenValueBase::Disconnect();
// If a Request has been disconnected, we don't guarantee that the
// resolve/reject runnable will be dispatched. Null out our refcounted
// this-value now so that it's released predictably on the dispatch thread.
mThisVal = nullptr;
}
protected:
virtual already_AddRefed<MozPromise> DoResolveOrRejectInternal(const ResolveOrRejectValue& aValue) override
{
RefPtr<MozPromise> completion;
if (aValue.IsResolve()) {
completion = InvokeCallbackMethod(mThisVal.get(), mResolveMethod, aValue.ResolveValue());
} else {
completion = InvokeCallbackMethod(mThisVal.get(), mRejectMethod, aValue.RejectValue());
}
// Null out mThisVal after invoking the callback so that any references are
// released predictably on the dispatch thread. Otherwise, it would be
// released on whatever thread last drops its reference to the ThenValue,
// which may or may not be ok.
mThisVal = nullptr;
return completion.forget();
}
private:
RefPtr<ThisType> mThisVal; // Only accessed and refcounted on dispatch thread.
ResolveMethodType mResolveMethod;
RejectMethodType mRejectMethod;
};
// NB: We could use std::function here instead of a template if it were supported. :-(
template<typename ResolveFunction, typename RejectFunction>
class FunctionThenValue : public ThenValueBase
{
public:
FunctionThenValue(AbstractThread* aResponseTarget,
ResolveFunction&& aResolveFunction,
RejectFunction&& aRejectFunction,
const char* aCallSite)
: ThenValueBase(aResponseTarget, aCallSite)
{
mResolveFunction.emplace(Move(aResolveFunction));
mRejectFunction.emplace(Move(aRejectFunction));
}
virtual void Disconnect() override
{
ThenValueBase::Disconnect();
// If a Request has been disconnected, we don't guarantee that the
// resolve/reject runnable will be dispatched. Destroy our callbacks
// now so that any references in closures are released predictable on
// the dispatch thread.
mResolveFunction.reset();
mRejectFunction.reset();
}
protected:
virtual already_AddRefed<MozPromise> DoResolveOrRejectInternal(const ResolveOrRejectValue& aValue) override
{
// Note: The usage of InvokeCallbackMethod here requires that
// ResolveFunction/RejectFunction are capture-lambdas (i.e. anonymous
// classes with ::operator()), since it allows us to share code more easily.
// We could fix this if need be, though it's quite easy to work around by
// just capturing something.
RefPtr<MozPromise> completion;
if (aValue.IsResolve()) {
completion = InvokeCallbackMethod(mResolveFunction.ptr(), &ResolveFunction::operator(), aValue.ResolveValue());
} else {
completion = InvokeCallbackMethod(mRejectFunction.ptr(), &RejectFunction::operator(), aValue.RejectValue());
}
// Destroy callbacks after invocation so that any references in closures are
// released predictably on the dispatch thread. Otherwise, they would be
// released on whatever thread last drops its reference to the ThenValue,
// which may or may not be ok.
mResolveFunction.reset();
mRejectFunction.reset();
return completion.forget();
}
private:
Maybe<ResolveFunction> mResolveFunction; // Only accessed and deleted on dispatch thread.
Maybe<RejectFunction> mRejectFunction; // Only accessed and deleted on dispatch thread.
};
public:
void ThenInternal(AbstractThread* aResponseThread, ThenValueBase* aThenValue,
const char* aCallSite)
{
PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic && mMagic3 == sMagic && mMagic4 == mMutex.mLock);
MutexAutoLock lock(mMutex);
MOZ_ASSERT(aResponseThread->IsDispatchReliable());
MOZ_DIAGNOSTIC_ASSERT(!IsExclusive || !mHaveRequest);
mHaveRequest = true;
PROMISE_LOG("%s invoking Then() [this=%p, aThenValue=%p, isPending=%d]",
aCallSite, this, aThenValue, (int) IsPending());
if (!IsPending()) {
aThenValue->Dispatch(this);
} else {
mThenValues.AppendElement(aThenValue);
}
}
public:
template<typename ThisType, typename ResolveMethodType, typename RejectMethodType>
RefPtr<Request> Then(AbstractThread* aResponseThread, const char* aCallSite, ThisType* aThisVal,
ResolveMethodType aResolveMethod, RejectMethodType aRejectMethod)
{
RefPtr<ThenValueBase> thenValue = new MethodThenValue<ThisType, ResolveMethodType, RejectMethodType>(
aResponseThread, aThisVal, aResolveMethod, aRejectMethod, aCallSite);
ThenInternal(aResponseThread, thenValue, aCallSite);
return thenValue.forget(); // Implicit conversion from already_AddRefed<ThenValueBase> to RefPtr<Request>.
}
template<typename ResolveFunction, typename RejectFunction>
RefPtr<Request> Then(AbstractThread* aResponseThread, const char* aCallSite,
ResolveFunction&& aResolveFunction, RejectFunction&& aRejectFunction)
{
RefPtr<ThenValueBase> thenValue = new FunctionThenValue<ResolveFunction, RejectFunction>(aResponseThread,
Move(aResolveFunction), Move(aRejectFunction), aCallSite);
ThenInternal(aResponseThread, thenValue, aCallSite);
return thenValue.forget(); // Implicit conversion from already_AddRefed<ThenValueBase> to RefPtr<Request>.
}
void ChainTo(already_AddRefed<Private> aChainedPromise, const char* aCallSite)
{
MutexAutoLock lock(mMutex);
MOZ_DIAGNOSTIC_ASSERT(!IsExclusive || !mHaveRequest);
mHaveRequest = true;
RefPtr<Private> chainedPromise = aChainedPromise;
PROMISE_LOG("%s invoking Chain() [this=%p, chainedPromise=%p, isPending=%d]",
aCallSite, this, chainedPromise.get(), (int) IsPending());
if (!IsPending()) {
ForwardTo(chainedPromise);
} else {
mChainedPromises.AppendElement(chainedPromise);
}
}
// Note we expose the function AssertIsDead() instead of IsDead() since
// checking IsDead() is a data race in the situation where the request is not
// dead. Therefore we enforce the form |Assert(IsDead())| by exposing
// AssertIsDead() only.
void AssertIsDead()
{
PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic && mMagic3 == sMagic && mMagic4 == mMutex.mLock);
MutexAutoLock lock(mMutex);
for (auto&& then : mThenValues) {
then->AssertIsDead();
}
for (auto&& chained : mChainedPromises) {
chained->AssertIsDead();
}
}
protected:
bool IsPending() const { return mValue.IsNothing(); }
const ResolveOrRejectValue& Value() const
{
// This method should only be called once the value has stabilized. As
// such, we don't need to acquire the lock here.
MOZ_DIAGNOSTIC_ASSERT(!IsPending());
return mValue;
}
void DispatchAll()
{
mMutex.AssertCurrentThreadOwns();
for (size_t i = 0; i < mThenValues.Length(); ++i) {
mThenValues[i]->Dispatch(this);
}
mThenValues.Clear();
for (size_t i = 0; i < mChainedPromises.Length(); ++i) {
ForwardTo(mChainedPromises[i]);
}
mChainedPromises.Clear();
}
void ForwardTo(Private* aOther)
{
MOZ_ASSERT(!IsPending());
if (mValue.IsResolve()) {
aOther->Resolve(mValue.ResolveValue(), "<chained promise>");
} else {
aOther->Reject(mValue.RejectValue(), "<chained promise>");
}
}
virtual ~MozPromise()
{
PROMISE_LOG("MozPromise::~MozPromise [this=%p]", this);
AssertIsDead();
// We can't guarantee a completion promise will always be revolved or
// rejected since ResolveOrRejectRunnable might not run when dispatch fails.
if (!mIsCompletionPromise) {
MOZ_ASSERT(!IsPending());
MOZ_ASSERT(mThenValues.IsEmpty());
MOZ_ASSERT(mChainedPromises.IsEmpty());
}
#ifdef PROMISE_DEBUG
mMagic1 = 0;
mMagic2 = 0;
mMagic3 = 0;
mMagic4 = nullptr;
#endif
};
const char* mCreationSite; // For logging
Mutex mMutex;
ResolveOrRejectValue mValue;
#ifdef PROMISE_DEBUG
uint32_t mMagic1 = sMagic;
#endif
nsTArray<RefPtr<ThenValueBase>> mThenValues;
#ifdef PROMISE_DEBUG
uint32_t mMagic2 = sMagic;
#endif
nsTArray<RefPtr<Private>> mChainedPromises;
#ifdef PROMISE_DEBUG
uint32_t mMagic3 = sMagic;
#endif
bool mHaveRequest;
const bool mIsCompletionPromise;
#ifdef PROMISE_DEBUG
void* mMagic4;
#endif
};
template<typename ResolveValueT, typename RejectValueT, bool IsExclusive>
class MozPromise<ResolveValueT, RejectValueT, IsExclusive>::Private
: public MozPromise<ResolveValueT, RejectValueT, IsExclusive>
{
public:
explicit Private(const char* aCreationSite, bool aIsCompletionPromise = false)
: MozPromise(aCreationSite, aIsCompletionPromise) {}
template<typename ResolveValueT_>
void Resolve(ResolveValueT_&& aResolveValue, const char* aResolveSite)
{
PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic && mMagic3 == sMagic && mMagic4 == mMutex.mLock);
MutexAutoLock lock(mMutex);
MOZ_ASSERT(IsPending());
PROMISE_LOG("%s resolving MozPromise (%p created at %s)", aResolveSite, this, mCreationSite);
mValue.SetResolve(Forward<ResolveValueT_>(aResolveValue));
DispatchAll();
}
template<typename RejectValueT_>
void Reject(RejectValueT_&& aRejectValue, const char* aRejectSite)
{
PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic && mMagic3 == sMagic && mMagic4 == mMutex.mLock);
MutexAutoLock lock(mMutex);
MOZ_ASSERT(IsPending());
PROMISE_LOG("%s rejecting MozPromise (%p created at %s)", aRejectSite, this, mCreationSite);
mValue.SetReject(Forward<RejectValueT_>(aRejectValue));
DispatchAll();
}
template<typename ResolveOrRejectValue_>
void ResolveOrReject(ResolveOrRejectValue_&& aValue, const char* aSite)
{
PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic && mMagic3 == sMagic && mMagic4 == mMutex.mLock);
MutexAutoLock lock(mMutex);
MOZ_ASSERT(IsPending());
PROMISE_LOG("%s resolveOrRejecting MozPromise (%p created at %s)", aSite, this, mCreationSite);
mValue = Forward<ResolveOrRejectValue_>(aValue);
DispatchAll();
}
};
// A generic promise type that does the trick for simple use cases.
typedef MozPromise<bool, nsresult, /* IsExclusive = */ false> GenericPromise;
/*
* Class to encapsulate a promise for a particular role. Use this as the member
* variable for a class whose method returns a promise.
*/
template<typename PromiseType>
class MozPromiseHolder
{
public:
MozPromiseHolder()
: mMonitor(nullptr) {}
// Move semantics.
MozPromiseHolder& operator=(MozPromiseHolder&& aOther)
{
MOZ_ASSERT(!mMonitor && !aOther.mMonitor);
MOZ_DIAGNOSTIC_ASSERT(!mPromise);
mPromise = aOther.mPromise;
aOther.mPromise = nullptr;
return *this;
}
~MozPromiseHolder() { MOZ_ASSERT(!mPromise); }
already_AddRefed<PromiseType> Ensure(const char* aMethodName) {
if (mMonitor) {
mMonitor->AssertCurrentThreadOwns();
}
if (!mPromise) {
mPromise = new (typename PromiseType::Private)(aMethodName);
}
RefPtr<PromiseType> p = mPromise.get();
return p.forget();
}
// Provide a Monitor that should always be held when accessing this instance.
void SetMonitor(Monitor* aMonitor) { mMonitor = aMonitor; }
bool IsEmpty() const
{
if (mMonitor) {
mMonitor->AssertCurrentThreadOwns();
}
return !mPromise;
}
already_AddRefed<typename PromiseType::Private> Steal()
{
if (mMonitor) {
mMonitor->AssertCurrentThreadOwns();
}
RefPtr<typename PromiseType::Private> p = mPromise;
mPromise = nullptr;
return p.forget();
}
void Resolve(typename PromiseType::ResolveValueType aResolveValue,
const char* aMethodName)
{
if (mMonitor) {
mMonitor->AssertCurrentThreadOwns();
}
MOZ_ASSERT(mPromise);
mPromise->Resolve(aResolveValue, aMethodName);
mPromise = nullptr;
}
void ResolveIfExists(typename PromiseType::ResolveValueType aResolveValue,
const char* aMethodName)
{
if (!IsEmpty()) {
Resolve(aResolveValue, aMethodName);
}
}
void Reject(typename PromiseType::RejectValueType aRejectValue,
const char* aMethodName)
{
if (mMonitor) {
mMonitor->AssertCurrentThreadOwns();
}
MOZ_ASSERT(mPromise);
mPromise->Reject(aRejectValue, aMethodName);
mPromise = nullptr;
}
void RejectIfExists(typename PromiseType::RejectValueType aRejectValue,
const char* aMethodName)
{
if (!IsEmpty()) {
Reject(aRejectValue, aMethodName);
}
}
private:
Monitor* mMonitor;
RefPtr<typename PromiseType::Private> mPromise;
};
/*
* Class to encapsulate a MozPromise::Request reference. Use this as the member
* variable for a class waiting on a MozPromise.
*/
template<typename PromiseType>
class MozPromiseRequestHolder
{
public:
MozPromiseRequestHolder() {}
~MozPromiseRequestHolder() { MOZ_ASSERT(!mRequest); }
void Begin(RefPtr<typename PromiseType::Request>&& aRequest)
{
MOZ_DIAGNOSTIC_ASSERT(!Exists());
mRequest = Move(aRequest);
}
void Begin(typename PromiseType::Request* aRequest)
{
MOZ_DIAGNOSTIC_ASSERT(!Exists());
mRequest = aRequest;
}
void Complete()
{
MOZ_DIAGNOSTIC_ASSERT(Exists());
mRequest = nullptr;
}
// Disconnects and forgets an outstanding promise. The resolve/reject methods
// will never be called.
void Disconnect() {
MOZ_ASSERT(Exists());
mRequest->Disconnect();
mRequest = nullptr;
}
void DisconnectIfExists() {
if (Exists()) {
Disconnect();
}
}
bool Exists() const { return !!mRequest; }
private:
RefPtr<typename PromiseType::Request> mRequest;
};
// Asynchronous Potentially-Cross-Thread Method Calls.
//
// This machinery allows callers to schedule a promise-returning method to be
// invoked asynchronously on a given thread, while at the same time receiving
// a promise upon which to invoke Then() immediately. InvokeAsync dispatches
// a task to invoke the method on the proper thread and also chain the resulting
// promise to the one that the caller received, so that resolve/reject values
// are forwarded through.
namespace detail {
template<typename ReturnType, typename ThisType, typename... ArgTypes, size_t... Indices>
ReturnType
MethodCallInvokeHelper(ReturnType(ThisType::*aMethod)(ArgTypes...), ThisType* aThisVal,
Tuple<ArgTypes...>& aArgs, IndexSequence<Indices...>)
{
return ((*aThisVal).*aMethod)(Get<Indices>(aArgs)...);
}
// Non-templated base class to allow us to use MOZ_COUNT_{C,D}TOR, which cause
// assertions when used on templated types.
class MethodCallBase
{
public:
MethodCallBase() { MOZ_COUNT_CTOR(MethodCallBase); }
virtual ~MethodCallBase() { MOZ_COUNT_DTOR(MethodCallBase); }
};
template<typename PromiseType, typename ThisType, typename... ArgTypes>
class MethodCall : public MethodCallBase
{
public:
typedef RefPtr<PromiseType>(ThisType::*MethodType)(ArgTypes...);
MethodCall(MethodType aMethod, ThisType* aThisVal, ArgTypes... aArgs)
: mMethod(aMethod)
, mThisVal(aThisVal)
, mArgs(Forward<ArgTypes>(aArgs)...)
{}
RefPtr<PromiseType> Invoke()
{
return MethodCallInvokeHelper(mMethod, mThisVal.get(), mArgs, typename IndexSequenceFor<ArgTypes...>::Type());
}
private:
MethodType mMethod;
RefPtr<ThisType> mThisVal;
Tuple<ArgTypes...> mArgs;
};
template<typename PromiseType, typename ThisType, typename ...ArgTypes>
class ProxyRunnable : public Runnable
{
public:
ProxyRunnable(typename PromiseType::Private* aProxyPromise, MethodCall<PromiseType, ThisType, ArgTypes...>* aMethodCall)
: mProxyPromise(aProxyPromise), mMethodCall(aMethodCall) {}
NS_IMETHOD Run() override
{
RefPtr<PromiseType> p = mMethodCall->Invoke();
mMethodCall = nullptr;
p->ChainTo(mProxyPromise.forget(), "<Proxy Promise>");
return NS_OK;
}
private:
RefPtr<typename PromiseType::Private> mProxyPromise;
nsAutoPtr<MethodCall<PromiseType, ThisType, ArgTypes...>> mMethodCall;
};
constexpr bool Any()
{
return false;
}
template <typename T1>
constexpr bool Any(T1 a)
{
return static_cast<bool>(a);
}
template <typename T1, typename... Ts>
constexpr bool Any(T1 a, Ts... aOthers)
{
return a || Any(aOthers...);
}
} // namespace detail
template<typename PromiseType, typename ThisType, typename ...ArgTypes, typename ...ActualArgTypes>
static RefPtr<PromiseType>
InvokeAsync(AbstractThread* aTarget, ThisType* aThisVal, const char* aCallerName,
RefPtr<PromiseType>(ThisType::*aMethod)(ArgTypes...), ActualArgTypes&&... aArgs)
{
static_assert(!detail::Any(IsReference<ArgTypes>::value...),
"Cannot pass reference types through InvokeAsync, see bug 1313497 if you require it");
typedef detail::MethodCall<PromiseType, ThisType, ArgTypes...> MethodCallType;
typedef detail::ProxyRunnable<PromiseType, ThisType, ArgTypes...> ProxyRunnableType;
MethodCallType* methodCall = new MethodCallType(aMethod, aThisVal, Forward<ActualArgTypes>(aArgs)...);
RefPtr<typename PromiseType::Private> p = new (typename PromiseType::Private)(aCallerName);
RefPtr<ProxyRunnableType> r = new ProxyRunnableType(p, methodCall);
MOZ_ASSERT(aTarget->IsDispatchReliable());
aTarget->Dispatch(r.forget());
return p.forget();
}
#undef PROMISE_LOG
#undef PROMISE_ASSERT
#undef PROMISE_DEBUG
} // namespace mozilla
#endif
|