Home | History | Annotate | Download | only in core
      1 // Copyright 2016 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "mojo/core/request_context.h"
      6 
      7 #include "base/lazy_instance.h"
      8 #include "base/logging.h"
      9 #include "base/threading/thread_local.h"
     10 
     11 namespace mojo {
     12 namespace core {
     13 
     14 namespace {
     15 
     16 base::LazyInstance<base::ThreadLocalPointer<RequestContext>>::Leaky
     17     g_current_context = LAZY_INSTANCE_INITIALIZER;
     18 
     19 }  // namespace
     20 
     21 RequestContext::RequestContext() : RequestContext(Source::LOCAL_API_CALL) {}
     22 
     23 RequestContext::RequestContext(Source source)
     24     : source_(source), tls_context_(g_current_context.Pointer()) {
     25   // We allow nested RequestContexts to exist as long as they aren't actually
     26   // used for anything.
     27   if (!tls_context_->Get())
     28     tls_context_->Set(this);
     29 }
     30 
     31 RequestContext::~RequestContext() {
     32   if (IsCurrent()) {
     33     // NOTE: Callbacks invoked by this destructor are allowed to initiate new
     34     // EDK requests on this thread, so we need to reset the thread-local context
     35     // pointer before calling them. We persist the original notification source
     36     // since we're starting over at the bottom of the stack.
     37     tls_context_->Set(nullptr);
     38 
     39     MojoTrapEventFlags flags = MOJO_TRAP_EVENT_FLAG_NONE;
     40     if (source_ == Source::LOCAL_API_CALL)
     41       flags |= MOJO_TRAP_EVENT_FLAG_WITHIN_API_CALL;
     42 
     43     // We send all cancellation notifications first. This is necessary because
     44     // it's possible that cancelled watches have other pending notifications
     45     // attached to this RequestContext.
     46     //
     47     // From the application's perspective the watch is cancelled as soon as this
     48     // notification is received, and dispatching the cancellation notification
     49     // updates some internal Watch state to ensure no further notifications
     50     // fire. Because notifications on a single Watch are mutually exclusive,
     51     // this is sufficient to guarantee that MOJO_RESULT_CANCELLED is the last
     52     // notification received; which is the guarantee the API makes.
     53     for (const scoped_refptr<Watch>& watch :
     54          watch_cancel_finalizers_.container()) {
     55       static const HandleSignalsState closed_state = {0, 0};
     56 
     57       // Establish a new RequestContext to capture and run any new notifications
     58       // triggered by the callback invocation. Note that while it would be safe
     59       // to inherit |source_| from the perspective of Mojo core re-entrancy,
     60       // upper application layers may use the flag as a signal to allow
     61       // synchronous event dispatch and in turn shoot themselves in the foot
     62       // with e.g. mutually recursive event handlers. We avoid that by
     63       // treating all nested trap events as if they originated from a local API
     64       // call even if this is a system RequestContext.
     65       RequestContext inner_context(Source::LOCAL_API_CALL);
     66       watch->InvokeCallback(MOJO_RESULT_CANCELLED, closed_state, flags);
     67     }
     68 
     69     for (const WatchNotifyFinalizer& watch :
     70          watch_notify_finalizers_.container()) {
     71       RequestContext inner_context(source_);
     72       watch.watch->InvokeCallback(watch.result, watch.state, flags);
     73     }
     74   } else {
     75     // It should be impossible for nested contexts to have finalizers.
     76     DCHECK(watch_notify_finalizers_.container().empty());
     77     DCHECK(watch_cancel_finalizers_.container().empty());
     78   }
     79 }
     80 
     81 // static
     82 RequestContext* RequestContext::current() {
     83   DCHECK(g_current_context.Pointer()->Get());
     84   return g_current_context.Pointer()->Get();
     85 }
     86 
     87 void RequestContext::AddWatchNotifyFinalizer(scoped_refptr<Watch> watch,
     88                                              MojoResult result,
     89                                              const HandleSignalsState& state) {
     90   DCHECK(IsCurrent());
     91   watch_notify_finalizers_->push_back(
     92       WatchNotifyFinalizer(std::move(watch), result, state));
     93 }
     94 
     95 void RequestContext::AddWatchCancelFinalizer(scoped_refptr<Watch> watch) {
     96   DCHECK(IsCurrent());
     97   watch_cancel_finalizers_->push_back(std::move(watch));
     98 }
     99 
    100 bool RequestContext::IsCurrent() const {
    101   return tls_context_->Get() == this;
    102 }
    103 
    104 RequestContext::WatchNotifyFinalizer::WatchNotifyFinalizer(
    105     scoped_refptr<Watch> watch,
    106     MojoResult result,
    107     const HandleSignalsState& state)
    108     : watch(std::move(watch)), result(result), state(state) {}
    109 
    110 RequestContext::WatchNotifyFinalizer::WatchNotifyFinalizer(
    111     const WatchNotifyFinalizer& other) = default;
    112 
    113 RequestContext::WatchNotifyFinalizer::~WatchNotifyFinalizer() {}
    114 
    115 }  // namespace core
    116 }  // namespace mojo
    117