• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
RequestContext()21 RequestContext::RequestContext() : RequestContext(Source::LOCAL_API_CALL) {}
22 
RequestContext(Source source)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 
~RequestContext()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
current()82 RequestContext* RequestContext::current() {
83   DCHECK(g_current_context.Pointer()->Get());
84   return g_current_context.Pointer()->Get();
85 }
86 
AddWatchNotifyFinalizer(scoped_refptr<Watch> watch,MojoResult result,const HandleSignalsState & state)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 
AddWatchCancelFinalizer(scoped_refptr<Watch> watch)95 void RequestContext::AddWatchCancelFinalizer(scoped_refptr<Watch> watch) {
96   DCHECK(IsCurrent());
97   watch_cancel_finalizers_->push_back(std::move(watch));
98 }
99 
IsCurrent() const100 bool RequestContext::IsCurrent() const {
101   return tls_context_->Get() == this;
102 }
103 
WatchNotifyFinalizer(scoped_refptr<Watch> watch,MojoResult result,const HandleSignalsState & state)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 
~WatchNotifyFinalizer()113 RequestContext::WatchNotifyFinalizer::~WatchNotifyFinalizer() {}
114 
115 }  // namespace core
116 }  // namespace mojo
117