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